Separate InstallPath from OutputPath

Create a new type InstallPath that is similar to OutputPath to
differentiate intermediates output paths from installed output
paths.

RelPathString is a poorly defined, undocumented function that is
primarily used to get an install path relative to out/soong to
generate an equivalent install path for Make relative to $(OUT_DIR).
Move it to InstallPath for now, and fix the one remaining user on
OutputPath.

Add a method to create an NDK install path so that ndk_sysroot.go
doesn't have to do it manually with PathForOutput.

Bug: 141877526
Test: m checkbuild
Change-Id: I83c5a0bd1fd6c3dba8d3b6d20d039f64f353ddd5
diff --git a/android/hooks.go b/android/hooks.go
index 1e5ff21..604cb9c 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -75,7 +75,7 @@
 
 type InstallHookContext interface {
 	ModuleContext
-	Path() OutputPath
+	Path() InstallPath
 	Symlink() bool
 }
 
@@ -89,11 +89,11 @@
 
 type installHookContext struct {
 	ModuleContext
-	path    OutputPath
+	path    InstallPath
 	symlink bool
 }
 
-func (x *installHookContext) Path() OutputPath {
+func (x *installHookContext) Path() InstallPath {
 	return x.path
 }
 
@@ -101,7 +101,7 @@
 	return x.symlink
 }
 
-func (x *hooks) runInstallHooks(ctx ModuleContext, path OutputPath, symlink bool) {
+func (x *hooks) runInstallHooks(ctx ModuleContext, path InstallPath, symlink bool) {
 	if len(x.install) > 0 {
 		mctx := &installHookContext{
 			ModuleContext: ctx,
diff --git a/android/module.go b/android/module.go
index 2d0c20d..40b2b57 100644
--- a/android/module.go
+++ b/android/module.go
@@ -147,10 +147,10 @@
 	ExpandSource(srcFile, prop string) Path
 	ExpandOptionalSource(srcFile *string, prop string) OptionalPath
 
-	InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath
-	InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath
+	InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
+	InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
 	CheckbuildFile(srcPath Path)
 
 	InstallInData() bool
@@ -1536,7 +1536,7 @@
 	return m.module.InstallBypassMake()
 }
 
-func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool {
+func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
@@ -1561,18 +1561,18 @@
 	return false
 }
 
-func (m *moduleContext) InstallFile(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, Cp, deps)
 }
 
-func (m *moduleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, CpExecutable, deps)
 }
 
-func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath Path,
-	rule blueprint.Rule, deps []Path) OutputPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path,
+	rule blueprint.Rule, deps []Path) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, false)
@@ -1607,7 +1607,7 @@
 	return fullInstallPath
 }
 
-func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath {
+func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
@@ -1636,7 +1636,7 @@
 
 // installPath/name -> absPath where absPath might be a path that is available only at runtime
 // (e.g. /apex/...)
-func (m *moduleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
+func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
diff --git a/android/notices.go b/android/notices.go
index 7b61d65..bf273b5 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -60,7 +60,7 @@
 	})
 }
 
-func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
+func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string,
 	noticePaths []Path) NoticeOutputs {
 	// Merge all NOTICE files into one.
 	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
diff --git a/android/paths.go b/android/paths.go
index e8b08b5..b89edba 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -793,7 +793,7 @@
 	return OptionalPathForPath(PathForSource(ctx, relPath))
 }
 
-// OutputPath is a Path representing a file path rooted from the build directory
+// OutputPath is a Path representing an intermediates file path rooted from the build directory
 type OutputPath struct {
 	basePath
 }
@@ -824,12 +824,12 @@
 // pathForInstallInMakeDir is used by PathForModuleInstall when the module returns true
 // for InstallBypassMake to produce an OutputPath that installs to $OUT_DIR instead of
 // $OUT_DIR/soong.
-func pathForInstallInMakeDir(ctx PathContext, pathComponents ...string) OutputPath {
+func pathForInstallInMakeDir(ctx PathContext, pathComponents ...string) InstallPath {
 	path, err := validatePath(pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	return OutputPath{basePath{"../" + path, ctx.Config(), ""}}
+	return InstallPath{basePath{"../" + path, ctx.Config(), ""}}
 }
 
 // PathsForOutput returns Paths rooted from buildDir
@@ -847,10 +847,6 @@
 	return filepath.Join(p.config.buildDir, p.path)
 }
 
-func (p OutputPath) RelPathString() string {
-	return p.path
-}
-
 // Join creates a new OutputPath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
@@ -1119,9 +1115,39 @@
 	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
 }
 
+// InstallPath is a Path representing a installed file path rooted from the build directory
+type InstallPath struct {
+	basePath
+}
+
+func (p InstallPath) writablePath() {}
+
+func (p InstallPath) String() string {
+	return filepath.Join(p.config.buildDir, p.path)
+}
+
+// Join creates a new InstallPath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return p.withRel(path)
+}
+
+func (p InstallPath) withRel(rel string) InstallPath {
+	p.basePath = p.basePath.withRel(rel)
+	return p
+}
+
+func (p InstallPath) RelPathString() string {
+	return p.path
+}
+
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
-func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
+func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
 	var outPaths []string
 	if ctx.Device() {
 		partition := modulePartition(ctx)
@@ -1144,10 +1170,24 @@
 	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
 		return pathForInstallInMakeDir(ctx, outPaths...)
 	}
-	return PathForOutput(ctx, outPaths...)
+
+	path, err := validatePath(outPaths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return InstallPath{basePath{path, ctx.Config(), ""}}
 }
 
-func InstallPathToOnDevicePath(ctx PathContext, path OutputPath) string {
+func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
+	paths = append([]string{"ndk"}, paths...)
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return InstallPath{basePath{path, ctx.Config(), ""}}
+}
+
+func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
 	rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
 
 	return "/" + rel
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index d29ed16..3aeda1c 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -65,7 +65,7 @@
 	installDirBase string
 	// The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
 	socInstallDirBase      string
-	installDirPath         OutputPath
+	installDirPath         InstallPath
 	additionalDependencies *Paths
 }
 
@@ -91,7 +91,7 @@
 	return PathForModuleSrc(ctx, String(p.properties.Src))
 }
 
-func (p *PrebuiltEtc) InstallDirPath() OutputPath {
+func (p *PrebuiltEtc) InstallDirPath() InstallPath {
 	return p.installDirPath
 }
 
diff --git a/apex/apex.go b/apex/apex.go
index aff8d50..1e5cf91 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -561,8 +561,8 @@
 
 	bundleModuleFile android.WritablePath
 	outputFiles      map[apexPackaging]android.WritablePath
-	flattenedOutput  android.OutputPath
-	installDir       android.OutputPath
+	flattenedOutput  android.InstallPath
+	installDir       android.InstallPath
 
 	prebuiltFileToDelete string
 
@@ -1840,7 +1840,7 @@
 	properties PrebuiltProperties
 
 	inputApex       android.Path
-	installDir      android.OutputPath
+	installDir      android.InstallPath
 	installFilename string
 	outputApex      android.WritablePath
 }
diff --git a/cc/installer.go b/cc/installer.go
index a52ccf1..610252c 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -52,7 +52,7 @@
 	relative string
 	location installLocation
 
-	path android.OutputPath
+	path android.InstallPath
 }
 
 var _ installer = (*baseInstaller)(nil)
@@ -61,7 +61,7 @@
 	return []interface{}{&installer.Properties}
 }
 
-func (installer *baseInstaller) installDir(ctx ModuleContext) android.OutputPath {
+func (installer *baseInstaller) installDir(ctx ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
 		dir = installer.dir64
diff --git a/cc/library.go b/cc/library.go
index a41ddc2..d8c9b90 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -791,9 +791,7 @@
 
 	// Optimize out relinking against shared libraries whose interface hasn't changed by
 	// depending on a table of contents file instead of the library itself.
-	tocPath := outputFile.RelPathString()
-	tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
-	tocFile := android.PathForOutput(ctx, tocPath)
+	tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
 	library.tocFile = android.OptionalPathForPath(tocFile)
 	TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 4065128..b8423be 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -48,7 +48,7 @@
 }
 
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
+func getCurrentIncludePath(ctx android.ModuleContext) android.InstallPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -94,7 +94,7 @@
 }
 
 func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
-	to string) android.OutputPath {
+	to string) android.InstallPath {
 	// Output path is the sysroot base + "usr/include" + to directory + directory component
 	// of the file without the leading from directory stripped.
 	//
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e39bae5..f6de4ef 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -66,12 +66,12 @@
 	pctx.Import("android/soong/android")
 }
 
-func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
-	return android.PathForOutput(ctx, "ndk")
+func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
+	return android.PathForNdkInstall(ctx)
 }
 
 // Returns the main install directory for the NDK sysroot. Usable with --sysroot.
-func getNdkSysrootBase(ctx android.PathContext) android.OutputPath {
+func getNdkSysrootBase(ctx android.PathContext) android.InstallPath {
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
diff --git a/java/app.go b/java/app.go
index 3ee8b8d..e033661 100644
--- a/java/app.go
+++ b/java/app.go
@@ -126,7 +126,7 @@
 	// the install APK name is normally the same as the module name, but can be overridden with PRODUCT_PACKAGE_NAME_OVERRIDES.
 	installApkName string
 
-	installDir android.OutputPath
+	installDir android.InstallPath
 
 	onDeviceDir string
 
@@ -773,7 +773,7 @@
 
 	usesLibrary usesLibrary
 
-	installPath android.OutputPath
+	installPath android.InstallPath
 }
 
 type AndroidAppImportProperties struct {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 6214dac..db6b455 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -22,7 +22,7 @@
 type dexpreopter struct {
 	dexpreoptProperties DexpreoptProperties
 
-	installPath         android.OutputPath
+	installPath         android.InstallPath
 	uncompressedDex     bool
 	isSDKLibrary        bool
 	isTest              bool
@@ -94,7 +94,7 @@
 	return false
 }
 
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.OutputPath) bool {
+func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
 	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
 }
 
diff --git a/java/java.go b/java/java.go
index f7b0f53..4264ba9 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1795,7 +1795,7 @@
 	isWrapperVariant bool
 
 	wrapperFile android.Path
-	binaryFile  android.OutputPath
+	binaryFile  android.InstallPath
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 3d46077..0c973c5 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -30,7 +30,7 @@
 	android.ModuleBase
 
 	properties     platformCompatConfigProperties
-	installDirPath android.OutputPath
+	installDirPath android.InstallPath
 	configFile     android.OutputPath
 }
 
diff --git a/python/installer.go b/python/installer.go
index 62f36f4..b0a25b9 100644
--- a/python/installer.go
+++ b/python/installer.go
@@ -33,7 +33,7 @@
 	dir64    string
 	relative string
 
-	path android.OutputPath
+	path android.InstallPath
 
 	androidMkSharedLibs []string
 }
@@ -47,7 +47,7 @@
 
 var _ installer = (*pythonInstaller)(nil)
 
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.OutputPath {
+func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
 		dir = installer.dir64
diff --git a/rust/compiler.go b/rust/compiler.go
index 3bfef76..76d5ad8 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -94,7 +94,7 @@
 	dir64    string
 	subDir   string
 	relative string
-	path     android.OutputPath
+	path     android.InstallPath
 }
 
 var _ compiler = (*baseCompiler)(nil)
@@ -173,7 +173,7 @@
 	return compiler.Properties.Crate_name
 }
 
-func (compiler *baseCompiler) installDir(ctx ModuleContext) android.OutputPath {
+func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath {
 	dir := compiler.dir
 	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
 		dir = compiler.dir64