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 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 664 From: proto.String(tool.srcPath.String()), 665 To: proto.String(sboxPathForPackagedToolRel(tool)), 666 Executable: proto.Bool(tool.executable), 667 }) 668 tools = append(tools, tool.srcPath) 669 } 670 } 671 } 672 673 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input 674 // into the sbox directory. 675 if r.sboxInputs { 676 for _, input := range inputs { 677 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 678 From: proto.String(input.String()), 679 To: proto.String(r.sboxPathForInputRel(input)), 680 }) 681 } 682 for _, input := range r.OrderOnlys() { 683 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ 684 From: proto.String(input.String()), 685 To: proto.String(r.sboxPathForInputRel(input)), 686 }) 687 } 688 689 // If using rsp files copy them and their contents into the sbox directory with 690 // the appropriate path mappings. 691 for _, rspFile := range rspFiles { 692 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{ 693 File: proto.String(rspFile.file.String()), 694 // These have to match the logic in sboxPathForInputRel 695 PathMappings: []*sbox_proto.PathMapping{ 696 { 697 From: proto.String(r.outDir.String()), 698 To: proto.String(sboxOutSubDir), 699 }, 700 { 701 From: proto.String(r.ctx.Config().OutDir()), 702 To: proto.String(sboxOutSubDir), 703 }, 704 }, 705 }) 706 } 707 708 // Only allow the build to access certain environment variables 709 command.DontInheritEnv = proto.Bool(true) 710 command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} { 711 // The list of allowed variables was found by running builds of all 712 // genrules and seeing what failed 713 var result []*sbox_proto.EnvironmentVariable 714 inheritedVars := []string{ 715 "PATH", 716 "JAVA_HOME", 717 "TMPDIR", 718 // Allow RBE variables because the art tests invoke RBE manually 719 "RBE_log_dir", 720 "RBE_platform", 721 "RBE_server_address", 722 // TODO: RBE_exec_root is set to the absolute path to the root of the source 723 // tree, which we don't want sandboxed actions to find. Remap it to ".". 724 "RBE_exec_root", 725 } 726 for _, v := range inheritedVars { 727 result = append(result, &sbox_proto.EnvironmentVariable{ 728 Name: proto.String(v), 729 State: &sbox_proto.EnvironmentVariable_Inherit{ 730 Inherit: true, 731 }, 732 }) 733 } 734 // Set OUT_DIR to the relative path of the sandboxed out directory. 735 // Otherwise, OUT_DIR will be inherited from the rest of the build, 736 // which will allow scripts to escape the sandbox if OUT_DIR is an 737 // absolute path. 738 result = append(result, &sbox_proto.EnvironmentVariable{ 739 Name: proto.String("OUT_DIR"), 740 State: &sbox_proto.EnvironmentVariable_Value{ 741 Value: sboxOutSubDir, 742 }, 743 }) 744 return result 745 }).([]*sbox_proto.EnvironmentVariable) 746 command.Chdir = proto.Bool(true) 747 } 748 749 // Add copy rules to the manifest to copy each output file from the sbox directory. 750 // to the output directory after running the commands. 751 for _, output := range outputs { 752 rel := Rel(r.ctx, r.outDir.String(), output.String()) 753 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{ 754 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)), 755 To: proto.String(output.String()), 756 }) 757 } 758 759 // Outputs that were marked Temporary will not be checked that they are in the output 760 // directory by the loop above, check them here. 761 for path := range r.temporariesSet { 762 Rel(r.ctx, r.outDir.String(), path.String()) 763 } 764 765 // Add a hash of the list of input files to the manifest so that the textproto file 766 // changes when the list of input files changes and causes the sbox rule that 767 // depends on it to rerun. 768 command.InputHash = proto.String(hashSrcFiles(inputs)) 769 770 // Verify that the manifest textproto is not inside the sbox output directory, otherwise 771 // it will get deleted when the sbox rule clears its output directory. 772 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String()) 773 if manifestInOutDir { 774 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q", 775 name, r.sboxManifestPath.String(), r.outDir.String()) 776 } 777 778 // Create a rule to write the manifest as textproto. Pretty print it by indenting and 779 // splitting across multiple lines. 780 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest) 781 if err != nil { 782 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err) 783 } 784 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) 785 786 // Generate a new string to use as the command line of the sbox rule. This uses 787 // a RuleBuilderCommand as a convenience method of building the command line, then 788 // converts it to a string to replace commandString. 789 sboxCmd := &RuleBuilderCommand{ 790 rule: &RuleBuilder{ 791 ctx: r.ctx, 792 }, 793 } 794 sboxCmd.builtToolWithoutDeps("sbox"). 795 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())). 796 FlagWithArg("--output-dir ", r.outDir.String()). 797 FlagWithInput("--manifest ", r.sboxManifestPath) 798 799 if r.restat { 800 sboxCmd.Flag("--write-if-changed") 801 } 802 803 // Replace the command string, and add the sbox tool and manifest textproto to the 804 // dependencies of the final sbox rule. 805 commandString = sboxCmd.buf.String() 806 tools = append(tools, sboxCmd.tools...) 807 inputs = append(inputs, sboxCmd.inputs...) 808 809 if r.rbeParams != nil { 810 // RBE needs a list of input files to copy to the remote builder. For inputs already 811 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest, 812 // create a new rsp file to pass to rewrapper. 813 var remoteRspFiles Paths 814 var remoteInputs Paths 815 816 remoteInputs = append(remoteInputs, inputs...) 817 remoteInputs = append(remoteInputs, tools...) 818 819 for _, rspFile := range rspFiles { 820 remoteInputs = append(remoteInputs, rspFile.file) 821 remoteRspFiles = append(remoteRspFiles, rspFile.file) 822 } 823 824 if len(remoteInputs) > 0 { 825 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list") 826 writeRspFileRule(r.ctx, inputsListFile, remoteInputs) 827 remoteRspFiles = append(remoteRspFiles, inputsListFile) 828 // Add the new rsp file as an extra input to the rule. 829 inputs = append(inputs, inputsListFile) 830 } 831 832 r.rbeParams.OutputFiles = outputs.Strings() 833 r.rbeParams.RSPFiles = remoteRspFiles.Strings() 834 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper()) 835 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'" 836 } 837 } 838 839 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to 840 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and 841 // ImplicitOutputs doesn't matter. 842 output := outputs[0] 843 implicitOutputs := outputs[1:] 844 845 var rspFile, rspFileContent string 846 var rspFileInputs Paths 847 if len(rspFiles) > 0 { 848 // The first rsp files uses Ninja's rsp file support for the rule 849 rspFile = rspFiles[0].file.String() 850 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency 851 // list and in the contents of the rsp file. Inputs to the rule that are not in the 852 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in". 853 rspFileContent = "$in" 854 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...) 855 856 for _, rspFile := range rspFiles[1:] { 857 // Any additional rsp files need an extra rule to write the file. 858 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths) 859 // The main rule needs to depend on the inputs listed in the extra rsp file. 860 inputs = append(inputs, rspFile.paths...) 861 // The main rule needs to depend on the extra rsp file. 862 inputs = append(inputs, rspFile.file) 863 } 864 } 865 866 var pool blueprint.Pool 867 if r.ctx.Config().UseGoma() && r.remoteable.Goma { 868 // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool. 869 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE { 870 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool. 871 pool = remotePool 872 } else if r.highmem { 873 pool = highmemPool 874 } else if r.ctx.Config().UseRemoteBuild() { 875 pool = localPool 876 } 877 878 // If the command length is getting close to linux's maximum, dump it to a file, which allows 879 // for longer commands. 880 if len(commandString) > 100000 { 881 hasher := sha256.New() 882 hasher.Write([]byte(output.String())) 883 script := PathForOutput(r.ctx, "rule_builder_scripts", fmt.Sprintf("%x.sh", hasher.Sum(nil))) 884 commandString = "set -eu\n\n" + commandString + "\n" 885 WriteExecutableFileRuleVerbatim(r.ctx, script, commandString) 886 inputs = append(inputs, script) 887 commandString = script.String() 888 } 889 890 commandString = proptools.NinjaEscape(commandString) 891 892 args_vars := make([]string, len(r.args)) 893 i := 0 894 for k, _ := range r.args { 895 args_vars[i] = k 896 i++ 897 } 898 r.ctx.Build(r.pctx, BuildParams{ 899 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{ 900 Command: commandString, 901 CommandDeps: proptools.NinjaEscapeList(tools.Strings()), 902 Restat: r.restat, 903 Rspfile: proptools.NinjaEscape(rspFile), 904 RspfileContent: rspFileContent, 905 Pool: pool, 906 }, args_vars...), 907 Inputs: rspFileInputs, 908 Implicits: inputs, 909 OrderOnly: r.OrderOnlys(), 910 Validations: r.Validations(), 911 Output: output, 912 ImplicitOutputs: implicitOutputs, 913 Depfile: depFile, 914 Deps: depFormat, 915 Description: desc, 916 Args: r.args, 917 }) 918} 919 920// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the 921// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the 922// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single 923// space as a separator from the previous method. 924type RuleBuilderCommand struct { 925 rule *RuleBuilder 926 927 buf strings.Builder 928 inputs Paths 929 implicits Paths 930 orderOnlys Paths 931 validations Paths 932 outputs WritablePaths 933 depFiles WritablePaths 934 tools Paths 935 packagedTools []PackagingSpec 936 rspFiles []rspFileAndPaths 937 implicitDirectories DirectoryPaths 938} 939 940type rspFileAndPaths struct { 941 file WritablePath 942 paths Paths 943} 944 945func checkPathNotNil(path Path) { 946 if path == nil { 947 panic("rule_builder paths cannot be nil") 948 } 949} 950 951func (c *RuleBuilderCommand) addInput(path Path) string { 952 checkPathNotNil(path) 953 c.inputs = append(c.inputs, path) 954 return c.PathForInput(path) 955} 956 957func (c *RuleBuilderCommand) addImplicit(path Path) { 958 checkPathNotNil(path) 959 c.implicits = append(c.implicits, path) 960} 961 962func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) { 963 c.implicitDirectories = append(c.implicitDirectories, path) 964} 965 966func (c *RuleBuilderCommand) addOrderOnly(path Path) { 967 checkPathNotNil(path) 968 c.orderOnlys = append(c.orderOnlys, path) 969} 970 971// PathForInput takes an input path and returns the appropriate path to use on the command line. If 972// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a 973// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the 974// original path. 975func (c *RuleBuilderCommand) PathForInput(path Path) string { 976 if c.rule.sbox { 977 rel, inSandbox := c.rule._sboxPathForInputRel(path) 978 if inSandbox { 979 rel = filepath.Join(sboxSandboxBaseDir, rel) 980 } 981 return rel 982 } else if c.rule.nsjail { 983 return c.rule.nsjailPathForInputRel(path) 984 } 985 return path.String() 986} 987 988// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the 989// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it 990// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it 991// returns the original paths. 992func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string { 993 ret := make([]string, len(paths)) 994 for i, path := range paths { 995 ret[i] = c.PathForInput(path) 996 } 997 return ret 998} 999 1000// PathForOutput takes an output path and returns the appropriate path to use on the command 1001// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the 1002// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the 1003// original path. 1004func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string { 1005 if c.rule.sbox { 1006 // Errors will be handled in RuleBuilder.Build where we have a context to report them 1007 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String()) 1008 return filepath.Join(sboxOutDir, rel) 1009 } else if c.rule.nsjail { 1010 // Errors will be handled in RuleBuilder.Build where we have a context to report them 1011 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String()) 1012 return filepath.Join(nsjailOutDir, rel) 1013 } 1014 return path.String() 1015} 1016 1017func sboxPathForToolRel(ctx BuilderContext, path Path) string { 1018 // Errors will be handled in RuleBuilder.Build where we have a context to report them 1019 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "") 1020 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String()) 1021 if isRelOutSoong { 1022 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out 1023 return filepath.Join(sboxToolsSubDir, "out", relOutSoong) 1024 } 1025 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src 1026 return filepath.Join(sboxToolsSubDir, "src", path.String()) 1027} 1028 1029func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) { 1030 // Errors will be handled in RuleBuilder.Build where we have a context to report them 1031 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String()) 1032 if isRelSboxOut { 1033 return filepath.Join(sboxOutSubDir, rel), true 1034 } 1035 if r.sboxInputs { 1036 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that 1037 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they 1038 // will be copied to relative paths under __SBOX_OUT_DIR__/out. 1039 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String()) 1040 if isRelOut { 1041 return filepath.Join(sboxOutSubDir, rel), true 1042 } 1043 } 1044 return path.String(), false 1045} 1046 1047func (r *RuleBuilder) sboxPathForInputRel(path Path) string { 1048 rel, _ := r._sboxPathForInputRel(path) 1049 return rel 1050} 1051 1052func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string { 1053 ret := make([]string, len(paths)) 1054 for i, path := range paths { 1055 ret[i] = r.sboxPathForInputRel(path) 1056 } 1057 return ret 1058} 1059 1060func sboxPathForPackagedToolRel(spec PackagingSpec) string { 1061 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage) 1062} 1063 1064func nsjailPathForToolRel(ctx BuilderContext, path Path) string { 1065 // Errors will be handled in RuleBuilder.Build where we have a context to report them 1066 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "") 1067 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String()) 1068 if isRelOutSoong { 1069 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out 1070 return filepath.Join(nsjailToolsSubDir, "out", relOutSoong) 1071 } 1072 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src 1073 return filepath.Join(nsjailToolsSubDir, "src", path.String()) 1074} 1075 1076func (r *RuleBuilder) nsjailPathForInputRel(path Path) string { 1077 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String()) 1078 if isRelSboxOut { 1079 return filepath.Join(nsjailOutDir, rel) 1080 } 1081 return path.String() 1082} 1083 1084func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string { 1085 ret := make([]string, len(paths)) 1086 for i, path := range paths { 1087 ret[i] = r.nsjailPathForInputRel(path) 1088 } 1089 return ret 1090} 1091 1092func nsjailPathForPackagedToolRel(spec PackagingSpec) string { 1093 return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage) 1094} 1095 1096// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the 1097// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to 1098// reference the tool. 1099func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string { 1100 if c.rule.sboxTools { 1101 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec)) 1102 } else if c.rule.nsjail { 1103 return nsjailPathForPackagedToolRel(spec) 1104 } else { 1105 panic("PathForPackagedTool() requires SandboxTools() or Nsjail()") 1106 } 1107} 1108 1109// PathForTool takes a path to a tool, which may be an output file or a source file, and returns 1110// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path 1111// if it is not. This can be used on the RuleBuilder command line to reference the tool. 1112func (c *RuleBuilderCommand) PathForTool(path Path) string { 1113 if c.rule.sbox && c.rule.sboxTools { 1114 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)) 1115 } else if c.rule.nsjail { 1116 return nsjailPathForToolRel(c.rule.ctx, path) 1117 } 1118 return path.String() 1119} 1120 1121// PathsForTools takes a list of paths to tools, which may be output files or source files, and 1122// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the 1123// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool. 1124func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string { 1125 if c.rule.sbox && c.rule.sboxTools { 1126 var ret []string 1127 for _, path := range paths { 1128 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))) 1129 } 1130 return ret 1131 } else if c.rule.nsjail { 1132 var ret []string 1133 for _, path := range paths { 1134 ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path)) 1135 } 1136 return ret 1137 } 1138 return paths.Strings() 1139} 1140 1141// PackagedTool adds the specified tool path to the command line. It can only be used with tool 1142// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox. 1143func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand { 1144 c.packagedTools = append(c.packagedTools, spec) 1145 if c.rule.sboxTools { 1146 c.Text(sboxPathForPackagedToolRel(spec)) 1147 } else if c.rule.nsjail { 1148 c.Text(nsjailPathForPackagedToolRel(spec)) 1149 } else { 1150 panic("PackagedTool() requires SandboxTools() or Nsjail()") 1151 } 1152 return c 1153} 1154 1155// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command 1156// line. It can only be used with tool sandboxing enabled by SandboxTools(). 1157func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand { 1158 if !c.rule.sboxTools && !c.rule.nsjail { 1159 panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()") 1160 } 1161 1162 c.packagedTools = append(c.packagedTools, spec) 1163 return c 1164} 1165 1166// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command 1167// line. It can only be used with tool sandboxing enabled by SandboxTools(). 1168func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand { 1169 if !c.rule.sboxTools && !c.rule.nsjail { 1170 panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()") 1171 } 1172 1173 c.packagedTools = append(c.packagedTools, specs...) 1174 return c 1175} 1176 1177// Text adds the specified raw text to the command line. The text should not contain input or output paths or the 1178// rule will not have them listed in its dependencies or outputs. 1179func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand { 1180 if c.buf.Len() > 0 { 1181 c.buf.WriteByte(' ') 1182 } 1183 c.buf.WriteString(text) 1184 return c 1185} 1186 1187// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or 1188// the rule will not have them listed in its dependencies or outputs. 1189func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand { 1190 return c.Text(fmt.Sprintf(format, a...)) 1191} 1192 1193// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the 1194// rule will not have them listed in its dependencies or outputs. 1195func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand { 1196 return c.Text(flag) 1197} 1198 1199// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or 1200// output paths or the rule will not have them listed in its dependencies or outputs. 1201func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand { 1202 if flag != nil { 1203 c.Text(*flag) 1204 } 1205 1206 return c 1207} 1208 1209// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the 1210// rule will not have them listed in its dependencies or outputs. 1211func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand { 1212 for _, flag := range flags { 1213 c.Text(flag) 1214 } 1215 return c 1216} 1217 1218// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag 1219// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or 1220// outputs. 1221func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand { 1222 return c.Text(flag + arg) 1223} 1224 1225// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to 1226// calling FlagWithArg for argument. 1227func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand { 1228 for _, arg := range args { 1229 c.FlagWithArg(flag, arg) 1230 } 1231 return c 1232} 1233 1234// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep 1235// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or 1236// the rule will not have them listed in its dependencies or outputs. 1237func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand { 1238 return c.Text(flag + strings.Join(list, sep)) 1239} 1240 1241// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by 1242// RuleBuilder.Tools. 1243func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand { 1244 checkPathNotNil(path) 1245 c.tools = append(c.tools, path) 1246 return c.Text(c.PathForTool(path)) 1247} 1248 1249// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools. 1250func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand { 1251 checkPathNotNil(path) 1252 c.tools = append(c.tools, path) 1253 return c 1254} 1255 1256// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools. 1257func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand { 1258 for _, path := range paths { 1259 c.ImplicitTool(path) 1260 } 1261 return c 1262} 1263 1264// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will 1265// be also added to the dependencies returned by RuleBuilder.Tools. 1266// 1267// It is equivalent to: 1268// 1269// cmd.Tool(ctx.Config().HostToolPath(ctx, tool)) 1270func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand { 1271 if c.rule.ctx.Config().UseHostMusl() { 1272 // If the host is using musl, assume that the tool was built against musl libc and include 1273 // libc_musl.so in the sandbox. 1274 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions 1275 // this could be a dependency + TransitivePackagingSpecs. 1276 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl")) 1277 } 1278 return c.builtToolWithoutDeps(tool) 1279} 1280 1281// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used 1282// internally by RuleBuilder for helper tools that are known to be compiled statically. 1283func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand { 1284 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool)) 1285} 1286 1287// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the 1288// dependencies returned by RuleBuilder.Tools. 1289// 1290// It is equivalent to: 1291// 1292// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool)) 1293func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand { 1294 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool)) 1295} 1296 1297// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by 1298// RuleBuilder.Inputs. 1299func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand { 1300 return c.Text(c.addInput(path)) 1301} 1302 1303// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the 1304// dependencies returned by RuleBuilder.Inputs. 1305func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand { 1306 for _, path := range paths { 1307 c.Input(path) 1308 } 1309 return c 1310} 1311 1312// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the 1313// command line. 1314func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand { 1315 c.addImplicit(path) 1316 return c 1317} 1318 1319// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the 1320// command line. 1321func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand { 1322 for _, path := range paths { 1323 c.addImplicit(path) 1324 } 1325 return c 1326} 1327 1328// ImplicitDirectory adds the specified input directory to the dependencies without modifying the 1329// command line. Added directories will be bind-mounted for the nsjail. 1330func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand { 1331 if !c.rule.nsjail { 1332 panic("ImplicitDirectory() must be called after Nsjail()") 1333 } 1334 c.addImplicitDirectory(path) 1335 return c 1336} 1337 1338// GetImplicits returns the command's implicit inputs. 1339func (c *RuleBuilderCommand) GetImplicits() Paths { 1340 return c.implicits 1341} 1342 1343// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys 1344// without modifying the command line. 1345func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand { 1346 c.addOrderOnly(path) 1347 return c 1348} 1349 1350// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys 1351// without modifying the command line. 1352func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand { 1353 for _, path := range paths { 1354 c.addOrderOnly(path) 1355 } 1356 return c 1357} 1358 1359// Validation adds the specified input path to the validation dependencies by 1360// RuleBuilder.Validations without modifying the command line. 1361func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand { 1362 checkPathNotNil(path) 1363 c.validations = append(c.validations, path) 1364 return c 1365} 1366 1367// Validations adds the specified input paths to the validation dependencies by 1368// RuleBuilder.Validations without modifying the command line. 1369func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand { 1370 for _, path := range paths { 1371 c.Validation(path) 1372 } 1373 return c 1374} 1375 1376// Output adds the specified output path to the command line. The path will also be added to the outputs returned by 1377// RuleBuilder.Outputs. 1378func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand { 1379 checkPathNotNil(path) 1380 c.outputs = append(c.outputs, path) 1381 return c.Text(c.PathForOutput(path)) 1382} 1383 1384// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to 1385// the outputs returned by RuleBuilder.Outputs. 1386func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand { 1387 for _, path := range paths { 1388 c.Output(path) 1389 } 1390 return c 1391} 1392 1393// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox, 1394// and will be the temporary output directory managed by sbox, not the final one. 1395func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand { 1396 if !c.rule.sbox { 1397 panic("OutputDir only valid with Sbox") 1398 } 1399 path := sboxOutDir 1400 if len(subPathComponents) > 0 { 1401 path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...) 1402 } 1403 return c.Text(path) 1404} 1405 1406// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command 1407// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to 1408// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together. 1409func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand { 1410 checkPathNotNil(path) 1411 c.depFiles = append(c.depFiles, path) 1412 return c.Text(c.PathForOutput(path)) 1413} 1414 1415// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying 1416// the command line. 1417func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand { 1418 c.outputs = append(c.outputs, path) 1419 return c 1420} 1421 1422// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying 1423// the command line. 1424func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand { 1425 c.outputs = append(c.outputs, paths...) 1426 return c 1427} 1428 1429// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying 1430// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles 1431// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the 1432// depfiles together. 1433func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand { 1434 c.depFiles = append(c.depFiles, path) 1435 return c 1436} 1437 1438// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path 1439// will also be added to the dependencies returned by RuleBuilder.Inputs. 1440func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand { 1441 return c.Text(flag + c.addInput(path)) 1442} 1443 1444// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep 1445// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by 1446// RuleBuilder.Inputs. 1447func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand { 1448 strs := make([]string, len(paths)) 1449 for i, path := range paths { 1450 strs[i] = c.addInput(path) 1451 } 1452 return c.FlagWithList(flag, strs, sep) 1453} 1454 1455// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also 1456// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for 1457// each input path. 1458func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand { 1459 for _, path := range paths { 1460 c.FlagWithInput(flag, path) 1461 } 1462 return c 1463} 1464 1465// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path 1466// will also be added to the outputs returned by RuleBuilder.Outputs. 1467func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand { 1468 c.outputs = append(c.outputs, path) 1469 return c.Text(flag + c.PathForOutput(path)) 1470} 1471 1472// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path 1473// will also be added to the outputs returned by RuleBuilder.Outputs. 1474func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand { 1475 c.depFiles = append(c.depFiles, path) 1476 return c.Text(flag + c.PathForOutput(path)) 1477} 1478 1479// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with 1480// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the 1481// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any 1482// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional 1483// uses will result in an auxiliary rules to write the rspFile contents. 1484func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand { 1485 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be 1486 // generated. 1487 if paths == nil { 1488 paths = Paths{} 1489 } 1490 1491 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths}) 1492 1493 if c.rule.sbox { 1494 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel { 1495 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q", 1496 rspFile.String(), c.rule.outDir.String())) 1497 } 1498 } 1499 1500 c.FlagWithArg(flag, c.PathForInput(rspFile)) 1501 return c 1502} 1503 1504// String returns the command line. 1505func (c *RuleBuilderCommand) String() string { 1506 return c.buf.String() 1507} 1508 1509// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox() 1510// and returns sbox testproto generated by the RuleBuilder. 1511func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest { 1512 t.Helper() 1513 content := ContentFromFileRuleForTests(t, ctx, params) 1514 manifest := sbox_proto.Manifest{} 1515 err := prototext.Unmarshal([]byte(content), &manifest) 1516 if err != nil { 1517 t.Fatalf("failed to unmarshal manifest: %s", err.Error()) 1518 } 1519 return &manifest 1520} 1521 1522func ninjaNameEscape(s string) string { 1523 b := []byte(s) 1524 escaped := false 1525 for i, c := range b { 1526 valid := (c >= 'a' && c <= 'z') || 1527 (c >= 'A' && c <= 'Z') || 1528 (c >= '0' && c <= '9') || 1529 (c == '_') || 1530 (c == '-') || 1531 (c == '.') 1532 if !valid { 1533 b[i] = '_' 1534 escaped = true 1535 } 1536 } 1537 if escaped { 1538 s = string(b) 1539 } 1540 return s 1541} 1542 1543// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line 1544// or the sbox textproto manifest change even if the input files are not listed on the command line. 1545func hashSrcFiles(srcFiles Paths) string { 1546 h := sha256.New() 1547 srcFileList := strings.Join(srcFiles.Strings(), "\n") 1548 h.Write([]byte(srcFileList)) 1549 return fmt.Sprintf("%x", h.Sum(nil)) 1550} 1551 1552// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests 1553// that need to call methods that take a BuilderContext. 1554func BuilderContextForTesting(config Config) BuilderContext { 1555 pathCtx := PathContextForTesting(config) 1556 return builderContextForTests{ 1557 PathContext: pathCtx, 1558 } 1559} 1560 1561type builderContextForTests struct { 1562 PathContext 1563} 1564 1565func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule { 1566 return nil 1567} 1568func (builderContextForTests) Build(PackageContext, BuildParams) {} 1569 1570func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) { 1571 buf := &strings.Builder{} 1572 err := response.WriteRspFile(buf, paths.Strings()) 1573 if err != nil { 1574 // There should never be I/O errors writing to a bytes.Buffer. 1575 panic(err) 1576 } 1577 WriteFileRule(ctx, rspFile, buf.String()) 1578} 1579