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