Use `Path` instead of string for file paths

This centralizes verification and common operations, like converting the
path to a source file to the path for a built object.

It also embeds the configuration knowledge into the path, so that we can
remove "${SrcDir}/path" from the ninja file. When SrcDir is '.', that
leads to paths like './path' instead of just 'path' like make is doing,
causing differences in compiled binaries.

Change-Id: Ib4e8910a6e867ce1b7b420d927c04f1142a7589e
diff --git a/java/app.go b/java/app.go
index 74e3269..f0eb3c8 100644
--- a/java/app.go
+++ b/java/app.go
@@ -17,12 +17,10 @@
 // This file contains the module types for compiling Android apps.
 
 import (
-	"os"
 	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 
 	"android/soong/common"
 )
@@ -63,8 +61,8 @@
 
 	appProperties androidAppProperties
 
-	aaptJavaFileList string
-	exportPackage    string
+	aaptJavaFileList common.Path
+	exportPackage    common.Path
 }
 
 func (a *AndroidApp) JavaDependencies(ctx AndroidJavaModuleContext) []string {
@@ -117,7 +115,7 @@
 	}
 
 	// apps manifests are handled by aapt, don't let javaBase see them
-	a.properties.Manifest = ""
+	a.properties.Manifest = nil
 
 	//if !ctx.ContainsProperty("proguard.enabled") {
 	//	a.properties.Proguard.Enabled = true
@@ -141,16 +139,16 @@
 
 	certificate := a.appProperties.Certificate
 	if certificate == "" {
-		certificate = ctx.AConfig().DefaultAppCertificate()
+		certificate = ctx.AConfig().DefaultAppCertificate(ctx).String()
 	} else if dir, _ := filepath.Split(certificate); dir == "" {
-		certificate = filepath.Join(ctx.AConfig().DefaultAppCertificateDir(), certificate)
+		certificate = filepath.Join(ctx.AConfig().DefaultAppCertificateDir(ctx).String(), certificate)
 	} else {
-		certificate = filepath.Join(ctx.AConfig().SrcDir(), certificate)
+		certificate = filepath.Join(common.PathForSource(ctx).String(), certificate)
 	}
 
 	certificates := []string{certificate}
 	for _, c := range a.appProperties.Additional_certificates {
-		certificates = append(certificates, filepath.Join(ctx.AConfig().SrcDir(), c))
+		certificates = append(certificates, filepath.Join(common.PathForSource(ctx).String(), c))
 	}
 
 	a.outputFile = CreateAppPackage(ctx, aaptPackageFlags, a.outputFile, certificates)
@@ -169,7 +167,7 @@
 	"*~",
 }
 
-func (a *AndroidApp) aaptFlags(ctx common.AndroidModuleContext) ([]string, []string, bool) {
+func (a *AndroidApp) aaptFlags(ctx common.AndroidModuleContext) ([]string, common.Paths, bool) {
 	aaptFlags := a.appProperties.Aaptflags
 	hasVersionCode := false
 	hasVersionName := false
@@ -185,54 +183,17 @@
 		aaptFlags = append(aaptFlags, "-z")
 	}
 
-	assetDirs := a.appProperties.Asset_dirs
-	if len(assetDirs) == 0 {
-		defaultAssetDir := filepath.Join(common.ModuleSrcDir(ctx), "assets")
-		if _, err := os.Stat(defaultAssetDir); err == nil {
-			assetDirs = []string{defaultAssetDir}
-		} else {
-			// Default asset directory doesn't exist, add a dep on the parent directory to
-			// regenerate the manifest if it is created later
-			// TODO: use glob to avoid rerunning whole regenerate if a different file is created?
-			ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx))
-		}
-	} else {
-		assetDirs = pathtools.PrefixPaths(assetDirs, common.ModuleSrcDir(ctx))
-	}
+	assetDirs := common.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Asset_dirs, "assets")
+	resourceDirs := common.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Android_resource_dirs, "res")
 
-	resourceDirs := a.appProperties.Android_resource_dirs
-	if len(resourceDirs) == 0 {
-		defaultResourceDir := filepath.Join(common.ModuleSrcDir(ctx), "res")
-		if _, err := os.Stat(defaultResourceDir); err == nil {
-			resourceDirs = []string{defaultResourceDir}
-		} else {
-			// Default resource directory doesn't exist, add a dep on the parent directory to
-			// regenerate the manifest if it is created later
-			// TODO: use glob to avoid rerunning whole regenerate if a different file is created?
-			ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx))
-		}
-	} else {
-		resourceDirs = pathtools.PrefixPaths(resourceDirs, common.ModuleSrcDir(ctx))
-	}
-
-	rootSrcDir := ctx.AConfig().SrcDir()
-	var overlayResourceDirs []string
+	var overlayResourceDirs common.Paths
 	// For every resource directory, check if there is an overlay directory with the same path.
 	// If found, it will be prepended to the list of resource directories.
 	for _, overlayDir := range ctx.AConfig().ResourceOverlays() {
 		for _, resourceDir := range resourceDirs {
-			relResourceDir, err := filepath.Rel(rootSrcDir, resourceDir)
-			if err != nil {
-				ctx.ModuleErrorf("resource directory %q is not in source tree", resourceDir)
-				continue
-			}
-			overlayResourceDir := filepath.Join(overlayDir, relResourceDir)
-			if _, err := os.Stat(overlayResourceDir); err == nil {
-				overlayResourceDirs = append(overlayResourceDirs, overlayResourceDir)
-			} else {
-				// Overlay resource directory doesn't exist, add a dep to regenerate the manifest if
-				// it is created later
-				ctx.AddNinjaFileDeps(overlayResourceDir)
+			overlay := overlayDir.OverlayPath(ctx, resourceDir)
+			if overlay.Valid() {
+				overlayResourceDirs = append(overlayResourceDirs, overlay.Path())
 			}
 		}
 	}
@@ -243,44 +204,46 @@
 
 	// aapt needs to rerun if any files are added or modified in the assets or resource directories,
 	// use glob to create a filelist.
-	var aaptDeps []string
+	var aaptDeps common.Paths
 	var hasResources bool
 	for _, d := range resourceDirs {
-		newDeps := ctx.Glob("app_resources", filepath.Join(d, "**/*"), aaptIgnoreFilenames)
+		newDeps := ctx.Glob("app_resources", filepath.Join(d.String(), "**/*"), aaptIgnoreFilenames)
 		aaptDeps = append(aaptDeps, newDeps...)
 		if len(newDeps) > 0 {
 			hasResources = true
 		}
 	}
 	for _, d := range assetDirs {
-		newDeps := ctx.Glob("app_assets", filepath.Join(d, "**/*"), aaptIgnoreFilenames)
+		newDeps := ctx.Glob("app_assets", filepath.Join(d.String(), "**/*"), aaptIgnoreFilenames)
 		aaptDeps = append(aaptDeps, newDeps...)
 	}
 
-	manifestFile := a.properties.Manifest
-	if manifestFile == "" {
+	var manifestFile string
+	if a.properties.Manifest == nil {
 		manifestFile = "AndroidManifest.xml"
+	} else {
+		manifestFile = *a.properties.Manifest
 	}
 
-	manifestFile = filepath.Join(common.ModuleSrcDir(ctx), manifestFile)
-	aaptDeps = append(aaptDeps, manifestFile)
+	manifestPath := common.PathForModuleSrc(ctx, manifestFile)
+	aaptDeps = append(aaptDeps, manifestPath)
 
-	aaptFlags = append(aaptFlags, "-M "+manifestFile)
-	aaptFlags = append(aaptFlags, common.JoinWithPrefix(assetDirs, "-A "))
-	aaptFlags = append(aaptFlags, common.JoinWithPrefix(resourceDirs, "-S "))
+	aaptFlags = append(aaptFlags, "-M "+manifestPath.String())
+	aaptFlags = append(aaptFlags, common.JoinWithPrefix(assetDirs.Strings(), "-A "))
+	aaptFlags = append(aaptFlags, common.JoinWithPrefix(resourceDirs.Strings(), "-S "))
 
 	ctx.VisitDirectDeps(func(module blueprint.Module) {
-		var depFile string
+		var depFile common.OptionalPath
 		if sdkDep, ok := module.(sdkDependency); ok {
-			depFile = sdkDep.ClasspathFile()
+			depFile = common.OptionalPathForPath(sdkDep.ClasspathFile())
 		} else if javaDep, ok := module.(JavaDependency); ok {
 			if ctx.OtherModuleName(module) == "framework-res" {
-				depFile = javaDep.(*javaBase).module.(*AndroidApp).exportPackage
+				depFile = common.OptionalPathForPath(javaDep.(*javaBase).module.(*AndroidApp).exportPackage)
 			}
 		}
-		if depFile != "" {
-			aaptFlags = append(aaptFlags, "-I "+depFile)
-			aaptDeps = append(aaptDeps, depFile)
+		if depFile.Valid() {
+			aaptFlags = append(aaptFlags, "-I "+depFile.String())
+			aaptDeps = append(aaptDeps, depFile.Path())
 		}
 	})
 
diff --git a/java/app_builder.go b/java/app_builder.go
index 849abfd..2a47519 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -19,7 +19,6 @@
 // functions.
 
 import (
-	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -75,44 +74,40 @@
 )
 
 func init() {
-	pctx.StaticVariable("androidManifestMergerCmd", "${srcDir}/prebuilts/devtools/tools/lib/manifest-merger.jar")
-	pctx.VariableFunc("aaptCmd", func(c interface{}) (string, error) {
-		return c.(common.Config).HostBinTool("aapt")
-	})
-	pctx.VariableFunc("signapkCmd", func(c interface{}) (string, error) {
-		return c.(common.Config).HostJavaTool("signapk.jar")
-	})
+	pctx.SourcePathVariable("androidManifestMergerCmd", "prebuilts/devtools/tools/lib/manifest-merger.jar")
+	pctx.HostBinToolVariable("aaptCmd", "aapt")
+	pctx.HostJavaToolVariable("signapkCmd", "signapk.jar")
 }
 
 func CreateResourceJavaFiles(ctx common.AndroidModuleContext, flags []string,
-	deps []string) (string, string, string) {
-	javaDir := filepath.Join(common.ModuleGenDir(ctx), "R")
-	javaFileList := filepath.Join(common.ModuleOutDir(ctx), "R.filelist")
-	publicResourcesFile := filepath.Join(common.ModuleOutDir(ctx), "public_resources.xml")
-	proguardOptionsFile := filepath.Join(common.ModuleOutDir(ctx), "proguard.options")
+	deps common.Paths) (common.Path, common.Path, common.Path) {
+	javaDir := common.PathForModuleGen(ctx, "R")
+	javaFileList := common.PathForModuleOut(ctx, "R.filelist")
+	publicResourcesFile := common.PathForModuleOut(ctx, "public_resources.xml")
+	proguardOptionsFile := common.PathForModuleOut(ctx, "proguard.options")
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      aaptCreateResourceJavaFile,
-		Outputs:   []string{publicResourcesFile, proguardOptionsFile, javaFileList},
+		Outputs:   common.WritablePaths{publicResourcesFile, proguardOptionsFile, javaFileList},
 		Implicits: deps,
 		Args: map[string]string{
 			"aaptFlags":           strings.Join(flags, " "),
-			"publicResourcesFile": publicResourcesFile,
-			"proguardOptionsFile": proguardOptionsFile,
-			"javaDir":             javaDir,
-			"javaFileList":        javaFileList,
+			"publicResourcesFile": publicResourcesFile.String(),
+			"proguardOptionsFile": proguardOptionsFile.String(),
+			"javaDir":             javaDir.String(),
+			"javaFileList":        javaFileList.String(),
 		},
 	})
 
 	return publicResourcesFile, proguardOptionsFile, javaFileList
 }
 
-func CreateExportPackage(ctx common.AndroidModuleContext, flags []string, deps []string) string {
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "package-export.apk")
+func CreateExportPackage(ctx common.AndroidModuleContext, flags []string, deps common.Paths) common.ModuleOutPath {
+	outputFile := common.PathForModuleOut(ctx, "package-export.apk")
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      aaptCreateAssetsPackage,
-		Outputs:   []string{outputFile},
+		Output:    outputFile,
 		Implicits: deps,
 		Args: map[string]string{
 			"aaptFlags": strings.Join(flags, " "),
@@ -122,31 +117,31 @@
 	return outputFile
 }
 
-func CreateAppPackage(ctx common.AndroidModuleContext, flags []string, jarFile string,
-	certificates []string) string {
+func CreateAppPackage(ctx common.AndroidModuleContext, flags []string, jarFile common.Path,
+	certificates []string) common.Path {
 
-	resourceApk := filepath.Join(common.ModuleOutDir(ctx), "resources.apk")
+	resourceApk := common.PathForModuleOut(ctx, "resources.apk")
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    aaptAddResources,
-		Outputs: []string{resourceApk},
-		Inputs:  []string{jarFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   aaptAddResources,
+		Output: resourceApk,
+		Input:  jarFile,
 		Args: map[string]string{
 			"aaptFlags": strings.Join(flags, " "),
 		},
 	})
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "package.apk")
+	outputFile := common.PathForModuleOut(ctx, "package.apk")
 
 	var certificateArgs []string
 	for _, c := range certificates {
 		certificateArgs = append(certificateArgs, c+".x509.pem", c+".pk8")
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    signapk,
-		Outputs: []string{outputFile},
-		Inputs:  []string{resourceApk},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   signapk,
+		Output: outputFile,
+		Input:  resourceApk,
 		Args: map[string]string{
 			"certificates": strings.Join(certificateArgs, " "),
 		},
diff --git a/java/builder.go b/java/builder.go
index 36506ae..024af43 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -29,7 +29,7 @@
 )
 
 var (
-	pctx = blueprint.NewPackageContext("android/soong/java")
+	pctx = common.NewPackageContext("android/soong/java")
 
 	// Compiling java is not conducive to proper dependency tracking.  The path-matches-class-name
 	// requirement leads to unpredictable generated source file names, and a single .java file
@@ -91,12 +91,8 @@
 	pctx.StaticVariable("commonJdkFlags", "-source 1.7 -target 1.7 -Xmaxerrs 9999999")
 	pctx.StaticVariable("javacCmd", "javac -J-Xmx1024M $commonJdkFlags")
 	pctx.StaticVariable("jarCmd", filepath.Join("${bootstrap.BinDir}", "soong_jar"))
-	pctx.VariableFunc("dxCmd", func(c interface{}) (string, error) {
-		return c.(common.Config).HostBinTool("dx")
-	})
-	pctx.VariableFunc("jarjarCmd", func(c interface{}) (string, error) {
-		return c.(common.Config).HostJavaTool("jarjar.jar")
-	})
+	pctx.HostBinToolVariable("dxCmd", "dx")
+	pctx.HostJavaToolVariable("jarjarCmd", "jarjar.jar")
 }
 
 type javaBuilderFlags struct {
@@ -108,33 +104,33 @@
 }
 
 type jarSpec struct {
-	fileList, dir string
+	fileList, dir common.Path
 }
 
 func (j jarSpec) soongJarArgs() string {
-	return "-C " + j.dir + " -l " + j.fileList
+	return "-C " + j.dir.String() + " -l " + j.fileList.String()
 }
 
-func TransformJavaToClasses(ctx common.AndroidModuleContext, srcFiles []string, srcFileLists []string,
-	flags javaBuilderFlags, deps []string) jarSpec {
+func TransformJavaToClasses(ctx common.AndroidModuleContext, srcFiles common.Paths, srcFileLists common.Paths,
+	flags javaBuilderFlags, deps common.Paths) jarSpec {
 
-	classDir := filepath.Join(common.ModuleOutDir(ctx), "classes")
-	classFileList := filepath.Join(common.ModuleOutDir(ctx), "classes.list")
+	classDir := common.PathForModuleOut(ctx, "classes")
+	classFileList := common.PathForModuleOut(ctx, "classes.list")
 
-	javacFlags := flags.javacFlags + common.JoinWithPrefix(srcFileLists, "@")
+	javacFlags := flags.javacFlags + common.JoinWithPrefix(srcFileLists.Strings(), "@")
 
 	deps = append(deps, srcFileLists...)
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      javac,
-		Outputs:   []string{classFileList},
+		Output:    classFileList,
 		Inputs:    srcFiles,
 		Implicits: deps,
 		Args: map[string]string{
 			"javacFlags":    javacFlags,
 			"bootClasspath": flags.bootClasspath,
 			"classpath":     flags.classpath,
-			"outDir":        classDir,
+			"outDir":        classDir.String(),
 		},
 	})
 
@@ -142,11 +138,11 @@
 }
 
 func TransformClassesToJar(ctx common.AndroidModuleContext, classes []jarSpec,
-	manifest string) string {
+	manifest common.OptionalPath) common.Path {
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes-full-debug.jar")
+	outputFile := common.PathForModuleOut(ctx, "classes-full-debug.jar")
 
-	deps := []string{}
+	deps := common.Paths{}
 	jarArgs := []string{}
 
 	for _, j := range classes {
@@ -154,14 +150,14 @@
 		jarArgs = append(jarArgs, j.soongJarArgs())
 	}
 
-	if manifest != "" {
-		deps = append(deps, manifest)
-		jarArgs = append(jarArgs, "-m "+manifest)
+	if manifest.Valid() {
+		deps = append(deps, manifest.Path())
+		jarArgs = append(jarArgs, "-m "+manifest.String())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      jar,
-		Outputs:   []string{outputFile},
+		Output:    outputFile,
 		Implicits: deps,
 		Args: map[string]string{
 			"jarArgs": strings.Join(jarArgs, " "),
@@ -171,19 +167,19 @@
 	return outputFile
 }
 
-func TransformClassesJarToDex(ctx common.AndroidModuleContext, classesJar string,
+func TransformClassesJarToDex(ctx common.AndroidModuleContext, classesJar common.Path,
 	flags javaBuilderFlags) jarSpec {
 
-	outDir := filepath.Join(common.ModuleOutDir(ctx), "dex")
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "dex.filelist")
+	outDir := common.PathForModuleOut(ctx, "dex")
+	outputFile := common.PathForModuleOut(ctx, "dex.filelist")
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    dx,
-		Outputs: []string{outputFile},
-		Inputs:  []string{classesJar},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   dx,
+		Output: outputFile,
+		Input:  classesJar,
 		Args: map[string]string{
 			"dxFlags": flags.dxFlags,
-			"outDir":  outDir,
+			"outDir":  outDir.String(),
 		},
 	})
 
@@ -191,10 +187,10 @@
 }
 
 func TransformDexToJavaLib(ctx common.AndroidModuleContext, resources []jarSpec,
-	dexJarSpec jarSpec) string {
+	dexJarSpec jarSpec) common.Path {
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "javalib.jar")
-	var deps []string
+	outputFile := common.PathForModuleOut(ctx, "javalib.jar")
+	var deps common.Paths
 	var jarArgs []string
 
 	for _, j := range resources {
@@ -205,9 +201,9 @@
 	deps = append(deps, dexJarSpec.fileList)
 	jarArgs = append(jarArgs, dexJarSpec.soongJarArgs())
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      jar,
-		Outputs:   []string{outputFile},
+		Output:    outputFile,
 		Implicits: deps,
 		Args: map[string]string{
 			"jarArgs": strings.Join(jarArgs, " "),
@@ -217,14 +213,15 @@
 	return outputFile
 }
 
-func TransformJarJar(ctx common.AndroidModuleContext, classesJar string, rulesFile string) string {
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes-jarjar.jar")
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    jarjar,
-		Outputs: []string{outputFile},
-		Inputs:  []string{classesJar},
+func TransformJarJar(ctx common.AndroidModuleContext, classesJar common.Path, rulesFile common.Path) common.Path {
+	outputFile := common.PathForModuleOut(ctx, "classes-jarjar.jar")
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:     jarjar,
+		Output:   outputFile,
+		Input:    classesJar,
+		Implicit: rulesFile,
 		Args: map[string]string{
-			"rulesFile": rulesFile,
+			"rulesFile": rulesFile.String(),
 		},
 	})
 
@@ -232,21 +229,20 @@
 }
 
 func TransformPrebuiltJarToClasses(ctx common.AndroidModuleContext,
-	prebuilt string) (classJarSpec, resourceJarSpec jarSpec) {
+	prebuilt common.Path) (classJarSpec, resourceJarSpec jarSpec) {
 
-	extractedDir := filepath.Join(common.ModuleOutDir(ctx), "extracted")
-	classDir := filepath.Join(extractedDir, "classes")
-	classFileList := filepath.Join(extractedDir, "classes.list")
-	resourceFileList := filepath.Join(extractedDir, "resources.list")
+	classDir := common.PathForModuleOut(ctx, "extracted/classes")
+	classFileList := common.PathForModuleOut(ctx, "extracted/classes.list")
+	resourceFileList := common.PathForModuleOut(ctx, "extracted/resources.list")
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:    extractPrebuilt,
-		Outputs: []string{classFileList, resourceFileList},
-		Inputs:  []string{prebuilt},
+		Outputs: common.WritablePaths{classFileList, resourceFileList},
+		Input:   prebuilt,
 		Args: map[string]string{
-			"outDir":       classDir,
-			"classFile":    classFileList,
-			"resourceFile": resourceFileList,
+			"outDir":       classDir.String(),
+			"classFile":    classFileList.String(),
+			"resourceFile": resourceFileList.String(),
 		},
 	})
 
diff --git a/java/gen.go b/java/gen.go
index f989875..51f9959 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -19,25 +19,17 @@
 // functions.
 
 import (
-	"path/filepath"
-
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 
 	"android/soong/common"
 )
 
 func init() {
-	pctx.VariableFunc("aidlCmd", func(c interface{}) (string, error) {
-		return c.(common.Config).HostBinTool("aidl")
-	})
-	pctx.StaticVariable("logtagsCmd", "${srcDir}/build/tools/java-event-log-tags.py")
-	pctx.StaticVariable("mergeLogtagsCmd", "${srcDir}/build/tools/merge-event-log-tags.py")
-	pctx.VariableConfigMethod("srcDir", common.Config.SrcDir)
+	pctx.HostBinToolVariable("aidlCmd", "aidl")
+	pctx.SourcePathVariable("logtagsCmd", "build/tools/java-event-log-tags.py")
+	pctx.SourcePathVariable("mergeLogtagsCmd", "build/tools/merge-event-log-tags.py")
 
-	pctx.VariableFunc("allLogtagsFile", func(c interface{}) (string, error) {
-		return filepath.Join(c.(common.Config).IntermediatesDir(), "all-event-log-tags.txt"), nil
-	})
+	pctx.IntermediatesPathVariable("allLogtagsFile", "all-event-log-tags.txt")
 }
 
 var (
@@ -64,16 +56,14 @@
 		})
 )
 
-func genAidl(ctx common.AndroidModuleContext, aidlFile, aidlFlags string) string {
-	javaFile := common.SrcDirRelPath(ctx, aidlFile)
-	javaFile = filepath.Join(common.ModuleGenDir(ctx), javaFile)
-	javaFile = pathtools.ReplaceExtension(javaFile, "java")
-	depFile := javaFile + ".d"
+func genAidl(ctx common.AndroidModuleContext, aidlFile common.Path, aidlFlags string) common.Path {
+	javaFile := common.GenPathWithExt(ctx, aidlFile, "java")
+	depFile := javaFile.String() + ".d"
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    aidl,
-		Outputs: []string{javaFile},
-		Inputs:  []string{aidlFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   aidl,
+		Output: javaFile,
+		Input:  aidlFile,
 		Args: map[string]string{
 			"depFile":   depFile,
 			"aidlFlags": aidlFlags,
@@ -83,25 +73,23 @@
 	return javaFile
 }
 
-func genLogtags(ctx common.AndroidModuleContext, logtagsFile string) string {
-	javaFile := common.SrcDirRelPath(ctx, logtagsFile)
-	javaFile = filepath.Join(common.ModuleGenDir(ctx), javaFile)
-	javaFile = pathtools.ReplaceExtension(javaFile, "java")
+func genLogtags(ctx common.AndroidModuleContext, logtagsFile common.Path) common.Path {
+	javaFile := common.GenPathWithExt(ctx, logtagsFile, "java")
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    logtags,
-		Outputs: []string{javaFile},
-		Inputs:  []string{logtagsFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   logtags,
+		Output: javaFile,
+		Input:  logtagsFile,
 	})
 
 	return javaFile
 }
 
-func (j *javaBase) genSources(ctx common.AndroidModuleContext, srcFiles []string,
-	flags javaBuilderFlags) []string {
+func (j *javaBase) genSources(ctx common.AndroidModuleContext, srcFiles common.Paths,
+	flags javaBuilderFlags) common.Paths {
 
 	for i, srcFile := range srcFiles {
-		switch filepath.Ext(srcFile) {
+		switch srcFile.Ext() {
 		case ".aidl":
 			javaFile := genAidl(ctx, srcFile, flags.aidlFlags)
 			srcFiles[i] = javaFile
@@ -120,13 +108,13 @@
 }
 
 type logtagsProducer interface {
-	logtags() []string
+	logtags() common.Paths
 }
 
 type logtagsSingleton struct{}
 
 func (l *logtagsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	var allLogtags []string
+	var allLogtags common.Paths
 	ctx.VisitAllModules(func(module blueprint.Module) {
 		if logtags, ok := module.(logtagsProducer); ok {
 			allLogtags = append(allLogtags, logtags.logtags()...)
@@ -136,6 +124,6 @@
 	ctx.Build(pctx, blueprint.BuildParams{
 		Rule:    mergeLogtags,
 		Outputs: []string{"$allLogtagsFile"},
-		Inputs:  allLogtags,
+		Inputs:  allLogtags.Strings(),
 	})
 }
diff --git a/java/java.go b/java/java.go
index 2bd5bff..839fb01 100644
--- a/java/java.go
+++ b/java/java.go
@@ -20,11 +20,9 @@
 
 import (
 	"fmt"
-	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 
 	"android/soong"
 	"android/soong/common"
@@ -93,7 +91,7 @@
 	Java_static_libs []string `android:"arch_variant"`
 
 	// manifest file to be included in resulting jar
-	Manifest string
+	Manifest *string
 
 	// if not blank, set to the version of the sdk to compile against
 	Sdk_version string
@@ -103,7 +101,7 @@
 	Dex bool `blueprint:"mutated"`
 
 	// if not blank, run jarjar using the specified rules file
-	Jarjar_rules string
+	Jarjar_rules *string
 
 	// directories to pass to aidl tool
 	Aidl_includes []string
@@ -122,10 +120,10 @@
 	properties javaBaseProperties
 
 	// output file suitable for inserting into the classpath of another compile
-	classpathFile string
+	classpathFile common.Path
 
 	// output file suitable for installing or running
-	outputFile string
+	outputFile common.Path
 
 	// jarSpecs suitable for inserting classes from a static library into another jar
 	classJarSpecs []jarSpec
@@ -133,16 +131,16 @@
 	// jarSpecs suitable for inserting resources from a static library into another jar
 	resourceJarSpecs []jarSpec
 
-	exportAidlIncludeDirs []string
+	exportAidlIncludeDirs common.Paths
 
-	logtagsSrcs []string
+	logtagsSrcs common.Paths
 
 	// filelists of extra source files that should be included in the javac command line,
 	// for example R.java generated by aapt for android apps
-	ExtraSrcLists []string
+	ExtraSrcLists common.Paths
 
 	// installed file for binary dependency
-	installFile string
+	installFile common.Path
 }
 
 type AndroidJavaModuleContext common.AndroidBaseContext
@@ -153,10 +151,10 @@
 }
 
 type JavaDependency interface {
-	ClasspathFile() string
+	ClasspathFile() common.Path
 	ClassJarSpecs() []jarSpec
 	ResourceJarSpecs() []jarSpec
-	AidlIncludeDirs() []string
+	AidlIncludeDirs() common.Paths
 }
 
 func NewJavaBase(base *javaBase, module JavaModuleType, hod common.HostOrDeviceSupported,
@@ -217,35 +215,35 @@
 	return deps
 }
 
-func (j *javaBase) aidlFlags(ctx common.AndroidModuleContext, aidlPreprocess string,
-	aidlIncludeDirs []string) []string {
+func (j *javaBase) aidlFlags(ctx common.AndroidModuleContext, aidlPreprocess common.OptionalPath,
+	aidlIncludeDirs common.Paths) []string {
 
-	localAidlIncludes := pathtools.PrefixPaths(j.properties.Aidl_includes, common.ModuleSrcDir(ctx))
+	localAidlIncludes := common.PathsForModuleSrc(ctx, j.properties.Aidl_includes)
 
 	var flags []string
-	if aidlPreprocess != "" {
-		flags = append(flags, "-p"+aidlPreprocess)
+	if aidlPreprocess.Valid() {
+		flags = append(flags, "-p"+aidlPreprocess.String())
 	} else {
-		flags = append(flags, common.JoinWithPrefix(aidlIncludeDirs, "-I"))
+		flags = append(flags, common.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
 	}
 
-	flags = append(flags, common.JoinWithPrefix(j.exportAidlIncludeDirs, "-I"))
-	flags = append(flags, common.JoinWithPrefix(localAidlIncludes, "-I"))
-	flags = append(flags, "-I"+common.ModuleSrcDir(ctx))
-	flags = append(flags, "-I"+filepath.Join(common.ModuleSrcDir(ctx), "src"))
+	flags = append(flags, common.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
+	flags = append(flags, common.JoinWithPrefix(localAidlIncludes.Strings(), "-I"))
+	flags = append(flags, "-I"+common.PathForModuleSrc(ctx).String())
+	flags = append(flags, "-I"+common.PathForModuleSrc(ctx, "src").String())
 
 	return flags
 }
 
-func (j *javaBase) collectDeps(ctx common.AndroidModuleContext) (classpath []string,
-	bootClasspath string, classJarSpecs, resourceJarSpecs []jarSpec, aidlPreprocess string,
-	aidlIncludeDirs []string, srcFileLists []string) {
+func (j *javaBase) collectDeps(ctx common.AndroidModuleContext) (classpath common.Paths,
+	bootClasspath common.OptionalPath, classJarSpecs, resourceJarSpecs []jarSpec, aidlPreprocess common.OptionalPath,
+	aidlIncludeDirs common.Paths, srcFileLists common.Paths) {
 
 	ctx.VisitDirectDeps(func(module blueprint.Module) {
 		otherName := ctx.OtherModuleName(module)
 		if javaDep, ok := module.(JavaDependency); ok {
 			if otherName == j.BootClasspath(ctx) {
-				bootClasspath = javaDep.ClasspathFile()
+				bootClasspath = common.OptionalPathForPath(javaDep.ClasspathFile())
 			} else if inList(otherName, defaultJavaLibraries) {
 				classpath = append(classpath, javaDep.ClasspathFile())
 			} else if inList(otherName, j.properties.Java_libs) {
@@ -265,8 +263,8 @@
 			}
 			aidlIncludeDirs = append(aidlIncludeDirs, javaDep.AidlIncludeDirs()...)
 			if sdkDep, ok := module.(sdkDependency); ok {
-				if sdkDep.AidlPreprocessed() != "" {
-					if aidlPreprocess != "" {
+				if sdkDep.AidlPreprocessed().Valid() {
+					if aidlPreprocess.Valid() {
 						ctx.ModuleErrorf("multiple dependencies with preprocessed aidls:\n %q\n %q",
 							aidlPreprocess, sdkDep.AidlPreprocessed())
 					} else {
@@ -287,8 +285,7 @@
 
 func (j *javaBase) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
 
-	j.exportAidlIncludeDirs = pathtools.PrefixPaths(j.properties.Export_aidl_include_dirs,
-		common.ModuleSrcDir(ctx))
+	j.exportAidlIncludeDirs = common.PathsForModuleSrc(ctx, j.properties.Export_aidl_include_dirs)
 
 	classpath, bootClasspath, classJarSpecs, resourceJarSpecs, aidlPreprocess,
 		aidlIncludeDirs, srcFileLists := j.collectDeps(ctx)
@@ -307,15 +304,15 @@
 		flags.aidlFlags = "$aidlFlags"
 	}
 
-	var javacDeps []string
+	var javacDeps common.Paths
 
-	if bootClasspath != "" {
-		flags.bootClasspath = "-bootclasspath " + bootClasspath
-		javacDeps = append(javacDeps, bootClasspath)
+	if bootClasspath.Valid() {
+		flags.bootClasspath = "-bootclasspath " + bootClasspath.String()
+		javacDeps = append(javacDeps, bootClasspath.Path())
 	}
 
 	if len(classpath) > 0 {
-		flags.classpath = "-classpath " + strings.Join(classpath, ":")
+		flags.classpath = "-classpath " + strings.Join(classpath.Strings(), ":")
 		javacDeps = append(javacDeps, classpath...)
 	}
 
@@ -344,10 +341,7 @@
 	resourceJarSpecs = append(ResourceDirsToJarSpecs(ctx, j.properties.Java_resource_dirs, j.properties.Exclude_java_resource_dirs),
 		resourceJarSpecs...)
 
-	manifest := j.properties.Manifest
-	if manifest != "" {
-		manifest = filepath.Join(common.ModuleSrcDir(ctx), manifest)
-	}
+	manifest := common.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
 
 	allJarSpecs := append([]jarSpec(nil), classJarSpecs...)
 	allJarSpecs = append(allJarSpecs, resourceJarSpecs...)
@@ -358,8 +352,8 @@
 		return
 	}
 
-	if j.properties.Jarjar_rules != "" {
-		jarjar_rules := filepath.Join(common.ModuleSrcDir(ctx), j.properties.Jarjar_rules)
+	if j.properties.Jarjar_rules != nil {
+		jarjar_rules := common.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
 		// Transform classes-full-debug.jar into classes-jarjar.jar
 		outputFile = TransformJarJar(ctx, outputFile, jarjar_rules)
 		if ctx.Failed() {
@@ -394,7 +388,7 @@
 			dxFlags = append(dxFlags,
 				"--debug",
 				"--verbose",
-				"--dump-to="+filepath.Join(common.ModuleOutDir(ctx), "classes.lst"),
+				"--dump-to="+common.PathForModuleOut(ctx, "classes.lst").String(),
 				"--dump-width=1000")
 		}
 
@@ -415,7 +409,7 @@
 
 var _ JavaDependency = (*JavaLibrary)(nil)
 
-func (j *javaBase) ClasspathFile() string {
+func (j *javaBase) ClasspathFile() common.Path {
 	return j.classpathFile
 }
 
@@ -427,13 +421,13 @@
 	return j.resourceJarSpecs
 }
 
-func (j *javaBase) AidlIncludeDirs() []string {
+func (j *javaBase) AidlIncludeDirs() common.Paths {
 	return j.exportAidlIncludeDirs
 }
 
 var _ logtagsProducer = (*javaBase)(nil)
 
-func (j *javaBase) logtags() []string {
+func (j *javaBase) logtags() common.Paths {
 	return j.logtagsSrcs
 }
 
@@ -485,7 +479,7 @@
 
 	// Depend on the installed jar (j.installFile) so that the wrapper doesn't get executed by
 	// another build rule before the jar has been installed.
-	ctx.InstallFile("bin", filepath.Join(common.ModuleSrcDir(ctx), j.binaryProperties.Wrapper),
+	ctx.InstallFile("bin", common.PathForModuleSrc(ctx, j.binaryProperties.Wrapper),
 		j.installFile)
 }
 
@@ -516,7 +510,7 @@
 
 	properties javaPrebuiltProperties
 
-	classpathFile                   string
+	classpathFile                   common.Path
 	classJarSpecs, resourceJarSpecs []jarSpec
 }
 
@@ -525,7 +519,7 @@
 		ctx.ModuleErrorf("expected exactly one jar in srcs")
 		return
 	}
-	prebuilt := filepath.Join(common.ModuleSrcDir(ctx), j.properties.Srcs[0])
+	prebuilt := common.PathForModuleSrc(ctx, j.properties.Srcs[0])
 
 	classJarSpec, resourceJarSpec := TransformPrebuiltJarToClasses(ctx, prebuilt)
 
@@ -537,7 +531,7 @@
 
 var _ JavaDependency = (*JavaPrebuilt)(nil)
 
-func (j *JavaPrebuilt) ClasspathFile() string {
+func (j *JavaPrebuilt) ClasspathFile() common.Path {
 	return j.classpathFile
 }
 
@@ -549,7 +543,7 @@
 	return j.resourceJarSpecs
 }
 
-func (j *JavaPrebuilt) AidlIncludeDirs() []string {
+func (j *JavaPrebuilt) AidlIncludeDirs() common.Paths {
 	return nil
 }
 
@@ -566,13 +560,13 @@
 
 type sdkDependency interface {
 	JavaDependency
-	AidlPreprocessed() string
+	AidlPreprocessed() common.OptionalPath
 }
 
 var _ sdkDependency = (*sdkPrebuilt)(nil)
 
 type sdkPrebuiltProperties struct {
-	Aidl_preprocessed string
+	Aidl_preprocessed *string
 }
 
 type sdkPrebuilt struct {
@@ -580,18 +574,16 @@
 
 	sdkProperties sdkPrebuiltProperties
 
-	aidlPreprocessed string
+	aidlPreprocessed common.OptionalPath
 }
 
 func (j *sdkPrebuilt) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
 	j.JavaPrebuilt.GenerateAndroidBuildActions(ctx)
 
-	if j.sdkProperties.Aidl_preprocessed != "" {
-		j.aidlPreprocessed = filepath.Join(common.ModuleSrcDir(ctx), j.sdkProperties.Aidl_preprocessed)
-	}
+	j.aidlPreprocessed = common.OptionalPathForModuleSrc(ctx, j.sdkProperties.Aidl_preprocessed)
 }
 
-func (j *sdkPrebuilt) AidlPreprocessed() string {
+func (j *sdkPrebuilt) AidlPreprocessed() common.OptionalPath {
 	return j.aidlPreprocessed
 }
 
diff --git a/java/resources.go b/java/resources.go
index 405d8b0..4f734f2 100644
--- a/java/resources.go
+++ b/java/resources.go
@@ -42,7 +42,7 @@
 	var excludes []string
 
 	for _, exclude := range excludeDirs {
-		excludes = append(excludes, filepath.Join(common.ModuleSrcDir(ctx), exclude, "**/*"))
+		excludes = append(excludes, common.PathForModuleSrc(ctx, exclude, "**/*").String())
 	}
 
 	excludes = append(excludes, resourceExcludes...)
@@ -53,15 +53,14 @@
 		if isStringInSlice(resourceDir, excludeDirs) {
 			continue
 		}
-		resourceDir := filepath.Join(common.ModuleSrcDir(ctx), resourceDir)
-		dirs := ctx.Glob("java_resources", resourceDir, nil)
+		resourceDir := common.PathForModuleSrc(ctx, resourceDir)
+		dirs := ctx.Glob("java_resources", resourceDir.String(), nil)
 		for _, dir := range dirs {
-			relDir := common.SrcDirRelPath(ctx, dir)
-			fileListFile := filepath.Join(common.ModuleOutDir(ctx), "res", relDir, "resources.list")
-			depFile := fileListFile + ".d"
+			fileListFile := common.ResPathWithName(ctx, dir, "resources.list")
+			depFile := fileListFile.String() + ".d"
 
-			glob := filepath.Join(dir, "**/*")
-			common.GlobRule(ctx, glob, excludes, fileListFile, depFile)
+			glob := filepath.Join(dir.String(), "**/*")
+			common.GlobRule(ctx, glob, excludes, fileListFile.String(), depFile)
 			jarSpecs = append(jarSpecs, jarSpec{fileListFile, dir})
 		}
 	}