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