Merge "Move genrule on top of RuleBuilder"

This commit is contained in:
Colin Cross 2020-11-18 18:44:19 +00:00 committed by Gerrit Code Review
commit d4a63eaeb7
7 changed files with 266 additions and 207 deletions

View file

@ -15,7 +15,9 @@
package android
import (
"crypto/sha256"
"fmt"
"path/filepath"
"sort"
"strings"
@ -25,6 +27,8 @@ import (
"android/soong/shared"
)
const sboxOutDir = "__SBOX_OUT_DIR__"
// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
// graph.
type RuleBuilder struct {
@ -134,7 +138,7 @@ func (r *RuleBuilder) Install(from Path, to string) {
func (r *RuleBuilder) Command() *RuleBuilderCommand {
command := &RuleBuilderCommand{
sbox: r.sbox,
sboxOutDir: r.sboxOutDir,
outDir: r.sboxOutDir,
}
r.commands = append(r.commands, command)
return command
@ -163,7 +167,7 @@ func (r *RuleBuilder) DeleteTemporaryFiles() {
}
// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
// input paths, such as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or
// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
func (r *RuleBuilder) Inputs() Paths {
@ -362,7 +366,7 @@ func (r *RuleBuilder) Commands() []string {
return commands
}
// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
// RuleBuilder.Command.
func (r *RuleBuilder) NinjaEscapedCommands() []string {
var commands []string
@ -427,6 +431,7 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
tools := r.Tools()
commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
inputs := r.Inputs()
if len(commands) == 0 {
return
@ -440,7 +445,7 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
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())
sboxOutputs[i] = filepath.Join(sboxOutDir, Rel(ctx, r.sboxOutDir.String(), output.String()))
}
commandString = proptools.ShellEscape(commandString)
@ -458,10 +463,19 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
sboxCmd.Flag("--depfile-out").Text(depFile.String())
}
// Add a hash of the list of input files to the xbox command line so that ninja reruns
// it when the list of input files changes.
sboxCmd.FlagWithArg("--input-hash ", hashSrcFiles(inputs))
sboxCmd.Flags(sboxOutputs)
commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
} else {
// If not using sbox the rule will run the command directly, put the hash of the
// list of input files in a comment at the end of the command line to ensure ninja
// reruns the rule when the list of input files changes.
commandString += " # hash of input list: " + hashSrcFiles(inputs)
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
@ -499,7 +513,7 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
Pool: pool,
}),
Inputs: rspFileInputs,
Implicits: r.Inputs(),
Implicits: inputs,
Output: output,
ImplicitOutputs: implicitOutputs,
SymlinkOutputs: r.SymlinkOutputs(),
@ -528,13 +542,15 @@ type RuleBuilderCommand struct {
unescapedSpans [][2]int
sbox bool
sboxOutDir WritablePath
// outDir is the directory that will contain the output files of the rules. sbox will copy
// the output files from the sandbox directory to this directory when it finishes.
outDir 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
if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
c.inputs = append(c.inputs, path)
@ -543,8 +559,8 @@ func (c *RuleBuilderCommand) addInput(path Path) string {
func (c *RuleBuilderCommand) addImplicit(path Path) string {
if c.sbox {
if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
return "__SBOX_OUT_DIR__/" + rel
if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
c.implicits = append(c.implicits, path)
@ -555,15 +571,22 @@ func (c *RuleBuilderCommand) addOrderOnly(path Path) {
c.orderOnlys = append(c.orderOnlys, path)
}
func (c *RuleBuilderCommand) outputStr(path Path) string {
func (c *RuleBuilderCommand) outputStr(path WritablePath) 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 SboxPathForOutput(path, c.outDir)
}
return path.String()
}
// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
// and returns the corresponding path for the output in the sbox sandbox. This can be used
// on the RuleBuilder command line to reference the output.
func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
// Errors will be handled in RuleBuilder.Build where we have a context to report them
rel, _, _ := maybeRelErr(outDir.String(), path.String())
return filepath.Join(sboxOutDir, rel)
}
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@ -727,7 +750,7 @@ func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
if !c.sbox {
panic("OutputDir only valid with Sbox")
}
return c.Text("__SBOX_OUT_DIR__")
return c.Text(sboxOutDir)
}
// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
@ -906,3 +929,12 @@ func ninjaNameEscape(s string) string {
}
return s
}
// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
// or the sbox textproto manifest change even if the input files are not listed on the command line.
func hashSrcFiles(srcFiles Paths) string {
h := sha256.New()
srcFileList := strings.Join(srcFiles.Strings(), "\n")
h.Write([]byte(srcFileList))
return fmt.Sprintf("%x", h.Sum(nil))
}

View file

@ -18,6 +18,7 @@ import (
"fmt"
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"
@ -441,7 +442,7 @@ func testRuleBuilderFactory() Module {
type testRuleBuilderModule struct {
ModuleBase
properties struct {
Src string
Srcs []string
Restat bool
Sbox bool
@ -449,7 +450,7 @@ type testRuleBuilderModule struct {
}
func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
in := PathForSource(ctx, t.properties.Src)
in := PathsForSource(ctx, t.properties.Srcs)
out := PathForModuleOut(ctx, ctx.ModuleName())
outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
outDir := PathForModuleOut(ctx)
@ -468,17 +469,17 @@ func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
out := PathForOutput(ctx, "baz")
outDep := PathForOutput(ctx, "baz.d")
outDir := PathForOutput(ctx)
testRuleBuilder_Build(ctx, in, out, outDep, outDir, true, false)
testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, true, false)
}
func testRuleBuilder_Build(ctx BuilderContext, in Path, out, outDep, outDir WritablePath, restat, sbox bool) {
func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir WritablePath, restat, sbox bool) {
rule := NewRuleBuilder()
if sbox {
rule.Sbox(outDir)
}
rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out).ImplicitDepFile(outDep)
rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
if restat {
rule.Restat()
@ -496,12 +497,12 @@ func TestRuleBuilder_Build(t *testing.T) {
bp := `
rule_builder_test {
name: "foo",
src: "bar",
srcs: ["bar"],
restat: true,
}
rule_builder_test {
name: "foo_sbox",
src: "bar",
srcs: ["bar"],
sbox: true,
}
`
@ -519,7 +520,10 @@ func TestRuleBuilder_Build(t *testing.T) {
check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
t.Helper()
if params.RuleParams.Command != wantCommand {
command := params.RuleParams.Command
re := regexp.MustCompile(" (# hash of input list:|--input-hash) [a-z0-9]*")
command = re.ReplaceAllLiteralString(command, "")
if command != wantCommand {
t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
}
@ -651,3 +655,78 @@ func Test_ninjaEscapeExceptForSpans(t *testing.T) {
})
}
}
func TestRuleBuilderHashInputs(t *testing.T) {
// The basic idea here is to verify that the command (in the case of a
// non-sbox rule) or the sbox textproto manifest contain a hash of the
// inputs.
// By including a hash of the inputs, we cause the rule to re-run if
// the list of inputs changes because the command line or a dependency
// changes.
bp := `
rule_builder_test {
name: "hash0",
srcs: ["in1.txt", "in2.txt"],
}
rule_builder_test {
name: "hash0_sbox",
srcs: ["in1.txt", "in2.txt"],
sbox: true,
}
rule_builder_test {
name: "hash1",
srcs: ["in1.txt", "in2.txt", "in3.txt"],
}
rule_builder_test {
name: "hash1_sbox",
srcs: ["in1.txt", "in2.txt", "in3.txt"],
sbox: true,
}
`
testcases := []struct {
name string
expectedHash string
}{
{
name: "hash0",
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
},
{
name: "hash1",
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
}
config := TestConfig(buildDir, nil, bp, nil)
ctx := NewTestContext(config)
ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
FailIfErrored(t, errs)
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
t.Run("sbox", func(t *testing.T) {
gen := ctx.ModuleForTests(test.name+"_sbox", "")
command := gen.Output(test.name + "_sbox").RuleParams.Command
if g, w := command, " --input-hash "+test.expectedHash; !strings.Contains(g, w) {
t.Errorf("Expected command line to end with %q, got %q", w, g)
}
})
t.Run("", func(t *testing.T) {
gen := ctx.ModuleForTests(test.name+"", "")
command := gen.Output(test.name).RuleParams.Command
if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
t.Errorf("Expected command line to end with %q, got %q", w, g)
}
})
})
}
}

View file

@ -75,7 +75,11 @@ func genYacc(ctx android.ModuleContext, rule *android.RuleBuilder, yaccFile andr
cmd := rule.Command()
// Fix up #line markers to not use the sbox temporary directory
sedCmd := "sed -i.bak 's#__SBOX_OUT_DIR__#" + outDir.String() + "#'"
// android.SboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
// directory itself, without any filename appended.
// TODO(ccross): make this cmd.PathForOutput(outDir) instead.
sboxOutDir := android.SboxPathForOutput(outDir, outDir)
sedCmd := "sed -i.bak 's#" + sboxOutDir + "#" + outDir.String() + "#'"
rule.Command().Text(sedCmd).Input(outFile)
rule.Command().Text(sedCmd).Input(headerFile)

View file

@ -66,14 +66,14 @@ func TestArchGenruleCmd(t *testing.T) {
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
expected := []string{"foo"}
if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
t.Errorf(`want arm inputs %v, got %v`, expected, gen.Inputs.Strings())
if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
}
gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
expected = []string{"bar"}
if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Implicits.Strings())
}
}
@ -108,10 +108,10 @@ func TestLibraryGenruleCmd(t *testing.T) {
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
var got []string
for _, input := range gen.Inputs {
for _, input := range gen.Implicits {
got = append(got, input.Base())
}
if !reflect.DeepEqual(expected, got) {
if !reflect.DeepEqual(expected, got[:len(expected)]) {
t.Errorf(`want inputs %v, got %v`, expected, got)
}
}

View file

@ -17,6 +17,7 @@ package genrule
import (
"fmt"
"io"
"path/filepath"
"strconv"
"strings"
@ -25,9 +26,6 @@ import (
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/shared"
"crypto/sha256"
"path/filepath"
)
func init() {
@ -158,9 +156,9 @@ type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles androi
type generateTask struct {
in android.Paths
out android.WritablePaths
depFile android.WritablePath
copyTo android.WritablePaths
genDir android.WritablePath
sandboxOuts []string
cmd string
shard int
shards int
@ -330,19 +328,23 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
var zipArgs strings.Builder
for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
for _, out := range task.out {
addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
if len(task.out) == 0 {
ctx.ModuleErrorf("must have at least one output file")
return
}
for _, out := range task.out {
addLocationLabel(out.Rel(), []string{android.SboxPathForOutput(out, task.genDir)})
}
referencedIn := false
referencedDepfile := false
rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
reportError := func(fmt string, args ...interface{}) (string, bool, error) {
reportError := func(fmt string, args ...interface{}) (string, error) {
ctx.PropertyErrorf("cmd", fmt, args...)
return "SOONG_ERROR", false, nil
return "SOONG_ERROR", nil
}
switch name {
@ -357,20 +359,23 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
return locationLabels[firstLabel][0], false, nil
return locationLabels[firstLabel][0], nil
case "in":
referencedIn = true
return "${in}", true, nil
return strings.Join(srcFiles.Strings(), " "), nil
case "out":
return "__SBOX_OUT_FILES__", false, nil
var sandboxOuts []string
for _, out := range task.out {
sandboxOuts = append(sandboxOuts, android.SboxPathForOutput(out, task.genDir))
}
return strings.Join(sandboxOuts, " "), nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
return reportError("$(depfile) used without depfile property")
}
return "__SBOX_DEPFILE__", false, nil
return "__SBOX_DEPFILE__", nil
case "genDir":
return "__SBOX_OUT_DIR__", false, nil
return android.SboxPathForOutput(task.genDir, task.genDir), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@ -381,7 +386,7 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
return paths[0], false, nil
return paths[0], nil
} else {
return reportError("unknown location label %q", label)
}
@ -391,7 +396,7 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
return strings.Join(paths, " "), false, nil
return strings.Join(paths, " "), nil
} else {
return reportError("unknown locations label %q", label)
}
@ -410,50 +415,39 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
return
}
// tell the sbox command which directory to use as its sandbox root
buildDir := android.PathForOutput(ctx).String()
sandboxPath := shared.TempDirForOutDir(buildDir)
// recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
// to be replaced later by ninja_strings.go
depfilePlaceholder := ""
if Bool(g.properties.Depfile) {
depfilePlaceholder = "$depfileArgs"
}
// Escape the command for the shell
rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
g.rawCommands = append(g.rawCommands, rawCommand)
sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s",
task.genDir, sandboxPath, task.genDir)
if !referencedIn {
sandboxCommand = sandboxCommand + hashSrcFiles(srcFiles)
}
sandboxCommand = sandboxCommand + fmt.Sprintf(" -c %s %s $allouts",
rawCommand, depfilePlaceholder)
ruleParams := blueprint.RuleParams{
Command: sandboxCommand,
CommandDeps: []string{"$sboxCmd"},
}
args := []string{"allouts"}
if Bool(g.properties.Depfile) {
ruleParams.Deps = blueprint.DepsGCC
args = append(args, "depfileArgs")
}
// Pick a unique rule name and the user-visible description.
desc := "generate"
name := "generator"
if task.shards > 1 {
if task.shards > 0 {
desc += " " + strconv.Itoa(task.shard)
name += strconv.Itoa(task.shard)
} else if len(task.out) == 1 {
desc += " " + task.out[0].Base()
}
rule := ctx.Rule(pctx, name, ruleParams, args...)
g.generateSourceFile(ctx, task, rule)
// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
rule := android.NewRuleBuilder().Sbox(task.genDir)
cmd := rule.Command()
cmd.Text(rawCommand)
cmd.ImplicitOutputs(task.out)
cmd.Implicits(task.in)
cmd.Implicits(g.deps)
if Bool(g.properties.Depfile) {
cmd.ImplicitDepFile(task.depFile)
}
// Create the rule to run the genrule command inside sbox.
rule.Build(pctx, ctx, name, desc)
if len(task.copyTo) > 0 {
// If copyTo is set, multiple shards need to be copied into a single directory.
// task.out contains the per-shard paths, and copyTo contains the corresponding
// final path. The files need to be copied into the final directory by a
// single rule so it can remove the directory before it starts to ensure no
// old files remain. zipsync already does this, so build up zipArgs that
// zip all the per-shard directories into a single zip.
outputFiles = append(outputFiles, task.copyTo...)
copyFrom = append(copyFrom, task.out.Paths()...)
zipArgs.WriteString(" -C " + task.genDir.String())
@ -464,6 +458,8 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
if len(copyFrom) > 0 {
// Create a rule that zips all the per-shard directories into a single zip and then
// uses zipsync to unzip it into the final directory.
ctx.Build(pctx, android.BuildParams{
Rule: gensrcsMerge,
Implicits: copyFrom,
@ -501,51 +497,6 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
}
}
func hashSrcFiles(srcFiles android.Paths) string {
h := sha256.New()
for _, src := range srcFiles {
h.Write([]byte(src.String()))
}
return fmt.Sprintf(" --input-hash %x", h.Sum(nil))
}
func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
desc := "generate"
if len(task.out) == 0 {
ctx.ModuleErrorf("must have at least one output file")
return
}
if len(task.out) == 1 {
desc += " " + task.out[0].Base()
}
var depFile android.ModuleGenPath
if Bool(g.properties.Depfile) {
depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
}
if task.shards > 1 {
desc += " " + strconv.Itoa(task.shard)
}
params := android.BuildParams{
Rule: rule,
Description: desc,
Output: task.out[0],
ImplicitOutputs: task.out[1:],
Inputs: task.in,
Implicits: g.deps,
Args: map[string]string{
"allouts": strings.Join(task.sandboxOuts, " "),
},
}
if Bool(g.properties.Depfile) {
params.Depfile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
params.Args["depfileArgs"] = "--depfile-out " + depFile.String()
}
ctx.Build(pctx, params)
}
// Collect information for opening IDE project files in java/jdeps.go.
func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
@ -610,16 +561,6 @@ func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext)
func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
func pathToSandboxOut(path android.Path, genDir android.Path) string {
relOut, err := filepath.Rel(genDir.String(), path.String())
if err != nil {
panic(fmt.Sprintf("Could not make ${out} relative: %v", err))
}
return filepath.Join("__SBOX_OUT_DIR__", relOut)
}
func NewGenSrcs() *Module {
properties := &genSrcsProperties{}
@ -638,7 +579,7 @@ func NewGenSrcs() *Module {
var outFiles android.WritablePaths
var copyTo android.WritablePaths
var shardDir android.WritablePath
var sandboxOuts []string
var depFile android.WritablePath
if len(shards) > 1 {
shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i))
@ -646,9 +587,11 @@ func NewGenSrcs() *Module {
shardDir = genDir
}
for _, in := range shard {
for j, in := range shard {
outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension))
sandboxOutfile := pathToSandboxOut(outFile, genDir)
if j == 0 {
depFile = outFile.ReplaceExtension(ctx, "d")
}
if len(shards) > 1 {
shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension))
@ -657,14 +600,13 @@ func NewGenSrcs() *Module {
}
outFiles = append(outFiles, outFile)
sandboxOuts = append(sandboxOuts, sandboxOutfile)
command, err := android.Expand(rawCommand, func(name string) (string, error) {
switch name {
case "in":
return in.String(), nil
case "out":
return sandboxOutfile, nil
return android.SboxPathForOutput(outFile, shardDir), nil
default:
return "$(" + name + ")", nil
}
@ -682,9 +624,9 @@ func NewGenSrcs() *Module {
generateTasks = append(generateTasks, generateTask{
in: shard,
out: outFiles,
depFile: depFile,
copyTo: copyTo,
genDir: shardDir,
sandboxOuts: sandboxOuts,
cmd: fullCommand,
shard: i,
shards: len(shards),
@ -720,17 +662,19 @@ func NewGenRule() *Module {
taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
outs := make(android.WritablePaths, len(properties.Out))
sandboxOuts := make([]string, len(properties.Out))
genDir := android.PathForModuleGen(ctx)
var depFile android.WritablePath
for i, out := range properties.Out {
outs[i] = android.PathForModuleGen(ctx, out)
sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
outPath := android.PathForModuleGen(ctx, out)
if i == 0 {
depFile = outPath.ReplaceExtension(ctx, "d")
}
outs[i] = outPath
}
return []generateTask{{
in: srcFiles,
out: outs,
depFile: depFile,
genDir: android.PathForModuleGen(ctx),
sandboxOuts: sandboxOuts,
cmd: rawCommand,
}}
}

View file

@ -141,7 +141,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location) > $(out)",
`,
expect: "out/tool > __SBOX_OUT_FILES__",
expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool2",
@ -150,7 +150,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location) > $(out)",
`,
expect: "out/tool > __SBOX_OUT_FILES__",
expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool file",
@ -159,7 +159,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location) > $(out)",
`,
expect: "tool_file1 > __SBOX_OUT_FILES__",
expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool file fg",
@ -168,7 +168,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location) > $(out)",
`,
expect: "tool_file1 > __SBOX_OUT_FILES__",
expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool and tool file",
@ -178,7 +178,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location) > $(out)",
`,
expect: "out/tool > __SBOX_OUT_FILES__",
expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool",
@ -187,7 +187,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location tool) > $(out)",
`,
expect: "out/tool > __SBOX_OUT_FILES__",
expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool2",
@ -196,7 +196,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location :tool) > $(out)",
`,
expect: "out/tool > __SBOX_OUT_FILES__",
expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool file",
@ -205,7 +205,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location tool_file1) > $(out)",
`,
expect: "tool_file1 > __SBOX_OUT_FILES__",
expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "tool file fg",
@ -214,7 +214,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(location :1tool_file) > $(out)",
`,
expect: "tool_file1 > __SBOX_OUT_FILES__",
expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "tool files",
@ -223,7 +223,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "$(locations :tool_files) > $(out)",
`,
expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
expect: "tool_file1 tool_file2 > __SBOX_OUT_DIR__/out",
},
{
name: "in1",
@ -232,7 +232,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
expect: "cat ${in} > __SBOX_OUT_FILES__",
expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "in1 fg",
@ -241,7 +241,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
expect: "cat ${in} > __SBOX_OUT_FILES__",
expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "ins",
@ -250,7 +250,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
expect: "cat ${in} > __SBOX_OUT_FILES__",
expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "ins fg",
@ -259,7 +259,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
expect: "cat ${in} > __SBOX_OUT_FILES__",
expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "location in1",
@ -268,7 +268,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
expect: "cat in1 > __SBOX_OUT_FILES__",
expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location in1 fg",
@ -277,7 +277,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(location :1in) > $(out)",
`,
expect: "cat in1 > __SBOX_OUT_FILES__",
expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location ins",
@ -286,7 +286,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
expect: "cat in1 > __SBOX_OUT_FILES__",
expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location ins fg",
@ -295,7 +295,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "cat $(locations :ins) > $(out)",
`,
expect: "cat in1 in2 > __SBOX_OUT_FILES__",
expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "outs",
@ -303,7 +303,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out", "out2"],
cmd: "echo foo > $(out)",
`,
expect: "echo foo > __SBOX_OUT_FILES__",
expect: "echo foo > __SBOX_OUT_DIR__/out __SBOX_OUT_DIR__/out2",
},
{
name: "location out",
@ -320,7 +320,7 @@ func TestGenruleCmd(t *testing.T) {
depfile: true,
cmd: "echo foo > $(out) && touch $(depfile)",
`,
expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
expect: "echo foo > __SBOX_OUT_DIR__/out && touch __SBOX_DEPFILE__",
},
{
name: "gendir",
@ -328,7 +328,7 @@ func TestGenruleCmd(t *testing.T) {
out: ["out"],
cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
`,
expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_DIR__/out",
},
{
@ -443,7 +443,7 @@ func TestGenruleCmd(t *testing.T) {
allowMissingDependencies: true,
expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
expect: "cat ***missing srcs :missing*** > __SBOX_OUT_DIR__/out",
},
{
name: "tool allow missing dependencies",
@ -455,7 +455,7 @@ func TestGenruleCmd(t *testing.T) {
allowMissingDependencies: true,
expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
expect: "***missing tool :missing*** > __SBOX_OUT_DIR__/out",
},
}
@ -495,7 +495,7 @@ func TestGenruleCmd(t *testing.T) {
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
if g, w := gen.rawCommands[0], "'"+test.expect+"'"; w != g {
if g, w := gen.rawCommands[0], test.expect; w != g {
t.Errorf("want %q, got %q", w, g)
}
})
@ -542,18 +542,18 @@ func TestGenruleHashInputs(t *testing.T) {
}{
{
name: "hash0",
// sha256 value obtained from: echo -n 'in1.txtin2.txt' | sha256sum
expectedHash: "031097e11e0a8c822c960eb9742474f46336360a515744000d086d94335a9cb9",
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
},
{
name: "hash1",
// sha256 value obtained from: echo -n 'in1.txtin2.txtin3.txt' | sha256sum
expectedHash: "de5d22a4a7ab50d250cc59fcdf7a7e0775790d270bfca3a7a9e1f18a70dd996c",
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
{
name: "hash2",
// $(in) is present, option should not appear
expectedHash: "",
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
}
@ -575,7 +575,7 @@ func TestGenruleHashInputs(t *testing.T) {
if len(test.expectedHash) > 0 {
// We add spaces before and after to make sure that
// this option doesn't abutt another sbox option.
expectedInputHashOption := " --input-hash " + test.expectedHash + " "
expectedInputHashOption := " --input-hash " + test.expectedHash
if !strings.Contains(command, expectedInputHashOption) {
t.Errorf("Expected command \"%s\" to contain \"%s\"", command, expectedInputHashOption)
@ -609,7 +609,7 @@ func TestGenSrcs(t *testing.T) {
cmd: "$(location) $(in) > $(out)",
`,
cmds: []string{
"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
"bash -c 'out/tool in1.txt > __SBOX_OUT_DIR__/in1.h' && bash -c 'out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
@ -623,8 +623,8 @@ func TestGenSrcs(t *testing.T) {
shard_size: 2,
`,
cmds: []string{
"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
"'bash -c '\\''out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'\\'''",
"bash -c 'out/tool in1.txt > __SBOX_OUT_DIR__/in1.h' && bash -c 'out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'",
"bash -c 'out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
@ -710,7 +710,7 @@ func TestGenruleDefaults(t *testing.T) {
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
expectedCmd := "cp in1 __SBOX_OUT_DIR__/out"
if gen.rawCommands[0] != expectedCmd {
t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
}

View file

@ -1379,8 +1379,8 @@ func TestJarGenrules(t *testing.T) {
baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() {
t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings())
if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
t.Errorf("expected jargen inputs [%q], got %q", w, g)
}
if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {