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