• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package bootstrap
16
17import (
18	"bufio"
19	"fmt"
20	"io"
21	"os"
22	"path/filepath"
23	"runtime"
24	"runtime/debug"
25	"runtime/pprof"
26	"runtime/trace"
27
28	"github.com/google/blueprint"
29)
30
31type Args struct {
32	ModuleListFile string
33	OutFile        string
34
35	EmptyNinjaFile bool
36
37	NoGC       bool
38	Cpuprofile string
39	Memprofile string
40	TraceFile  string
41}
42
43// RunBlueprint emits `args.OutFile` (a Ninja file) and returns the list of
44// its dependencies. These can be written to a `${args.OutFile}.d` file
45// so that it is correctly rebuilt when needed in case Blueprint is itself
46// invoked from Ninja
47func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, config interface{}) []string {
48	runtime.GOMAXPROCS(runtime.NumCPU())
49
50	if args.NoGC {
51		debug.SetGCPercent(-1)
52	}
53
54	if args.Cpuprofile != "" {
55		f, err := os.Create(joinPath(ctx.SrcDir(), args.Cpuprofile))
56		if err != nil {
57			fatalf("error opening cpuprofile: %s", err)
58		}
59		pprof.StartCPUProfile(f)
60		defer f.Close()
61		defer pprof.StopCPUProfile()
62	}
63
64	if args.TraceFile != "" {
65		f, err := os.Create(joinPath(ctx.SrcDir(), args.TraceFile))
66		if err != nil {
67			fatalf("error opening trace: %s", err)
68		}
69		trace.Start(f)
70		defer f.Close()
71		defer trace.Stop()
72	}
73
74	if args.ModuleListFile == "" {
75		fatalf("-l <moduleListFile> is required and must be nonempty")
76	}
77	ctx.SetModuleListFile(args.ModuleListFile)
78
79	var ninjaDeps []string
80	ninjaDeps = append(ninjaDeps, args.ModuleListFile)
81
82	ctx.BeginEvent("list_modules")
83	var filesToParse []string
84	if f, err := ctx.ListModulePaths("."); err != nil {
85		fatalf("could not enumerate files: %v\n", err.Error())
86	} else {
87		filesToParse = f
88	}
89	ctx.EndEvent("list_modules")
90
91	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
92	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
93	ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
94	ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
95	blueprint.RegisterPackageIncludesModuleType(ctx)
96
97	ctx.BeginEvent("parse_bp")
98	if blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config); len(errs) > 0 {
99		fatalErrors(errs)
100	} else {
101		ctx.EndEvent("parse_bp")
102		ninjaDeps = append(ninjaDeps, blueprintFiles...)
103	}
104
105	if resolvedDeps, errs := ctx.ResolveDependencies(config); len(errs) > 0 {
106		fatalErrors(errs)
107	} else {
108		ninjaDeps = append(ninjaDeps, resolvedDeps...)
109	}
110
111	if stopBefore == StopBeforePrepareBuildActions {
112		return ninjaDeps
113	}
114
115	if ctx.BeforePrepareBuildActionsHook != nil {
116		if err := ctx.BeforePrepareBuildActionsHook(); err != nil {
117			fatalErrors([]error{err})
118		}
119	}
120
121	if buildActionsDeps, errs := ctx.PrepareBuildActions(config); len(errs) > 0 {
122		fatalErrors(errs)
123	} else {
124		ninjaDeps = append(ninjaDeps, buildActionsDeps...)
125	}
126
127	if stopBefore == StopBeforeWriteNinja {
128		return ninjaDeps
129	}
130
131	const outFilePermissions = 0666
132	var out io.StringWriter
133	var f *os.File
134	var buf *bufio.Writer
135
136	ctx.BeginEvent("write_files")
137	defer ctx.EndEvent("write_files")
138	if args.EmptyNinjaFile {
139		if err := os.WriteFile(joinPath(ctx.SrcDir(), args.OutFile), []byte(nil), outFilePermissions); err != nil {
140			fatalf("error writing empty Ninja file: %s", err)
141		}
142		out = io.Discard.(io.StringWriter)
143	} else {
144		f, err := os.OpenFile(joinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
145		if err != nil {
146			fatalf("error opening Ninja file: %s", err)
147		}
148		defer f.Close()
149		buf = bufio.NewWriterSize(f, 16*1024*1024)
150		out = buf
151	}
152
153	if err := ctx.WriteBuildFile(out); err != nil {
154		fatalf("error writing Ninja file contents: %s", err)
155	}
156
157	if buf != nil {
158		if err := buf.Flush(); err != nil {
159			fatalf("error flushing Ninja file contents: %s", err)
160		}
161	}
162
163	if f != nil {
164		if err := f.Close(); err != nil {
165			fatalf("error closing Ninja file: %s", err)
166		}
167	}
168
169	if args.Memprofile != "" {
170		f, err := os.Create(joinPath(ctx.SrcDir(), args.Memprofile))
171		if err != nil {
172			fatalf("error opening memprofile: %s", err)
173		}
174		defer f.Close()
175		pprof.WriteHeapProfile(f)
176	}
177
178	return ninjaDeps
179}
180
181func fatalf(format string, args ...interface{}) {
182	fmt.Printf(format, args...)
183	fmt.Print("\n")
184	os.Exit(1)
185}
186
187func fatalErrors(errs []error) {
188	red := "\x1b[31m"
189	unred := "\x1b[0m"
190
191	for _, err := range errs {
192		switch err := err.(type) {
193		case *blueprint.BlueprintError,
194			*blueprint.ModuleError,
195			*blueprint.PropertyError:
196			fmt.Printf("%serror:%s %s\n", red, unred, err.Error())
197		default:
198			fmt.Printf("%sinternal error:%s %s\n", red, unred, err)
199		}
200	}
201	os.Exit(1)
202}
203
204func joinPath(base, path string) string {
205	if filepath.IsAbs(path) {
206		return path
207	}
208	return filepath.Join(base, path)
209}
210