Pass pctx and ctx to NewRuleBuilder

Enable the RuleBuilder and RuleBuilderCommand methods to access
the BuilderContext by passing it to NewRuleBuilder instead of
RuleBuilder.Build.

Test: genrule_test.go
Test: rule_builder_test.go
Test: m checkbuild
Change-Id: I63e6597e19167393876dc2259d6f521363b7dabc
diff --git a/android/packaging.go b/android/packaging.go
index f168fa6..7755401 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -163,7 +163,7 @@
 		return true
 	})
 
-	builder := NewRuleBuilder()
+	builder := NewRuleBuilder(pctx, ctx)
 
 	dir := PathForModuleOut(ctx, ".zip").OutputPath
 	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
@@ -190,13 +190,13 @@
 	}
 
 	builder.Command().
-		BuiltTool(ctx, "soong_zip").
+		BuiltTool("soong_zip").
 		FlagWithOutput("-o ", zipOut).
 		FlagWithArg("-C ", dir.String()).
 		Flag("-L 0"). // no compression because this will be unzipped soon
 		FlagWithArg("-D ", dir.String())
 	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
 
-	builder.Build(pctx, ctx, "zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
+	builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
 	return entries
 }
diff --git a/android/proto.go b/android/proto.go
index b712258..0be7893 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -122,7 +122,7 @@
 	} `android:"arch_variant"`
 }
 
-func ProtoRule(ctx ModuleContext, rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
+func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
 	outDir WritablePath, depFile WritablePath, outputs WritablePaths) {
 
 	var protoBase string
@@ -134,7 +134,7 @@
 	}
 
 	rule.Command().
-		BuiltTool(ctx, "aprotoc").
+		BuiltTool("aprotoc").
 		FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
 		FlagWithDepFile("--dependency_out=", depFile).
 		FlagWithArg("-I ", protoBase).
@@ -144,5 +144,5 @@
 		ImplicitOutputs(outputs)
 
 	rule.Command().
-		BuiltTool(ctx, "dep_fixer").Flag(depFile.String())
+		BuiltTool("dep_fixer").Flag(depFile.String())
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 3efe9f8..e2d8187 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -37,6 +37,9 @@
 // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
 // graph.
 type RuleBuilder struct {
+	pctx PackageContext
+	ctx  BuilderContext
+
 	commands         []*RuleBuilderCommand
 	installs         RuleBuilderInstalls
 	temporariesSet   map[WritablePath]bool
@@ -44,14 +47,16 @@
 	sbox             bool
 	highmem          bool
 	remoteable       RemoteRuleSupports
-	sboxOutDir       WritablePath
+	outDir           WritablePath
 	sboxManifestPath WritablePath
 	missingDeps      []string
 }
 
 // NewRuleBuilder returns a newly created RuleBuilder.
-func NewRuleBuilder() *RuleBuilder {
+func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
 	return &RuleBuilder{
+		pctx:           pctx,
+		ctx:            ctx,
 		temporariesSet: make(map[WritablePath]bool),
 	}
 }
@@ -130,7 +135,7 @@
 		panic("Sbox() is not compatible with Restat()")
 	}
 	r.sbox = true
-	r.sboxOutDir = outputDir
+	r.outDir = outputDir
 	r.sboxManifestPath = manifestPath
 	return r
 }
@@ -146,8 +151,7 @@
 // race with any call to Build.
 func (r *RuleBuilder) Command() *RuleBuilderCommand {
 	command := &RuleBuilderCommand{
-		sbox:   r.sbox,
-		outDir: r.sboxOutDir,
+		rule: r,
 	}
 	r.commands = append(r.commands, command)
 	return command
@@ -395,19 +399,19 @@
 var _ BuilderContext = ModuleContext(nil)
 var _ BuilderContext = SingletonContext(nil)
 
-func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
+func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
 	return r.Command().
-		BuiltTool(ctx, "dep_fixer").
+		BuiltTool("dep_fixer").
 		Inputs(depFiles.Paths())
 }
 
 // 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(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+func (r *RuleBuilder) Build(name string, desc string) {
 	name = ninjaNameEscape(name)
 
 	if len(r.missingDeps) > 0 {
-		ctx.Build(pctx, BuildParams{
+		r.ctx.Build(pctx, BuildParams{
 			Rule:        ErrorRule,
 			Outputs:     r.Outputs(),
 			OrderOnly:   r.OrderOnlys(),
@@ -426,13 +430,13 @@
 		depFormat = blueprint.DepsGCC
 		if len(depFiles) > 1 {
 			// Add a command locally that merges all depfiles together into the first depfile.
-			r.depFileMergerCmd(ctx, depFiles)
+			r.depFileMergerCmd(depFiles)
 
 			if r.sbox {
 				// Check for Rel() errors, as all depfiles should be in the output dir.  Errors
 				// will be reported to the ctx.
 				for _, path := range depFiles[1:] {
-					Rel(ctx, r.sboxOutDir.String(), path.String())
+					Rel(r.ctx, r.outDir.String(), path.String())
 				}
 			}
 		}
@@ -468,7 +472,7 @@
 		// to the output directory.
 		sboxOutputs := make([]string, len(outputs))
 		for i, output := range outputs {
-			rel := Rel(ctx, r.sboxOutDir.String(), output.String())
+			rel := Rel(r.ctx, r.outDir.String(), output.String())
 			sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
 			command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
 				From: proto.String(filepath.Join(sboxOutSubDir, rel)),
@@ -483,23 +487,27 @@
 
 		// Verify that the manifest textproto is not inside the sbox output directory, otherwise
 		// it will get deleted when the sbox rule clears its output directory.
-		_, manifestInOutDir := MaybeRel(ctx, r.sboxOutDir.String(), r.sboxManifestPath.String())
+		_, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
 		if manifestInOutDir {
-			ReportPathErrorf(ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
-				name, r.sboxManifestPath.String(), r.sboxOutDir.String())
+			ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
+				name, r.sboxManifestPath.String(), r.outDir.String())
 		}
 
 		// Create a rule to write the manifest as a the textproto.
-		WriteFileRule(ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
+		WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
 
 		// 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
 		// converts it to a string to replace commandString.
-		sboxCmd := &RuleBuilderCommand{}
-		sboxCmd.Text("rm -rf").Output(r.sboxOutDir)
+		sboxCmd := &RuleBuilderCommand{
+			rule: &RuleBuilder{
+				ctx: r.ctx,
+			},
+		}
+		sboxCmd.Text("rm -rf").Output(r.outDir)
 		sboxCmd.Text("&&")
-		sboxCmd.BuiltTool(ctx, "sbox").
-			Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
+		sboxCmd.BuiltTool("sbox").
+			Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
 			Flag("--manifest").Input(r.sboxManifestPath)
 
 		// Replace the command string, and add the sbox tool and manifest textproto to the
@@ -528,19 +536,19 @@
 	}
 
 	var pool blueprint.Pool
-	if ctx.Config().UseGoma() && r.remoteable.Goma {
+	if r.ctx.Config().UseGoma() && r.remoteable.Goma {
 		// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
-	} else if ctx.Config().UseRBE() && r.remoteable.RBE {
+	} else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
 		// When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
 		pool = remotePool
 	} else if r.highmem {
 		pool = highmemPool
-	} else if ctx.Config().UseRemoteBuild() {
+	} else if r.ctx.Config().UseRemoteBuild() {
 		pool = localPool
 	}
 
-	ctx.Build(pctx, BuildParams{
-		Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
+	r.ctx.Build(r.pctx, BuildParams{
+		Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
 			Command:        commandString,
 			CommandDeps:    tools.Strings(),
 			Restat:         r.restat,
@@ -564,6 +572,8 @@
 // RuleBuilderCommand, so they can be used chained or unchained.  All methods that add text implicitly add a single
 // space as a separator from the previous method.
 type RuleBuilderCommand struct {
+	rule *RuleBuilder
+
 	buf            strings.Builder
 	inputs         Paths
 	implicits      Paths
@@ -576,16 +586,11 @@
 
 	// spans [start,end) of the command that should not be ninja escaped
 	unescapedSpans [][2]int
-
-	sbox bool
-	// 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.outDir.String(), path.String()); isRel {
+	if c.rule.sbox {
+		if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
 			return filepath.Join(sboxOutDir, rel)
 		}
 	}
@@ -594,8 +599,8 @@
 }
 
 func (c *RuleBuilderCommand) addImplicit(path Path) string {
-	if c.sbox {
-		if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+	if c.rule.sbox {
+		if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
 			return filepath.Join(sboxOutDir, rel)
 		}
 	}
@@ -607,22 +612,19 @@
 	c.orderOnlys = append(c.orderOnlys, path)
 }
 
-func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
-	if c.sbox {
-		return SboxPathForOutput(path, c.outDir)
+// PathForOutput takes an output path and returns the appropriate path to use on the command
+// line.  If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
+// placeholder prefix used for outputs in sbox.  If sbox is not enabled it returns the
+// original path.
+func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
+	if c.rule.sbox {
+		// Errors will be handled in RuleBuilder.Build where we have a context to report them
+		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+		return filepath.Join(sboxOutDir, rel)
 	}
 	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 {
@@ -699,8 +701,8 @@
 //
 // It is equivalent to:
 //  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
-func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
-	return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+	return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
 }
 
 // PrebuiltBuildTool adds the specified tool path from prebuils/build-tools.  The path will be also added to the
@@ -768,7 +770,7 @@
 // RuleBuilder.Outputs.
 func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
 	c.outputs = append(c.outputs, path)
-	return c.Text(c.outputStr(path))
+	return c.Text(c.PathForOutput(path))
 }
 
 // Outputs adds the specified output paths to the command line, separated by spaces.  The paths will also be added to
@@ -783,7 +785,7 @@
 // OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
 // and will be the temporary output directory managed by sbox, not the final one.
 func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
-	if !c.sbox {
+	if !c.rule.sbox {
 		panic("OutputDir only valid with Sbox")
 	}
 	return c.Text(sboxOutDir)
@@ -794,7 +796,7 @@
 // commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
 func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
 	c.depFiles = append(c.depFiles, path)
-	return c.Text(c.outputStr(path))
+	return c.Text(c.PathForOutput(path))
 }
 
 // ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
@@ -885,14 +887,14 @@
 // will also be added to the outputs returned by RuleBuilder.Outputs.
 func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
 	c.outputs = append(c.outputs, path)
-	return c.Text(flag + c.outputStr(path))
+	return c.Text(flag + c.PathForOutput(path))
 }
 
 // FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them.  The path
 // will also be added to the outputs returned by RuleBuilder.Outputs.
 func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
 	c.depFiles = append(c.depFiles, path)
-	return c.Text(flag + c.outputStr(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
@@ -987,3 +989,21 @@
 	h.Write([]byte(srcFileList))
 	return fmt.Sprintf("%x", h.Sum(nil))
 }
+
+// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
+// that need to call methods that take a BuilderContext.
+func BuilderContextForTesting(config Config) BuilderContext {
+	pathCtx := PathContextForTesting(config)
+	return builderContextForTests{
+		PathContext: pathCtx,
+	}
+}
+
+type builderContextForTests struct {
+	PathContext
+}
+
+func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
+	return nil
+}
+func (builderContextForTests) Build(PackageContext, BuildParams) {}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index dc360c3..e676e4a 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -27,8 +27,8 @@
 	"android/soong/shared"
 )
 
-func pathContext() PathContext {
-	return PathContextForTesting(TestConfig("out", nil, "", map[string][]byte{
+func builderContext() BuilderContext {
+	return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
 		"ld":      nil,
 		"a.o":     nil,
 		"b.o":     nil,
@@ -44,9 +44,9 @@
 }
 
 func ExampleRuleBuilder() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	rule.Command().
 		Tool(PathForSource(ctx, "ld")).
@@ -55,7 +55,7 @@
 	rule.Command().Text("echo success")
 
 	// To add the command to the build graph:
-	// rule.Build(pctx, ctx, "link", "link")
+	// rule.Build("link", "link")
 
 	fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
 	fmt.Printf("tools: %q\n", rule.Tools())
@@ -70,9 +70,9 @@
 }
 
 func ExampleRuleBuilder_SymlinkOutputs() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	rule.Command().
 		Tool(PathForSource(ctx, "ln")).
@@ -96,9 +96,9 @@
 }
 
 func ExampleRuleBuilder_Temporary() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	rule.Command().
 		Tool(PathForSource(ctx, "cp")).
@@ -123,9 +123,9 @@
 }
 
 func ExampleRuleBuilder_DeleteTemporaryFiles() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	rule.Command().
 		Tool(PathForSource(ctx, "cp")).
@@ -151,9 +151,9 @@
 }
 
 func ExampleRuleBuilder_Installs() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	out := PathForOutput(ctx, "linked")
 
@@ -171,9 +171,9 @@
 }
 
 func ExampleRuleBuilderCommand() {
-	rule := NewRuleBuilder()
+	ctx := builderContext()
 
-	ctx := pathContext()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	// chained
 	rule.Command().
@@ -194,24 +194,24 @@
 }
 
 func ExampleRuleBuilderCommand_Flag() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "ls")).Flag("-l"))
 	// Output:
 	// ls -l
 }
 
 func ExampleRuleBuilderCommand_Flags() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
 	// Output:
 	// ls -l -a
 }
 
 func ExampleRuleBuilderCommand_FlagWithArg() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "ls")).
 		FlagWithArg("--sort=", "time"))
 	// Output:
@@ -219,8 +219,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagForEachArg() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "ls")).
 		FlagForEachArg("--sort=", []string{"time", "size"}))
 	// Output:
@@ -228,8 +228,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagForEachInput() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "turbine")).
 		FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
 	// Output:
@@ -237,8 +237,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagWithInputList() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "java")).
 		FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
 	// Output:
@@ -246,8 +246,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagWithInput() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "java")).
 		FlagWithInput("-classpath=", PathForSource(ctx, "a")))
 	// Output:
@@ -255,8 +255,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagWithList() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "ls")).
 		FlagWithList("--sort=", []string{"time", "size"}, ","))
 	// Output:
@@ -264,8 +264,8 @@
 }
 
 func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
-	ctx := pathContext()
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "javac")).
 		FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
 		NinjaEscapedString())
@@ -274,7 +274,8 @@
 }
 
 func ExampleRuleBuilderCommand_String() {
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Text("FOO=foo").
 		Text("echo $FOO").
 		String())
@@ -283,7 +284,8 @@
 }
 
 func ExampleRuleBuilderCommand_NinjaEscapedString() {
-	fmt.Println(NewRuleBuilder().Command().
+	ctx := builderContext()
+	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Text("FOO=foo").
 		Text("echo $FOO").
 		NinjaEscapedString())
@@ -305,7 +307,10 @@
 		"input3":     nil,
 	}
 
-	ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
+	pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
+	ctx := builderContextForTests{
+		PathContext: pathCtx,
+	}
 
 	addCommands := func(rule *RuleBuilder) {
 		cmd := rule.Command().
@@ -355,7 +360,7 @@
 	wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
 
 	t.Run("normal", func(t *testing.T) {
-		rule := NewRuleBuilder()
+		rule := NewRuleBuilder(pctx, ctx)
 		addCommands(rule)
 
 		wantCommands := []string{
@@ -389,13 +394,13 @@
 			t.Errorf("\nwant rule.OrderOnlys() = %#v\n                got %#v", w, g)
 		}
 
-		if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+		if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
 			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
 		}
 	})
 
 	t.Run("sbox", func(t *testing.T) {
-		rule := NewRuleBuilder().Sbox(PathForOutput(ctx, ""),
+		rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
 			PathForOutput(ctx, "sbox.textproto"))
 		addCommands(rule)
 
@@ -427,7 +432,7 @@
 			t.Errorf("\nwant rule.OrderOnlys() = %#v\n                got %#v", w, g)
 		}
 
-		if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+		if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
 			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
 		}
 	})
@@ -476,7 +481,7 @@
 }
 
 func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
-	rule := NewRuleBuilder()
+	rule := NewRuleBuilder(pctx, ctx)
 
 	if sbox {
 		rule.Sbox(outDir, manifestPath)
@@ -488,7 +493,7 @@
 		rule.Restat()
 	}
 
-	rule.Build(pctx, ctx, "rule", "desc")
+	rule.Build("rule", "desc")
 }
 
 func TestRuleBuilder_Build(t *testing.T) {
diff --git a/android/test_suites.go b/android/test_suites.go
index 19444a8..7ecb8d2 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -63,13 +63,13 @@
 	testCasesDir := pathForInstall(ctx, BuildOs, X86, "testcases", false).ToMakePath()
 
 	outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
-	rule := NewRuleBuilder()
-	rule.Command().BuiltTool(ctx, "soong_zip").
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
 		FlagWithArg("-C ", testCasesDir.String()).
 		FlagWithRspFileInputList("-r ", installedPaths.Paths())
-	rule.Build(pctx, ctx, "robolectric_tests_zip", "robolectric-tests.zip")
+	rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
 
 	return outputFile
 }