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 "errors" 20 "fmt" 21 "io" 22 "os" 23 "runtime" 24 "runtime/debug" 25 "runtime/pprof" 26 "runtime/trace" 27 "strings" 28 29 "github.com/google/blueprint" 30 "github.com/google/blueprint/proptools" 31) 32 33type Args struct { 34 ModuleListFile string 35 OutFile string 36 37 EmptyNinjaFile bool 38 39 NoGC bool 40 Cpuprofile string 41 Memprofile string 42 TraceFile string 43 44 // Debug data json file 45 ModuleDebugFile string 46} 47 48// RegisterGoModuleTypes adds module types to build tools written in golang 49func RegisterGoModuleTypes(ctx *blueprint.Context) { 50 ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory()) 51 ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory()) 52} 53 54// RunBlueprint emits `args.OutFile` (a Ninja file) and returns the list of 55// its dependencies. These can be written to a `${args.OutFile}.d` file 56// so that it is correctly rebuilt when needed in case Blueprint is itself 57// invoked from Ninja 58func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, config interface{}) ([]string, error) { 59 runtime.GOMAXPROCS(runtime.NumCPU()) 60 61 if args.NoGC { 62 debug.SetGCPercent(-1) 63 } 64 65 if args.Cpuprofile != "" { 66 f, err := os.Create(blueprint.JoinPath(ctx.SrcDir(), args.Cpuprofile)) 67 if err != nil { 68 return nil, fmt.Errorf("error opening cpuprofile: %s", err) 69 } 70 pprof.StartCPUProfile(f) 71 defer f.Close() 72 defer pprof.StopCPUProfile() 73 } 74 75 if args.TraceFile != "" { 76 f, err := os.Create(blueprint.JoinPath(ctx.SrcDir(), args.TraceFile)) 77 if err != nil { 78 return nil, fmt.Errorf("error opening trace: %s", err) 79 } 80 trace.Start(f) 81 defer f.Close() 82 defer trace.Stop() 83 } 84 85 if args.ModuleListFile == "" { 86 return nil, fmt.Errorf("-l <moduleListFile> is required and must be nonempty") 87 } 88 ctx.SetModuleListFile(args.ModuleListFile) 89 90 var ninjaDeps []string 91 ninjaDeps = append(ninjaDeps, args.ModuleListFile) 92 93 ctx.BeginEvent("list_modules") 94 var filesToParse []string 95 if f, err := ctx.ListModulePaths("."); err != nil { 96 return nil, fmt.Errorf("could not enumerate files: %v\n", err.Error()) 97 } else { 98 filesToParse = f 99 } 100 ctx.EndEvent("list_modules") 101 102 ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps) 103 ctx.RegisterSingletonType("bootstrap", newSingletonFactory(), false) 104 RegisterGoModuleTypes(ctx) 105 106 ctx.BeginEvent("parse_bp") 107 if blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config); len(errs) > 0 { 108 return nil, fatalErrors(errs) 109 } else { 110 ctx.EndEvent("parse_bp") 111 ninjaDeps = append(ninjaDeps, blueprintFiles...) 112 } 113 114 if resolvedDeps, errs := ctx.ResolveDependencies(config); len(errs) > 0 { 115 return nil, fatalErrors(errs) 116 } else { 117 ninjaDeps = append(ninjaDeps, resolvedDeps...) 118 } 119 120 if stopBefore == StopBeforePrepareBuildActions { 121 return ninjaDeps, nil 122 } 123 124 if ctx.BeforePrepareBuildActionsHook != nil { 125 if err := ctx.BeforePrepareBuildActionsHook(); err != nil { 126 return nil, fatalErrors([]error{err}) 127 } 128 } 129 130 if buildActionsDeps, errs := ctx.PrepareBuildActions(config); len(errs) > 0 { 131 return nil, fatalErrors(errs) 132 } else { 133 ninjaDeps = append(ninjaDeps, buildActionsDeps...) 134 } 135 136 if args.ModuleDebugFile != "" { 137 ctx.GenerateModuleDebugInfo(args.ModuleDebugFile) 138 } 139 140 if stopBefore == StopBeforeWriteNinja { 141 return ninjaDeps, nil 142 } 143 144 providersValidationChan := make(chan []error, 1) 145 if ctx.GetVerifyProvidersAreUnchanged() { 146 go func() { 147 providersValidationChan <- ctx.VerifyProvidersWereUnchanged() 148 }() 149 } else { 150 providersValidationChan <- nil 151 } 152 153 var out blueprint.StringWriterWriter 154 var f *os.File 155 var buf *bufio.Writer 156 157 ctx.BeginEvent("write_files") 158 defer ctx.EndEvent("write_files") 159 if args.EmptyNinjaFile { 160 if err := os.WriteFile(blueprint.JoinPath(ctx.SrcDir(), args.OutFile), []byte(nil), blueprint.OutFilePermissions); err != nil { 161 return nil, fmt.Errorf("error writing empty Ninja file: %s", err) 162 } 163 out = io.Discard.(blueprint.StringWriterWriter) 164 } else { 165 f, err := os.OpenFile(blueprint.JoinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, blueprint.OutFilePermissions) 166 if err != nil { 167 return nil, fmt.Errorf("error opening Ninja file: %s", err) 168 } 169 defer f.Close() 170 buf = bufio.NewWriterSize(f, 16*1024*1024) 171 out = buf 172 } 173 174 if err := ctx.WriteBuildFile(out, !strings.Contains(args.OutFile, "bootstrap.ninja") && !args.EmptyNinjaFile, args.OutFile); err != nil { 175 return nil, fmt.Errorf("error writing Ninja file contents: %s", err) 176 } 177 178 if buf != nil { 179 if err := buf.Flush(); err != nil { 180 return nil, fmt.Errorf("error flushing Ninja file contents: %s", err) 181 } 182 } 183 184 if f != nil { 185 if err := f.Close(); err != nil { 186 return nil, fmt.Errorf("error closing Ninja file: %s", err) 187 } 188 } 189 190 providerValidationErrors := <-providersValidationChan 191 if providerValidationErrors != nil { 192 return nil, proptools.MergeErrors(providerValidationErrors) 193 } 194 195 if args.Memprofile != "" { 196 f, err := os.Create(blueprint.JoinPath(ctx.SrcDir(), args.Memprofile)) 197 if err != nil { 198 return nil, fmt.Errorf("error opening memprofile: %s", err) 199 } 200 defer f.Close() 201 pprof.WriteHeapProfile(f) 202 } 203 204 return ninjaDeps, nil 205} 206 207func fatalErrors(errs []error) error { 208 red := "\x1b[31m" 209 unred := "\x1b[0m" 210 211 for _, err := range errs { 212 switch err := err.(type) { 213 case *blueprint.BlueprintError, 214 *blueprint.ModuleError, 215 *blueprint.PropertyError: 216 fmt.Printf("%serror:%s %s\n", red, unred, err.Error()) 217 default: 218 fmt.Printf("%sinternal error:%s %s\n", red, unred, err) 219 } 220 } 221 222 return errors.New("fatal errors encountered") 223} 224