InstallPath keeps its partition dir

This change introduces the concept of partition dir for InstallPaths.
It's the path to the partition where the InstallPath is rooted at. For
example, it's out/soong/target/product/<device>/<partitoon> for paths
created for device modules. For host modules, it is defined as
out/soong/host/<host_os>-<host_arch>.

The partition dir is obtained using the new PartitionDir() function.

Another change is that a freshly created InstallPath (usually via
PathForModuleInstall) is the result of joining PartitionDir() and the
remaining path elements. For example, PathForModuleInstall(ctx, "foo",
"bar").Rel() now returns "foo/bar". Previously, that call returned the
relative path from config.buildDir() ("out/soong"). This change is in
line with the behavior of other path-creating functions like
PathForModuleSrc where Rel() returns the path relative to the
contextually determined path like the module source directory.

Notice that the Join() call to InstallPath doesn't change
PartitionDir(), while does change the result of Rel().

p := PathForModuleInstall(ctx, "foo", "bar")
p.PartitionDir() is out/soong/host/linux-x86
p.Rel() is foo/bar

q := p.Join(ctx, "baz")
q.PartitionDir() is still out/soong/host/linux-x86
q.Rel() now returns baz

Bug: N/A
Test: m nothing
Change-Id: I916bb1c782a4bfe0fbd4854e349cd2a2a42f56b6
diff --git a/android/paths.go b/android/paths.go
index 3825d45..842aaac 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1236,7 +1236,12 @@
 type InstallPath struct {
 	basePath
 
-	baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths
+	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
+	// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
+	partitionDir string
+
+	// makePath indicates whether this path is for Soong (false) or Make (true).
+	makePath bool
 }
 
 func (p InstallPath) buildDir() string {
@@ -1249,7 +1254,23 @@
 func (p InstallPath) writablePath() {}
 
 func (p InstallPath) String() string {
-	return filepath.Join(p.config.buildDir, p.baseDir, p.path)
+	if p.makePath {
+		// Make path starts with out/ instead of out/soong.
+		return filepath.Join(p.config.buildDir, "../", p.path)
+	} else {
+		return filepath.Join(p.config.buildDir, p.path)
+	}
+}
+
+// PartitionDir returns the path to the partition where the install path is rooted at. It is
+// out/soong/target/product/<device>/<partition> for device modules, and out/soong/host/<os>-<arch> for host modules.
+// The ./soong is dropped if the install path is for Make.
+func (p InstallPath) PartitionDir() string {
+	if p.makePath {
+		return filepath.Join(p.config.buildDir, "../", p.partitionDir)
+	} else {
+		return filepath.Join(p.config.buildDir, p.partitionDir)
+	}
 }
 
 // Join creates a new InstallPath with paths... joined with the current path. The
@@ -1270,7 +1291,7 @@
 // ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
 // i.e. out/ instead of out/soong/.
 func (p InstallPath) ToMakePath() InstallPath {
-	p.baseDir = "../"
+	p.makePath = true
 	return p
 }
 
@@ -1300,10 +1321,10 @@
 func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool,
 	pathComponents ...string) InstallPath {
 
-	var outPaths []string
+	var partionPaths []string
 
 	if os.Class == Device {
-		outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
+		partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
 	} else {
 		osName := os.String()
 		if os == Linux {
@@ -1319,30 +1340,33 @@
 		if os.Class == Host && (arch == X86_64 || arch == Common) {
 			archName = "x86"
 		}
-		outPaths = []string{"host", osName + "-" + archName, partition}
+		partionPaths = []string{"host", osName + "-" + archName, partition}
 	}
 	if debug {
-		outPaths = append([]string{"debug"}, outPaths...)
+		partionPaths = append([]string{"debug"}, partionPaths...)
 	}
-	outPaths = append(outPaths, pathComponents...)
 
-	path, err := validatePath(outPaths...)
+	partionPath, err := validatePath(partionPaths...)
 	if err != nil {
 		reportPathError(ctx, err)
 	}
 
-	ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
+	base := InstallPath{
+		basePath:     basePath{partionPath, ctx.Config(), ""},
+		partitionDir: partionPath,
+		makePath:     false,
+	}
 
-	return ret
+	return base.Join(ctx, pathComponents...)
 }
 
 func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
-	paths = append([]string{prefix}, paths...)
-	path, err := validatePath(paths...)
-	if err != nil {
-		reportPathError(ctx, err)
+	base := InstallPath{
+		basePath:     basePath{prefix, ctx.Config(), ""},
+		partitionDir: prefix,
+		makePath:     false,
 	}
-	return InstallPath{basePath{path, ctx.Config(), ""}, ""}
+	return base.Join(ctx, paths...)
 }
 
 func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
diff --git a/android/paths_test.go b/android/paths_test.go
index 108bd6c..cb5495e 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -259,10 +259,11 @@
 	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
 
 	testCases := []struct {
-		name string
-		ctx  *moduleInstallPathContextImpl
-		in   []string
-		out  string
+		name         string
+		ctx          *moduleInstallPathContextImpl
+		in           []string
+		out          string
+		partitionDir string
 	}{
 		{
 			name: "host binary",
@@ -272,8 +273,9 @@
 					target: hostTarget,
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "host/linux-x86/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "host/linux-x86/bin/my_test",
+			partitionDir: "host/linux-x86",
 		},
 
 		{
@@ -284,8 +286,9 @@
 					target: deviceTarget,
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/system/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/system/bin/my_test",
+			partitionDir: "target/product/test_device/system",
 		},
 		{
 			name: "vendor binary",
@@ -298,8 +301,9 @@
 					},
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/vendor/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/vendor/bin/my_test",
+			partitionDir: "target/product/test_device/vendor",
 		},
 		{
 			name: "odm binary",
@@ -312,8 +316,9 @@
 					},
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/odm/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/odm/bin/my_test",
+			partitionDir: "target/product/test_device/odm",
 		},
 		{
 			name: "product binary",
@@ -326,8 +331,9 @@
 					},
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/product/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/product/bin/my_test",
+			partitionDir: "target/product/test_device/product",
 		},
 		{
 			name: "system_ext binary",
@@ -340,8 +346,9 @@
 					},
 				},
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/system_ext/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/system_ext/bin/my_test",
+			partitionDir: "target/product/test_device/system_ext",
 		},
 		{
 			name: "root binary",
@@ -352,8 +359,9 @@
 				},
 				inRoot: true,
 			},
-			in:  []string{"my_test"},
-			out: "target/product/test_device/root/my_test",
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/root/my_test",
+			partitionDir: "target/product/test_device/root",
 		},
 		{
 			name: "recovery binary",
@@ -364,8 +372,9 @@
 				},
 				inRecovery: true,
 			},
-			in:  []string{"bin/my_test"},
-			out: "target/product/test_device/recovery/root/system/bin/my_test",
+			in:           []string{"bin/my_test"},
+			out:          "target/product/test_device/recovery/root/system/bin/my_test",
+			partitionDir: "target/product/test_device/recovery/root/system",
 		},
 		{
 			name: "recovery root binary",
@@ -377,8 +386,9 @@
 				inRecovery: true,
 				inRoot:     true,
 			},
-			in:  []string{"my_test"},
-			out: "target/product/test_device/recovery/root/my_test",
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/recovery/root/my_test",
+			partitionDir: "target/product/test_device/recovery/root",
 		},
 
 		{
@@ -390,8 +400,9 @@
 				},
 				inData: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data",
 		},
 		{
 			name: "vendor native test binary",
@@ -405,8 +416,9 @@
 				},
 				inData: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data",
 		},
 		{
 			name: "odm native test binary",
@@ -420,8 +432,9 @@
 				},
 				inData: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data",
 		},
 		{
 			name: "product native test binary",
@@ -435,8 +448,9 @@
 				},
 				inData: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data",
 		},
 
 		{
@@ -451,8 +465,9 @@
 				},
 				inData: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data",
 		},
 
 		{
@@ -464,8 +479,9 @@
 				},
 				inSanitizerDir: true,
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/system/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/data/asan/system/bin/my_test",
+			partitionDir: "target/product/test_device/data/asan/system",
 		},
 		{
 			name: "sanitized vendor binary",
@@ -479,8 +495,9 @@
 				},
 				inSanitizerDir: true,
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/vendor/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/data/asan/vendor/bin/my_test",
+			partitionDir: "target/product/test_device/data/asan/vendor",
 		},
 		{
 			name: "sanitized odm binary",
@@ -494,8 +511,9 @@
 				},
 				inSanitizerDir: true,
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/odm/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/data/asan/odm/bin/my_test",
+			partitionDir: "target/product/test_device/data/asan/odm",
 		},
 		{
 			name: "sanitized product binary",
@@ -509,8 +527,9 @@
 				},
 				inSanitizerDir: true,
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/product/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/data/asan/product/bin/my_test",
+			partitionDir: "target/product/test_device/data/asan/product",
 		},
 
 		{
@@ -525,8 +544,9 @@
 				},
 				inSanitizerDir: true,
 			},
-			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/system_ext/bin/my_test",
+			in:           []string{"bin", "my_test"},
+			out:          "target/product/test_device/data/asan/system_ext/bin/my_test",
+			partitionDir: "target/product/test_device/data/asan/system_ext",
 		},
 
 		{
@@ -539,8 +559,9 @@
 				inData:         true,
 				inSanitizerDir: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data/asan/data",
 		},
 		{
 			name: "sanitized vendor native test binary",
@@ -555,8 +576,9 @@
 				inData:         true,
 				inSanitizerDir: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data/asan/data",
 		},
 		{
 			name: "sanitized odm native test binary",
@@ -571,8 +593,9 @@
 				inData:         true,
 				inSanitizerDir: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data/asan/data",
 		},
 		{
 			name: "sanitized product native test binary",
@@ -587,8 +610,9 @@
 				inData:         true,
 				inSanitizerDir: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data/asan/data",
 		},
 		{
 			name: "sanitized system_ext native test binary",
@@ -603,8 +627,9 @@
 				inData:         true,
 				inSanitizerDir: true,
 			},
-			in:  []string{"nativetest", "my_test"},
-			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+			in:           []string{"nativetest", "my_test"},
+			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
+			partitionDir: "target/product/test_device/data/asan/data",
 		}, {
 			name: "device testcases",
 			ctx: &moduleInstallPathContextImpl{
@@ -614,8 +639,9 @@
 				},
 				inTestcases: true,
 			},
-			in:  []string{"my_test", "my_test_bin"},
-			out: "target/product/test_device/testcases/my_test/my_test_bin",
+			in:           []string{"my_test", "my_test_bin"},
+			out:          "target/product/test_device/testcases/my_test/my_test_bin",
+			partitionDir: "target/product/test_device/testcases",
 		}, {
 			name: "host testcases",
 			ctx: &moduleInstallPathContextImpl{
@@ -625,8 +651,9 @@
 				},
 				inTestcases: true,
 			},
-			in:  []string{"my_test", "my_test_bin"},
-			out: "host/linux-x86/testcases/my_test/my_test_bin",
+			in:           []string{"my_test", "my_test_bin"},
+			out:          "host/linux-x86/testcases/my_test/my_test_bin",
+			partitionDir: "host/linux-x86/testcases",
 		}, {
 			name: "forced host testcases",
 			ctx: &moduleInstallPathContextImpl{
@@ -638,8 +665,9 @@
 				forceOS:     &Linux,
 				forceArch:   &X86,
 			},
-			in:  []string{"my_test", "my_test_bin"},
-			out: "host/linux-x86/testcases/my_test/my_test_bin",
+			in:           []string{"my_test", "my_test_bin"},
+			out:          "host/linux-x86/testcases/my_test/my_test_bin",
+			partitionDir: "host/linux-x86/testcases",
 		},
 	}
 
@@ -652,10 +680,48 @@
 					output.basePath.path,
 					tc.out)
 			}
+			if output.partitionDir != tc.partitionDir {
+				t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n",
+					output.partitionDir, tc.partitionDir)
+			}
 		})
 	}
 }
 
+func TestBaseDirForInstallPath(t *testing.T) {
+	testConfig := pathTestConfig("")
+	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
+
+	ctx := &moduleInstallPathContextImpl{
+		baseModuleContext: baseModuleContext{
+			os:     deviceTarget.Os,
+			target: deviceTarget,
+		},
+	}
+	ctx.baseModuleContext.config = testConfig
+
+	actual := PathForModuleInstall(ctx, "foo", "bar")
+	expectedBaseDir := "target/product/test_device/system"
+	if actual.partitionDir != expectedBaseDir {
+		t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n", actual.partitionDir, expectedBaseDir)
+	}
+	expectedRelPath := "foo/bar"
+	if actual.Rel() != expectedRelPath {
+		t.Errorf("unexpected Rel():\n got: %q\nwant: %q\n", actual.Rel(), expectedRelPath)
+	}
+
+	actualAfterJoin := actual.Join(ctx, "baz")
+	// partitionDir is preserved even after joining
+	if actualAfterJoin.partitionDir != expectedBaseDir {
+		t.Errorf("unexpected partitionDir after joining:\n got: %q\nwant: %q\n", actualAfterJoin.partitionDir, expectedBaseDir)
+	}
+	// Rel() is updated though
+	expectedRelAfterJoin := "baz"
+	if actualAfterJoin.Rel() != expectedRelAfterJoin {
+		t.Errorf("unexpected Rel() after joining:\n got: %q\nwant: %q\n", actualAfterJoin.Rel(), expectedRelAfterJoin)
+	}
+}
+
 func TestDirectorySortedPaths(t *testing.T) {
 	config := TestConfig("out", nil, "", map[string][]byte{
 		"Android.bp": nil,