Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f9bb266 authored by Sam Delmerico's avatar Sam Delmerico
Browse files

allow Ninja variables in RuleBuilder API

The RuleBuilder API would not expand Ninja variables because the
variables would be written verbatim to the sandbox manifest file. This
commit allows a rule to specify that the manifest file should be written
in an un-escaped format so that Ninja variables are expanded before
writing the manifest file.

Bug: 286077158
Test: rust sandboxing topic + go test
Change-Id: I1915431f6e24d04d343dacc213c9079674ec8251
parent 553edff9
Loading
Loading
Loading
Loading
+37 −4
Original line number Diff line number Diff line
@@ -474,13 +474,23 @@ func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderComma
		Inputs(depFiles.Paths())
}

// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
	r.build(name, desc, false)
}

// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs.
func (r *RuleBuilder) Build(name string, desc string) {
	r.build(name, desc, true)
}

func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
	name = ninjaNameEscape(name)

	if len(r.missingDeps) > 0 {
		r.ctx.Build(pctx, BuildParams{
		r.ctx.Build(r.pctx, BuildParams{
			Rule:        ErrorRule,
			Outputs:     r.Outputs(),
			Description: desc,
@@ -619,12 +629,35 @@ func (r *RuleBuilder) Build(name string, desc string) {
				name, r.sboxManifestPath.String(), r.outDir.String())
		}

		// Create a rule to write the manifest as a the textproto.
		// Create a rule to write the manifest as textproto.
		pbText, err := prototext.Marshal(&manifest)
		if err != nil {
			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
		}
		if ninjaEscapeCommandString {
			WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
		} else {
			// We need  to have a rule to write files that is
			// defined on the RuleBuilder's pctx in order to
			// write Ninja variables in the string.
			// The WriteFileRule function above rule can only write
			// raw strings because it is defined on the android
			// package's pctx, and it can't access variables defined
			// in another context.
			r.ctx.Build(r.pctx, BuildParams{
				Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
					Command:        `rm -rf ${out} && cat ${out}.rsp > ${out}`,
					Rspfile:        "${out}.rsp",
					RspfileContent: "${content}",
					Description:    "write file",
				}, "content"),
				Output:      r.sboxManifestPath,
				Description: "write sbox manifest " + r.sboxManifestPath.Base(),
				Args: map[string]string{
					"content": string(pbText),
				},
			})
		}

		// Generate a new string to use as the command line of the sbox rule.  This uses
		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -724,7 +757,7 @@ func (r *RuleBuilder) Build(name string, desc string) {
	}

	r.ctx.Build(r.pctx, BuildParams{
		Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
		Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
			Command:        proptools.NinjaEscape(commandString),
			CommandDeps:    proptools.NinjaEscapeList(tools.Strings()),
			Restat:         r.restat,
+75 −11
Original line number Diff line number Diff line
@@ -28,6 +28,17 @@ import (
	"android/soong/shared"
)

var (
	pctx_ruleBuilderTest           = NewPackageContext("android/soong/rule_builder")
	pctx_ruleBuilderTestSubContext = NewPackageContext("android/soong/rule_builder/config")
)

func init() {
	pctx_ruleBuilderTest.Import("android/soong/rule_builder/config")
	pctx_ruleBuilderTest.StaticVariable("cmdFlags", "${config.ConfigFlags}")
	pctx_ruleBuilderTestSubContext.StaticVariable("ConfigFlags", "--some-clang-flag")
}

func builderContext() BuilderContext {
	return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
		"ld":      nil,
@@ -497,10 +508,12 @@ type testRuleBuilderModule struct {
	ModuleBase
	properties struct {
		Srcs  []string
		Flags []string

		Restat              bool
		Sbox                bool
		Sbox_inputs         bool
		Unescape_ninja_vars bool
	}
}

@@ -518,8 +531,9 @@ func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
	rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
	manifestPath := PathForModuleOut(ctx, "sbox.textproto")

	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs,
	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags,
		out, outDep, outDir,
		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars,
		rspFile, rspFileContents, rspFile2, rspFileContents2)
}

@@ -543,17 +557,18 @@ func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
	rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
	manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")

	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
		manifestPath, true, false, false,
	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir,
		manifestPath, true, false, false, false,
		rspFile, rspFileContents, rspFile2, rspFileContents2)
}

func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path,
	flags []string,
	out, outDep, outDir, manifestPath WritablePath,
	restat, sbox, sboxInputs bool,
	restat, sbox, sboxInputs, unescapeNinjaVars bool,
	rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {

	rule := NewRuleBuilder(pctx, ctx)
	rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx)

	if sbox {
		rule.Sbox(outDir, manifestPath)
@@ -564,6 +579,7 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, va

	rule.Command().
		Tool(PathForSource(ctx, "cp")).
		Flags(flags).
		Inputs(in).
		Implicit(implicit).
		OrderOnly(orderOnly).
@@ -577,8 +593,12 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, va
		rule.Restat()
	}

	if unescapeNinjaVars {
		rule.BuildWithUnescapedNinjaVars("rule", "desc")
	} else {
		rule.Build("rule", "desc")
	}
}

var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
	ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
@@ -792,3 +812,47 @@ func TestRuleBuilderHashInputs(t *testing.T) {
		})
	}
}

func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) {
	bp := `
		rule_builder_test {
			name: "foo_sbox_escaped_ninja",
			flags: ["${cmdFlags}"],
			sbox: true,
			sbox_inputs: true,
		}
		rule_builder_test {
			name: "foo_sbox",
			flags: ["${cmdFlags}"],
			sbox: true,
			sbox_inputs: true,
			unescape_ninja_vars: true,
		}
	`
	result := GroupFixturePreparers(
		prepareForRuleBuilderTest,
		FixtureWithRootAndroidBp(bp),
	).RunTest(t)

	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped_ninja", "").Rule("writeFile")
	AssertStringDoesContain(
		t,
		"",
		escapedNinjaMod.BuildParams.Args["content"],
		"$${cmdFlags}",
	)

	unescapedNinjaMod := result.ModuleForTests("foo_sbox", "").Rule("unescapedWriteFile")
	AssertStringDoesContain(
		t,
		"",
		unescapedNinjaMod.BuildParams.Args["content"],
		"${cmdFlags}",
	)
	AssertStringDoesNotContain(
		t,
		"",
		unescapedNinjaMod.BuildParams.Args["content"],
		"$${cmdFlags}",
	)
}
+3 −0
Original line number Diff line number Diff line
@@ -119,6 +119,9 @@ func run() error {
	}

	manifest, err := readManifest(manifestFile)
	if err != nil {
		return err
	}

	if len(manifest.Commands) == 0 {
		return fmt.Errorf("at least one commands entry is required in %q", manifestFile)