Add dist support to Soong

When dist'ing a library or binary that has use_version_lib set, always
distribute the stamped version, even for the device.

Test: m test_build_version_test dist
Change-Id: I2995ec516b1d182ce18f099aeaa4d186ffbcf01f
diff --git a/android/androidmk.go b/android/androidmk.go
index 493ba97..6224361 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -40,6 +40,7 @@
 type AndroidMkData struct {
 	Class      string
 	SubName    string
+	DistFile   OptionalPath
 	OutputFile OptionalPath
 	Disabled   bool
 	Include    string
@@ -220,6 +221,45 @@
 		}
 	}
 
+	if len(amod.commonProperties.Dist.Targets) > 0 {
+		distFile := data.DistFile
+		if !distFile.Valid() {
+			distFile = data.OutputFile
+		}
+		if distFile.Valid() {
+			dest := filepath.Base(distFile.String())
+
+			if amod.commonProperties.Dist.Dest != nil {
+				var err error
+				dest, err = validateSafePath(*amod.commonProperties.Dist.Dest)
+				if err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			if amod.commonProperties.Dist.Suffix != nil {
+				ext := filepath.Ext(dest)
+				suffix := *amod.commonProperties.Dist.Suffix
+				dest = strings.TrimSuffix(dest, ext) + suffix + ext
+			}
+
+			if amod.commonProperties.Dist.Dir != nil {
+				var err error
+				dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest)
+				if err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
+			fmt.Fprintln(&data.preamble, ".PHONY:", goals)
+			fmt.Fprintf(&data.preamble, "$(call dist-for-goals,%s,%s:%s)\n",
+				goals, distFile.String(), dest)
+		}
+	}
+
 	fmt.Fprintln(&data.preamble, "\ninclude $(CLEAR_VARS)")
 	fmt.Fprintln(&data.preamble, "LOCAL_PATH :=", filepath.Dir(ctx.BlueprintFile(mod)))
 	fmt.Fprintln(&data.preamble, "LOCAL_MODULE :=", name+data.SubName)
diff --git a/android/module.go b/android/module.go
index bf49ca2..bbe7d36 100644
--- a/android/module.go
+++ b/android/module.go
@@ -265,6 +265,24 @@
 	// relative path to a file to include in the list of notices for the device
 	Notice *string
 
+	Dist struct {
+		// copy the output of this module to the $DIST_DIR when `dist` is specified on the
+		// command line and  any of these targets are also on the command line, or otherwise
+		// built
+		Targets []string `android:"arch_variant"`
+
+		// The name of the output artifact. This defaults to the basename of the output of
+		// the module.
+		Dest *string `android:"arch_variant"`
+
+		// The directory within the dist directory to store the artifact. Defaults to the
+		// top level directory ("").
+		Dir *string `android:"arch_variant"`
+
+		// A suffix to add to the artifact file name (before any extension).
+		Suffix *string `android:"arch_variant"`
+	} `android:"arch_variant"`
+
 	// Set by TargetMutator
 	CompileTarget       Target   `blueprint:"mutated"`
 	CompileMultiTargets []Target `blueprint:"mutated"`
@@ -781,6 +799,25 @@
 	}
 	ctx.Variable(pctx, "moduleDescSuffix", s)
 
+	// Some common property checks for properties that will be used later in androidmk.go
+	if a.commonProperties.Dist.Dest != nil {
+		_, err := validateSafePath(*a.commonProperties.Dist.Dest)
+		if err != nil {
+			ctx.PropertyErrorf("dist.dest", "%s", err.Error())
+		}
+	}
+	if a.commonProperties.Dist.Dir != nil {
+		_, err := validateSafePath(*a.commonProperties.Dist.Dir)
+		if err != nil {
+			ctx.PropertyErrorf("dist.dir", "%s", err.Error())
+		}
+	}
+	if a.commonProperties.Dist.Suffix != nil {
+		if strings.Contains(*a.commonProperties.Dist.Suffix, "/") {
+			ctx.PropertyErrorf("dist.suffix", "Suffix may not contain a '/' character.")
+		}
+	}
+
 	if a.Enabled() {
 		a.module.GenerateAndroidBuildActions(ctx)
 		if ctx.Failed() {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 5e38973..f5e04bb 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -151,6 +151,7 @@
 		ret.Class = "HEADER_LIBRARIES"
 	}
 
+	ret.DistFile = library.distFile
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		library.androidMkWriteExportedFlags(w)
 		fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES := ")
@@ -194,6 +195,7 @@
 	ctx.subAndroidMk(ret, binary.baseInstaller)
 
 	ret.Class = "EXECUTABLES"
+	ret.DistFile = binary.distFile
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
 		if len(binary.symlinks) > 0 {
diff --git a/cc/binary.go b/cc/binary.go
index 15db2ad..6923f2b 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -88,6 +88,9 @@
 
 	// Output archive of gcno coverage information
 	coverageOutputFile android.OptionalPath
+
+	// Location of the file that should be copied to dist dir when requested
+	distFile android.OptionalPath
 }
 
 var _ linker = (*binaryDecorator)(nil)
@@ -330,10 +333,23 @@
 			flagsToBuilderFlags(flags), afterPrefixSymbols)
 	}
 
-	if Bool(binary.baseLinker.Properties.Use_version_lib) && ctx.Host() {
-		versionedOutputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
-		binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+	if Bool(binary.baseLinker.Properties.Use_version_lib) {
+		if ctx.Host() {
+			versionedOutputFile := outputFile
+			outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+			binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		} else {
+			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+			binary.distFile = android.OptionalPathForPath(versionedOutputFile)
+
+			if binary.stripper.needsStrip(ctx) {
+				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
+				binary.distFile = android.OptionalPathForPath(out)
+				binary.stripper.strip(ctx, versionedOutputFile, out, builderFlags)
+			}
+
+			binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		}
 	}
 
 	if ctx.Os() == android.LinuxBionic && !binary.static() {
diff --git a/cc/libbuildversion/libbuildversion.cpp b/cc/libbuildversion/libbuildversion.cpp
index d80d587..5242025 100644
--- a/cc/libbuildversion/libbuildversion.cpp
+++ b/cc/libbuildversion/libbuildversion.cpp
@@ -23,9 +23,19 @@
 namespace android {
 namespace build {
 
+#define PLACEHOLDER "SOONG BUILD NUMBER PLACEHOLDER"
+
+extern "C" {
+  char soong_build_number[128] = PLACEHOLDER;
+}
+
 #ifdef __ANDROID__
 
 std::string GetBuildNumber() {
+  if (strcmp(PLACEHOLDER, soong_build_number) != 0) {
+    return soong_build_number;
+  }
+
   const prop_info* pi = __system_property_find("ro.build.version.incremental");
   if (pi == nullptr) return "";
 
@@ -42,10 +52,6 @@
 
 #else
 
-extern "C" {
-  char soong_build_number[128] = "SOONG BUILD NUMBER PLACEHOLDER";
-}
-
 std::string GetBuildNumber() {
   return soong_build_number;
 }
diff --git a/cc/libbuildversion/tests/Android.bp b/cc/libbuildversion/tests/Android.bp
index a18bc6c..b3b2061 100644
--- a/cc/libbuildversion/tests/Android.bp
+++ b/cc/libbuildversion/tests/Android.bp
@@ -2,10 +2,53 @@
     name: "build_version_test_defaults",
     use_version_lib: true,
     host_supported: true,
+    dist: {
+        targets: ["test_build_version_test"],
+    },
     target: {
+        android_arm: {
+            dist: {
+                dir: "android/arm",
+            },
+        },
+        android_arm64: {
+            dist: {
+                dir: "android/arm64",
+            },
+        },
+        android_x86: {
+            dist: {
+                dir: "android/x86",
+            },
+        },
+        android_x86_64: {
+            dist: {
+                dir: "android/x86_64",
+            },
+        },
+        darwin: {
+            dist: {
+                dir: "host/",
+            },
+        },
+        linux_glibc_x86: {
+            dist: {
+                dir: "host32/",
+            },
+        },
+        linux_glibc_x86_64: {
+            dist: {
+                dir: "host/",
+            },
+        },
         windows: {
             enabled: true,
         },
+        windows_x86_64: {
+            dist: {
+                dest: "win64/build_ver_test.exe",
+            },
+        },
     },
 }
 
@@ -20,6 +63,11 @@
         not_windows: {
             shared_libs: ["libbuild_version_test"],
         },
+        host: {
+            dist: {
+                suffix: "_host",
+            },
+        },
     },
 }
 
diff --git a/cc/library.go b/cc/library.go
index abaa6c4..a9d63f9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -267,6 +267,9 @@
 	// Location of the linked, unstripped library for shared libraries
 	unstrippedOutputFile android.Path
 
+	// Location of the file that should be copied to dist dir when requested
+	distFile android.OptionalPath
+
 	versionScriptPath android.ModuleGenPath
 
 	// Decorated interafaces
@@ -542,10 +545,16 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	builderFlags := flagsToBuilderFlags(flags)
 
-	if Bool(library.baseLinker.Properties.Use_version_lib) && ctx.Host() {
-		versionedOutputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
-		library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+	if Bool(library.baseLinker.Properties.Use_version_lib) {
+		if ctx.Host() {
+			versionedOutputFile := outputFile
+			outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		} else {
+			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+			library.distFile = android.OptionalPathForPath(versionedOutputFile)
+			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		}
 	}
 
 	TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
@@ -619,10 +628,23 @@
 
 	library.unstrippedOutputFile = outputFile
 
-	if Bool(library.baseLinker.Properties.Use_version_lib) && ctx.Host() {
-		versionedOutputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
-		library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+	if Bool(library.baseLinker.Properties.Use_version_lib) {
+		if ctx.Host() {
+			versionedOutputFile := outputFile
+			outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		} else {
+			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+			library.distFile = android.OptionalPathForPath(versionedOutputFile)
+
+			if library.stripper.needsStrip(ctx) {
+				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
+				library.distFile = android.OptionalPathForPath(out)
+				library.stripper.strip(ctx, versionedOutputFile, out, builderFlags)
+			}
+
+			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+		}
 	}
 
 	sharedLibs := deps.SharedLibs