1// Copyright 2018 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 android 16 17import ( 18 "fmt" 19 "sort" 20 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24) 25 26// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build 27// graph. 28type RuleBuilder struct { 29 commands []*RuleBuilderCommand 30 installs RuleBuilderInstalls 31 temporariesSet map[WritablePath]bool 32 restat bool 33 missingDeps []string 34} 35 36// NewRuleBuilder returns a newly created RuleBuilder. 37func NewRuleBuilder() *RuleBuilder { 38 return &RuleBuilder{ 39 temporariesSet: make(map[WritablePath]bool), 40 } 41} 42 43// RuleBuilderInstall is a tuple of install from and to locations. 44type RuleBuilderInstall struct { 45 From Path 46 To string 47} 48 49type RuleBuilderInstalls []RuleBuilderInstall 50 51// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated 52// list of from:to tuples. 53func (installs RuleBuilderInstalls) String() string { 54 sb := strings.Builder{} 55 for i, install := range installs { 56 if i != 0 { 57 sb.WriteRune(' ') 58 } 59 sb.WriteString(install.From.String()) 60 sb.WriteRune(':') 61 sb.WriteString(install.To) 62 } 63 return sb.String() 64} 65 66// MissingDeps adds modules to the list of missing dependencies. If MissingDeps 67// is called with a non-empty input, any call to Build will result in a rule 68// that will print an error listing the missing dependencies and fail. 69// MissingDeps should only be called if Config.AllowMissingDependencies() is 70// true. 71func (r *RuleBuilder) MissingDeps(missingDeps []string) { 72 r.missingDeps = append(r.missingDeps, missingDeps...) 73} 74 75// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat. 76func (r *RuleBuilder) Restat() *RuleBuilder { 77 r.restat = true 78 return r 79} 80 81// Install associates an output of the rule with an install location, which can be retrieved later using 82// RuleBuilder.Installs. 83func (r *RuleBuilder) Install(from Path, to string) { 84 r.installs = append(r.installs, RuleBuilderInstall{from, to}) 85} 86 87// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were 88// created by this method. That can be mutated through their methods in any order, as long as the mutations do not 89// race with any call to Build. 90func (r *RuleBuilder) Command() *RuleBuilderCommand { 91 command := &RuleBuilderCommand{} 92 r.commands = append(r.commands, command) 93 return command 94} 95 96// Temporary marks an output of a command as an intermediate file that will be used as an input to another command 97// in the same rule, and should not be listed in Outputs. 98func (r *RuleBuilder) Temporary(path WritablePath) { 99 r.temporariesSet[path] = true 100} 101 102// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary 103// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary. 104func (r *RuleBuilder) DeleteTemporaryFiles() { 105 var temporariesList WritablePaths 106 107 for intermediate := range r.temporariesSet { 108 temporariesList = append(temporariesList, intermediate) 109 } 110 111 sort.Slice(temporariesList, func(i, j int) bool { 112 return temporariesList[i].String() < temporariesList[j].String() 113 }) 114 115 r.Command().Text("rm").Flag("-f").Outputs(temporariesList) 116} 117 118// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such 119// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command 120// that are also outputs of another command in the same RuleBuilder are filtered out. 121func (r *RuleBuilder) Inputs() Paths { 122 outputs := r.outputSet() 123 124 inputs := make(map[string]Path) 125 for _, c := range r.commands { 126 for _, input := range c.inputs { 127 if _, isOutput := outputs[input.String()]; !isOutput { 128 inputs[input.String()] = input 129 } 130 } 131 } 132 133 var inputList Paths 134 for _, input := range inputs { 135 inputList = append(inputList, input) 136 } 137 138 sort.Slice(inputList, func(i, j int) bool { 139 return inputList[i].String() < inputList[j].String() 140 }) 141 142 return inputList 143} 144 145func (r *RuleBuilder) outputSet() map[string]WritablePath { 146 outputs := make(map[string]WritablePath) 147 for _, c := range r.commands { 148 for _, output := range c.outputs { 149 outputs[output.String()] = output 150 } 151 } 152 return outputs 153} 154 155// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such 156// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput. 157func (r *RuleBuilder) Outputs() WritablePaths { 158 outputs := r.outputSet() 159 160 var outputList WritablePaths 161 for _, output := range outputs { 162 if !r.temporariesSet[output] { 163 outputList = append(outputList, output) 164 } 165 } 166 167 sort.Slice(outputList, func(i, j int) bool { 168 return outputList[i].String() < outputList[j].String() 169 }) 170 171 return outputList 172} 173 174// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such 175// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile. 176func (r *RuleBuilder) DepFiles() WritablePaths { 177 var depFiles WritablePaths 178 179 for _, c := range r.commands { 180 for _, depFile := range c.depFiles { 181 depFiles = append(depFiles, depFile) 182 } 183 } 184 185 return depFiles 186} 187 188// Installs returns the list of tuples passed to Install. 189func (r *RuleBuilder) Installs() RuleBuilderInstalls { 190 return append(RuleBuilderInstalls(nil), r.installs...) 191} 192 193func (r *RuleBuilder) toolsSet() map[string]Path { 194 tools := make(map[string]Path) 195 for _, c := range r.commands { 196 for _, tool := range c.tools { 197 tools[tool.String()] = tool 198 } 199 } 200 201 return tools 202} 203 204// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. 205func (r *RuleBuilder) Tools() Paths { 206 toolsSet := r.toolsSet() 207 208 var toolsList Paths 209 for _, tool := range toolsSet { 210 toolsList = append(toolsList, tool) 211 } 212 213 sort.Slice(toolsList, func(i, j int) bool { 214 return toolsList[i].String() < toolsList[j].String() 215 }) 216 217 return toolsList 218} 219 220// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command. 221func (r *RuleBuilder) Commands() []string { 222 var commands []string 223 for _, c := range r.commands { 224 commands = append(commands, string(c.buf)) 225 } 226 return commands 227} 228 229// BuilderContext is a subset of ModuleContext and SingletonContext. 230type BuilderContext interface { 231 PathContext 232 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule 233 Build(PackageContext, BuildParams) 234} 235 236var _ BuilderContext = ModuleContext(nil) 237var _ BuilderContext = SingletonContext(nil) 238 239func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand { 240 return (&RuleBuilderCommand{}). 241 Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")). 242 Flags(depFiles.Strings()) 243} 244 245// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for 246// Outputs. 247func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) { 248 name = ninjaNameEscape(name) 249 250 if len(r.missingDeps) > 0 { 251 ctx.Build(pctx, BuildParams{ 252 Rule: ErrorRule, 253 Outputs: r.Outputs(), 254 Description: desc, 255 Args: map[string]string{ 256 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "), 257 }, 258 }) 259 return 260 } 261 262 tools := r.Tools() 263 commands := r.Commands() 264 265 var depFile WritablePath 266 var depFormat blueprint.Deps 267 if depFiles := r.DepFiles(); len(depFiles) > 0 { 268 depFile = depFiles[0] 269 depFormat = blueprint.DepsGCC 270 if len(depFiles) > 1 { 271 // Add a command locally that merges all depfiles together into the first depfile. 272 cmd := r.depFileMergerCmd(ctx, depFiles) 273 commands = append(commands, string(cmd.buf)) 274 tools = append(tools, cmd.tools...) 275 } 276 } 277 278 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to 279 // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs 280 // doesn't matter. 281 var output WritablePath 282 var implicitOutputs WritablePaths 283 if outputs := r.Outputs(); len(outputs) > 0 { 284 output = outputs[0] 285 implicitOutputs = outputs[1:] 286 } 287 288 if len(commands) > 0 { 289 ctx.Build(pctx, BuildParams{ 290 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{ 291 Command: strings.Join(proptools.NinjaEscapeList(commands), " && "), 292 CommandDeps: tools.Strings(), 293 Restat: r.restat, 294 }), 295 Implicits: r.Inputs(), 296 Output: output, 297 ImplicitOutputs: implicitOutputs, 298 Depfile: depFile, 299 Deps: depFormat, 300 Description: desc, 301 }) 302 } 303} 304 305// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the 306// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the 307// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single 308// space as a separator from the previous method. 309type RuleBuilderCommand struct { 310 buf []byte 311 inputs Paths 312 outputs WritablePaths 313 depFiles WritablePaths 314 tools Paths 315} 316 317// Text adds the specified raw text to the command line. The text should not contain input or output paths or the 318// rule will not have them listed in its dependencies or outputs. 319func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand { 320 if len(c.buf) > 0 { 321 c.buf = append(c.buf, ' ') 322 } 323 c.buf = append(c.buf, text...) 324 return c 325} 326 327// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or 328// the rule will not have them listed in its dependencies or outputs. 329func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand { 330 return c.Text(fmt.Sprintf(format, a...)) 331} 332 333// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the 334// rule will not have them listed in its dependencies or outputs. 335func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand { 336 return c.Text(flag) 337} 338 339// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the 340// rule will not have them listed in its dependencies or outputs. 341func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand { 342 for _, flag := range flags { 343 c.Text(flag) 344 } 345 return c 346} 347 348// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag 349// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or 350// outputs. 351func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand { 352 return c.Text(flag + arg) 353} 354 355// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to 356// calling FlagWithArg for argument. 357func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand { 358 for _, arg := range args { 359 c.FlagWithArg(flag, arg) 360 } 361 return c 362} 363 364// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep 365// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or 366// the rule will not have them listed in its dependencies or outputs. 367func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand { 368 return c.Text(flag + strings.Join(list, sep)) 369} 370 371// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by 372// RuleBuilder.Tools. 373func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand { 374 c.tools = append(c.tools, path) 375 return c.Text(path.String()) 376} 377 378// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by 379// RuleBuilder.Inputs. 380func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand { 381 c.inputs = append(c.inputs, path) 382 return c.Text(path.String()) 383} 384 385// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the 386// dependencies returned by RuleBuilder.Inputs. 387func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand { 388 for _, path := range paths { 389 c.Input(path) 390 } 391 return c 392} 393 394// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the 395// command line. 396func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand { 397 c.inputs = append(c.inputs, path) 398 return c 399} 400 401// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the 402// command line. 403func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand { 404 c.inputs = append(c.inputs, paths...) 405 return c 406} 407 408// Output adds the specified output path to the command line. The path will also be added to the outputs returned by 409// RuleBuilder.Outputs. 410func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand { 411 c.outputs = append(c.outputs, path) 412 return c.Text(path.String()) 413} 414 415// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to 416// the outputs returned by RuleBuilder.Outputs. 417func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand { 418 for _, path := range paths { 419 c.Output(path) 420 } 421 return c 422} 423 424// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command 425// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to 426// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together. 427func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand { 428 c.depFiles = append(c.depFiles, path) 429 return c.Text(path.String()) 430} 431 432// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying 433// the command line. 434func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand { 435 c.outputs = append(c.outputs, path) 436 return c 437} 438 439// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying 440// the command line. 441func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand { 442 c.outputs = append(c.outputs, paths...) 443 return c 444} 445 446// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying 447// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles 448// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the 449// depfiles together. 450func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand { 451 c.depFiles = append(c.depFiles, path) 452 return c 453} 454 455// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path 456// will also be added to the dependencies returned by RuleBuilder.Inputs. 457func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand { 458 c.inputs = append(c.inputs, path) 459 return c.Text(flag + path.String()) 460} 461 462// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep 463// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by 464// RuleBuilder.Inputs. 465func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand { 466 c.inputs = append(c.inputs, paths...) 467 return c.FlagWithList(flag, paths.Strings(), sep) 468} 469 470// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also 471// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for 472// each input path. 473func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand { 474 for _, path := range paths { 475 c.FlagWithInput(flag, path) 476 } 477 return c 478} 479 480// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path 481// will also be added to the outputs returned by RuleBuilder.Outputs. 482func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand { 483 c.outputs = append(c.outputs, path) 484 return c.Text(flag + path.String()) 485} 486 487// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path 488// will also be added to the outputs returned by RuleBuilder.Outputs. 489func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand { 490 c.depFiles = append(c.depFiles, path) 491 return c.Text(flag + path.String()) 492} 493 494// String returns the command line. 495func (c *RuleBuilderCommand) String() string { 496 return string(c.buf) 497} 498 499func ninjaNameEscape(s string) string { 500 b := []byte(s) 501 escaped := false 502 for i, c := range b { 503 valid := (c >= 'a' && c <= 'z') || 504 (c >= 'A' && c <= 'Z') || 505 (c >= '0' && c <= '9') || 506 (c == '_') || 507 (c == '-') || 508 (c == '.') 509 if !valid { 510 b[i] = '_' 511 escaped = true 512 } 513 } 514 if escaped { 515 s = string(b) 516 } 517 return s 518} 519