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