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/pprof" 26 27 "github.com/google/blueprint" 28 "github.com/google/blueprint/deptools" 29 ) 30 31 var ( 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 44 func 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(×tampFile, "timestamp", "", "file to write before the output file") 49 flag.StringVar(×tampDepFile, "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 56 func 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 173 func fatalf(format string, args ...interface{}) { 174 fmt.Printf(format, args...) 175 fmt.Print("\n") 176 os.Exit(1) 177 } 178 179 func 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