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