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	"bytes"
19	"flag"
20	"fmt"
21	"io/ioutil"
22	"os"
23	"path/filepath"
24	"runtime"
25	"runtime/pprof"
26
27	"github.com/google/blueprint"
28	"github.com/google/blueprint/deptools"
29)
30
31var (
32	outFile          string
33	depFile          string
34	timestampFile    string
35	timestampDepFile string
36	manifestFile     string
37	docFile          string
38	cpuprofile       string
39	runGoTests       bool
40
41	BuildDir string
42)
43
44func init() {
45	flag.StringVar(&outFile, "o", "build.ninja.in", "the Ninja file to output")
46	flag.StringVar(&BuildDir, "b", ".", "the build output directory")
47	flag.StringVar(&depFile, "d", "", "the dependency file to output")
48	flag.StringVar(&timestampFile, "timestamp", "", "file to write before the output file")
49	flag.StringVar(&timestampDepFile, "timestampdep", "", "the dependency file for the timestamp file")
50	flag.StringVar(&manifestFile, "m", "", "the bootstrap manifest file")
51	flag.StringVar(&docFile, "docs", "", "build documentation file to output")
52	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
53	flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
54}
55
56func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...string) {
57	if !flag.Parsed() {
58		flag.Parse()
59	}
60
61	runtime.GOMAXPROCS(runtime.NumCPU())
62
63	if cpuprofile != "" {
64		f, err := os.Create(cpuprofile)
65		if err != nil {
66			fatalf("error opening cpuprofile: %s", err)
67		}
68		pprof.StartCPUProfile(f)
69		defer f.Close()
70		defer pprof.StopCPUProfile()
71	}
72
73	if flag.NArg() != 1 {
74		fatalf("no Blueprints file specified")
75	}
76
77	stage := StageMain
78	if c, ok := config.(ConfigInterface); ok {
79		if c.GeneratingBootstrapper() {
80			stage = StageBootstrap
81		}
82		if c.GeneratingPrimaryBuilder() {
83			stage = StagePrimary
84		}
85	}
86
87	bootstrapConfig := &Config{
88		stage: stage,
89		topLevelBlueprintsFile: flag.Arg(0),
90		runGoTests:             runGoTests,
91	}
92
93	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
94	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))
95	ctx.RegisterModuleType("bootstrap_core_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StageBootstrap))
96	ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StagePrimary))
97	ctx.RegisterTopDownMutator("bootstrap_stage", propagateStageBootstrap)
98	ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))
99
100	deps, errs := ctx.ParseBlueprintsFiles(bootstrapConfig.topLevelBlueprintsFile)
101	if len(errs) > 0 {
102		fatalErrors(errs)
103	}
104
105	// Add extra ninja file dependencies
106	deps = append(deps, extraNinjaFileDeps...)
107
108	errs = ctx.ResolveDependencies(config)
109	if len(errs) > 0 {
110		fatalErrors(errs)
111	}
112
113	if docFile != "" {
114		err := writeDocs(ctx, filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), docFile)
115		if err != nil {
116			fatalErrors([]error{err})
117		}
118		return
119	}
120
121	extraDeps, errs := ctx.PrepareBuildActions(config)
122	if len(errs) > 0 {
123		fatalErrors(errs)
124	}
125	deps = append(deps, extraDeps...)
126
127	buf := bytes.NewBuffer(nil)
128	err := ctx.WriteBuildFile(buf)
129	if err != nil {
130		fatalf("error generating Ninja file contents: %s", err)
131	}
132
133	const outFilePermissions = 0666
134	if timestampFile != "" {
135		err := ioutil.WriteFile(timestampFile, []byte{}, outFilePermissions)
136		if err != nil {
137			fatalf("error writing %s: %s", timestampFile, err)
138		}
139
140		if timestampDepFile != "" {
141			err := deptools.WriteDepFile(timestampDepFile, timestampFile, deps)
142			if err != nil {
143				fatalf("error writing depfile: %s", err)
144			}
145		}
146	}
147
148	err = ioutil.WriteFile(outFile, buf.Bytes(), outFilePermissions)
149	if err != nil {
150		fatalf("error writing %s: %s", outFile, err)
151	}
152
153	if depFile != "" {
154		err := deptools.WriteDepFile(depFile, outFile, deps)
155		if err != nil {
156			fatalf("error writing depfile: %s", err)
157		}
158		err = deptools.WriteDepFile(depFile+".timestamp", outFile+".timestamp", deps)
159		if err != nil {
160			fatalf("error writing depfile: %s", err)
161		}
162	}
163
164	if c, ok := config.(ConfigRemoveAbandonedFiles); !ok || c.RemoveAbandonedFiles() {
165		srcDir := filepath.Dir(bootstrapConfig.topLevelBlueprintsFile)
166		err := removeAbandonedFiles(ctx, bootstrapConfig, srcDir, manifestFile)
167		if err != nil {
168			fatalf("error removing abandoned files: %s", err)
169		}
170	}
171}
172
173func fatalf(format string, args ...interface{}) {
174	fmt.Printf(format, args...)
175	fmt.Print("\n")
176	os.Exit(1)
177}
178
179func fatalErrors(errs []error) {
180	red := "\x1b[31m"
181	unred := "\x1b[0m"
182
183	for _, err := range errs {
184		switch err := err.(type) {
185		case *blueprint.Error:
186			fmt.Printf("%serror:%s %s\n", red, unred, err.Error())
187		default:
188			fmt.Printf("%sinternal error:%s %s\n", red, unred, err)
189		}
190	}
191	os.Exit(1)
192}
193