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