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 "crypto/sha256" 19 "fmt" 20 "path/filepath" 21 "sort" 22 "strings" 23 "testing" 24 25 "github.com/google/blueprint" 26 "github.com/google/blueprint/proptools" 27 "google.golang.org/protobuf/encoding/prototext" 28 "google.golang.org/protobuf/proto" 29 30 "android/soong/cmd/sbox/sbox_proto" 31 "android/soong/remoteexec" 32 "android/soong/response" 33 "android/soong/shared" 34) 35 36const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__" 37const sboxOutSubDir = "out" 38const sboxToolsSubDir = "tools" 39const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir 40 41// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build 42// graph. 43type RuleBuilder struct { 44 pctx PackageContext 45 ctx BuilderContext 46 47 commands []*RuleBuilderCommand 48 installs RuleBuilderInstalls 49 temporariesSet map[WritablePath]bool 50 restat bool 51 sbox bool 52 highmem bool 53 remoteable RemoteRuleSupports 54 rbeParams *remoteexec.REParams 55 outDir WritablePath 56 sboxTools bool 57 sboxInputs bool 58 sboxManifestPath WritablePath 59 missingDeps []string 60} 61 62// NewRuleBuilder returns a newly created RuleBuilder. 63func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder { 64 return &RuleBuilder{ 65 pctx: pctx, 66 ctx: ctx, 67 temporariesSet: make(map[WritablePath]bool), 68 } 69} 70 71// RuleBuilderInstall is a tuple of install from and to locations. 72type RuleBuilderInstall struct { 73 From Path 74 To string 75} 76 77type RuleBuilderInstalls []RuleBuilderInstall 78 79// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated 80// list of from:to tuples. 81func (installs RuleBuilderInstalls) String() string { 82 sb := strings.Builder{} 83 for i, install := range installs { 84 if i != 0 { 85 sb.WriteRune(' ') 86 } 87 sb.WriteString(install.From.String()) 88 sb.WriteRune(':') 89 sb.WriteString(install.To) 90 } 91 return sb.String() 92} 93 94// MissingDeps adds modules to the list of missing dependencies. If MissingDeps 95// is called with a non-empty input, any call to Build will result in a rule 96// that will print an error listing the missing dependencies and fail. 97// MissingDeps should only be called if Config.AllowMissingDependencies() is 98// true. 99func (r *RuleBuilder) MissingDeps(missingDeps []string) { 100 r.missingDeps = append(r.missingDeps, missingDeps...) 101} 102 103// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat. 104func (r *RuleBuilder) Restat() *RuleBuilder { 105 r.restat = true 106 return r 107} 108 109// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory 110// rules. 111func (r *RuleBuilder) HighMem() *RuleBuilder { 112 r.highmem = true 113 return r 114} 115 116// Remoteable marks the rule as supporting remote execution. 117func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder { 118 r.remoteable = supports 119 return r 120} 121 122// Rewrapper marks the rule as running inside rewrapper using the given params in order to support 123// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs 124// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's 125// command line. 126func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder { 127 if !r.sboxInputs { 128 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs")) 129 } 130 r.rbeParams = params 131 return r 132} 133 134// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output 135// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should 136// point to a location where sbox's manifest will be written and must be outside outputDir. sbox 137// will ensure that all outputs have been written, and will discard any output files that were not 138// specified. 139func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder { 140 if r.sbox { 141 panic("Sbox() may not be called more than once") 142 } 143 if len(r.commands) > 0 { 144 panic("Sbox() may not be called after Command()") 145 } 146 r.sbox = true 147 r.outDir = outputDir 148 r.sboxManifestPath = manifestPath 149 return r 150} 151 152// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the 153// sandbox. 154func (r *RuleBuilder) SandboxTools() *RuleBuilder { 155 if !r.sbox { 156 panic("SandboxTools() must be called after Sbox()") 157 } 158 if len(r.commands) > 0 { 159 panic("SandboxTools() may not be called after Command()") 160 } 161 r.sboxTools = true 162 return r 163} 164 165// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the 166// sandbox. It also implies SandboxTools(). 167// 168// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths 169// that are passed to RuleBuilder outside of the methods that expect inputs, for example 170// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches 171// the sandbox layout. 172func (r *RuleBuilder) SandboxInputs() *RuleBuilder { 173 if !r.sbox { 174 panic("SandboxInputs() must be called after Sbox()") 175 } 176 if len(r.commands) > 0 { 177 panic("SandboxInputs() may not be called after Command()") 178 } 179 r.sboxTools = true 180 r.sboxInputs = true 181 return r 182} 183 184// Install associates an output of the rule with an install location, which can be retrieved later using 185// RuleBuilder.Installs. 186func (r *RuleBuilder) Install(from Path, to string) { 187 r.installs = append(r.installs, RuleBuilderInstall{from, to}) 188} 189 190// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were 191// created by this method. That can be mutated through their methods in any order, as long as the mutations do not 192// race with any call to Build. 193func (r *RuleBuilder) Command() *RuleBuilderCommand { 194 command := &RuleBuilderCommand{ 195 rule: r, 196 } 197 r.commands = append(r.commands, command) 198 return command 199} 200 201// Temporary marks an output of a command as an intermediate file that will be used as an input to another command 202// in the same rule, and should not be listed in Outputs. 203func (r *RuleBuilder) Temporary(path WritablePath) { 204 r.temporariesSet[path] = true 205} 206 207// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary 208// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary. 209func (r *RuleBuilder) DeleteTemporaryFiles() { 210 var temporariesList WritablePaths 211 212 for intermediate := range r.temporariesSet { 213 temporariesList = append(temporariesList, intermediate) 214 } 215 216 sort.Slice(temporariesList, func(i, j int) bool { 217 return temporariesList[i].String() < temporariesList[j].String() 218 }) 219 220 r.Command().Text("rm").Flag("-f").Outputs(temporariesList) 221} 222 223// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take 224// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or 225// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command 226// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed. 227func (r *RuleBuilder) Inputs() Paths { 228 outputs := r.outputSet() 229 depFiles := r.depFileSet() 230 231 inputs := make(map[string]Path) 232 for _, c := range r.commands { 233 for _, input := range append(c.inputs, c.implicits...) { 234 inputStr := input.String() 235 if _, isOutput := outputs[inputStr]; !isOutput { 236 if _, isDepFile := depFiles[inputStr]; !isDepFile { 237 inputs[input.String()] = input 238 } 239 } 240 } 241 } 242 243 var inputList Paths 244 for _, input := range inputs { 245 inputList = append(inputList, input) 246 } 247 248 sort.Slice(inputList, func(i, j int) bool { 249 return inputList[i].String() < inputList[j].String() 250 }) 251 252 return inputList 253} 254 255// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or 256// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed. 257func (r *RuleBuilder) OrderOnlys() Paths { 258 orderOnlys := make(map[string]Path) 259 for _, c := range r.commands { 260 for _, orderOnly := range c.orderOnlys { 261 orderOnlys[orderOnly.String()] = orderOnly 262 } 263 } 264 265 var orderOnlyList Paths 266 for _, orderOnly := range orderOnlys { 267 orderOnlyList = append(orderOnlyList, orderOnly) 268 } 269 270 sort.Slice(orderOnlyList, func(i, j int) bool { 271 return orderOnlyList[i].String() < orderOnlyList[j].String() 272 }) 273 274 return orderOnlyList 275} 276 277// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or 278// RuleBuilderCommand.Validations. The list is sorted and duplicates removed. 279func (r *RuleBuilder) Validations() Paths { 280 validations := make(map[string]Path) 281 for _, c := range r.commands { 282 for _, validation := range c.validations { 283 validations[validation.String()] = validation 284 } 285 } 286 287 var validationList Paths 288 for _, validation := range validations { 289 validationList = append(validationList, validation) 290 } 291 292 sort.Slice(validationList, func(i, j int) bool { 293 return validationList[i].String() < validationList[j].String() 294 }) 295 296 return validationList 297} 298 299func (r *RuleBuilder) outputSet() map[string]WritablePath { 300 outputs := make(map[string]WritablePath) 301 for _, c := range r.commands { 302 for _, output := range c.outputs { 303 outputs[output.String()] = output 304 } 305 } 306 return outputs 307} 308 309// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take 310// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or 311// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed. 312func (r *RuleBuilder) Outputs() WritablePaths { 313 outputs := r.outputSet() 314 315 var outputList WritablePaths 316 for _, output := range outputs { 317 if !r.temporariesSet[output] { 318 outputList = append(outputList, output) 319 } 320 } 321 322 sort.Slice(outputList, func(i, j int) bool { 323 return outputList[i].String() < outputList[j].String() 324 }) 325 326 return outputList 327} 328 329func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath { 330 symlinkOutputs := make(map[string]WritablePath) 331 for _, c := range r.commands { 332 for _, symlinkOutput := range c.symlinkOutputs { 333 symlinkOutputs[symlinkOutput.String()] = symlinkOutput 334 } 335 } 336 return symlinkOutputs 337} 338 339// SymlinkOutputs returns the list of paths that the executor (Ninja) would 340// verify, after build edge completion, that: 341// 342// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer) 343// 2) Created output files are *not* declared in this list. 344// 345// These symlink outputs are expected to be a subset of outputs or implicit 346// outputs, or they would fail validation at build param construction time 347// later, to support other non-rule-builder approaches for constructing 348// statements. 349func (r *RuleBuilder) SymlinkOutputs() WritablePaths { 350 symlinkOutputs := r.symlinkOutputSet() 351 352 var symlinkOutputList WritablePaths 353 for _, symlinkOutput := range symlinkOutputs { 354 symlinkOutputList = append(symlinkOutputList, symlinkOutput) 355 } 356 357 sort.Slice(symlinkOutputList, func(i, j int) bool { 358 return symlinkOutputList[i].String() < symlinkOutputList[j].String() 359 }) 360 361 return symlinkOutputList 362} 363 364func (r *RuleBuilder) depFileSet() map[string]WritablePath { 365 depFiles := make(map[string]WritablePath) 366 for _, c := range r.commands { 367 for _, depFile := range c.depFiles { 368 depFiles[depFile.String()] = depFile 369 } 370 } 371 return depFiles 372} 373 374// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such 375// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile. 376func (r *RuleBuilder) DepFiles() WritablePaths { 377 var depFiles WritablePaths 378 379 for _, c := range r.commands { 380 for _, depFile := range c.depFiles { 381 depFiles = append(depFiles, depFile) 382 } 383 } 384 385 return depFiles 386} 387 388// Installs returns the list of tuples passed to Install. 389func (r *RuleBuilder) Installs() RuleBuilderInstalls { 390 return append(RuleBuilderInstalls(nil), r.installs...) 391} 392 393func (r *RuleBuilder) toolsSet() map[string]Path { 394 tools := make(map[string]Path) 395 for _, c := range r.commands { 396 for _, tool := range c.tools { 397 tools[tool.String()] = tool 398 } 399 } 400 401 return tools 402} 403 404// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The 405// list is sorted and duplicates removed. 406func (r *RuleBuilder) Tools() Paths { 407 toolsSet := r.toolsSet() 408 409 var toolsList Paths 410 for _, tool := range toolsSet { 411 toolsList = append(toolsList, tool) 412 } 413 414 sort.Slice(toolsList, func(i, j int) bool { 415 return toolsList[i].String() < toolsList[j].String() 416 }) 417 418 return toolsList 419} 420 421// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method. 422func (r *RuleBuilder) RspFileInputs() Paths { 423 var rspFileInputs Paths 424 for _, c := range r.commands { 425 for _, rspFile := range c.rspFiles { 426 rspFileInputs = append(rspFileInputs, rspFile.paths...) 427 } 428 } 429 430 return rspFileInputs 431} 432 433func (r *RuleBuilder) rspFiles() []rspFileAndPaths { 434 var rspFiles []rspFileAndPaths 435 for _, c := range r.commands { 436 rspFiles = append(rspFiles, c.rspFiles...) 437 } 438 439 return rspFiles 440} 441 442// Commands returns a slice containing the built command line for each call to RuleBuilder.Command. 443func (r *RuleBuilder) Commands() []string { 444 var commands []string 445 for _, c := range r.commands { 446 commands = append(commands, c.String()) 447 } 448 return commands 449} 450 451// BuilderContext is a subset of ModuleContext and SingletonContext. 452type BuilderContext interface { 453 PathContext 454 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule 455 Build(PackageContext, BuildParams) 456} 457 458var _ BuilderContext = ModuleContext(nil) 459var _ BuilderContext = SingletonContext(nil) 460 461func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand { 462 return r.Command(). 463 builtToolWithoutDeps("dep_fixer"). 464 Inputs(depFiles.Paths()) 465} 466 467// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for 468// Outputs. 469func (r *RuleBuilder) Build(name string, desc string) { 470 name = ninjaNameEscape(name) 471 472 if len(r.missingDeps) > 0 { 473 r.ctx.Build(pctx, BuildParams{ 474 Rule: ErrorRule, 475 Outputs: r.Outputs(), 476 Description: desc, 477 Args: map[string]string{ 478 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "), 479 }, 480 }) 481 return 482 } 483 484 var depFile WritablePath 485 var depFormat blueprint.Deps 486 if depFiles := r.DepFiles(); len(depFiles) > 0 { 487 depFile = depFiles[0] 488 depFormat = blueprint.DepsGCC 489 if len(depFiles) > 1 { 490 // Add a command locally that merges all depfiles together into the first depfile. 491 r.depFileMergerCmd(depFiles) 492 493 if r.sbox { 494 // Check for Rel() errors, as all depfiles should be in the output dir. Errors 495 // will be reported to the ctx. 496 for _, path := range depFiles[1:] { 497 Rel(r.ctx, r.outDir.String(), path.String()) 498 } 499 } 500 } 501 } 502 503 tools := r.Tools() 504 commands := r.Commands() 505 outputs := r.Outputs() 506 inputs := r.Inputs() 507 rspFiles := r.rspFiles() 508 509 if len(commands) == 0 { 510 return 511 } 512 if len(outputs) == 0 { 513 panic("No outputs specified from any Commands") 514 } 515 516 commandString := strings.Join(commands, " && ") 517 518 if r.sbox { 519 // If running the command inside sbox, write the rule data out to an sbox 520 // manifest.textproto. 521 manifest := sbox_proto.Manifest{} 522 command := sbox_proto.Command{} 523 manifest.Commands = append(manifest.Commands, &command) 524 command.Command = proto.String(commandString) 525 526 if depFile != nil { 527 manifest.OutputDepfile = proto.String(depFile.String()) 528 } 529 530 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool 531 // into the sbox directory. 532 if r.sboxTools { 533 for _, tool := range tools { 534 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 535 From: proto.String(tool.String()), 536 To: proto.String(sboxPathForToolRel(r.ctx, tool)), 537 }) 538 } 539 for _, c := range r.commands { 540 for _, tool := range c.packagedTools { 541 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 542 From: proto.String(tool.srcPath.String()), 543 To: proto.String(sboxPathForPackagedToolRel(tool)), 544 Executable: proto.Bool(tool.executable), 545 }) 546 tools = append(tools, tool.srcPath) 547 } 548 } 549 } 550 551 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input 552 // into the sbox directory. 553 if r.sboxInputs { 554 for _, input := range inputs { 555 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 556 From: proto.String(input.String()), 557 To: proto.String(r.sboxPathForInputRel(input)), 558 }) 559 } 560 561 // If using rsp files copy them and their contents into the sbox directory with 562 // the appropriate path mappings. 563 for _, rspFile := range rspFiles { 564 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{ 565 File: proto.String(rspFile.file.String()), 566 // These have to match the logic in sboxPathForInputRel 567 PathMappings: []*sbox_proto.PathMapping{ 568 { 569 From: proto.String(r.outDir.String()), 570 To: proto.String(sboxOutSubDir), 571 }, 572 { 573 From: proto.String(PathForOutput(r.ctx).String()), 574 To: proto.String(sboxOutSubDir), 575 }, 576 }, 577 }) 578 } 579 580 command.Chdir = proto.Bool(true) 581 } 582 583 // Add copy rules to the manifest to copy each output file from the sbox directory. 584 // to the output directory after running the commands. 585 sboxOutputs := make([]string, len(outputs)) 586 for i, output := range outputs { 587 rel := Rel(r.ctx, r.outDir.String(), output.String()) 588 sboxOutputs[i] = filepath.Join(sboxOutDir, rel) 589 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{ 590 From: proto.String(filepath.Join(sboxOutSubDir, rel)), 591 To: proto.String(output.String()), 592 }) 593 } 594 595 // Outputs that were marked Temporary will not be checked that they are in the output 596 // directory by the loop above, check them here. 597 for path := range r.temporariesSet { 598 Rel(r.ctx, r.outDir.String(), path.String()) 599 } 600 601 // Add a hash of the list of input files to the manifest so that the textproto file 602 // changes when the list of input files changes and causes the sbox rule that 603 // depends on it to rerun. 604 command.InputHash = proto.String(hashSrcFiles(inputs)) 605 606 // Verify that the manifest textproto is not inside the sbox output directory, otherwise 607 // it will get deleted when the sbox rule clears its output directory. 608 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String()) 609 if manifestInOutDir { 610 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q", 611 name, r.sboxManifestPath.String(), r.outDir.String()) 612 } 613 614 // Create a rule to write the manifest as a the textproto. 615 pbText, err := prototext.Marshal(&manifest) 616 if err != nil { 617 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err) 618 } 619 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) 620 621 // Generate a new string to use as the command line of the sbox rule. This uses 622 // a RuleBuilderCommand as a convenience method of building the command line, then 623 // converts it to a string to replace commandString. 624 sboxCmd := &RuleBuilderCommand{ 625 rule: &RuleBuilder{ 626 ctx: r.ctx, 627 }, 628 } 629 sboxCmd.builtToolWithoutDeps("sbox"). 630 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())). 631 FlagWithArg("--output-dir ", r.outDir.String()). 632 FlagWithInput("--manifest ", r.sboxManifestPath) 633 634 if r.restat { 635 sboxCmd.Flag("--write-if-changed") 636 } 637 638 // Replace the command string, and add the sbox tool and manifest textproto to the 639 // dependencies of the final sbox rule. 640 commandString = sboxCmd.buf.String() 641 tools = append(tools, sboxCmd.tools...) 642 inputs = append(inputs, sboxCmd.inputs...) 643 644 if r.rbeParams != nil { 645 // RBE needs a list of input files to copy to the remote builder. For inputs already 646 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest, 647 // create a new rsp file to pass to rewrapper. 648 var remoteRspFiles Paths 649 var remoteInputs Paths 650 651 remoteInputs = append(remoteInputs, inputs...) 652 remoteInputs = append(remoteInputs, tools...) 653 654 for _, rspFile := range rspFiles { 655 remoteInputs = append(remoteInputs, rspFile.file) 656 remoteRspFiles = append(remoteRspFiles, rspFile.file) 657 } 658 659 if len(remoteInputs) > 0 { 660 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list") 661 writeRspFileRule(r.ctx, inputsListFile, remoteInputs) 662 remoteRspFiles = append(remoteRspFiles, inputsListFile) 663 // Add the new rsp file as an extra input to the rule. 664 inputs = append(inputs, inputsListFile) 665 } 666 667 r.rbeParams.OutputFiles = outputs.Strings() 668 r.rbeParams.RSPFiles = remoteRspFiles.Strings() 669 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper()) 670 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'" 671 } 672 } else { 673 // If not using sbox the rule will run the command directly, put the hash of the 674 // list of input files in a comment at the end of the command line to ensure ninja 675 // reruns the rule when the list of input files changes. 676 commandString += " # hash of input list: " + hashSrcFiles(inputs) 677 } 678 679 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to 680 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and 681 // ImplicitOutputs doesn't matter. 682 output := outputs[0] 683 implicitOutputs := outputs[1:] 684 685 var rspFile, rspFileContent string 686 var rspFileInputs Paths 687 if len(rspFiles) > 0 { 688 // The first rsp files uses Ninja's rsp file support for the rule 689 rspFile = rspFiles[0].file.String() 690 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency 691 // list and in the contents of the rsp file. Inputs to the rule that are not in the 692 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in". 693 rspFileContent = "$in" 694 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...) 695 696 for _, rspFile := range rspFiles[1:] { 697 // Any additional rsp files need an extra rule to write the file. 698 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths) 699 // The main rule needs to depend on the inputs listed in the extra rsp file. 700 inputs = append(inputs, rspFile.paths...) 701 // The main rule needs to depend on the extra rsp file. 702 inputs = append(inputs, rspFile.file) 703 } 704 } 705 706 var pool blueprint.Pool 707 if r.ctx.Config().UseGoma() && r.remoteable.Goma { 708 // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool. 709 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE { 710 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool. 711 pool = remotePool 712 } else if r.highmem { 713 pool = highmemPool 714 } else if r.ctx.Config().UseRemoteBuild() { 715 pool = localPool 716 } 717 718 r.ctx.Build(r.pctx, BuildParams{ 719 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{ 720 Command: proptools.NinjaEscape(commandString), 721 CommandDeps: proptools.NinjaEscapeList(tools.Strings()), 722 Restat: r.restat, 723 Rspfile: proptools.NinjaEscape(rspFile), 724 RspfileContent: rspFileContent, 725 Pool: pool, 726 }), 727 Inputs: rspFileInputs, 728 Implicits: inputs, 729 OrderOnly: r.OrderOnlys(), 730 Validations: r.Validations(), 731 Output: output, 732 ImplicitOutputs: implicitOutputs, 733 SymlinkOutputs: r.SymlinkOutputs(), 734 Depfile: depFile, 735 Deps: depFormat, 736 Description: desc, 737 }) 738} 739 740// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the 741// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the 742// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single 743// space as a separator from the previous method. 744type RuleBuilderCommand struct { 745 rule *RuleBuilder 746 747 buf strings.Builder 748 inputs Paths 749 implicits Paths 750 orderOnlys Paths 751 validations Paths 752 outputs WritablePaths 753 symlinkOutputs WritablePaths 754 depFiles WritablePaths 755 tools Paths 756 packagedTools []PackagingSpec 757 rspFiles []rspFileAndPaths 758} 759 760type rspFileAndPaths struct { 761 file WritablePath 762 paths Paths 763} 764 765func checkPathNotNil(path Path) { 766 if path == nil { 767 panic("rule_builder paths cannot be nil") 768 } 769} 770 771func (c *RuleBuilderCommand) addInput(path Path) string { 772 checkPathNotNil(path) 773 c.inputs = append(c.inputs, path) 774 return c.PathForInput(path) 775} 776 777func (c *RuleBuilderCommand) addImplicit(path Path) { 778 checkPathNotNil(path) 779 c.implicits = append(c.implicits, path) 780} 781 782func (c *RuleBuilderCommand) addOrderOnly(path Path) { 783 checkPathNotNil(path) 784 c.orderOnlys = append(c.orderOnlys, path) 785} 786 787// PathForInput takes an input path and returns the appropriate path to use on the command line. If 788// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a 789// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the 790// original path. 791func (c *RuleBuilderCommand) PathForInput(path Path) string { 792 if c.rule.sbox { 793 rel, inSandbox := c.rule._sboxPathForInputRel(path) 794 if inSandbox { 795 rel = filepath.Join(sboxSandboxBaseDir, rel) 796 } 797 return rel 798 } 799 return path.String() 800} 801 802// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the 803// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it 804// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it 805// returns the original paths. 806func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string { 807 ret := make([]string, len(paths)) 808 for i, path := range paths { 809 ret[i] = c.PathForInput(path) 810 } 811 return ret 812} 813 814// PathForOutput takes an output path and returns the appropriate path to use on the command 815// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the 816// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the 817// original path. 818func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string { 819 if c.rule.sbox { 820 // Errors will be handled in RuleBuilder.Build where we have a context to report them 821 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String()) 822 return filepath.Join(sboxOutDir, rel) 823 } 824 return path.String() 825} 826 827func sboxPathForToolRel(ctx BuilderContext, path Path) string { 828 // Errors will be handled in RuleBuilder.Build where we have a context to report them 829 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", false) 830 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String()) 831 if isRelOutSoong { 832 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out 833 return filepath.Join(sboxToolsSubDir, "out", relOutSoong) 834 } 835 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src 836 return filepath.Join(sboxToolsSubDir, "src", path.String()) 837} 838 839func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) { 840 // Errors will be handled in RuleBuilder.Build where we have a context to report them 841 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String()) 842 if isRelSboxOut { 843 return filepath.Join(sboxOutSubDir, rel), true 844 } 845 if r.sboxInputs { 846 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that 847 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they 848 // will be copied to relative paths under __SBOX_OUT_DIR__/out. 849 rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String()) 850 if isRelOut { 851 return filepath.Join(sboxOutSubDir, rel), true 852 } 853 } 854 return path.String(), false 855} 856 857func (r *RuleBuilder) sboxPathForInputRel(path Path) string { 858 rel, _ := r._sboxPathForInputRel(path) 859 return rel 860} 861 862func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string { 863 ret := make([]string, len(paths)) 864 for i, path := range paths { 865 ret[i] = r.sboxPathForInputRel(path) 866 } 867 return ret 868} 869 870func sboxPathForPackagedToolRel(spec PackagingSpec) string { 871 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage) 872} 873 874// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the 875// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to 876// reference the tool. 877func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string { 878 if !c.rule.sboxTools { 879 panic("PathForPackagedTool() requires SandboxTools()") 880 } 881 882 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec)) 883} 884 885// PathForTool takes a path to a tool, which may be an output file or a source file, and returns 886// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path 887// if it is not. This can be used on the RuleBuilder command line to reference the tool. 888func (c *RuleBuilderCommand) PathForTool(path Path) string { 889 if c.rule.sbox && c.rule.sboxTools { 890 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)) 891 } 892 return path.String() 893} 894 895// PathsForTools takes a list of paths to tools, which may be output files or source files, and 896// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the 897// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool. 898func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string { 899 if c.rule.sbox && c.rule.sboxTools { 900 var ret []string 901 for _, path := range paths { 902 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))) 903 } 904 return ret 905 } 906 return paths.Strings() 907} 908 909// PackagedTool adds the specified tool path to the command line. It can only be used with tool 910// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox. 911func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand { 912 if !c.rule.sboxTools { 913 panic("PackagedTool() requires SandboxTools()") 914 } 915 916 c.packagedTools = append(c.packagedTools, spec) 917 c.Text(sboxPathForPackagedToolRel(spec)) 918 return c 919} 920 921// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command 922// line. It can only be used with tool sandboxing enabled by SandboxTools(). 923func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand { 924 if !c.rule.sboxTools { 925 panic("ImplicitPackagedTool() requires SandboxTools()") 926 } 927 928 c.packagedTools = append(c.packagedTools, spec) 929 return c 930} 931 932// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command 933// line. It can only be used with tool sandboxing enabled by SandboxTools(). 934func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand { 935 if !c.rule.sboxTools { 936 panic("ImplicitPackagedTools() requires SandboxTools()") 937 } 938 939 c.packagedTools = append(c.packagedTools, specs...) 940 return c 941} 942 943// Text adds the specified raw text to the command line. The text should not contain input or output paths or the 944// rule will not have them listed in its dependencies or outputs. 945func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand { 946 if c.buf.Len() > 0 { 947 c.buf.WriteByte(' ') 948 } 949 c.buf.WriteString(text) 950 return c 951} 952 953// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or 954// the rule will not have them listed in its dependencies or outputs. 955func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand { 956 return c.Text(fmt.Sprintf(format, a...)) 957} 958 959// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the 960// rule will not have them listed in its dependencies or outputs. 961func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand { 962 return c.Text(flag) 963} 964 965// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or 966// output paths or the rule will not have them listed in its dependencies or outputs. 967func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand { 968 if flag != nil { 969 c.Text(*flag) 970 } 971 972 return c 973} 974 975// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the 976// rule will not have them listed in its dependencies or outputs. 977func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand { 978 for _, flag := range flags { 979 c.Text(flag) 980 } 981 return c 982} 983 984// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag 985// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or 986// outputs. 987func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand { 988 return c.Text(flag + arg) 989} 990 991// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to 992// calling FlagWithArg for argument. 993func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand { 994 for _, arg := range args { 995 c.FlagWithArg(flag, arg) 996 } 997 return c 998} 999 1000// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep 1001// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or 1002// the rule will not have them listed in its dependencies or outputs. 1003func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand { 1004 return c.Text(flag + strings.Join(list, sep)) 1005} 1006 1007// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by 1008// RuleBuilder.Tools. 1009func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand { 1010 checkPathNotNil(path) 1011 c.tools = append(c.tools, path) 1012 return c.Text(c.PathForTool(path)) 1013} 1014 1015// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools. 1016func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand { 1017 checkPathNotNil(path) 1018 c.tools = append(c.tools, path) 1019 return c 1020} 1021 1022// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools. 1023func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand { 1024 for _, path := range paths { 1025 c.ImplicitTool(path) 1026 } 1027 return c 1028} 1029 1030// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will 1031// be also added to the dependencies returned by RuleBuilder.Tools. 1032// 1033// It is equivalent to: 1034// cmd.Tool(ctx.Config().HostToolPath(ctx, tool)) 1035func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand { 1036 if c.rule.ctx.Config().UseHostMusl() { 1037 // If the host is using musl, assume that the tool was built against musl libc and include 1038 // libc_musl.so in the sandbox. 1039 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions 1040 // this could be a dependency + TransitivePackagingSpecs. 1041 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl")) 1042 } 1043 return c.builtToolWithoutDeps(tool) 1044} 1045 1046// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used 1047// internally by RuleBuilder for helper tools that are known to be compiled statically. 1048func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand { 1049 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool)) 1050} 1051 1052// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the 1053// dependencies returned by RuleBuilder.Tools. 1054// 1055// It is equivalent to: 1056// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool)) 1057func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand { 1058 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool)) 1059} 1060 1061// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by 1062// RuleBuilder.Inputs. 1063func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand { 1064 return c.Text(c.addInput(path)) 1065} 1066 1067// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the 1068// dependencies returned by RuleBuilder.Inputs. 1069func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand { 1070 for _, path := range paths { 1071 c.Input(path) 1072 } 1073 return c 1074} 1075 1076// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the 1077// command line. 1078func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand { 1079 c.addImplicit(path) 1080 return c 1081} 1082 1083// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the 1084// command line. 1085func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand { 1086 for _, path := range paths { 1087 c.addImplicit(path) 1088 } 1089 return c 1090} 1091 1092// GetImplicits returns the command's implicit inputs. 1093func (c *RuleBuilderCommand) GetImplicits() Paths { 1094 return c.implicits 1095} 1096 1097// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys 1098// without modifying the command line. 1099func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand { 1100 c.addOrderOnly(path) 1101 return c 1102} 1103 1104// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys 1105// without modifying the command line. 1106func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand { 1107 for _, path := range paths { 1108 c.addOrderOnly(path) 1109 } 1110 return c 1111} 1112 1113// Validation adds the specified input path to the validation dependencies by 1114// RuleBuilder.Validations without modifying the command line. 1115func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand { 1116 checkPathNotNil(path) 1117 c.validations = append(c.validations, path) 1118 return c 1119} 1120 1121// Validations adds the specified input paths to the validation dependencies by 1122// RuleBuilder.Validations without modifying the command line. 1123func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand { 1124 for _, path := range paths { 1125 c.Validation(path) 1126 } 1127 return c 1128} 1129 1130// Output adds the specified output path to the command line. The path will also be added to the outputs returned by 1131// RuleBuilder.Outputs. 1132func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand { 1133 checkPathNotNil(path) 1134 c.outputs = append(c.outputs, path) 1135 return c.Text(c.PathForOutput(path)) 1136} 1137 1138// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to 1139// the outputs returned by RuleBuilder.Outputs. 1140func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand { 1141 for _, path := range paths { 1142 c.Output(path) 1143 } 1144 return c 1145} 1146 1147// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox, 1148// and will be the temporary output directory managed by sbox, not the final one. 1149func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand { 1150 if !c.rule.sbox { 1151 panic("OutputDir only valid with Sbox") 1152 } 1153 return c.Text(sboxOutDir) 1154} 1155 1156// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command 1157// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to 1158// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together. 1159func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand { 1160 checkPathNotNil(path) 1161 c.depFiles = append(c.depFiles, path) 1162 return c.Text(c.PathForOutput(path)) 1163} 1164 1165// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying 1166// the command line. 1167func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand { 1168 c.outputs = append(c.outputs, path) 1169 return c 1170} 1171 1172// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying 1173// the command line. 1174func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand { 1175 c.outputs = append(c.outputs, paths...) 1176 return c 1177} 1178 1179// ImplicitSymlinkOutput declares the specified path as an implicit output that 1180// will be a symlink instead of a regular file. Does not modify the command 1181// line. 1182func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand { 1183 checkPathNotNil(path) 1184 c.symlinkOutputs = append(c.symlinkOutputs, path) 1185 return c.ImplicitOutput(path) 1186} 1187 1188// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that 1189// will be a symlinks instead of regular files. Does not modify the command 1190// line. 1191func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand { 1192 for _, path := range paths { 1193 c.ImplicitSymlinkOutput(path) 1194 } 1195 return c 1196} 1197 1198// SymlinkOutput declares the specified path as an output that will be a symlink 1199// instead of a regular file. Modifies the command line. 1200func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand { 1201 checkPathNotNil(path) 1202 c.symlinkOutputs = append(c.symlinkOutputs, path) 1203 return c.Output(path) 1204} 1205 1206// SymlinkOutputsl declares the specified paths as outputs that will be symlinks 1207// instead of regular files. Modifies the command line. 1208func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand { 1209 for _, path := range paths { 1210 c.SymlinkOutput(path) 1211 } 1212 return c 1213} 1214 1215// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying 1216// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles 1217// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the 1218// depfiles together. 1219func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand { 1220 c.depFiles = append(c.depFiles, path) 1221 return c 1222} 1223 1224// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path 1225// will also be added to the dependencies returned by RuleBuilder.Inputs. 1226func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand { 1227 return c.Text(flag + c.addInput(path)) 1228} 1229 1230// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep 1231// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by 1232// RuleBuilder.Inputs. 1233func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand { 1234 strs := make([]string, len(paths)) 1235 for i, path := range paths { 1236 strs[i] = c.addInput(path) 1237 } 1238 return c.FlagWithList(flag, strs, sep) 1239} 1240 1241// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also 1242// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for 1243// each input path. 1244func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand { 1245 for _, path := range paths { 1246 c.FlagWithInput(flag, path) 1247 } 1248 return c 1249} 1250 1251// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path 1252// will also be added to the outputs returned by RuleBuilder.Outputs. 1253func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand { 1254 c.outputs = append(c.outputs, path) 1255 return c.Text(flag + c.PathForOutput(path)) 1256} 1257 1258// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path 1259// will also be added to the outputs returned by RuleBuilder.Outputs. 1260func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand { 1261 c.depFiles = append(c.depFiles, path) 1262 return c.Text(flag + c.PathForOutput(path)) 1263} 1264 1265// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with 1266// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the 1267// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any 1268// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional 1269// uses will result in an auxiliary rules to write the rspFile contents. 1270func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand { 1271 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be 1272 // generated. 1273 if paths == nil { 1274 paths = Paths{} 1275 } 1276 1277 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths}) 1278 1279 if c.rule.sbox { 1280 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel { 1281 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q", 1282 rspFile.String(), c.rule.outDir.String())) 1283 } 1284 } 1285 1286 c.FlagWithArg(flag, c.PathForInput(rspFile)) 1287 return c 1288} 1289 1290// String returns the command line. 1291func (c *RuleBuilderCommand) String() string { 1292 return c.buf.String() 1293} 1294 1295// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox() 1296// and returns sbox testproto generated by the RuleBuilder. 1297func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest { 1298 t.Helper() 1299 content := ContentFromFileRuleForTests(t, params) 1300 manifest := sbox_proto.Manifest{} 1301 err := prototext.Unmarshal([]byte(content), &manifest) 1302 if err != nil { 1303 t.Fatalf("failed to unmarshal manifest: %s", err.Error()) 1304 } 1305 return &manifest 1306} 1307 1308func ninjaNameEscape(s string) string { 1309 b := []byte(s) 1310 escaped := false 1311 for i, c := range b { 1312 valid := (c >= 'a' && c <= 'z') || 1313 (c >= 'A' && c <= 'Z') || 1314 (c >= '0' && c <= '9') || 1315 (c == '_') || 1316 (c == '-') || 1317 (c == '.') 1318 if !valid { 1319 b[i] = '_' 1320 escaped = true 1321 } 1322 } 1323 if escaped { 1324 s = string(b) 1325 } 1326 return s 1327} 1328 1329// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line 1330// or the sbox textproto manifest change even if the input files are not listed on the command line. 1331func hashSrcFiles(srcFiles Paths) string { 1332 h := sha256.New() 1333 srcFileList := strings.Join(srcFiles.Strings(), "\n") 1334 h.Write([]byte(srcFileList)) 1335 return fmt.Sprintf("%x", h.Sum(nil)) 1336} 1337 1338// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests 1339// that need to call methods that take a BuilderContext. 1340func BuilderContextForTesting(config Config) BuilderContext { 1341 pathCtx := PathContextForTesting(config) 1342 return builderContextForTests{ 1343 PathContext: pathCtx, 1344 } 1345} 1346 1347type builderContextForTests struct { 1348 PathContext 1349} 1350 1351func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule { 1352 return nil 1353} 1354func (builderContextForTests) Build(PackageContext, BuildParams) {} 1355 1356func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) { 1357 buf := &strings.Builder{} 1358 err := response.WriteRspFile(buf, paths.Strings()) 1359 if err != nil { 1360 // There should never be I/O errors writing to a bytes.Buffer. 1361 panic(err) 1362 } 1363 WriteFileRule(ctx, rspFile, buf.String()) 1364} 1365