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