Merge "add PackagingSpec"
diff --git a/android/Android.bp b/android/Android.bp
index 7bd1450..c6f01fe 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -37,6 +37,7 @@
         "override_module.go",
         "package.go",
         "package_ctx.go",
+        "packaging.go",
         "path_properties.go",
         "paths.go",
         "phony.go",
diff --git a/android/module.go b/android/module.go
index d677406..3cef3f5 100644
--- a/android/module.go
+++ b/android/module.go
@@ -440,6 +440,7 @@
 	TargetRequiredModuleNames() []string
 
 	FilesToInstall() InstallPaths
+	PackagingSpecs() []PackagingSpec
 }
 
 // Qualified id for a module
@@ -934,6 +935,7 @@
 	noAddressSanitizer bool
 	installFiles       InstallPaths
 	checkbuildFiles    Paths
+	packagingSpecs     []PackagingSpec
 	noticeFiles        Paths
 	phonies            map[string]Paths
 
@@ -1259,6 +1261,10 @@
 	return m.installFiles
 }
 
+func (m *ModuleBase) PackagingSpecs() []PackagingSpec {
+	return m.packagingSpecs
+}
+
 func (m *ModuleBase) NoAddressSanitizer() bool {
 	return m.noAddressSanitizer
 }
@@ -1581,6 +1587,7 @@
 
 		m.installFiles = append(m.installFiles, ctx.installFiles...)
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
+		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
 		m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
 		m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
 		for k, v := range ctx.phonies {
@@ -1748,6 +1755,7 @@
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
+	packagingSpecs  []PackagingSpec
 	installDeps     InstallPaths
 	installFiles    InstallPaths
 	checkbuildFiles Paths
@@ -2284,16 +2292,15 @@
 
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...Path) InstallPath {
-	return m.installFile(installPath, name, srcPath, Cp, deps)
+	return m.installFile(installPath, name, srcPath, deps, false)
 }
 
 func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
 	deps ...Path) InstallPath {
-	return m.installFile(installPath, name, srcPath, CpExecutable, deps)
+	return m.installFile(installPath, name, srcPath, deps, true)
 }
 
-func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path,
-	rule blueprint.Rule, deps []Path) InstallPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, executable bool) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
@@ -2312,6 +2319,11 @@
 			orderOnlyDeps = deps
 		}
 
+		rule := Cp
+		if executable {
+			rule = CpExecutable
+		}
+
 		m.Build(pctx, BuildParams{
 			Rule:        rule,
 			Description: "install " + fullInstallPath.Base(),
@@ -2324,6 +2336,14 @@
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
+
+	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
+		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:          srcPath,
+		symlinkTarget:    "",
+		executable:       executable,
+	})
+
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 	return fullInstallPath
 }
@@ -2332,12 +2352,12 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true)
 
+	relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
+	if err != nil {
+		panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
+	}
 	if !m.skipInstall(fullInstallPath) {
 
-		relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
-		if err != nil {
-			panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
-		}
 		m.Build(pctx, BuildParams{
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base(),
@@ -2352,6 +2372,14 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 	}
+
+	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
+		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:          nil,
+		symlinkTarget:    relPath,
+		executable:       false,
+	})
+
 	return fullInstallPath
 }
 
@@ -2374,6 +2402,14 @@
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
+
+	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
+		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:          nil,
+		symlinkTarget:    absPath,
+		executable:       false,
+	})
+
 	return fullInstallPath
 }
 
diff --git a/android/packaging.go b/android/packaging.go
new file mode 100644
index 0000000..8d0de9e
--- /dev/null
+++ b/android/packaging.go
@@ -0,0 +1,34 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License")
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+// PackagingSpec abstracts a request to place a built artifact at a certain path in a package.
+// A package can be the traditional <partition>.img, but isn't limited to those. Other examples could
+// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS running
+// on a VM), or a zip archive for some of the host tools.
+type PackagingSpec struct {
+	// Path relative to the root of the package
+	relPathInPackage string
+
+	// The path to the built artifact
+	srcPath Path
+
+	// If this is not empty, then relPathInPackage should be a symlink to this target. (Then
+	// srcPath is of course ignored.)
+	symlinkTarget string
+
+	// Whether relPathInPackage should be marked as executable or not
+	executable bool
+}