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