Merge changes Ie8721dcd,If1827b9d,Iec250a2d,I3f46f611,Idf136919, ... am: ba5fff85a7 am: 0b0fcae3ca am: defc5c9f8a

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1651847

Change-Id: Ifc60075b65f4f27134aaab9315e4c0ffe90f2481
This commit is contained in:
Colin Cross 2021-03-25 23:03:02 +00:00 committed by Automerger Merge Worker
commit 8a63fa9fd2
23 changed files with 748 additions and 345 deletions

View File

@ -14,6 +14,7 @@ bootstrap_go_package {
"soong-bazel", "soong-bazel",
"soong-cquery", "soong-cquery",
"soong-remoteexec", "soong-remoteexec",
"soong-response",
"soong-shared", "soong-shared",
"soong-ui-metrics_proto", "soong-ui-metrics_proto",
], ],

View File

@ -28,6 +28,7 @@ import (
"android/soong/cmd/sbox/sbox_proto" "android/soong/cmd/sbox/sbox_proto"
"android/soong/remoteexec" "android/soong/remoteexec"
"android/soong/response"
"android/soong/shared" "android/soong/shared"
) )
@ -408,30 +409,21 @@ func (r *RuleBuilder) Tools() Paths {
func (r *RuleBuilder) RspFileInputs() Paths { func (r *RuleBuilder) RspFileInputs() Paths {
var rspFileInputs Paths var rspFileInputs Paths
for _, c := range r.commands { for _, c := range r.commands {
if c.rspFileInputs != nil { for _, rspFile := range c.rspFiles {
if rspFileInputs != nil { rspFileInputs = append(rspFileInputs, rspFile.paths...)
panic("Multiple commands in a rule may not have rsp file inputs")
}
rspFileInputs = c.rspFileInputs
} }
} }
return rspFileInputs return rspFileInputs
} }
// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method. func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
func (r *RuleBuilder) RspFile() WritablePath { var rspFiles []rspFileAndPaths
var rspFile WritablePath
for _, c := range r.commands { for _, c := range r.commands {
if c.rspFile != nil { rspFiles = append(rspFiles, c.rspFiles...)
if rspFile != nil {
panic("Multiple commands in a rule may not have rsp file inputs")
}
rspFile = c.rspFile
}
} }
return rspFile return rspFiles
} }
// Commands returns a slice containing the built command line for each call to RuleBuilder.Command. // Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
@ -459,26 +451,6 @@ func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderComma
Inputs(depFiles.Paths()) Inputs(depFiles.Paths())
} }
// composeRspFileContent returns a string that will serve as the contents of the rsp file to pass
// the listed input files to the command running in the sandbox.
func (r *RuleBuilder) composeRspFileContent(rspFileInputs Paths) string {
if r.sboxInputs {
if len(rspFileInputs) > 0 {
// When SandboxInputs is used the paths need to be rewritten to be relative to the sandbox
// directory so that they are valid after sbox chdirs into the sandbox directory.
return proptools.NinjaEscape(strings.Join(r.sboxPathsForInputsRel(rspFileInputs), " "))
} else {
// If the list of inputs is empty fall back to "$in" so that the rspfilecontent Ninja
// variable is set to something non-empty, otherwise ninja will complain. The inputs
// will be empty (all the non-rspfile inputs are implicits), so $in will evaluate to
// an empty string.
return "$in"
}
} else {
return "$in"
}
}
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs. // Outputs.
func (r *RuleBuilder) Build(name string, desc string) { func (r *RuleBuilder) Build(name string, desc string) {
@ -520,8 +492,7 @@ func (r *RuleBuilder) Build(name string, desc string) {
commands := r.Commands() commands := r.Commands()
outputs := r.Outputs() outputs := r.Outputs()
inputs := r.Inputs() inputs := r.Inputs()
rspFileInputs := r.RspFileInputs() rspFiles := r.rspFiles()
rspFilePath := r.RspFile()
if len(commands) == 0 { if len(commands) == 0 {
return return
@ -575,11 +546,22 @@ func (r *RuleBuilder) Build(name string, desc string) {
}) })
} }
// If using an rsp file copy it into the sbox directory. // If using rsp files copy them and their contents into the sbox directory with
if rspFilePath != nil { // the appropriate path mappings.
command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ for _, rspFile := range rspFiles {
From: proto.String(rspFilePath.String()), command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
To: proto.String(r.sboxPathForInputRel(rspFilePath)), File: proto.String(rspFile.file.String()),
// These have to match the logic in sboxPathForInputRel
PathMappings: []*sbox_proto.PathMapping{
{
From: proto.String(r.outDir.String()),
To: proto.String(sboxOutSubDir),
},
{
From: proto.String(PathForOutput(r.ctx).String()),
To: proto.String(sboxOutSubDir),
},
},
}) })
} }
@ -641,20 +623,30 @@ func (r *RuleBuilder) Build(name string, desc string) {
inputs = append(inputs, sboxCmd.inputs...) inputs = append(inputs, sboxCmd.inputs...)
if r.rbeParams != nil { if r.rbeParams != nil {
var remoteInputs []string // RBE needs a list of input files to copy to the remote builder. For inputs already
remoteInputs = append(remoteInputs, inputs.Strings()...) // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
remoteInputs = append(remoteInputs, tools.Strings()...) // create a new rsp file to pass to rewrapper.
remoteInputs = append(remoteInputs, rspFileInputs.Strings()...) var remoteRspFiles Paths
if rspFilePath != nil { var remoteInputs Paths
remoteInputs = append(remoteInputs, rspFilePath.String())
remoteInputs = append(remoteInputs, inputs...)
remoteInputs = append(remoteInputs, tools...)
for _, rspFile := range rspFiles {
remoteInputs = append(remoteInputs, rspFile.file)
remoteRspFiles = append(remoteRspFiles, rspFile.file)
}
if len(remoteInputs) > 0 {
inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
remoteRspFiles = append(remoteRspFiles, inputsListFile)
// Add the new rsp file as an extra input to the rule.
inputs = append(inputs, inputsListFile)
} }
inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
inputsListContents := rspFileForInputs(remoteInputs)
WriteFileRule(r.ctx, inputsListFile, inputsListContents)
inputs = append(inputs, inputsListFile)
r.rbeParams.OutputFiles = outputs.Strings() r.rbeParams.OutputFiles = outputs.Strings()
r.rbeParams.RSPFile = inputsListFile.String() r.rbeParams.RSPFiles = remoteRspFiles.Strings()
rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper()) rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'" commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
} }
@ -672,9 +664,24 @@ func (r *RuleBuilder) Build(name string, desc string) {
implicitOutputs := outputs[1:] implicitOutputs := outputs[1:]
var rspFile, rspFileContent string var rspFile, rspFileContent string
if rspFilePath != nil { var rspFileInputs Paths
rspFile = rspFilePath.String() if len(rspFiles) > 0 {
rspFileContent = r.composeRspFileContent(rspFileInputs) // The first rsp files uses Ninja's rsp file support for the rule
rspFile = rspFiles[0].file.String()
// Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
// list and in the contents of the rsp file. Inputs to the rule that are not in the
// rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
rspFileContent = "$in"
rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
for _, rspFile := range rspFiles[1:] {
// Any additional rsp files need an extra rule to write the file.
writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
// The main rule needs to depend on the inputs listed in the extra rsp file.
inputs = append(inputs, rspFile.paths...)
// The main rule needs to depend on the extra rsp file.
inputs = append(inputs, rspFile.file)
}
} }
var pool blueprint.Pool var pool blueprint.Pool
@ -725,8 +732,12 @@ type RuleBuilderCommand struct {
depFiles WritablePaths depFiles WritablePaths
tools Paths tools Paths
packagedTools []PackagingSpec packagedTools []PackagingSpec
rspFileInputs Paths rspFiles []rspFileAndPaths
rspFile WritablePath }
type rspFileAndPaths struct {
file WritablePath
paths Paths
} }
func (c *RuleBuilderCommand) addInput(path Path) string { func (c *RuleBuilderCommand) addInput(path Path) string {
@ -1170,22 +1181,19 @@ func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *Ru
return c.Text(flag + c.PathForOutput(path)) return c.Text(flag + c.PathForOutput(path))
} }
// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator // FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must // no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
// be outside the sbox directory. // rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
// uses will result in an auxiliary rules to write the rspFile contents.
func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand { func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
if c.rspFileInputs != nil {
panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
}
// Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
// generated. // generated.
if paths == nil { if paths == nil {
paths = Paths{} paths = Paths{}
} }
c.rspFileInputs = paths c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
c.rspFile = rspFile
if c.rule.sbox { if c.rule.sbox {
if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel { if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
@ -1264,13 +1272,12 @@ func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams,
} }
func (builderContextForTests) Build(PackageContext, BuildParams) {} func (builderContextForTests) Build(PackageContext, BuildParams) {}
func rspFileForInputs(paths []string) string { func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
s := strings.Builder{} buf := &strings.Builder{}
for i, path := range paths { err := response.WriteRspFile(buf, paths.Strings())
if i != 0 { if err != nil {
s.WriteByte(' ') // There should never be I/O errors writing to a bytes.Buffer.
} panic(err)
s.WriteString(proptools.ShellEscape(path))
} }
return s.String() WriteFileRule(ctx, rspFile, buf.String())
} }

View File

@ -376,8 +376,6 @@ func TestRuleBuilder(t *testing.T) {
wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " + wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
"out_local/module/DepFile out_local/module/depfile out_local/module/ImplicitDepFile out_local/module/depfile2" "out_local/module/DepFile out_local/module/depfile out_local/module/ImplicitDepFile out_local/module/depfile2"
wantRspFileContent := "$in"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs()) AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
@ -389,8 +387,6 @@ func TestRuleBuilder(t *testing.T) {
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
}) })
t.Run("sbox", func(t *testing.T) { t.Run("sbox", func(t *testing.T) {
@ -409,8 +405,6 @@ func TestRuleBuilder(t *testing.T) {
wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2" wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
wantRspFileContent := "$in"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs()) AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
@ -422,8 +416,6 @@ func TestRuleBuilder(t *testing.T) {
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
}) })
t.Run("sbox tools", func(t *testing.T) { t.Run("sbox tools", func(t *testing.T) {
@ -442,7 +434,34 @@ func TestRuleBuilder(t *testing.T) {
wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2" wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
wantRspFileContent := "$in" AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
})
t.Run("sbox inputs", func(t *testing.T) {
rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
PathForOutput(ctx, "sbox.textproto")).SandboxInputs()
addCommands(rule)
wantCommands := []string{
"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
"FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
}
wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
@ -455,8 +474,6 @@ func TestRuleBuilder(t *testing.T) {
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
}) })
} }
@ -472,8 +489,9 @@ type testRuleBuilderModule struct {
properties struct { properties struct {
Srcs []string Srcs []string
Restat bool Restat bool
Sbox bool Sbox bool
Sbox_inputs bool
} }
} }
@ -482,9 +500,15 @@ func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
out := PathForModuleOut(ctx, "gen", ctx.ModuleName()) out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d") outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
outDir := PathForModuleOut(ctx, "gen") outDir := PathForModuleOut(ctx, "gen")
rspFile := PathForModuleOut(ctx, "rsp")
rspFile2 := PathForModuleOut(ctx, "rsp2")
rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForModuleOut(ctx, "sbox.textproto") manifestPath := PathForModuleOut(ctx, "sbox.textproto")
testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox) testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat,
t.properties.Sbox, t.properties.Sbox_inputs, rspFile, rspFileContents,
rspFile2, rspFileContents2)
} }
type testRuleBuilderSingleton struct{} type testRuleBuilderSingleton struct{}
@ -498,18 +522,35 @@ func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
out := PathForOutput(ctx, "singleton/gen/baz") out := PathForOutput(ctx, "singleton/gen/baz")
outDep := PathForOutput(ctx, "singleton/gen/baz.d") outDep := PathForOutput(ctx, "singleton/gen/baz.d")
outDir := PathForOutput(ctx, "singleton/gen") outDir := PathForOutput(ctx, "singleton/gen")
rspFile := PathForOutput(ctx, "singleton/rsp")
rspFile2 := PathForOutput(ctx, "singleton/rsp2")
rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForOutput(ctx, "singleton/sbox.textproto") manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false) testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false, false,
rspFile, rspFileContents, rspFile2, rspFileContents2)
} }
func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) { func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath,
restat, sbox, sboxInputs bool,
rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
rule := NewRuleBuilder(pctx, ctx) rule := NewRuleBuilder(pctx, ctx)
if sbox { if sbox {
rule.Sbox(outDir, manifestPath) rule.Sbox(outDir, manifestPath)
if sboxInputs {
rule.SandboxInputs()
}
} }
rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep) rule.Command().
Tool(PathForSource(ctx, "cp")).
Inputs(in).
Output(out).
ImplicitDepFile(outDep).
FlagWithRspFileInputList("@", rspFile, rspFileContents).
FlagWithRspFileInputList("@", rspFile2, rspFileContents2)
if restat { if restat {
rule.Restat() rule.Restat()
@ -540,6 +581,12 @@ func TestRuleBuilder_Build(t *testing.T) {
srcs: ["bar"], srcs: ["bar"],
sbox: true, sbox: true,
} }
rule_builder_test {
name: "foo_sbox_inputs",
srcs: ["bar"],
sbox: true,
sbox_inputs: true,
}
` `
result := GroupFixturePreparers( result := GroupFixturePreparers(
@ -548,7 +595,10 @@ func TestRuleBuilder_Build(t *testing.T) {
fs.AddToFixture(), fs.AddToFixture(),
).RunTest(t) ).RunTest(t)
check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) { check := func(t *testing.T, params TestingBuildParams, rspFile2Params TestingBuildParams,
wantCommand, wantOutput, wantDepfile, wantRspFile, wantRspFile2 string,
wantRestat bool, extraImplicits, extraCmdDeps []string) {
t.Helper() t.Helper()
command := params.RuleParams.Command command := params.RuleParams.Command
re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$") re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
@ -561,9 +611,19 @@ func TestRuleBuilder_Build(t *testing.T) {
AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat) AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
wantInputs := []string{"rsp_in"}
AssertArrayString(t, "Inputs", wantInputs, params.Inputs.Strings())
wantImplicits := append([]string{"bar"}, extraImplicits...) wantImplicits := append([]string{"bar"}, extraImplicits...)
// The second rsp file and the files listed in it should be in implicits
wantImplicits = append(wantImplicits, "rsp_in2", wantRspFile2)
AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits) AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits)
wantRspFileContent := "$in"
AssertStringEquals(t, "RspfileContent", wantRspFileContent, params.RuleParams.RspfileContent)
AssertStringEquals(t, "Rspfile", wantRspFile, params.RuleParams.Rspfile)
AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output) AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output)
if len(params.ImplicitOutputs) != 0 { if len(params.ImplicitOutputs) != 0 {
@ -575,18 +635,42 @@ func TestRuleBuilder_Build(t *testing.T) {
if params.Deps != blueprint.DepsGCC { if params.Deps != blueprint.DepsGCC {
t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps) t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
} }
rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params)
AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content)
} }
t.Run("module", func(t *testing.T) { t.Run("module", func(t *testing.T) {
outFile := "out/soong/.intermediates/foo/gen/foo" outFile := "out/soong/.intermediates/foo/gen/foo"
check(t, result.ModuleForTests("foo", "").Rule("rule").RelativeToTop(), rspFile := "out/soong/.intermediates/foo/rsp"
"cp bar "+outFile, rspFile2 := "out/soong/.intermediates/foo/rsp2"
outFile, outFile+".d", true, nil, nil) module := result.ModuleForTests("foo", "")
check(t, module.Rule("rule").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
}) })
t.Run("sbox", func(t *testing.T) { t.Run("sbox", func(t *testing.T) {
outDir := "out/soong/.intermediates/foo_sbox" outDir := "out/soong/.intermediates/foo_sbox"
outFile := filepath.Join(outDir, "gen/foo_sbox") outFile := filepath.Join(outDir, "gen/foo_sbox")
depFile := filepath.Join(outDir, "gen/foo_sbox.d") depFile := filepath.Join(outDir, "gen/foo_sbox.d")
rspFile := filepath.Join(outDir, "rsp")
rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir("out/soong")
cmd := `rm -rf ` + outDir + `/gen && ` +
sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
module := result.ModuleForTests("foo_sbox", "")
check(t, module.Output("gen/foo_sbox").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
})
t.Run("sbox_inputs", func(t *testing.T) {
outDir := "out/soong/.intermediates/foo_sbox_inputs"
outFile := filepath.Join(outDir, "gen/foo_sbox_inputs")
depFile := filepath.Join(outDir, "gen/foo_sbox_inputs.d")
rspFile := filepath.Join(outDir, "rsp")
rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto") manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox") sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir("out/soong") sandboxPath := shared.TempDirForOutDir("out/soong")
@ -594,13 +678,18 @@ func TestRuleBuilder_Build(t *testing.T) {
cmd := `rm -rf ` + outDir + `/gen && ` + cmd := `rm -rf ` + outDir + `/gen && ` +
sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox").RelativeToTop(), module := result.ModuleForTests("foo_sbox_inputs", "")
cmd, outFile, depFile, false, []string{manifest}, []string{sbox}) check(t, module.Output("gen/foo_sbox_inputs").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
}) })
t.Run("singleton", func(t *testing.T) { t.Run("singleton", func(t *testing.T) {
outFile := filepath.Join("out/soong/singleton/gen/baz") outFile := filepath.Join("out/soong/singleton/gen/baz")
check(t, result.SingletonForTests("rule_builder_test").Rule("rule").RelativeToTop(), rspFile := filepath.Join("out/soong/singleton/rsp")
"cp bar "+outFile, outFile, outFile+".d", true, nil, nil) rspFile2 := filepath.Join("out/soong/singleton/rsp2")
singleton := result.SingletonForTests("rule_builder_test")
check(t, singleton.Rule("rule").RelativeToTop(), singleton.Output(rspFile2).RelativeToTop(),
"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
}) })
} }

View File

@ -73,7 +73,7 @@ var (
Labels: map[string]string{"type": "link", "tool": "clang"}, Labels: map[string]string{"type": "link", "tool": "clang"},
ExecStrategy: "${config.RECXXLinksExecStrategy}", ExecStrategy: "${config.RECXXLinksExecStrategy}",
Inputs: []string{"${out}.rsp", "$implicitInputs"}, Inputs: []string{"${out}.rsp", "$implicitInputs"},
RSPFile: "${out}.rsp", RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"${out}", "$implicitOutputs"}, OutputFiles: []string{"${out}", "$implicitOutputs"},
ToolchainInputs: []string{"$ldCmd"}, ToolchainInputs: []string{"$ldCmd"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"}, Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
@ -256,7 +256,7 @@ var (
Labels: map[string]string{"type": "tool", "name": "abi-linker"}, Labels: map[string]string{"type": "tool", "name": "abi-linker"},
ExecStrategy: "${config.REAbiLinkerExecStrategy}", ExecStrategy: "${config.REAbiLinkerExecStrategy}",
Inputs: []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicitInputs"}, Inputs: []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicitInputs"},
RSPFile: "${out}.rsp", RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"}, OutputFiles: []string{"$out"},
ToolchainInputs: []string{"$sAbiLinker"}, ToolchainInputs: []string{"$sAbiLinker"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"}, Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},

View File

@ -22,7 +22,7 @@ blueprint_go_binary {
"android-archive-zip", "android-archive-zip",
"blueprint-pathtools", "blueprint-pathtools",
"soong-jar", "soong-jar",
"soong-zip", "soong-response",
], ],
srcs: [ srcs: [
"merge_zips.go", "merge_zips.go",

View File

@ -25,12 +25,14 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings"
"android/soong/response"
"github.com/google/blueprint/pathtools" "github.com/google/blueprint/pathtools"
"android/soong/jar" "android/soong/jar"
"android/soong/third_party/zip" "android/soong/third_party/zip"
soongZip "android/soong/zip"
) )
// Input zip: we can open it, close it, and obtain an array of entries // Input zip: we can open it, close it, and obtain an array of entries
@ -690,15 +692,20 @@ func main() {
inputs := make([]string, 0) inputs := make([]string, 0)
for _, input := range args[1:] { for _, input := range args[1:] {
if input[0] == '@' { if input[0] == '@' {
bytes, err := ioutil.ReadFile(input[1:]) f, err := os.Open(strings.TrimPrefix(input[1:], "@"))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
continue rspInputs, err := response.ReadRspFile(f)
f.Close()
if err != nil {
log.Fatal(err)
}
inputs = append(inputs, rspInputs...)
} else {
inputs = append(inputs, input)
} }
inputs = append(inputs, input)
continue
} }
log.SetFlags(log.Lshortfile) log.SetFlags(log.Lshortfile)

View File

@ -21,6 +21,7 @@ blueprint_go_binary {
deps: [ deps: [
"sbox_proto", "sbox_proto",
"soong-makedeps", "soong-makedeps",
"soong-response",
], ],
srcs: [ srcs: [
"sbox.go", "sbox.go",

View File

@ -32,6 +32,7 @@ import (
"android/soong/cmd/sbox/sbox_proto" "android/soong/cmd/sbox/sbox_proto"
"android/soong/makedeps" "android/soong/makedeps"
"android/soong/response"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
) )
@ -218,6 +219,11 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er
return "", fmt.Errorf("command is required") return "", fmt.Errorf("command is required")
} }
pathToTempDirInSbox := tempDir
if command.GetChdir() {
pathToTempDirInSbox = "."
}
err = os.MkdirAll(tempDir, 0777) err = os.MkdirAll(tempDir, 0777)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create %q: %w", tempDir, err) return "", fmt.Errorf("failed to create %q: %w", tempDir, err)
@ -228,10 +234,9 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er
if err != nil { if err != nil {
return "", err return "", err
} }
err = copyRspFiles(command.RspFiles, tempDir, pathToTempDirInSbox)
pathToTempDirInSbox := tempDir if err != nil {
if command.GetChdir() { return "", err
pathToTempDirInSbox = "."
} }
if strings.Contains(rawCommand, depFilePlaceholder) { if strings.Contains(rawCommand, depFilePlaceholder) {
@ -409,6 +414,83 @@ func copyOneFile(from string, to string, executable bool) error {
return nil return nil
} }
// copyRspFiles copies rsp files into the sandbox with path mappings, and also copies the files
// listed into the sandbox.
func copyRspFiles(rspFiles []*sbox_proto.RspFile, toDir, toDirInSandbox string) error {
for _, rspFile := range rspFiles {
err := copyOneRspFile(rspFile, toDir, toDirInSandbox)
if err != nil {
return err
}
}
return nil
}
// copyOneRspFiles copies an rsp file into the sandbox with path mappings, and also copies the files
// listed into the sandbox.
func copyOneRspFile(rspFile *sbox_proto.RspFile, toDir, toDirInSandbox string) error {
in, err := os.Open(rspFile.GetFile())
if err != nil {
return err
}
defer in.Close()
files, err := response.ReadRspFile(in)
if err != nil {
return err
}
for i, from := range files {
// Convert the real path of the input file into the path inside the sandbox using the
// path mappings.
to := applyPathMappings(rspFile.PathMappings, from)
// Copy the file into the sandbox.
err := copyOneFile(from, joinPath(toDir, to), false)
if err != nil {
return err
}
// Rewrite the name in the list of files to be relative to the sandbox directory.
files[i] = joinPath(toDirInSandbox, to)
}
// Convert the real path of the rsp file into the path inside the sandbox using the path
// mappings.
outRspFile := joinPath(toDir, applyPathMappings(rspFile.PathMappings, rspFile.GetFile()))
err = os.MkdirAll(filepath.Dir(outRspFile), 0777)
if err != nil {
return err
}
out, err := os.Create(outRspFile)
if err != nil {
return err
}
defer out.Close()
// Write the rsp file with converted paths into the sandbox.
err = response.WriteRspFile(out, files)
if err != nil {
return err
}
return nil
}
// applyPathMappings takes a list of path mappings and a path, and returns the path with the first
// matching path mapping applied. If the path does not match any of the path mappings then it is
// returned unmodified.
func applyPathMappings(pathMappings []*sbox_proto.PathMapping, path string) string {
for _, mapping := range pathMappings {
if strings.HasPrefix(path, mapping.GetFrom()+"/") {
return joinPath(mapping.GetTo()+"/", strings.TrimPrefix(path, mapping.GetFrom()+"/"))
}
}
return path
}
// moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted // moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted
// to moving files where the source and destination are in the same filesystem. This is OK for // to moving files where the source and destination are in the same filesystem. This is OK for
// sbox because the temporary directory is inside the out directory. It updates the timestamp // sbox because the temporary directory is inside the out directory. It updates the timestamp

View File

@ -86,10 +86,13 @@ type Command struct {
CopyAfter []*Copy `protobuf:"bytes,4,rep,name=copy_after,json=copyAfter" json:"copy_after,omitempty"` CopyAfter []*Copy `protobuf:"bytes,4,rep,name=copy_after,json=copyAfter" json:"copy_after,omitempty"`
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns // An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line. // when the lists of inputs changes, even if the inputs are not on the command line.
InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"` InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` // A list of files that will be copied before the sandboxed command, and whose contents should be
XXX_unrecognized []byte `json:"-"` // copied as if they were listed in copy_before.
XXX_sizecache int32 `json:"-"` RspFiles []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Command) Reset() { *m = Command{} } func (m *Command) Reset() { *m = Command{} }
@ -152,6 +155,13 @@ func (m *Command) GetInputHash() string {
return "" return ""
} }
func (m *Command) GetRspFiles() []*RspFile {
if m != nil {
return m.RspFiles
}
return nil
}
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they // Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
// are relative to is specific to the context the Copy is used in and will be different for // are relative to is specific to the context the Copy is used in and will be different for
// from and to. // from and to.
@ -211,10 +221,110 @@ func (m *Copy) GetExecutable() bool {
return false return false
} }
// RspFile describes an rspfile that should be copied into the sandbox directory.
type RspFile struct {
// The path to the rsp file.
File *string `protobuf:"bytes,1,req,name=file" json:"file,omitempty"`
// A list of path mappings that should be applied to each file listed in the rsp file.
PathMappings []*PathMapping `protobuf:"bytes,2,rep,name=path_mappings,json=pathMappings" json:"path_mappings,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RspFile) Reset() { *m = RspFile{} }
func (m *RspFile) String() string { return proto.CompactTextString(m) }
func (*RspFile) ProtoMessage() {}
func (*RspFile) Descriptor() ([]byte, []int) {
return fileDescriptor_9d0425bf0de86ed1, []int{3}
}
func (m *RspFile) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RspFile.Unmarshal(m, b)
}
func (m *RspFile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RspFile.Marshal(b, m, deterministic)
}
func (m *RspFile) XXX_Merge(src proto.Message) {
xxx_messageInfo_RspFile.Merge(m, src)
}
func (m *RspFile) XXX_Size() int {
return xxx_messageInfo_RspFile.Size(m)
}
func (m *RspFile) XXX_DiscardUnknown() {
xxx_messageInfo_RspFile.DiscardUnknown(m)
}
var xxx_messageInfo_RspFile proto.InternalMessageInfo
func (m *RspFile) GetFile() string {
if m != nil && m.File != nil {
return *m.File
}
return ""
}
func (m *RspFile) GetPathMappings() []*PathMapping {
if m != nil {
return m.PathMappings
}
return nil
}
// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
type PathMapping struct {
From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
To *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PathMapping) Reset() { *m = PathMapping{} }
func (m *PathMapping) String() string { return proto.CompactTextString(m) }
func (*PathMapping) ProtoMessage() {}
func (*PathMapping) Descriptor() ([]byte, []int) {
return fileDescriptor_9d0425bf0de86ed1, []int{4}
}
func (m *PathMapping) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PathMapping.Unmarshal(m, b)
}
func (m *PathMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PathMapping.Marshal(b, m, deterministic)
}
func (m *PathMapping) XXX_Merge(src proto.Message) {
xxx_messageInfo_PathMapping.Merge(m, src)
}
func (m *PathMapping) XXX_Size() int {
return xxx_messageInfo_PathMapping.Size(m)
}
func (m *PathMapping) XXX_DiscardUnknown() {
xxx_messageInfo_PathMapping.DiscardUnknown(m)
}
var xxx_messageInfo_PathMapping proto.InternalMessageInfo
func (m *PathMapping) GetFrom() string {
if m != nil && m.From != nil {
return *m.From
}
return ""
}
func (m *PathMapping) GetTo() string {
if m != nil && m.To != nil {
return *m.To
}
return ""
}
func init() { func init() {
proto.RegisterType((*Manifest)(nil), "sbox.Manifest") proto.RegisterType((*Manifest)(nil), "sbox.Manifest")
proto.RegisterType((*Command)(nil), "sbox.Command") proto.RegisterType((*Command)(nil), "sbox.Command")
proto.RegisterType((*Copy)(nil), "sbox.Copy") proto.RegisterType((*Copy)(nil), "sbox.Copy")
proto.RegisterType((*RspFile)(nil), "sbox.RspFile")
proto.RegisterType((*PathMapping)(nil), "sbox.PathMapping")
} }
func init() { func init() {
@ -222,22 +332,27 @@ func init() {
} }
var fileDescriptor_9d0425bf0de86ed1 = []byte{ var fileDescriptor_9d0425bf0de86ed1 = []byte{
// 268 bytes of a gzipped FileDescriptorProto // 342 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x4f, 0x4b, 0xc3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x5f, 0x4b, 0xc3, 0x30,
0x10, 0xc5, 0xc9, 0x9f, 0xd2, 0x64, 0x6a, 0x7b, 0x18, 0x3c, 0xec, 0x45, 0x09, 0x01, 0x21, 0x45, 0x14, 0xc5, 0x69, 0xd7, 0xb9, 0xf6, 0xee, 0x0f, 0x18, 0x7c, 0xc8, 0x8b, 0x32, 0x0a, 0xc2, 0xa6,
0xe8, 0xc1, 0x6f, 0x60, 0xf5, 0x20, 0x82, 0x97, 0x1c, 0x45, 0x08, 0x9b, 0x64, 0x43, 0x02, 0x4d, 0x30, 0xd0, 0x07, 0xdf, 0x9d, 0x22, 0x22, 0x0c, 0x24, 0xe0, 0x8b, 0x08, 0x25, 0xeb, 0x52, 0x5b,
0x26, 0xec, 0x6e, 0xa0, 0xfd, 0x56, 0x7e, 0x44, 0xd9, 0x49, 0x2a, 0x82, 0xb7, 0x99, 0xdf, 0xe3, 0x58, 0x9b, 0x90, 0x64, 0xb0, 0x7d, 0x57, 0x3f, 0x8c, 0xe4, 0xa6, 0xd3, 0x82, 0x2f, 0xbe, 0xdd,
0xcd, 0x7b, 0x0c, 0x80, 0x29, 0xe9, 0x7c, 0x18, 0x35, 0x59, 0xc2, 0xd0, 0xcd, 0xe9, 0x17, 0x44, 0x7b, 0x0e, 0xf7, 0xdc, 0x5f, 0xc2, 0x05, 0x30, 0x6b, 0xb9, 0x5f, 0x28, 0x2d, 0xad, 0x24, 0x91,
0x1f, 0x72, 0xe8, 0x1a, 0x65, 0x2c, 0xee, 0x21, 0xaa, 0xa8, 0xef, 0xe5, 0x50, 0x1b, 0xe1, 0x25, 0xab, 0xd3, 0x0f, 0x88, 0x57, 0xbc, 0xa9, 0x0a, 0x61, 0x2c, 0x99, 0x43, 0x9c, 0xcb, 0xba, 0xe6,
0x41, 0xb6, 0x79, 0xda, 0x1e, 0xd8, 0xf0, 0x32, 0xd3, 0xfc, 0x57, 0xc6, 0x07, 0xd8, 0xd1, 0x64, 0xcd, 0xc6, 0xd0, 0x60, 0xda, 0x9b, 0x0d, 0x6f, 0xc7, 0x0b, 0x1c, 0x78, 0xf0, 0x2a, 0xfb, 0xb1,
0xc7, 0xc9, 0x16, 0xb5, 0x1a, 0x9b, 0xee, 0xa4, 0x84, 0x9f, 0x78, 0x59, 0x9c, 0x6f, 0x67, 0xfa, 0xc9, 0x25, 0x4c, 0xe4, 0xce, 0xaa, 0x9d, 0xcd, 0x36, 0x42, 0x15, 0xd5, 0x56, 0xd0, 0x70, 0x1a,
0x3a, 0xc3, 0xf4, 0xdb, 0x83, 0xf5, 0x62, 0xc6, 0x47, 0xd8, 0x54, 0x34, 0x5e, 0x8a, 0x52, 0x35, 0xcc, 0x12, 0x36, 0xf6, 0xea, 0xa3, 0x17, 0xd3, 0xaf, 0x00, 0x06, 0xed, 0x30, 0xb9, 0x86, 0x61,
0xa4, 0xd5, 0x12, 0x00, 0xd7, 0x80, 0xf1, 0x92, 0x83, 0x93, 0x8f, 0xac, 0xe2, 0x2d, 0xac, 0xaa, 0x2e, 0xd5, 0x21, 0x5b, 0x8b, 0x42, 0x6a, 0xd1, 0x2e, 0x80, 0xe3, 0x02, 0x75, 0x60, 0xe0, 0xec,
0xb6, 0xee, 0x34, 0x9f, 0x8d, 0xf2, 0x79, 0x41, 0x01, 0xeb, 0xa5, 0x81, 0x08, 0x12, 0x3f, 0x8b, 0x25, 0xba, 0xe4, 0x0c, 0xfa, 0x79, 0xb9, 0xa9, 0x34, 0xc6, 0xc6, 0xcc, 0x37, 0x84, 0xc2, 0xa0,
0xf3, 0xeb, 0x8a, 0x7b, 0x60, 0x77, 0x21, 0x1b, 0xab, 0xb4, 0x08, 0xff, 0xdd, 0x8e, 0x9d, 0xfa, 0x25, 0xa0, 0xbd, 0x69, 0x38, 0x4b, 0xd8, 0xb1, 0x25, 0x73, 0xc0, 0xe9, 0x8c, 0x17, 0x56, 0x68,
0xec, 0x44, 0xbc, 0x03, 0xe8, 0x06, 0xd7, 0xbc, 0x95, 0xa6, 0x15, 0x2b, 0xae, 0x1d, 0x33, 0x79, 0x1a, 0xfd, 0xc9, 0x4e, 0x9c, 0x7b, 0xef, 0x4c, 0x72, 0x0e, 0x50, 0x35, 0x8e, 0xbc, 0xe4, 0xa6,
0x93, 0xa6, 0x4d, 0xdf, 0x21, 0x74, 0x0e, 0x44, 0x08, 0x1b, 0x4d, 0xbd, 0xf0, 0x38, 0x88, 0x67, 0xa4, 0x7d, 0xc4, 0x4e, 0x50, 0x79, 0xe6, 0xa6, 0x24, 0x57, 0x90, 0x68, 0xa3, 0x32, 0x87, 0x6f,
0xdc, 0x81, 0x6f, 0x49, 0xf8, 0x4c, 0x7c, 0x4b, 0x78, 0x0f, 0xa0, 0xce, 0xaa, 0x9a, 0xac, 0x2c, 0xe8, 0x49, 0xf7, 0x17, 0x98, 0x51, 0x4f, 0xd5, 0x56, 0xb0, 0x58, 0xfb, 0xc2, 0xa4, 0x2f, 0x10,
0x4f, 0x4a, 0x04, 0x5c, 0xf5, 0x0f, 0x39, 0xde, 0x7c, 0xf2, 0xc3, 0x0b, 0x7e, 0xf8, 0x4f, 0x00, 0xb9, 0x74, 0x42, 0x20, 0x2a, 0xb4, 0xac, 0x69, 0x80, 0x50, 0x58, 0x93, 0x09, 0x84, 0x56, 0xd2,
0x00, 0x00, 0xff, 0xff, 0x78, 0x37, 0x3e, 0x6a, 0x7d, 0x01, 0x00, 0x00, 0x10, 0x95, 0xd0, 0x4a, 0x72, 0x01, 0x20, 0xf6, 0x22, 0xdf, 0x59, 0xbe, 0xde, 0x0a, 0xda, 0xc3,
0x67, 0x75, 0x94, 0xf4, 0x0d, 0x06, 0xed, 0x02, 0x8c, 0x73, 0x5f, 0x7a, 0x8c, 0x73, 0xda, 0x1d,
0x8c, 0x15, 0xb7, 0x65, 0x56, 0x73, 0xa5, 0xaa, 0xe6, 0xd3, 0xd0, 0x10, 0xd1, 0x4e, 0x3d, 0xda,
0x2b, 0xb7, 0xe5, 0xca, 0x3b, 0x6c, 0xa4, 0x7e, 0x1b, 0x93, 0xde, 0xc0, 0xb0, 0x63, 0xfe, 0x87,
0x74, 0x39, 0x7a, 0xc7, 0x33, 0xc9, 0xf0, 0x4c, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x82,
0xb0, 0xc3, 0x33, 0x02, 0x00, 0x00,
} }

View File

@ -47,6 +47,10 @@ message Command {
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns // An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line. // when the lists of inputs changes, even if the inputs are not on the command line.
optional string input_hash = 5; optional string input_hash = 5;
// A list of files that will be copied before the sandboxed command, and whose contents should be
// copied as if they were listed in copy_before.
repeated RspFile rsp_files = 6;
} }
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they // Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
@ -58,4 +62,19 @@ message Copy {
// If true, make the file executable after copying it. // If true, make the file executable after copying it.
optional bool executable = 3; optional bool executable = 3;
} }
// RspFile describes an rspfile that should be copied into the sandbox directory.
message RspFile {
// The path to the rsp file.
required string file = 1;
// A list of path mappings that should be applied to each file listed in the rsp file.
repeated PathMapping path_mappings = 2;
}
// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
message PathMapping {
required string from = 1;
required string to = 2;
}

View File

@ -150,7 +150,7 @@ var (
&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"}, &remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
ExecStrategy: "${config.RETurbineExecStrategy}", ExecStrategy: "${config.RETurbineExecStrategy}",
Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"}, Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
RSPFile: "${out}.rsp", RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out.tmp"}, OutputFiles: []string{"$out.tmp"},
OutputDirectories: []string{"$outDir"}, OutputDirectories: []string{"$outDir"},
ToolchainInputs: []string{"${config.JavaCmd}"}, ToolchainInputs: []string{"${config.JavaCmd}"},
@ -167,7 +167,7 @@ var (
&remoteexec.REParams{ &remoteexec.REParams{
ExecStrategy: "${config.REJarExecStrategy}", ExecStrategy: "${config.REJarExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp"}, Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp"},
RSPFile: "${out}.rsp", RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"}, OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, nil) }, []string{"jarArgs"}, nil)
@ -182,7 +182,7 @@ var (
&remoteexec.REParams{ &remoteexec.REParams{
ExecStrategy: "${config.REZipExecStrategy}", ExecStrategy: "${config.REZipExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"}, Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
RSPFile: "${out}.rsp", RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"}, OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, []string{"implicits"}) }, []string{"jarArgs"}, []string{"implicits"})

View File

@ -1209,7 +1209,7 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi
rule.Command().Text("mkdir -p").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String())
cmd := rule.Command() cmd := rule.Command()
cmd.FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()) cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") { if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
rule.Remoteable(android.RemoteRuleSupports{RBE: true}) rule.Remoteable(android.RemoteRuleSupports{RBE: true})
@ -1231,10 +1231,10 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi
Labels: labels, Labels: labels,
ExecStrategy: execStrategy, ExecStrategy: execStrategy,
Inputs: inputs, Inputs: inputs,
RSPFile: implicitsRsp.String(), RSPFiles: []string{implicitsRsp.String()},
ToolchainInputs: []string{config.JavaCmd(ctx).String()}, ToolchainInputs: []string{config.JavaCmd(ctx).String()},
Platform: map[string]string{remoteexec.PoolKey: pool}, Platform: map[string]string{remoteexec.PoolKey: pool},
EnvironmentVariables: []string{"ANDROID_SDK_HOME"}, EnvironmentVariables: []string{"ANDROID_PREFS_ROOT"},
}).NoVarTemplate(ctx.Config().RBEWrapper())) }).NoVarTemplate(ctx.Config().RBEWrapper()))
} }

View File

@ -628,7 +628,7 @@ prebuilt_stubs_sources {
} }
t.Run("empty/missing directory", func(t *testing.T) { t.Run("empty/missing directory", func(t *testing.T) {
test(t, "empty-directory", []string{}) test(t, "empty-directory", nil)
}) })
t.Run("non-empty set of sources", func(t *testing.T) { t.Run("non-empty set of sources", func(t *testing.T) {

View File

@ -182,11 +182,6 @@ type lintPaths struct {
cacheDir android.WritablePath cacheDir android.WritablePath
homeDir android.WritablePath homeDir android.WritablePath
srcjarDir android.WritablePath srcjarDir android.WritablePath
deps android.Paths
remoteInputs android.Paths
remoteRSPInputs android.Paths
} }
func lintRBEExecStrategy(ctx android.ModuleContext) string { func lintRBEExecStrategy(ctx android.ModuleContext) string {
@ -194,39 +189,6 @@ func lintRBEExecStrategy(ctx android.ModuleContext) string {
} }
func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths { func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
var deps android.Paths
var remoteInputs android.Paths
var remoteRSPInputs android.Paths
// Paths passed to trackInputDependency will be added as dependencies of the rule that runs
// lint and passed as inputs to the remote execution proxy.
trackInputDependency := func(paths ...android.Path) {
deps = append(deps, paths...)
remoteInputs = append(remoteInputs, paths...)
}
// Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
// lint, but the RSP file will be used by the remote execution proxy to find the files so that
// it doesn't overflow command line limits.
trackRSPDependency := func(paths android.Paths, rsp android.Path) {
deps = append(deps, paths...)
remoteRSPInputs = append(remoteRSPInputs, rsp)
}
var resourcesList android.WritablePath
if len(l.resources) > 0 {
// The list of resources may be too long to put on the command line, but
// we can't use the rsp file because it is already being used for srcs.
// Insert a second rule to write out the list of resources to a file.
resourcesList = android.PathForModuleOut(ctx, "resources.list")
resListRule := android.NewRuleBuilder(pctx, ctx)
resListRule.Command().Text("cp").
FlagWithRspFileInputList("", resourcesList.ReplaceExtension(ctx, "rsp"), l.resources).
Output(resourcesList)
resListRule.Build("lint_resources_list", "lint resources list")
trackRSPDependency(l.resources, resourcesList)
}
projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
// Lint looks for a lint.xml file next to the project.xml file, give it one. // Lint looks for a lint.xml file next to the project.xml file, give it one.
configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@ -235,20 +197,6 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru
srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars") srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
// TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
// by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
// not the extracted files.
trackRSPDependency(l.srcJars, srcJarList)
// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
// lint separately.
srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
rule.Command().Text("cp").
FlagWithRspFileInputList("", srcsListRsp, l.srcs).
Output(srcsList)
trackRSPDependency(l.srcs, srcsList)
rule.Temporary(srcsList)
cmd := rule.Command(). cmd := rule.Command().
BuiltTool("lint-project-xml"). BuiltTool("lint-project-xml").
@ -263,32 +211,31 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru
cmd.Flag("--test") cmd.Flag("--test")
} }
if l.manifest != nil { if l.manifest != nil {
cmd.FlagWithArg("--manifest ", cmd.PathForInput(l.manifest)) cmd.FlagWithInput("--manifest ", l.manifest)
trackInputDependency(l.manifest)
} }
if l.mergedManifest != nil { if l.mergedManifest != nil {
cmd.FlagWithArg("--merged_manifest ", cmd.PathForInput(l.mergedManifest)) cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
trackInputDependency(l.mergedManifest)
} }
cmd.FlagWithInput("--srcs ", srcsList) // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
// lint separately.
srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
cmd.FlagWithInput("--generated_srcs ", srcJarList) cmd.FlagWithInput("--generated_srcs ", srcJarList)
if resourcesList != nil { if len(l.resources) > 0 {
cmd.FlagWithInput("--resources ", resourcesList) resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
} }
if l.classes != nil { if l.classes != nil {
cmd.FlagWithArg("--classes ", cmd.PathForInput(l.classes)) cmd.FlagWithInput("--classes ", l.classes)
trackInputDependency(l.classes)
} }
cmd.FlagForEachArg("--classpath ", cmd.PathsForInputs(l.classpath)) cmd.FlagForEachInput("--classpath ", l.classpath)
trackInputDependency(l.classpath...)
cmd.FlagForEachArg("--extra_checks_jar ", cmd.PathsForInputs(l.extraLintCheckJars)) cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
trackInputDependency(l.extraLintCheckJars...)
cmd.FlagWithArg("--root_dir ", "$PWD") cmd.FlagWithArg("--root_dir ", "$PWD")
@ -309,11 +256,6 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru
configXML: configXMLPath, configXML: configXMLPath,
cacheDir: cacheDir, cacheDir: cacheDir,
homeDir: homeDir, homeDir: homeDir,
deps: deps,
remoteInputs: remoteInputs,
remoteRSPInputs: remoteRSPInputs,
} }
} }
@ -424,8 +366,7 @@ func (l *linter) lint(ctx android.ModuleContext) {
Flag("--exitcode"). Flag("--exitcode").
Flags(l.properties.Lint.Flags). Flags(l.properties.Lint.Flags).
Implicit(annotationsZipPath). Implicit(annotationsZipPath).
Implicit(apiVersionsXMLPath). Implicit(apiVersionsXMLPath)
Implicits(lintPaths.deps)
rule.Temporary(lintPaths.projectXML) rule.Temporary(lintPaths.projectXML)
rule.Temporary(lintPaths.configXML) rule.Temporary(lintPaths.configXML)

View File

@ -64,9 +64,8 @@ type REParams struct {
ExecStrategy string ExecStrategy string
// Inputs is a list of input paths or ninja variables. // Inputs is a list of input paths or ninja variables.
Inputs []string Inputs []string
// RSPFile is the name of the ninja variable used by the rule as a placeholder for an rsp // RSPFiles is the name of the files used by the rule as a placeholder for an rsp input.
// input. RSPFiles []string
RSPFile string
// OutputFiles is a list of output file paths or ninja variables as placeholders for rule // OutputFiles is a list of output file paths or ninja variables as placeholders for rule
// outputs. // outputs.
OutputFiles []string OutputFiles []string
@ -134,8 +133,8 @@ func (r *REParams) wrapperArgs() string {
args += " --inputs=" + strings.Join(r.Inputs, ",") args += " --inputs=" + strings.Join(r.Inputs, ",")
} }
if r.RSPFile != "" { if len(r.RSPFiles) > 0 {
args += " --input_list_paths=" + r.RSPFile args += " --input_list_paths=" + strings.Join(r.RSPFiles, ",")
} }
if len(r.OutputFiles) > 0 { if len(r.OutputFiles) > 0 {

View File

@ -45,14 +45,14 @@ func TestTemplate(t *testing.T) {
Inputs: []string{"$in"}, Inputs: []string{"$in"},
OutputFiles: []string{"$out"}, OutputFiles: []string{"$out"},
ExecStrategy: "remote", ExecStrategy: "remote",
RSPFile: "$out.rsp", RSPFiles: []string{"$out.rsp", "out2.rsp"},
ToolchainInputs: []string{"clang++"}, ToolchainInputs: []string{"clang++"},
Platform: map[string]string{ Platform: map[string]string{
ContainerImageKey: DefaultImage, ContainerImageKey: DefaultImage,
PoolKey: "default", PoolKey: "default",
}, },
}, },
want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage), want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
}, },
} }
for _, test := range tests { for _, test := range tests {

16
response/Android.bp Normal file
View File

@ -0,0 +1,16 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
bootstrap_go_package {
name: "soong-response",
pkgPath: "android/soong/response",
deps: [
],
srcs: [
"response.go",
],
testSrcs: [
"response_test.go",
],
}

112
response/response.go Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package response
import (
"io"
"io/ioutil"
"strings"
"unicode"
)
const noQuote = '\x00'
// ReadRspFile reads a file in Ninja's response file format and returns its contents.
func ReadRspFile(r io.Reader) ([]string, error) {
var files []string
var file []byte
buf, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
isEscaping := false
quotingStart := byte(noQuote)
for _, c := range buf {
switch {
case isEscaping:
if quotingStart == '"' {
if !(c == '"' || c == '\\') {
// '\"' or '\\' will be escaped under double quoting.
file = append(file, '\\')
}
}
file = append(file, c)
isEscaping = false
case c == '\\' && quotingStart != '\'':
isEscaping = true
case quotingStart == noQuote && (c == '\'' || c == '"'):
quotingStart = c
case quotingStart != noQuote && c == quotingStart:
quotingStart = noQuote
case quotingStart == noQuote && unicode.IsSpace(rune(c)):
// Current character is a space outside quotes
if len(file) != 0 {
files = append(files, string(file))
}
file = file[:0]
default:
file = append(file, c)
}
}
if len(file) != 0 {
files = append(files, string(file))
}
return files, nil
}
func rspUnsafeChar(r rune) bool {
switch {
case 'A' <= r && r <= 'Z',
'a' <= r && r <= 'z',
'0' <= r && r <= '9',
r == '_',
r == '+',
r == '-',
r == '.',
r == '/':
return false
default:
return true
}
}
var rspEscaper = strings.NewReplacer(`'`, `'\''`)
// WriteRspFile writes a list of files to a file in Ninja's response file format.
func WriteRspFile(w io.Writer, files []string) error {
for i, f := range files {
if i != 0 {
_, err := io.WriteString(w, " ")
if err != nil {
return err
}
}
if strings.IndexFunc(f, rspUnsafeChar) != -1 {
f = `'` + rspEscaper.Replace(f) + `'`
}
_, err := io.WriteString(w, f)
if err != nil {
return err
}
}
return nil
}

123
response/response_test.go Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package response
import (
"bytes"
"reflect"
"testing"
)
func TestReadRspFile(t *testing.T) {
testCases := []struct {
name, in string
out []string
}{
{
name: "single quoting test case 1",
in: `./cmd '"'-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "single quoting test case 2",
in: `./cmd '-C`,
out: []string{"./cmd", `-C`},
},
{
name: "single quoting test case 3",
in: `./cmd '\"'-C`,
out: []string{"./cmd", `\"-C`},
},
{
name: "single quoting test case 4",
in: `./cmd '\\'-C`,
out: []string{"./cmd", `\\-C`},
},
{
name: "none quoting test case 1",
in: `./cmd \'-C`,
out: []string{"./cmd", `'-C`},
},
{
name: "none quoting test case 2",
in: `./cmd \\-C`,
out: []string{"./cmd", `\-C`},
},
{
name: "none quoting test case 3",
in: `./cmd \"-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "double quoting test case 1",
in: `./cmd "'"-C`,
out: []string{"./cmd", `'-C`},
},
{
name: "double quoting test case 2",
in: `./cmd "\\"-C`,
out: []string{"./cmd", `\-C`},
},
{
name: "double quoting test case 3",
in: `./cmd "\""-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "ninja rsp file",
in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := ReadRspFile(bytes.NewBuffer([]byte(testCase.in)))
if err != nil {
t.Errorf("unexpected error: %q", err)
}
if !reflect.DeepEqual(got, testCase.out) {
t.Errorf("expected %q got %q", testCase.out, got)
}
})
}
}
func TestWriteRspFile(t *testing.T) {
testCases := []struct {
name string
in []string
out string
}{
{
name: "ninja rsp file",
in: []string{"a", "b", "@", "foo'bar", `foo"bar`},
out: "a b '@' 'foo'\\''bar' 'foo\"bar'",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
buf := &bytes.Buffer{}
err := WriteRspFile(buf, testCase.in)
if err != nil {
t.Errorf("unexpected error: %q", err)
}
if buf.String() != testCase.out {
t.Errorf("expected %q got %q", testCase.out, buf.String())
}
})
}
}

View File

@ -25,6 +25,7 @@ bootstrap_go_package {
"android-archive-zip", "android-archive-zip",
"blueprint-pathtools", "blueprint-pathtools",
"soong-jar", "soong-jar",
"soong-response",
], ],
srcs: [ srcs: [
"zip.go", "zip.go",

View File

@ -24,7 +24,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
@ -32,6 +31,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"android/soong/response"
"android/soong/zip" "android/soong/zip"
) )
@ -125,12 +125,18 @@ func main() {
var expandedArgs []string var expandedArgs []string
for _, arg := range os.Args { for _, arg := range os.Args {
if strings.HasPrefix(arg, "@") { if strings.HasPrefix(arg, "@") {
bytes, err := ioutil.ReadFile(strings.TrimPrefix(arg, "@")) f, err := os.Open(strings.TrimPrefix(arg, "@"))
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
respArgs, err := response.ReadRspFile(f)
f.Close()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err.Error()) fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1) os.Exit(1)
} }
respArgs := zip.ReadRespFile(bytes)
expandedArgs = append(expandedArgs, respArgs...) expandedArgs = append(expandedArgs, respArgs...)
} else { } else {
expandedArgs = append(expandedArgs, arg) expandedArgs = append(expandedArgs, arg)

View File

@ -29,7 +29,8 @@ import (
"sync" "sync"
"syscall" "syscall"
"time" "time"
"unicode"
"android/soong/response"
"github.com/google/blueprint/pathtools" "github.com/google/blueprint/pathtools"
@ -164,14 +165,12 @@ func (b *FileArgsBuilder) RspFile(name string) *FileArgsBuilder {
} }
defer f.Close() defer f.Close()
list, err := ioutil.ReadAll(f) arg := b.state
arg.SourceFiles, err = response.ReadRspFile(f)
if err != nil { if err != nil {
b.err = err b.err = err
return b return b
} }
arg := b.state
arg.SourceFiles = ReadRespFile(list)
for i := range arg.SourceFiles { for i := range arg.SourceFiles {
arg.SourceFiles[i] = pathtools.MatchEscape(arg.SourceFiles[i]) arg.SourceFiles[i] = pathtools.MatchEscape(arg.SourceFiles[i])
} }
@ -253,49 +252,6 @@ type ZipArgs struct {
Filesystem pathtools.FileSystem Filesystem pathtools.FileSystem
} }
const NOQUOTE = '\x00'
func ReadRespFile(bytes []byte) []string {
var args []string
var arg []rune
isEscaping := false
quotingStart := NOQUOTE
for _, c := range string(bytes) {
switch {
case isEscaping:
if quotingStart == '"' {
if !(c == '"' || c == '\\') {
// '\"' or '\\' will be escaped under double quoting.
arg = append(arg, '\\')
}
}
arg = append(arg, c)
isEscaping = false
case c == '\\' && quotingStart != '\'':
isEscaping = true
case quotingStart == NOQUOTE && (c == '\'' || c == '"'):
quotingStart = c
case quotingStart != NOQUOTE && c == quotingStart:
quotingStart = NOQUOTE
case quotingStart == NOQUOTE && unicode.IsSpace(c):
// Current character is a space outside quotes
if len(arg) != 0 {
args = append(args, string(arg))
}
arg = arg[:0]
default:
arg = append(arg, c)
}
}
if len(arg) != 0 {
args = append(args, string(arg))
}
return args
}
func zipTo(args ZipArgs, w io.Writer) error { func zipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar { if args.EmulateJar {
args.AddDirectoryEntriesToZip = true args.AddDirectoryEntriesToZip = true

View File

@ -535,78 +535,6 @@ func TestZip(t *testing.T) {
} }
} }
func TestReadRespFile(t *testing.T) {
testCases := []struct {
name, in string
out []string
}{
{
name: "single quoting test case 1",
in: `./cmd '"'-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "single quoting test case 2",
in: `./cmd '-C`,
out: []string{"./cmd", `-C`},
},
{
name: "single quoting test case 3",
in: `./cmd '\"'-C`,
out: []string{"./cmd", `\"-C`},
},
{
name: "single quoting test case 4",
in: `./cmd '\\'-C`,
out: []string{"./cmd", `\\-C`},
},
{
name: "none quoting test case 1",
in: `./cmd \'-C`,
out: []string{"./cmd", `'-C`},
},
{
name: "none quoting test case 2",
in: `./cmd \\-C`,
out: []string{"./cmd", `\-C`},
},
{
name: "none quoting test case 3",
in: `./cmd \"-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "double quoting test case 1",
in: `./cmd "'"-C`,
out: []string{"./cmd", `'-C`},
},
{
name: "double quoting test case 2",
in: `./cmd "\\"-C`,
out: []string{"./cmd", `\-C`},
},
{
name: "double quoting test case 3",
in: `./cmd "\""-C`,
out: []string{"./cmd", `"-C`},
},
{
name: "ninja rsp file",
in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got := ReadRespFile([]byte(testCase.in))
if !reflect.DeepEqual(got, testCase.out) {
t.Errorf("expected %q got %q", testCase.out, got)
}
})
}
}
func TestSrcJar(t *testing.T) { func TestSrcJar(t *testing.T) {
mockFs := pathtools.MockFs(map[string][]byte{ mockFs := pathtools.MockFs(map[string][]byte{
"wrong_package.java": []byte("package foo;"), "wrong_package.java": []byte("package foo;"),