• 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	"flag"
20	"fmt"
21	"io"
22	"io/ioutil"
23	"os"
24	"path/filepath"
25	"runtime"
26	"runtime/debug"
27	"runtime/pprof"
28	"runtime/trace"
29
30	"github.com/google/blueprint"
31	"github.com/google/blueprint/deptools"
32)
33
34var (
35	outFile        string
36	globFile       string
37	depFile        string
38	docFile        string
39	cpuprofile     string
40	memprofile     string
41	traceFile      string
42	runGoTests     bool
43	noGC           bool
44	moduleListFile string
45	emptyNinjaFile bool
46
47	BuildDir      string
48	NinjaBuildDir string
49	SrcDir        string
50)
51
52func init() {
53	flag.StringVar(&outFile, "o", "build.ninja", "the Ninja file to output")
54	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
55	flag.StringVar(&BuildDir, "b", ".", "the build output directory")
56	flag.StringVar(&NinjaBuildDir, "n", "", "the ninja builddir directory")
57	flag.StringVar(&depFile, "d", "", "the dependency file to output")
58	flag.StringVar(&docFile, "docs", "", "build documentation file to output")
59	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
60	flag.StringVar(&traceFile, "trace", "", "write trace to file")
61	flag.StringVar(&memprofile, "memprofile", "", "write memory profile to file")
62	flag.BoolVar(&noGC, "nogc", false, "turn off GC for debugging")
63	flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
64	flag.StringVar(&moduleListFile, "l", "", "file that lists filepaths to parse")
65	flag.BoolVar(&emptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
66}
67
68func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...string) {
69	if !flag.Parsed() {
70		flag.Parse()
71	}
72
73	runtime.GOMAXPROCS(runtime.NumCPU())
74
75	if noGC {
76		debug.SetGCPercent(-1)
77	}
78
79	if cpuprofile != "" {
80		f, err := os.Create(cpuprofile)
81		if err != nil {
82			fatalf("error opening cpuprofile: %s", err)
83		}
84		pprof.StartCPUProfile(f)
85		defer f.Close()
86		defer pprof.StopCPUProfile()
87	}
88
89	if traceFile != "" {
90		f, err := os.Create(traceFile)
91		if err != nil {
92			fatalf("error opening trace: %s", err)
93		}
94		trace.Start(f)
95		defer f.Close()
96		defer trace.Stop()
97	}
98
99	if flag.NArg() != 1 {
100		fatalf("no Blueprints file specified")
101	}
102
103	SrcDir = filepath.Dir(flag.Arg(0))
104	if moduleListFile != "" {
105		ctx.SetModuleListFile(moduleListFile)
106		extraNinjaFileDeps = append(extraNinjaFileDeps, moduleListFile)
107	} else {
108		fatalf("-l <moduleListFile> is required and must be nonempty")
109	}
110	filesToParse, err := ctx.ListModulePaths(SrcDir)
111	if err != nil {
112		fatalf("could not enumerate files: %v\n", err.Error())
113	}
114
115	if NinjaBuildDir == "" {
116		NinjaBuildDir = BuildDir
117	}
118
119	stage := StageMain
120	if c, ok := config.(ConfigInterface); ok {
121		if c.GeneratingPrimaryBuilder() {
122			stage = StagePrimary
123		}
124	}
125
126	bootstrapConfig := &Config{
127		stage: stage,
128
129		topLevelBlueprintsFile: flag.Arg(0),
130		emptyNinjaFile:         emptyNinjaFile,
131		runGoTests:             runGoTests,
132		moduleListFile:         moduleListFile,
133	}
134
135	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
136	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))
137	ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, false))
138	ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory(bootstrapConfig, true))
139	ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))
140
141	ctx.RegisterSingletonType("glob", globSingletonFactory(ctx))
142
143	deps, errs := ctx.ParseFileList(filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), filesToParse)
144	if len(errs) > 0 {
145		fatalErrors(errs)
146	}
147
148	// Add extra ninja file dependencies
149	deps = append(deps, extraNinjaFileDeps...)
150
151	extraDeps, errs := ctx.ResolveDependencies(config)
152	if len(errs) > 0 {
153		fatalErrors(errs)
154	}
155	deps = append(deps, extraDeps...)
156
157	if docFile != "" {
158		err := writeDocs(ctx, docFile)
159		if err != nil {
160			fatalErrors([]error{err})
161		}
162		return
163	}
164
165	if c, ok := config.(ConfigStopBefore); ok {
166		if c.StopBefore() == StopBeforePrepareBuildActions {
167			return
168		}
169	}
170
171	extraDeps, errs = ctx.PrepareBuildActions(config)
172	if len(errs) > 0 {
173		fatalErrors(errs)
174	}
175	deps = append(deps, extraDeps...)
176
177	const outFilePermissions = 0666
178	var out io.Writer
179	var f *os.File
180	var buf *bufio.Writer
181
182	if stage != StageMain || !emptyNinjaFile {
183		f, err = os.OpenFile(outFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
184		if err != nil {
185			fatalf("error opening Ninja file: %s", err)
186		}
187		buf = bufio.NewWriter(f)
188		out = buf
189	} else {
190		out = ioutil.Discard
191	}
192
193	err = ctx.WriteBuildFile(out)
194	if err != nil {
195		fatalf("error writing Ninja file contents: %s", err)
196	}
197
198	if buf != nil {
199		err = buf.Flush()
200		if err != nil {
201			fatalf("error flushing Ninja file contents: %s", err)
202		}
203	}
204
205	if f != nil {
206		err = f.Close()
207		if err != nil {
208			fatalf("error closing Ninja file: %s", err)
209		}
210	}
211
212	if globFile != "" {
213		buffer, errs := generateGlobNinjaFile(ctx.Globs)
214		if len(errs) > 0 {
215			fatalErrors(errs)
216		}
217
218		err = ioutil.WriteFile(globFile, buffer, outFilePermissions)
219		if err != nil {
220			fatalf("error writing %s: %s", outFile, err)
221		}
222	}
223
224	if depFile != "" {
225		err := deptools.WriteDepFile(depFile, outFile, deps)
226		if err != nil {
227			fatalf("error writing depfile: %s", err)
228		}
229	}
230
231	if c, ok := config.(ConfigRemoveAbandonedFilesUnder); ok {
232		under, except := c.RemoveAbandonedFilesUnder()
233		err := removeAbandonedFilesUnder(ctx, bootstrapConfig, SrcDir, under, except)
234		if err != nil {
235			fatalf("error removing abandoned files: %s", err)
236		}
237	}
238
239	if memprofile != "" {
240		f, err := os.Create(memprofile)
241		if err != nil {
242			fatalf("error opening memprofile: %s", err)
243		}
244		defer f.Close()
245		pprof.WriteHeapProfile(f)
246	}
247}
248
249func fatalf(format string, args ...interface{}) {
250	fmt.Printf(format, args...)
251	fmt.Print("\n")
252	os.Exit(1)
253}
254
255func fatalErrors(errs []error) {
256	red := "\x1b[31m"
257	unred := "\x1b[0m"
258
259	for _, err := range errs {
260		switch err := err.(type) {
261		case *blueprint.BlueprintError,
262			*blueprint.ModuleError,
263			*blueprint.PropertyError:
264			fmt.Printf("%serror:%s %s\n", red, unred, err.Error())
265		default:
266			fmt.Printf("%sinternal error:%s %s\n", red, unred, err)
267		}
268	}
269	os.Exit(1)
270}
271