Add RuleBuilder.Installs().String()

Add a RuleBuilderInstalls type for a slice of RuleBuilderInstalls,
and give it a String() method that returns the list of installs
in the format that is convenient for passing to Make.

Test: rule_builder_test.go
Change-Id: I2e9cd9abf4dfb0ad312d0a6662f1567baf9cd222
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 468b617..3b86947 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -28,7 +28,7 @@
 // graph.
 type RuleBuilder struct {
 	commands       []*RuleBuilderCommand
-	installs       []RuleBuilderInstall
+	installs       RuleBuilderInstalls
 	temporariesSet map[string]bool
 	restat         bool
 	missingDeps    []string
@@ -46,6 +46,23 @@
 	From, To string
 }
 
+type RuleBuilderInstalls []RuleBuilderInstall
+
+// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
+// list of from:to tuples.
+func (installs RuleBuilderInstalls) String() string {
+	sb := strings.Builder{}
+	for i, install := range installs {
+		if i != 0 {
+			sb.WriteRune(' ')
+		}
+		sb.WriteString(install.From)
+		sb.WriteRune(':')
+		sb.WriteString(install.To)
+	}
+	return sb.String()
+}
+
 // MissingDeps adds modules to the list of missing dependencies.  If MissingDeps
 // is called with a non-empty input, any call to Build will result in a rule
 // that will print an error listing the missing dependencies and fail.
@@ -145,8 +162,8 @@
 }
 
 // Installs returns the list of tuples passed to Install.
-func (r *RuleBuilder) Installs() []RuleBuilderInstall {
-	return append([]RuleBuilderInstall(nil), r.installs...)
+func (r *RuleBuilder) Installs() RuleBuilderInstalls {
+	return append(RuleBuilderInstalls(nil), r.installs...)
 }
 
 func (r *RuleBuilder) toolsSet() map[string]bool {