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 "io/ioutil" 22 "os" 23 "path/filepath" 24 "runtime" 25 "runtime/debug" 26 "runtime/pprof" 27 "runtime/trace" 28 29 "github.com/google/blueprint" 30) 31 32type Args struct { 33 ModuleListFile string 34 OutFile string 35 36 EmptyNinjaFile bool 37 38 NoGC bool 39 Cpuprofile string 40 Memprofile string 41 TraceFile string 42} 43 44// Returns the list of dependencies the emitted Ninja files has. These can be 45// written to the .d file for the output so that it is correctly rebuilt when 46// needed in case Blueprint is itself 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 srcDir := "." 75 76 ninjaDeps := make([]string, 0) 77 78 if args.ModuleListFile != "" { 79 ctx.SetModuleListFile(args.ModuleListFile) 80 ninjaDeps = append(ninjaDeps, args.ModuleListFile) 81 } else { 82 fatalf("-l <moduleListFile> is required and must be nonempty") 83 } 84 ctx.BeginEvent("list_modules") 85 filesToParse, err := ctx.ListModulePaths(srcDir) 86 ctx.EndEvent("list_modules") 87 if err != nil { 88 fatalf("could not enumerate files: %v\n", err.Error()) 89 } 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 blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config) 99 if len(errs) > 0 { 100 fatalErrors(errs) 101 } 102 ctx.EndEvent("parse_bp") 103 104 // Add extra ninja file dependencies 105 ninjaDeps = append(ninjaDeps, blueprintFiles...) 106 107 extraDeps, errs := ctx.ResolveDependencies(config) 108 if len(errs) > 0 { 109 fatalErrors(errs) 110 } 111 ninjaDeps = append(ninjaDeps, extraDeps...) 112 113 if stopBefore == StopBeforePrepareBuildActions { 114 return ninjaDeps 115 } 116 117 extraDeps, errs = ctx.PrepareBuildActions(config) 118 if len(errs) > 0 { 119 fatalErrors(errs) 120 } 121 ninjaDeps = append(ninjaDeps, extraDeps...) 122 123 if stopBefore == StopBeforeWriteNinja { 124 return ninjaDeps 125 } 126 127 const outFilePermissions = 0666 128 var out io.StringWriter 129 var f *os.File 130 var buf *bufio.Writer 131 132 ctx.BeginEvent("write_files") 133 defer ctx.EndEvent("write_files") 134 if args.EmptyNinjaFile { 135 if err := ioutil.WriteFile(joinPath(ctx.SrcDir(), args.OutFile), []byte(nil), outFilePermissions); err != nil { 136 fatalf("error writing empty Ninja file: %s", err) 137 } 138 } 139 140 if !args.EmptyNinjaFile { 141 f, err = os.OpenFile(joinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions) 142 if err != nil { 143 fatalf("error opening Ninja file: %s", err) 144 } 145 buf = bufio.NewWriterSize(f, 16*1024*1024) 146 out = buf 147 } else { 148 out = ioutil.Discard.(io.StringWriter) 149 } 150 151 err = ctx.WriteBuildFile(out) 152 if err != nil { 153 fatalf("error writing Ninja file contents: %s", err) 154 } 155 156 if buf != nil { 157 err = buf.Flush() 158 if err != nil { 159 fatalf("error flushing Ninja file contents: %s", err) 160 } 161 } 162 163 if f != nil { 164 err = f.Close() 165 if err != nil { 166 fatalf("error closing Ninja file: %s", err) 167 } 168 } 169 170 if args.Memprofile != "" { 171 f, err := os.Create(joinPath(ctx.SrcDir(), args.Memprofile)) 172 if err != nil { 173 fatalf("error opening memprofile: %s", err) 174 } 175 defer f.Close() 176 pprof.WriteHeapProfile(f) 177 } 178 179 return ninjaDeps 180} 181 182func fatalf(format string, args ...interface{}) { 183 fmt.Printf(format, args...) 184 fmt.Print("\n") 185 os.Exit(1) 186} 187 188func fatalErrors(errs []error) { 189 red := "\x1b[31m" 190 unred := "\x1b[0m" 191 192 for _, err := range errs { 193 switch err := err.(type) { 194 case *blueprint.BlueprintError, 195 *blueprint.ModuleError, 196 *blueprint.PropertyError: 197 fmt.Printf("%serror:%s %s\n", red, unred, err.Error()) 198 default: 199 fmt.Printf("%sinternal error:%s %s\n", red, unred, err) 200 } 201 } 202 os.Exit(1) 203} 204 205func joinPath(base, path string) string { 206 if filepath.IsAbs(path) { 207 return path 208 } 209 return filepath.Join(base, path) 210} 211