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(×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 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