aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2019-04-12 11:11:38 -0700
committerColin Cross <ccross@android.com>2019-11-21 14:47:46 -0800
commit0ce5d05f76989c4cc699e0dd6cd1f906a9e0f16d (patch)
treee0cc48f7aafafdb8ccda16d55e124800da9e1f16
parent51510a887743de47acc6f0e52ddc1e527d5bf3ab (diff)
downloadbuild_soong-0ce5d05f76989c4cc699e0dd6cd1f906a9e0f16d.tar.gz
build_soong-0ce5d05f76989c4cc699e0dd6cd1f906a9e0f16d.tar.bz2
build_soong-0ce5d05f76989c4cc699e0dd6cd1f906a9e0f16d.zip
Support RuleBuilder.Sbox to wrap commands in sbox
This essentially allows you to declare that everything in a directory will be created by the rule, and we'll ensure that your command actually writes out all of the claimed outputs, and remove any other files that previously existed in that directory. Bug: 144948629 Test: built-in tests Change-Id: I990dce2b3a0d89ebd2736ac1a0cadfb5864c6e73 Merged-In: I990dce2b3a0d89ebd2736ac1a0cadfb5864c6e73 (cherry picked from commit 633c50229544db07f48e75833cab655a294f677d)
-rw-r--r--Android.bp1
-rw-r--r--android/paths.go17
-rw-r--r--android/rule_builder.go196
-rw-r--r--android/rule_builder_test.go237
-rw-r--r--cmd/sbox/sbox.go2
5 files changed, 330 insertions, 123 deletions
diff --git a/Android.bp b/Android.bp
index 1d65dff0..2692b1be 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,6 +37,7 @@ bootstrap_go_package {
"blueprint-bootstrap",
"soong",
"soong-env",
+ "soong-shared",
],
srcs: [
"android/androidmk.go",
diff --git a/android/paths.go b/android/paths.go
index 8cc7057a..0f20b844 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1267,16 +1267,23 @@ func Rel(ctx PathContext, basePath string, targetPath string) string {
// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if
// targetPath is not inside basePath.
func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) {
+ rel, isRel, err := maybeRelErr(basePath, targetPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+ return rel, isRel
+}
+
+func maybeRelErr(basePath string, targetPath string) (string, bool, error) {
// filepath.Rel returns an error if one path is absolute and the other is not, handle that case first.
if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) {
- return "", false
+ return "", false, nil
}
rel, err := filepath.Rel(basePath, targetPath)
if err != nil {
- reportPathError(ctx, err)
- return "", false
+ return "", false, err
} else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") {
- return "", false
+ return "", false, nil
}
- return rel, true
+ return rel, true, nil
}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 2d0fac16..4a3b0223 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -21,6 +21,8 @@ import (
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
+
+ "android/soong/shared"
)
// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
@@ -30,6 +32,8 @@ type RuleBuilder struct {
installs RuleBuilderInstalls
temporariesSet map[WritablePath]bool
restat bool
+ sbox bool
+ sboxOutDir WritablePath
missingDeps []string
}
@@ -73,11 +77,36 @@ func (r *RuleBuilder) MissingDeps(missingDeps []string) {
}
// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
+//
+// Restat is not compatible with Sbox()
func (r *RuleBuilder) Restat() *RuleBuilder {
+ if r.sbox {
+ panic("Restat() is not compatible with Sbox()")
+ }
r.restat = true
return r
}
+// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
+// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
+// that all outputs have been written, and will discard any output files that were not specified.
+//
+// Sbox is not compatible with Restat()
+func (r *RuleBuilder) Sbox(outputDir WritablePath) *RuleBuilder {
+ if r.sbox {
+ panic("Sbox() may not be called more than once")
+ }
+ if len(r.commands) > 0 {
+ panic("Sbox() may not be called after Command()")
+ }
+ if r.restat {
+ panic("Sbox() is not compatible with Restat()")
+ }
+ r.sbox = true
+ r.sboxOutDir = outputDir
+ return r
+}
+
// Install associates an output of the rule with an install location, which can be retrieved later using
// RuleBuilder.Installs.
func (r *RuleBuilder) Install(from Path, to string) {
@@ -88,7 +117,10 @@ func (r *RuleBuilder) Install(from Path, to string) {
// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
// race with any call to Build.
func (r *RuleBuilder) Command() *RuleBuilderCommand {
- command := &RuleBuilderCommand{}
+ command := &RuleBuilderCommand{
+ sbox: r.sbox,
+ sboxOutDir: r.sboxOutDir,
+ }
r.commands = append(r.commands, command)
return command
}
@@ -120,12 +152,16 @@ func (r *RuleBuilder) DeleteTemporaryFiles() {
// that are also outputs of another command in the same RuleBuilder are filtered out.
func (r *RuleBuilder) Inputs() Paths {
outputs := r.outputSet()
+ depFiles := r.depFileSet()
inputs := make(map[string]Path)
for _, c := range r.commands {
for _, input := range c.inputs {
- if _, isOutput := outputs[input.String()]; !isOutput {
- inputs[input.String()] = input
+ inputStr := input.String()
+ if _, isOutput := outputs[inputStr]; !isOutput {
+ if _, isDepFile := depFiles[inputStr]; !isDepFile {
+ inputs[input.String()] = input
+ }
}
}
}
@@ -171,6 +207,16 @@ func (r *RuleBuilder) Outputs() WritablePaths {
return outputList
}
+func (r *RuleBuilder) depFileSet() map[string]WritablePath {
+ depFiles := make(map[string]WritablePath)
+ for _, c := range r.commands {
+ for _, depFile := range c.depFiles {
+ depFiles[depFile.String()] = depFile
+ }
+ }
+ return depFiles
+}
+
// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
func (r *RuleBuilder) DepFiles() WritablePaths {
@@ -237,9 +283,9 @@ var _ BuilderContext = ModuleContext(nil)
var _ BuilderContext = SingletonContext(nil)
func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
- return (&RuleBuilderCommand{}).
+ return r.Command().
Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
- Flags(depFiles.Strings())
+ Inputs(depFiles.Paths())
}
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
@@ -259,9 +305,6 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
return
}
- tools := r.Tools()
- commands := r.Commands()
-
var depFile WritablePath
var depFormat blueprint.Deps
if depFiles := r.DepFiles(); len(depFiles) > 0 {
@@ -269,37 +312,75 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
depFormat = blueprint.DepsGCC
if len(depFiles) > 1 {
// Add a command locally that merges all depfiles together into the first depfile.
- cmd := r.depFileMergerCmd(ctx, depFiles)
- commands = append(commands, string(cmd.buf))
- tools = append(tools, cmd.tools...)
+ r.depFileMergerCmd(ctx, depFiles)
+
+ if r.sbox {
+ // Check for Rel() errors, as all depfiles should be in the output dir
+ for _, path := range depFiles[1:] {
+ Rel(ctx, r.sboxOutDir.String(), path.String())
+ }
+ }
}
}
- // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
- // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
- // doesn't matter.
- var output WritablePath
- var implicitOutputs WritablePaths
- if outputs := r.Outputs(); len(outputs) > 0 {
- output = outputs[0]
- implicitOutputs = outputs[1:]
+ tools := r.Tools()
+ commands := r.Commands()
+ outputs := r.Outputs()
+
+ if len(commands) == 0 {
+ return
+ }
+ if len(outputs) == 0 {
+ panic("No outputs specified from any Commands")
}
- if len(commands) > 0 {
- ctx.Build(pctx, BuildParams{
- Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: strings.Join(proptools.NinjaEscapeList(commands), " && "),
- CommandDeps: tools.Strings(),
- Restat: r.restat,
- }),
- Implicits: r.Inputs(),
- Output: output,
- ImplicitOutputs: implicitOutputs,
- Depfile: depFile,
- Deps: depFormat,
- Description: desc,
- })
+ commandString := strings.Join(proptools.NinjaEscapeList(commands), " && ")
+
+ if r.sbox {
+ sboxOutputs := make([]string, len(outputs))
+ for i, output := range outputs {
+ sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
+ }
+
+ if depFile != nil {
+ sboxOutputs = append(sboxOutputs, "__SBOX_OUT_DIR__/"+Rel(ctx, r.sboxOutDir.String(), depFile.String()))
+ }
+
+ commandString = proptools.ShellEscape(commandString)
+ if !strings.HasPrefix(commandString, `'`) {
+ commandString = `'` + commandString + `'`
+ }
+
+ sboxCmd := &RuleBuilderCommand{}
+ sboxCmd.Tool(ctx.Config().HostToolPath(ctx, "sbox")).
+ Flag("-c").Text(commandString).
+ Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
+ Flag("--output-root").Text(r.sboxOutDir.String()).
+ Flags(sboxOutputs)
+
+ commandString = string(sboxCmd.buf)
+ tools = append(tools, sboxCmd.tools...)
}
+
+ // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
+ // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
+ // doesn't matter.
+ output := outputs[0]
+ implicitOutputs := outputs[1:]
+
+ ctx.Build(pctx, BuildParams{
+ Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
+ Command: commandString,
+ CommandDeps: tools.Strings(),
+ Restat: r.restat,
+ }),
+ Implicits: r.Inputs(),
+ Output: output,
+ ImplicitOutputs: implicitOutputs,
+ Depfile: depFile,
+ Deps: depFormat,
+ Description: desc,
+ })
}
// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
@@ -312,6 +393,28 @@ type RuleBuilderCommand struct {
outputs WritablePaths
depFiles WritablePaths
tools Paths
+
+ sbox bool
+ sboxOutDir WritablePath
+}
+
+func (c *RuleBuilderCommand) addInput(path Path) string {
+ if c.sbox {
+ if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
+ return "__SBOX_OUT_DIR__/" + rel
+ }
+ }
+ c.inputs = append(c.inputs, path)
+ return path.String()
+}
+
+func (c *RuleBuilderCommand) outputStr(path Path) string {
+ if c.sbox {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ rel, _, _ := maybeRelErr(c.sboxOutDir.String(), path.String())
+ return "__SBOX_OUT_DIR__/" + rel
+ }
+ return path.String()
}
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
@@ -378,8 +481,7 @@ func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
// RuleBuilder.Inputs.
func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
- c.inputs = append(c.inputs, path)
- return c.Text(path.String())
+ return c.Text(c.addInput(path))
}
// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
@@ -394,14 +496,16 @@ func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
// command line.
func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
- c.inputs = append(c.inputs, path)
+ c.addInput(path)
return c
}
// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
// command line.
func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
- c.inputs = append(c.inputs, paths...)
+ for _, path := range paths {
+ c.addInput(path)
+ }
return c
}
@@ -409,7 +513,7 @@ func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
// RuleBuilder.Outputs.
func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(path.String())
+ return c.Text(c.outputStr(path))
}
// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
@@ -426,7 +530,7 @@ func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(path.String())
+ return c.Text(c.outputStr(path))
}
// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
@@ -455,16 +559,18 @@ func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderComm
// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
// will also be added to the dependencies returned by RuleBuilder.Inputs.
func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
- c.inputs = append(c.inputs, path)
- return c.Text(flag + path.String())
+ return c.Text(flag + c.addInput(path))
}
// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
// RuleBuilder.Inputs.
func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
- c.inputs = append(c.inputs, paths...)
- return c.FlagWithList(flag, paths.Strings(), sep)
+ strs := make([]string, len(paths))
+ for i, path := range paths {
+ strs[i] = c.addInput(path)
+ }
+ return c.FlagWithList(flag, strs, sep)
}
// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
@@ -481,14 +587,14 @@ func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBui
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(flag + path.String())
+ return c.Text(flag + c.outputStr(path))
}
// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(flag + path.String())
+ return c.Text(flag + c.outputStr(path))
}
// String returns the command line.
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 7bad0258..df0f2564 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -22,6 +22,10 @@ import (
"reflect"
"strings"
"testing"
+
+ "github.com/google/blueprint"
+
+ "android/soong/shared"
)
func pathContext() PathContext {
@@ -234,8 +238,6 @@ func ExampleRuleBuilderCommand_FlagWithList() {
}
func TestRuleBuilder(t *testing.T) {
- rule := NewRuleBuilder()
-
fs := map[string][]byte{
"dep_fixer": nil,
"input": nil,
@@ -249,73 +251,114 @@ func TestRuleBuilder(t *testing.T) {
ctx := PathContextForTesting(TestConfig("out", nil), fs)
- cmd := rule.Command().
- DepFile(PathForOutput(ctx, "DepFile")).
- Flag("Flag").
- FlagWithArg("FlagWithArg=", "arg").
- FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
- FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
- FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
- Implicit(PathForSource(ctx, "Implicit")).
- ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
- ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
- Input(PathForSource(ctx, "Input")).
- Output(PathForOutput(ctx, "Output")).
- Text("Text").
- Tool(PathForSource(ctx, "Tool"))
-
- rule.Command().
- Text("command2").
- DepFile(PathForOutput(ctx, "depfile2")).
- Input(PathForSource(ctx, "input2")).
- Output(PathForOutput(ctx, "output2")).
- Tool(PathForSource(ctx, "tool2"))
-
- // Test updates to the first command after the second command has been started
- cmd.Text("after command2")
- // Test updating a command when the previous update did not replace the cmd variable
- cmd.Text("old cmd")
-
- // Test a command that uses the output of a previous command as an input
- rule.Command().
- Text("command3").
- Input(PathForSource(ctx, "input3")).
- Input(PathForOutput(ctx, "output2")).
- Output(PathForOutput(ctx, "output3"))
-
- wantCommands := []string{
- "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
- "command2 out/depfile2 input2 out/output2 tool2",
- "command3 input3 out/output2 out/output3",
+ addCommands := func(rule *RuleBuilder) {
+ cmd := rule.Command().
+ DepFile(PathForOutput(ctx, "DepFile")).
+ Flag("Flag").
+ FlagWithArg("FlagWithArg=", "arg").
+ FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
+ FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
+ FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
+ Implicit(PathForSource(ctx, "Implicit")).
+ ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
+ ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
+ Input(PathForSource(ctx, "Input")).
+ Output(PathForOutput(ctx, "Output")).
+ Text("Text").
+ Tool(PathForSource(ctx, "Tool"))
+
+ rule.Command().
+ Text("command2").
+ DepFile(PathForOutput(ctx, "depfile2")).
+ Input(PathForSource(ctx, "input2")).
+ Output(PathForOutput(ctx, "output2")).
+ Tool(PathForSource(ctx, "tool2"))
+
+ // Test updates to the first command after the second command has been started
+ cmd.Text("after command2")
+ // Test updating a command when the previous update did not replace the cmd variable
+ cmd.Text("old cmd")
+
+ // Test a command that uses the output of a previous command as an input
+ rule.Command().
+ Text("command3").
+ Input(PathForSource(ctx, "input3")).
+ Input(PathForOutput(ctx, "output2")).
+ Output(PathForOutput(ctx, "output3"))
}
- wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
-
wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"})
wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
- if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
- t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
- }
+ t.Run("normal", func(t *testing.T) {
+ rule := NewRuleBuilder()
+ addCommands(rule)
- if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
- t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
- }
+ wantCommands := []string{
+ "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
+ "command2 out/depfile2 input2 out/output2 tool2",
+ "command3 input3 out/output2 out/output3",
+ }
- if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
- }
+ wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
+
+ if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+ t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
+ }
+ })
+
+ t.Run("sbox", func(t *testing.T) {
+ rule := NewRuleBuilder().Sbox(PathForOutput(ctx))
+ addCommands(rule)
+
+ wantCommands := []string{
+ "__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output Text Tool after command2 old cmd",
+ "command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2",
+ "command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3",
+ }
+
+ wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_OUT_DIR__/DepFile __SBOX_OUT_DIR__/depfile __SBOX_OUT_DIR__/ImplicitDepFile __SBOX_OUT_DIR__/depfile2"
+
+ if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+ t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
+ }
+ })
}
func testRuleBuilderFactory() Module {
@@ -329,14 +372,19 @@ type testRuleBuilderModule struct {
ModuleBase
properties struct {
Src string
+
+ Restat bool
+ Sbox bool
}
}
func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
in := PathForSource(ctx, t.properties.Src)
out := PathForModuleOut(ctx, ctx.ModuleName())
+ outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
+ outDir := PathForModuleOut(ctx)
- testRuleBuilder_Build(ctx, in, out)
+ testRuleBuilder_Build(ctx, in, out, outDep, outDir, t.properties.Restat, t.properties.Sbox)
}
type testRuleBuilderSingleton struct{}
@@ -348,15 +396,23 @@ func testRuleBuilderSingletonFactory() Singleton {
func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
in := PathForSource(ctx, "bar")
out := PathForOutput(ctx, "baz")
- testRuleBuilder_Build(ctx, in, out)
+ outDep := PathForOutput(ctx, "baz.d")
+ outDir := PathForOutput(ctx)
+ testRuleBuilder_Build(ctx, in, out, outDep, outDir, true, false)
}
-func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
+func testRuleBuilder_Build(ctx BuilderContext, in Path, out, outDep, outDir WritablePath, restat, sbox bool) {
rule := NewRuleBuilder()
- rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out)
+ if sbox {
+ rule.Sbox(outDir)
+ }
+
+ rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out).ImplicitDepFile(outDep)
- rule.Restat()
+ if restat {
+ rule.Restat()
+ }
rule.Build(pctx, ctx, "rule", "desc")
}
@@ -372,6 +428,12 @@ func TestRuleBuilder_Build(t *testing.T) {
rule_builder_test {
name: "foo",
src: "bar",
+ restat: true,
+ }
+ rule_builder_test {
+ name: "foo_sbox",
+ src: "bar",
+ sbox: true,
}
`
@@ -391,9 +453,18 @@ func TestRuleBuilder_Build(t *testing.T) {
_, errs = ctx.PrepareBuildActions(config)
FailIfErrored(t, errs)
- check := func(t *testing.T, params TestingBuildParams, wantOutput string) {
- if len(params.RuleParams.CommandDeps) != 1 || params.RuleParams.CommandDeps[0] != "cp" {
- t.Errorf("want RuleParams.CommandDeps = [%q], got %q", "cp", params.RuleParams.CommandDeps)
+ check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
+ if params.RuleParams.Command != wantCommand {
+ t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
+ }
+
+ wantDeps := append([]string{"cp"}, extraCmdDeps...)
+ if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
+ t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
+ }
+
+ if params.RuleParams.Restat != wantRestat {
+ t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
}
if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
@@ -404,17 +475,39 @@ func TestRuleBuilder_Build(t *testing.T) {
t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
}
- if !params.RuleParams.Restat {
- t.Errorf("want RuleParams.Restat = true, got %v", params.RuleParams.Restat)
+ if len(params.ImplicitOutputs) != 0 {
+ t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
+ }
+
+ if params.Depfile.String() != wantDepfile {
+ t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
+ }
+
+ if params.Deps != blueprint.DepsGCC {
+ t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
}
}
t.Run("module", func(t *testing.T) {
+ outFile := filepath.Join(buildDir, ".intermediates", "foo", "foo")
check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
- filepath.Join(buildDir, ".intermediates", "foo", "foo"))
+ "cp bar "+outFile,
+ outFile, outFile+".d", true, nil)
+ })
+ t.Run("sbox", func(t *testing.T) {
+ outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
+ outFile := filepath.Join(outDir, "foo_sbox")
+ sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
+ sandboxPath := shared.TempDirForOutDir(buildDir)
+
+ cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " __SBOX_OUT_DIR__/foo_sbox __SBOX_OUT_DIR__/foo_sbox.d"
+
+ check(t, ctx.ModuleForTests("foo_sbox", "").Rule("rule"),
+ cmd, outFile, outFile+".d", false, []string{sbox})
})
t.Run("singleton", func(t *testing.T) {
+ outFile := filepath.Join(buildDir, "baz")
check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
- filepath.Join(buildDir, "baz"))
+ "cp bar "+outFile, outFile, outFile+".d", true, nil)
})
}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 4167edb3..4ac92953 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -56,7 +56,7 @@ func usageViolation(violation string) {
}
fmt.Fprintf(os.Stderr,
- "Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> --overwrite [--depfile-out depFile] <outputFile> [<outputFile>...]\n"+
+ "Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] <outputFile> [<outputFile>...]\n"+
"\n"+
"Deletes <outputRoot>,"+
"runs <commandToRun>,"+