Allow RuleBuilder to be used with SingletonContext

Make RuleBuilder.Build take a subset of ModuleContext and
SingletonContext, and dynamically call PathForModuleOut only
if it is available.

Test: rule_builder_test.go
Change-Id: Id825cb75236acf187e9d4a36353a47abcac71927
diff --git a/android/rule_builder.go b/android/rule_builder.go
index b7e8432..a743a86 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -111,22 +111,46 @@
 	return commands
 }
 
-func (r *RuleBuilder) Build(pctx PackageContext, ctx ModuleContext, name string, desc string) {
+type BuilderContext interface {
+	PathContext
+	Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
+	Build(PackageContext, BuildParams)
+}
+
+func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+	// TODO: convert RuleBuilder arguments and storage to Paths
+	mctx, _ := ctx.(ModuleContext)
 	var inputs Paths
 	for _, input := range r.Inputs() {
-		rel, isRel := MaybeRel(ctx, PathForModuleOut(ctx).String(), input)
-		if isRel {
-			inputs = append(inputs, PathForModuleOut(ctx, rel))
-		} else {
-			// TODO: use PathForOutput once boot image is moved to where PathForOutput can find it.
-			inputs = append(inputs, &unknownRulePath{input})
+		// Module output paths
+		if mctx != nil {
+			rel, isRel := MaybeRel(ctx, PathForModuleOut(mctx).String(), input)
+			if isRel {
+				inputs = append(inputs, PathForModuleOut(mctx, rel))
+				continue
+			}
 		}
+
+		// Other output paths
+		rel, isRel := MaybeRel(ctx, PathForOutput(ctx).String(), input)
+		if isRel {
+			inputs = append(inputs, PathForOutput(ctx, rel))
+			continue
+		}
+
+		// TODO: remove this once boot image is moved to where PathForOutput can find it.
+		inputs = append(inputs, &unknownRulePath{input})
 	}
 
 	var outputs WritablePaths
 	for _, output := range r.Outputs() {
-		rel := Rel(ctx, PathForModuleOut(ctx).String(), output)
-		outputs = append(outputs, PathForModuleOut(ctx, rel))
+		if mctx != nil {
+			rel := Rel(ctx, PathForModuleOut(mctx).String(), output)
+			outputs = append(outputs, PathForModuleOut(mctx, rel))
+		} else {
+			rel := Rel(ctx, PathForOutput(ctx).String(), output)
+			outputs = append(outputs, PathForOutput(ctx, rel))
+		}
 	}
 
 	if len(r.Commands()) > 0 {
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index b4c9e0e..533b090 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -93,11 +93,27 @@
 }
 
 func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	rule := RuleBuilder{}
-
 	in := PathForSource(ctx, t.properties.Src)
 	out := PathForModuleOut(ctx, ctx.ModuleName())
 
+	testRuleBuilder_Build(ctx, in, out)
+}
+
+type testRuleBuilderSingleton struct{}
+
+func testRuleBuilderSingletonFactory() Singleton {
+	return &testRuleBuilderSingleton{}
+}
+
+func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
+	in := PathForSource(ctx, "bar")
+	out := PathForOutput(ctx, "baz")
+	testRuleBuilder_Build(ctx, in, out)
+}
+
+func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
+	rule := RuleBuilder{}
+
 	rule.Command().Tool("cp").Input(in.String()).Output(out.String())
 
 	rule.Build(pctx, ctx, "rule", "desc")
@@ -125,6 +141,7 @@
 		"cp":         nil,
 	})
 	ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
+	ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
 	ctx.Register()
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})