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/Android.bp b/Android.bp
index 52bdc7c..b6f6a63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,10 +98,14 @@
         "common/glob.go",
         "common/module.go",
         "common/mutator.go",
+        "common/package_ctx.go",
         "common/paths.go",
         "common/util.go",
         "common/variable.go",
     ],
+    testSrcs: [
+        "common/paths_test.go",
+    ],
 }
 
 bootstrap_go_package {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 7554ef4..11d7614 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -16,7 +16,6 @@
 
 import (
 	"io"
-	"path/filepath"
 	"strings"
 
 	"android/soong/common"
@@ -29,7 +28,7 @@
 		ret.Class = "SHARED_LIBRARIES"
 	}
 	ret.OutputFile = c.outputFile()
-	ret.Extra = func(name, prefix, outputFile string, arch common.Arch) (ret []string) {
+	ret.Extra = func(name, prefix string, outputFile common.Path, arch common.Arch) (ret []string) {
 		exportedIncludes := c.exportedFlags()
 		for i := range exportedIncludes {
 			exportedIncludes[i] = strings.TrimPrefix(exportedIncludes[i], "-I")
@@ -38,7 +37,7 @@
 			ret = append(ret, "LOCAL_EXPORT_C_INCLUDE_DIRS := "+strings.Join(exportedIncludes, " "))
 		}
 
-		ret = append(ret, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(outputFile))
+		ret = append(ret, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
 		ret = append(ret, "LOCAL_SHARED_LIBRARIES_"+arch.ArchType.String()+" := "+strings.Join(c.savedDepNames.SharedLibs, " "))
 
 		if c.Properties.Relative_install_path != "" {
@@ -57,9 +56,9 @@
 func (c *ccObject) AndroidMk() (ret common.AndroidMkData) {
 	ret.OutputFile = c.outputFile()
 	ret.Custom = func(w io.Writer, name, prefix string) {
-		out := c.outputFile()
+		out := c.outputFile().Path()
 
-		io.WriteString(w, "$("+prefix+"TARGET_OUT_INTERMEDIATE_LIBRARIES)/"+name+objectExtension+": "+out+" | $(ACP)\n")
+		io.WriteString(w, "$("+prefix+"TARGET_OUT_INTERMEDIATE_LIBRARIES)/"+name+objectExtension+": "+out.String()+" | $(ACP)\n")
 		io.WriteString(w, "\t$(copy-file-to-target)\n")
 	}
 	return
@@ -67,7 +66,7 @@
 
 func (c *CCBinary) AndroidMk() (ret common.AndroidMkData) {
 	ret.Class = "EXECUTABLES"
-	ret.Extra = func(name, prefix, outputFile string, arch common.Arch) []string {
+	ret.Extra = func(name, prefix string, outputFile common.Path, arch common.Arch) []string {
 		ret := []string{
 			"LOCAL_CXX_STL := none",
 			"LOCAL_SYSTEM_SHARED_LIBRARIES :=",
diff --git a/cc/arm64_device.go b/cc/arm64_device.go
index 055f948..8d773ed 100644
--- a/cc/arm64_device.go
+++ b/cc/arm64_device.go
@@ -78,7 +78,7 @@
 func init() {
 	pctx.StaticVariable("arm64GccVersion", arm64GccVersion)
 
-	pctx.StaticVariable("arm64GccRoot",
+	pctx.SourcePathVariable("arm64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
 
 	pctx.StaticVariable("arm64GccTriple", "aarch64-linux-android")
diff --git a/cc/arm_device.go b/cc/arm_device.go
index ccfefba..5c91a82 100644
--- a/cc/arm_device.go
+++ b/cc/arm_device.go
@@ -154,7 +154,7 @@
 
 	pctx.StaticVariable("armGccVersion", armGccVersion)
 
-	pctx.StaticVariable("armGccRoot",
+	pctx.SourcePathVariable("armGccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
 
 	pctx.StaticVariable("armGccTriple", "arm-linux-androideabi")
diff --git a/cc/builder.go b/cc/builder.go
index 64437d2..f5fc9ee 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -28,7 +28,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 )
 
 const (
@@ -37,7 +36,7 @@
 )
 
 var (
-	pctx = blueprint.NewPackageContext("android/soong/cc")
+	pctx = common.NewPackageContext("android/soong/cc")
 
 	cc = pctx.StaticRule("cc",
 		blueprint.RuleParams{
@@ -102,7 +101,7 @@
 		},
 		"objcopyCmd", "prefix")
 
-	copyGccLibPath = pctx.StaticVariable("copyGccLibPath", "${SrcDir}/build/soong/copygcclib.sh")
+	copyGccLibPath = pctx.SourcePathVariable("copyGccLibPath", "build/soong/copygcclib.sh")
 
 	copyGccLib = pctx.StaticRule("copyGccLib",
 		blueprint.RuleParams{
@@ -141,45 +140,24 @@
 }
 
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func TransformSourceToObj(ctx common.AndroidModuleContext, subdir string, srcFiles []string,
-	flags builderFlags, deps []string) (objFiles []string) {
+func TransformSourceToObj(ctx common.AndroidModuleContext, subdir string, srcFiles common.Paths,
+	flags builderFlags, deps common.Paths) (objFiles common.Paths) {
 
-	srcRoot := ctx.AConfig().SrcDir()
-	intermediatesRoot := ctx.AConfig().IntermediatesDir()
-
-	objFiles = make([]string, len(srcFiles))
-	objDir := common.ModuleObjDir(ctx)
-	if subdir != "" {
-		objDir = filepath.Join(objDir, subdir)
-	}
+	objFiles = make(common.Paths, len(srcFiles))
 
 	cflags := flags.globalFlags + " " + flags.cFlags + " " + flags.conlyFlags
 	cppflags := flags.globalFlags + " " + flags.cFlags + " " + flags.cppFlags
 	asflags := flags.globalFlags + " " + flags.asFlags
 
 	for i, srcFile := range srcFiles {
-		var objFile string
-		if strings.HasPrefix(srcFile, intermediatesRoot) {
-			objFile = strings.TrimPrefix(srcFile, intermediatesRoot)
-			objFile = filepath.Join(objDir, "gen", objFile)
-		} else if strings.HasPrefix(srcFile, srcRoot) {
-			srcFile, _ = filepath.Rel(srcRoot, srcFile)
-			objFile = filepath.Join(objDir, srcFile)
-		} else if srcRoot == "." && srcFile[0] != '/' {
-			objFile = filepath.Join(objDir, srcFile)
-		} else {
-			ctx.ModuleErrorf("source file %q is not in source directory %q", srcFile, srcRoot)
-			continue
-		}
-
-		objFile = pathtools.ReplaceExtension(objFile, "o")
+		objFile := common.ObjPathWithExt(ctx, srcFile, subdir, "o")
 
 		objFiles[i] = objFile
 
 		var moduleCflags string
 		var ccCmd string
 
-		switch filepath.Ext(srcFile) {
+		switch srcFile.Ext() {
 		case ".S", ".s":
 			ccCmd = "gcc"
 			moduleCflags = asflags
@@ -204,15 +182,15 @@
 				panic("unrecoginzied ccCmd")
 			}
 
-			ccCmd = "${clangPath}" + ccCmd
+			ccCmd = "${clangPath}/" + ccCmd
 		} else {
 			ccCmd = gccCmd(flags.toolchain, ccCmd)
 		}
 
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 			Rule:      cc,
-			Outputs:   []string{objFile},
-			Inputs:    []string{srcFile},
+			Output:    objFile,
+			Input:     srcFile,
 			Implicits: deps,
 			Args: map[string]string{
 				"cFlags": moduleCflags,
@@ -225,16 +203,16 @@
 }
 
 // Generate a rule for compiling multiple .o files to a static library (.a)
-func TransformObjToStaticLib(ctx common.AndroidModuleContext, objFiles []string,
-	flags builderFlags, outputFile string) {
+func TransformObjToStaticLib(ctx common.AndroidModuleContext, objFiles common.Paths,
+	flags builderFlags, outputFile common.ModuleOutPath) {
 
 	arCmd := gccCmd(flags.toolchain, "ar")
 	arFlags := "crsPD"
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    ar,
-		Outputs: []string{outputFile},
-		Inputs:  objFiles,
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   ar,
+		Output: outputFile,
+		Inputs: objFiles,
 		Args: map[string]string{
 			"arFlags": arFlags,
 			"arCmd":   arCmd,
@@ -246,18 +224,20 @@
 // darwin.  The darwin ar tool doesn't support @file for list files, and has a
 // very small command line length limit, so we have to split the ar into multiple
 // steps, each appending to the previous one.
-func TransformDarwinObjToStaticLib(ctx common.AndroidModuleContext, objFiles []string,
-	flags builderFlags, outputFile string) {
+func TransformDarwinObjToStaticLib(ctx common.AndroidModuleContext, objFiles common.Paths,
+	flags builderFlags, outputPath common.ModuleOutPath) {
 
 	arCmd := "ar"
 	arFlags := "cqs"
 
 	// ARG_MAX on darwin is 262144, use half that to be safe
-	objFilesLists, err := splitListForSize(objFiles, 131072)
+	objFilesLists, err := splitListForSize(objFiles.Strings(), 131072)
 	if err != nil {
 		ctx.ModuleErrorf("%s", err.Error())
 	}
 
+	outputFile := outputPath.String()
+
 	var in, out string
 	for i, l := range objFilesLists {
 		in = out
@@ -295,12 +275,12 @@
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraires, to a shared library (.so) or dynamic executable
 func TransformObjToDynamicBinary(ctx common.AndroidModuleContext,
-	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps []string,
-	crtBegin, crtEnd string, groupLate bool, flags builderFlags, outputFile string) {
+	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps common.Paths,
+	crtBegin, crtEnd common.OptionalPath, groupLate bool, flags builderFlags, outputFile common.WritablePath) {
 
 	var ldCmd string
 	if flags.clang {
-		ldCmd = "${clangPath}clang++"
+		ldCmd = "${clangPath}/clang++"
 	} else {
 		ldCmd = gccCmd(flags.toolchain, "g++")
 	}
@@ -310,31 +290,31 @@
 
 	if len(wholeStaticLibs) > 0 {
 		if ctx.Host() && ctx.Darwin() {
-			libFlagsList = append(libFlagsList, common.JoinWithPrefix(wholeStaticLibs, "-force_load "))
+			libFlagsList = append(libFlagsList, common.JoinWithPrefix(wholeStaticLibs.Strings(), "-force_load "))
 		} else {
 			libFlagsList = append(libFlagsList, "-Wl,--whole-archive ")
-			libFlagsList = append(libFlagsList, wholeStaticLibs...)
+			libFlagsList = append(libFlagsList, wholeStaticLibs.Strings()...)
 			libFlagsList = append(libFlagsList, "-Wl,--no-whole-archive ")
 		}
 	}
 
-	libFlagsList = append(libFlagsList, staticLibs...)
+	libFlagsList = append(libFlagsList, staticLibs.Strings()...)
 
 	if groupLate && len(lateStaticLibs) > 0 {
 		libFlagsList = append(libFlagsList, "-Wl,--start-group")
 	}
-	libFlagsList = append(libFlagsList, lateStaticLibs...)
+	libFlagsList = append(libFlagsList, lateStaticLibs.Strings()...)
 	if groupLate && len(lateStaticLibs) > 0 {
 		libFlagsList = append(libFlagsList, "-Wl,--end-group")
 	}
 
 	for _, lib := range sharedLibs {
-		dir, file := filepath.Split(lib)
+		dir, file := filepath.Split(lib.String())
 		if !strings.HasPrefix(file, "lib") {
-			panic("shared library " + lib + " does not start with lib")
+			panic("shared library " + lib.String() + " does not start with lib")
 		}
 		if !strings.HasSuffix(file, flags.toolchain.ShlibSuffix()) {
-			panic("shared library " + lib + " does not end with " + flags.toolchain.ShlibSuffix())
+			panic("shared library " + lib.String() + " does not end with " + flags.toolchain.ShlibSuffix())
 		}
 		libFlagsList = append(libFlagsList,
 			"-l"+strings.TrimSuffix(strings.TrimPrefix(file, "lib"), flags.toolchain.ShlibSuffix()))
@@ -345,29 +325,29 @@
 	deps = append(deps, staticLibs...)
 	deps = append(deps, lateStaticLibs...)
 	deps = append(deps, wholeStaticLibs...)
-	if crtBegin != "" {
-		deps = append(deps, crtBegin, crtEnd)
+	if crtBegin.Valid() {
+		deps = append(deps, crtBegin.Path(), crtEnd.Path())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      ld,
-		Outputs:   []string{outputFile},
+		Output:    outputFile,
 		Inputs:    objFiles,
 		Implicits: deps,
 		Args: map[string]string{
 			"ldCmd":      ldCmd,
 			"ldDirFlags": ldDirsToFlags(ldDirs),
-			"crtBegin":   crtBegin,
+			"crtBegin":   crtBegin.String(),
 			"libFlags":   strings.Join(libFlagsList, " "),
 			"ldFlags":    flags.ldFlags,
-			"crtEnd":     crtEnd,
+			"crtEnd":     crtEnd.String(),
 		},
 	})
 }
 
 // Generate a rule for compiling multiple .o files to a .o using ld partial linking
-func TransformObjsToObj(ctx common.AndroidModuleContext, objFiles []string,
-	flags builderFlags, outputFile string) {
+func TransformObjsToObj(ctx common.AndroidModuleContext, objFiles common.Paths,
+	flags builderFlags, outputFile common.WritablePath) {
 
 	var ldCmd string
 	if flags.clang {
@@ -376,27 +356,27 @@
 		ldCmd = gccCmd(flags.toolchain, "g++")
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    partialLd,
-		Outputs: []string{outputFile},
-		Inputs:  objFiles,
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   partialLd,
+		Output: outputFile,
+		Inputs: objFiles,
 		Args: map[string]string{
-			"ldCmd": ldCmd,
+			"ldCmd":   ldCmd,
 			"ldFlags": flags.ldFlags,
 		},
 	})
 }
 
 // Generate a rule for runing objcopy --prefix-symbols on a binary
-func TransformBinaryPrefixSymbols(ctx common.AndroidModuleContext, prefix string, inputFile string,
-	flags builderFlags, outputFile string) {
+func TransformBinaryPrefixSymbols(ctx common.AndroidModuleContext, prefix string, inputFile common.Path,
+	flags builderFlags, outputFile common.WritablePath) {
 
 	objcopyCmd := gccCmd(flags.toolchain, "objcopy")
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    prefixSymbols,
-		Outputs: []string{outputFile},
-		Inputs:  []string{inputFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   prefixSymbols,
+		Output: outputFile,
+		Input:  inputFile,
 		Args: map[string]string{
 			"objcopyCmd": objcopyCmd,
 			"prefix":     prefix,
@@ -405,11 +385,11 @@
 }
 
 func CopyGccLib(ctx common.AndroidModuleContext, libName string,
-	flags builderFlags, outputFile string) {
+	flags builderFlags, outputFile common.WritablePath) {
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    copyGccLib,
-		Outputs: []string{outputFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   copyGccLib,
+		Output: outputFile,
 		Args: map[string]string{
 			"ccCmd":   gccCmd(flags.toolchain, "gcc"),
 			"cFlags":  flags.globalFlags,
diff --git a/cc/cc.go b/cc/cc.go
index 489bffe..b497d66 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -24,7 +24,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong"
@@ -64,10 +63,9 @@
 
 var (
 	HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", common.Config.PrebuiltOS)
-	SrcDir          = pctx.VariableConfigMethod("SrcDir", common.Config.SrcDir)
 
-	LibcRoot = pctx.StaticVariable("LibcRoot", "bionic/libc")
-	LibmRoot = pctx.StaticVariable("LibmRoot", "bionic/libm")
+	LibcRoot = pctx.SourcePathVariable("LibcRoot", "bionic/libc")
+	LibmRoot = pctx.SourcePathVariable("LibmRoot", "bionic/libm")
 )
 
 // Flags used by lots of devices.  Putting them in package static variables will save bytes in
@@ -127,19 +125,20 @@
 
 	// Everything in this list is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
-	pctx.StaticVariable("commonGlobalIncludes", strings.Join([]string{
-		"-isystem ${SrcDir}/system/core/include",
-		"-isystem ${SrcDir}/hardware/libhardware/include",
-		"-isystem ${SrcDir}/hardware/libhardware_legacy/include",
-		"-isystem ${SrcDir}/hardware/ril/include",
-		"-isystem ${SrcDir}/libnativehelper/include",
-		"-isystem ${SrcDir}/frameworks/native/include",
-		"-isystem ${SrcDir}/frameworks/native/opengl/include",
-		"-isystem ${SrcDir}/frameworks/av/include",
-		"-isystem ${SrcDir}/frameworks/base/include",
-	}, " "))
+	pctx.PrefixedPathsForSourceVariable("commonGlobalIncludes", "-isystem ",
+		[]string{
+			"system/core/include",
+			"hardware/libhardware/include",
+			"hardware/libhardware_legacy/include",
+			"hardware/ril/include",
+			"libnativehelper/include",
+			"frameworks/native/include",
+			"frameworks/native/opengl/include",
+			"frameworks/av/include",
+			"frameworks/base/include",
+		})
 
-	pctx.StaticVariable("clangPath", "${SrcDir}/prebuilts/clang/host/${HostPrebuiltTag}/3.8/bin/")
+	pctx.SourcePathVariable("clangPath", "prebuilts/clang/host/${HostPrebuiltTag}/3.8/bin")
 }
 
 type CCModuleContext common.AndroidBaseContext
@@ -162,23 +161,36 @@
 	depsMutator(common.AndroidBottomUpMutatorContext)
 
 	// Compile objects into final module
-	compileModule(common.AndroidModuleContext, CCFlags, CCDeps, []string)
+	compileModule(common.AndroidModuleContext, CCFlags, CCPathDeps, common.Paths)
 
 	// Install the built module.
 	installModule(common.AndroidModuleContext, CCFlags)
 
 	// Return the output file (.o, .a or .so) for use by other modules
-	outputFile() string
+	outputFile() common.OptionalPath
 }
 
 type CCDeps struct {
-	StaticLibs, SharedLibs, LateStaticLibs, WholeStaticLibs, ObjFiles, Cflags, ReexportedCflags []string
+	StaticLibs, SharedLibs, LateStaticLibs, WholeStaticLibs []string
 
-	WholeStaticLibObjFiles []string
+	ObjFiles common.Paths
+
+	Cflags, ReexportedCflags []string
 
 	CrtBegin, CrtEnd string
 }
 
+type CCPathDeps struct {
+	StaticLibs, SharedLibs, LateStaticLibs, WholeStaticLibs common.Paths
+
+	ObjFiles               common.Paths
+	WholeStaticLibObjFiles common.Paths
+
+	Cflags, ReexportedCflags []string
+
+	CrtBegin, CrtEnd common.OptionalPath
+}
+
 type CCFlags struct {
 	GlobalFlags []string // Flags that apply to C, C++, and assembly source files
 	AsFlags     []string // Flags that apply to assembly source files
@@ -440,7 +452,7 @@
 
 	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, c.savedDepNames.SharedLibs...)
 
-	ctx.AddDependency(ctx.Module(), c.savedDepNames.ObjFiles...)
+	ctx.AddDependency(ctx.Module(), c.savedDepNames.ObjFiles.Strings()...)
 	if c.savedDepNames.CrtBegin != "" {
 		ctx.AddDependency(ctx.Module(), c.savedDepNames.CrtBegin)
 	}
@@ -472,17 +484,14 @@
 	}
 
 	// Include dir cflags
-	common.CheckSrcDirsExist(ctx, c.Properties.Include_dirs, "include_dirs")
-	common.CheckModuleSrcDirsExist(ctx, c.Properties.Local_include_dirs, "local_include_dirs")
-
-	rootIncludeDirs := pathtools.PrefixPaths(c.Properties.Include_dirs, ctx.AConfig().SrcDir())
-	localIncludeDirs := pathtools.PrefixPaths(c.Properties.Local_include_dirs, common.ModuleSrcDir(ctx))
+	rootIncludeDirs := common.PathsForSource(ctx, c.Properties.Include_dirs)
+	localIncludeDirs := common.PathsForModuleSrc(ctx, c.Properties.Local_include_dirs)
 	flags.GlobalFlags = append(flags.GlobalFlags,
 		includeDirsToFlags(localIncludeDirs),
 		includeDirsToFlags(rootIncludeDirs))
 
-	rootIncludeFiles := pathtools.PrefixPaths(c.Properties.Include_files, ctx.AConfig().SrcDir())
-	localIncludeFiles := pathtools.PrefixPaths(c.Properties.Local_include_files, common.ModuleSrcDir(ctx))
+	rootIncludeFiles := common.PathsForSource(ctx, c.Properties.Include_files)
+	localIncludeFiles := common.PathsForModuleSrc(ctx, c.Properties.Local_include_files)
 
 	flags.GlobalFlags = append(flags.GlobalFlags,
 		includeFilesToFlags(rootIncludeFiles),
@@ -493,13 +502,13 @@
 			flags.GlobalFlags = append(flags.GlobalFlags,
 				"${commonGlobalIncludes}",
 				toolchain.IncludeFlags(),
-				"-I${SrcDir}/libnativehelper/include/nativehelper")
+				"-I"+common.PathForSource(ctx, "libnativehelper/include/nativehelper").String())
 		}
 
 		flags.GlobalFlags = append(flags.GlobalFlags, []string{
-			"-I" + common.ModuleSrcDir(ctx),
-			"-I" + common.ModuleOutDir(ctx),
-			"-I" + common.ModuleGenDir(ctx),
+			"-I" + common.PathForModuleSrc(ctx).String(),
+			"-I" + common.PathForModuleOut(ctx).String(),
+			"-I" + common.PathForModuleGen(ctx).String(),
 		}...)
 	}
 
@@ -636,18 +645,18 @@
 
 // Compile a list of source files into objects a specified subdirectory
 func (c *CCBase) customCompileObjs(ctx common.AndroidModuleContext, flags CCFlags,
-	subdir string, srcFiles, excludes []string) []string {
+	subdir string, srcFiles, excludes []string) common.Paths {
 
 	buildFlags := ccFlagsToBuilderFlags(flags)
 
-	srcFiles = ctx.ExpandSources(srcFiles, excludes)
-	srcFiles, deps := genSources(ctx, srcFiles, buildFlags)
+	inputFiles := ctx.ExpandSources(srcFiles, excludes)
+	srcPaths, deps := genSources(ctx, inputFiles, buildFlags)
 
-	return TransformSourceToObj(ctx, subdir, srcFiles, buildFlags, deps)
+	return TransformSourceToObj(ctx, subdir, srcPaths, buildFlags, deps)
 }
 
 // Compile files listed in c.Properties.Srcs into objects
-func (c *CCBase) compileObjs(ctx common.AndroidModuleContext, flags CCFlags) []string {
+func (c *CCBase) compileObjs(ctx common.AndroidModuleContext, flags CCFlags) common.Paths {
 
 	if c.Properties.SkipCompileObjs {
 		return nil
@@ -657,8 +666,8 @@
 }
 
 // Compile generated source files from dependencies
-func (c *CCBase) compileGeneratedObjs(ctx common.AndroidModuleContext, flags CCFlags) []string {
-	var srcs []string
+func (c *CCBase) compileGeneratedObjs(ctx common.AndroidModuleContext, flags CCFlags) common.Paths {
+	var srcs common.Paths
 
 	if c.Properties.SkipCompileObjs {
 		return nil
@@ -677,13 +686,13 @@
 	return TransformSourceToObj(ctx, "", srcs, ccFlagsToBuilderFlags(flags), nil)
 }
 
-func (c *CCBase) outputFile() string {
-	return ""
+func (c *CCBase) outputFile() common.OptionalPath {
+	return common.OptionalPath{}
 }
 
 func (c *CCBase) depsToPathsFromList(ctx common.AndroidModuleContext,
 	names []string) (modules []common.AndroidModule,
-	outputFiles []string, exportedFlags []string) {
+	outputFiles common.Paths, exportedFlags []string) {
 
 	for _, n := range names {
 		found := false
@@ -707,12 +716,12 @@
 					return
 				}
 
-				if outputFile := a.outputFile(); outputFile != "" {
+				if outputFile := a.outputFile(); outputFile.Valid() {
 					if found {
 						ctx.ModuleErrorf("multiple modules satisified dependency on %q", otherName)
 						return
 					}
-					outputFiles = append(outputFiles, outputFile)
+					outputFiles = append(outputFiles, outputFile.Path())
 					modules = append(modules, a)
 					if i, ok := a.(ccExportedFlagsProducer); ok {
 						exportedFlags = append(exportedFlags, i.exportedFlags()...)
@@ -735,10 +744,10 @@
 	return modules, outputFiles, exportedFlags
 }
 
-// Convert depenedency names to paths.  Takes a CCDeps containing names and returns a CCDeps
+// Convert dependency names to paths.  Takes a CCDeps containing names and returns a CCPathDeps
 // containing paths
-func (c *CCBase) depsToPaths(ctx common.AndroidModuleContext, depNames CCDeps) CCDeps {
-	var depPaths CCDeps
+func (c *CCBase) depsToPaths(ctx common.AndroidModuleContext, depNames CCDeps) CCPathDeps {
+	var depPaths CCPathDeps
 	var newCflags []string
 
 	var wholeStaticLibModules []common.AndroidModule
@@ -778,7 +787,12 @@
 					depPaths.CrtEnd = obj.object().outputFile()
 				}
 			} else {
-				depPaths.ObjFiles = append(depPaths.ObjFiles, obj.object().outputFile())
+				output := obj.object().outputFile()
+				if output.Valid() {
+					depPaths.ObjFiles = append(depPaths.ObjFiles, output.Path())
+				} else {
+					ctx.ModuleErrorf("module %s did not provide an output file", otherName)
+				}
 			}
 		}
 	})
@@ -908,11 +922,11 @@
 		// tree is in good enough shape to not need it.
 		// Host builds will use GNU libstdc++.
 		if ctx.Device() {
-			flags.CFlags = append(flags.CFlags, "-I${SrcDir}/bionic/libstdc++/include")
+			flags.CFlags = append(flags.CFlags, "-I"+common.PathForSource(ctx, "bionic/libstdc++/include").String())
 		}
 	case "ndk_system":
-		ndkSrcRoot := ctx.AConfig().SrcDir() + "/prebuilts/ndk/current/sources/"
-		flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot+"cxx-stl/system/include")
+		ndkSrcRoot := common.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
+		flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot.String())
 	case "ndk_libc++_shared", "ndk_libc++_static":
 		// TODO(danalbert): This really shouldn't be here...
 		flags.CppFlags = append(flags.CppFlags, "-std=c++11")
@@ -1067,23 +1081,23 @@
 	} `android:"arch_variant"`
 
 	// local file name to pass to the linker as --version_script
-	Version_script string `android:"arch_variant"`
+	Version_script *string `android:"arch_variant"`
 	// local file name to pass to the linker as -unexported_symbols_list
-	Unexported_symbols_list string `android:"arch_variant"`
+	Unexported_symbols_list *string `android:"arch_variant"`
 	// local file name to pass to the linker as -force_symbols_not_weak_list
-	Force_symbols_not_weak_list string `android:"arch_variant"`
+	Force_symbols_not_weak_list *string `android:"arch_variant"`
 	// local file name to pass to the linker as -force_symbols_weak_list
-	Force_symbols_weak_list string `android:"arch_variant"`
+	Force_symbols_weak_list *string `android:"arch_variant"`
 }
 
 type CCLibrary struct {
 	CCLinked
 
 	reuseFrom     ccLibraryInterface
-	reuseObjFiles []string
-	objFiles      []string
+	reuseObjFiles common.Paths
+	objFiles      common.Paths
 	exportFlags   []string
-	out           string
+	out           common.Path
 	systemLibs    []string
 
 	LibraryProperties CCLibraryProperties
@@ -1102,8 +1116,8 @@
 	ccLibrary() *CCLibrary
 	setReuseFrom(ccLibraryInterface)
 	getReuseFrom() ccLibraryInterface
-	getReuseObjFiles() []string
-	allObjFiles() []string
+	getReuseObjFiles() common.Paths
+	allObjFiles() common.Paths
 }
 
 var _ ccLibraryInterface = (*CCLibrary)(nil)
@@ -1154,11 +1168,11 @@
 	return depNames
 }
 
-func (c *CCLibrary) outputFile() string {
-	return c.out
+func (c *CCLibrary) outputFile() common.OptionalPath {
+	return common.OptionalPathForPath(c.out)
 }
 
-func (c *CCLibrary) getReuseObjFiles() []string {
+func (c *CCLibrary) getReuseObjFiles() common.Paths {
 	return c.reuseObjFiles
 }
 
@@ -1170,7 +1184,7 @@
 	return c.reuseFrom
 }
 
-func (c *CCLibrary) allObjFiles() []string {
+func (c *CCLibrary) allObjFiles() common.Paths {
 	return c.objFiles
 }
 
@@ -1225,7 +1239,7 @@
 }
 
 func (c *CCLibrary) compileStaticLibrary(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	staticFlags := flags
 	objFilesStatic := c.customCompileObjs(ctx, staticFlags, common.DeviceStaticLibrary,
@@ -1234,7 +1248,7 @@
 	objFiles = append(objFiles, objFilesStatic...)
 	objFiles = append(objFiles, deps.WholeStaticLibObjFiles...)
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+staticLibraryExtension)
+	outputFile := common.PathForModuleOut(ctx, ctx.ModuleName()+staticLibraryExtension)
 
 	if ctx.Darwin() {
 		TransformDarwinObjToStaticLib(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
@@ -1245,8 +1259,7 @@
 	c.objFiles = objFiles
 	c.out = outputFile
 
-	common.CheckModuleSrcDirsExist(ctx, c.Properties.Export_include_dirs, "export_include_dirs")
-	includeDirs := pathtools.PrefixPaths(c.Properties.Export_include_dirs, common.ModuleSrcDir(ctx))
+	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
 	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
 	c.exportFlags = append(c.exportFlags, deps.ReexportedCflags...)
 
@@ -1254,7 +1267,7 @@
 }
 
 func (c *CCLibrary) compileSharedLibrary(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	sharedFlags := flags
 	objFilesShared := c.customCompileObjs(ctx, sharedFlags, common.DeviceSharedLibrary,
@@ -1262,43 +1275,43 @@
 
 	objFiles = append(objFiles, objFilesShared...)
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+flags.Toolchain.ShlibSuffix())
+	outputFile := common.PathForModuleOut(ctx, ctx.ModuleName()+flags.Toolchain.ShlibSuffix())
 
-	var linkerDeps []string
+	var linkerDeps common.Paths
 
+	versionScript := common.OptionalPathForModuleSrc(ctx, c.LibraryProperties.Version_script)
+	unexportedSymbols := common.OptionalPathForModuleSrc(ctx, c.LibraryProperties.Unexported_symbols_list)
+	forceNotWeakSymbols := common.OptionalPathForModuleSrc(ctx, c.LibraryProperties.Force_symbols_not_weak_list)
+	forceWeakSymbols := common.OptionalPathForModuleSrc(ctx, c.LibraryProperties.Force_symbols_weak_list)
 	if !ctx.Darwin() {
-		if c.LibraryProperties.Version_script != "" {
-			versionScript := filepath.Join(common.ModuleSrcDir(ctx), c.LibraryProperties.Version_script)
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,--version-script,"+versionScript)
-			linkerDeps = append(linkerDeps, versionScript)
+		if versionScript.Valid() {
+			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,--version-script,"+versionScript.String())
+			linkerDeps = append(linkerDeps, versionScript.Path())
 		}
-		if c.LibraryProperties.Unexported_symbols_list != "" {
+		if unexportedSymbols.Valid() {
 			ctx.PropertyErrorf("unexported_symbols_list", "Only supported on Darwin")
 		}
-		if c.LibraryProperties.Force_symbols_not_weak_list != "" {
+		if forceNotWeakSymbols.Valid() {
 			ctx.PropertyErrorf("force_symbols_not_weak_list", "Only supported on Darwin")
 		}
-		if c.LibraryProperties.Force_symbols_weak_list != "" {
+		if forceWeakSymbols.Valid() {
 			ctx.PropertyErrorf("force_symbols_weak_list", "Only supported on Darwin")
 		}
 	} else {
-		if c.LibraryProperties.Version_script != "" {
+		if versionScript.Valid() {
 			ctx.PropertyErrorf("version_script", "Not supported on Darwin")
 		}
-		if c.LibraryProperties.Unexported_symbols_list != "" {
-			localFile := filepath.Join(common.ModuleSrcDir(ctx), c.LibraryProperties.Unexported_symbols_list)
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-unexported_symbols_list,"+localFile)
-			linkerDeps = append(linkerDeps, localFile)
+		if unexportedSymbols.Valid() {
+			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
+			linkerDeps = append(linkerDeps, unexportedSymbols.Path())
 		}
-		if c.LibraryProperties.Force_symbols_not_weak_list != "" {
-			localFile := filepath.Join(common.ModuleSrcDir(ctx), c.LibraryProperties.Force_symbols_not_weak_list)
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+localFile)
-			linkerDeps = append(linkerDeps, localFile)
+		if forceNotWeakSymbols.Valid() {
+			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
+			linkerDeps = append(linkerDeps, forceNotWeakSymbols.Path())
 		}
-		if c.LibraryProperties.Force_symbols_weak_list != "" {
-			localFile := filepath.Join(common.ModuleSrcDir(ctx), c.LibraryProperties.Force_symbols_weak_list)
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_weak_list,"+localFile)
-			linkerDeps = append(linkerDeps, localFile)
+		if forceWeakSymbols.Valid() {
+			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
+			linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
 		}
 	}
 
@@ -1307,13 +1320,13 @@
 		ccFlagsToBuilderFlags(sharedFlags), outputFile)
 
 	c.out = outputFile
-	includeDirs := pathtools.PrefixPaths(c.Properties.Export_include_dirs, common.ModuleSrcDir(ctx))
+	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
 	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
 	c.exportFlags = append(c.exportFlags, deps.ReexportedCflags...)
 }
 
 func (c *CCLibrary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	// Reuse the object files from the matching static library if it exists
 	if c.getReuseFrom().ccLibrary() == c {
@@ -1321,7 +1334,7 @@
 	} else {
 		if c.getReuseFrom().ccLibrary().LibraryProperties.Static.Cflags == nil &&
 			c.LibraryProperties.Shared.Cflags == nil {
-			objFiles = append([]string(nil), c.getReuseFrom().getReuseObjFiles()...)
+			objFiles = append(common.Paths(nil), c.getReuseFrom().getReuseObjFiles()...)
 		}
 	}
 
@@ -1363,7 +1376,7 @@
 
 type ccObject struct {
 	CCBase
-	out string
+	out common.OptionalPath
 }
 
 func (c *ccObject) object() *ccObject {
@@ -1382,19 +1395,20 @@
 }
 
 func (c *ccObject) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	objFiles = append(objFiles, deps.ObjFiles...)
 
-	var outputFile string
+	var outputFile common.Path
 	if len(objFiles) == 1 {
 		outputFile = objFiles[0]
 	} else {
-		outputFile = filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+objectExtension)
-		TransformObjsToObj(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
+		output := common.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
+		TransformObjsToObj(ctx, objFiles, ccFlagsToBuilderFlags(flags), output)
+		outputFile = output
 	}
 
-	c.out = outputFile
+	c.out = common.OptionalPathForPath(outputFile)
 
 	ctx.CheckbuildFile(outputFile)
 }
@@ -1403,7 +1417,7 @@
 	// Object files do not get installed.
 }
 
-func (c *ccObject) outputFile() string {
+func (c *ccObject) outputFile() common.OptionalPath {
 	return c.out
 }
 
@@ -1433,8 +1447,8 @@
 
 type CCBinary struct {
 	CCLinked
-	out              string
-	installFile      string
+	out              common.Path
+	installFile      common.Path
 	BinaryProperties CCBinaryProperties
 }
 
@@ -1569,23 +1583,23 @@
 }
 
 func (c *CCBinary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	if !Bool(c.BinaryProperties.Static_executable) && inList("libc", c.Properties.Static_libs) {
 		ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
 			"from static libs or set static_executable: true")
 	}
 
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), c.getStem(ctx)+flags.Toolchain.ExecutableSuffix())
+	outputFile := common.PathForModuleOut(ctx, c.getStem(ctx)+flags.Toolchain.ExecutableSuffix())
 	c.out = outputFile
 	if c.BinaryProperties.Prefix_symbols != "" {
 		afterPrefixSymbols := outputFile
-		outputFile = outputFile + ".intermediate"
+		outputFile = common.PathForModuleOut(ctx, c.getStem(ctx)+".intermediate")
 		TransformBinaryPrefixSymbols(ctx, c.BinaryProperties.Prefix_symbols, outputFile,
 			ccFlagsToBuilderFlags(flags), afterPrefixSymbols)
 	}
 
-	var linkerDeps []string
+	var linkerDeps common.Paths
 
 	TransformObjToDynamicBinary(ctx, objFiles, deps.SharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
@@ -1596,11 +1610,11 @@
 	c.installFile = ctx.InstallFile(filepath.Join("bin", c.Properties.Relative_install_path), c.out)
 }
 
-func (c *CCBinary) HostToolPath() string {
+func (c *CCBinary) HostToolPath() common.OptionalPath {
 	if c.HostOrDevice().Host() {
-		return c.installFile
+		return common.OptionalPathForPath(c.installFile)
 	}
-	return ""
+	return common.OptionalPath{}
 }
 
 func (c *CCBinary) testPerSrc() bool {
@@ -1649,7 +1663,7 @@
 
 	// TODO(danalbert): Make gtest export its dependencies.
 	flags.CFlags = append(flags.CFlags,
-		"-I"+filepath.Join(ctx.AConfig().SrcDir(), "external/gtest/include"))
+		"-I"+common.PathForSource(ctx, "external/gtest/include").String())
 
 	return flags
 }
@@ -1832,10 +1846,10 @@
 }
 
 func (c *toolchainLibrary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCDeps, objFiles []string) {
+	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
 
 	libName := ctx.ModuleName() + staticLibraryExtension
-	outputFile := filepath.Join(common.ModuleOutDir(ctx), libName)
+	outputFile := common.PathForModuleOut(ctx, libName)
 
 	CopyGccLib(ctx, libName, ccFlagsToBuilderFlags(flags), outputFile)
 
@@ -1854,19 +1868,19 @@
 // either (with the exception of the shared STLs, which are installed to the app's directory rather
 // than to the system image).
 
-func getNdkLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, version string) string {
-	return fmt.Sprintf("%s/prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/lib",
-		ctx.AConfig().SrcDir(), version, toolchain.Name())
+func getNdkLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, version string) common.SourcePath {
+	return common.PathForSource(ctx, fmt.Sprintf("prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/lib",
+		version, toolchain.Name()))
 }
 
 func ndkPrebuiltModuleToPath(ctx common.AndroidModuleContext, toolchain Toolchain,
-	ext string, version string) string {
+	ext string, version string) common.Path {
 
 	// NDK prebuilts are named like: ndk_NAME.EXT.SDK_VERSION.
 	// We want to translate to just NAME.EXT
 	name := strings.Split(strings.TrimPrefix(ctx.ModuleName(), "ndk_"), ".")[0]
 	dir := getNdkLibDir(ctx, toolchain, version)
-	return filepath.Join(dir, name+ext)
+	return dir.Join(ctx, name+ext)
 }
 
 type ndkPrebuiltObject struct {
@@ -1884,13 +1898,13 @@
 }
 
 func (c *ndkPrebuiltObject) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCDeps, objFiles []string) {
+	deps CCPathDeps, objFiles common.Paths) {
 	// A null build step, but it sets up the output path.
 	if !strings.HasPrefix(ctx.ModuleName(), "ndk_crt") {
 		ctx.ModuleErrorf("NDK prebuilts must have an ndk_crt prefixed name")
 	}
 
-	c.out = ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, c.Properties.Sdk_version)
+	c.out = common.OptionalPathForPath(ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, c.Properties.Sdk_version))
 }
 
 func (c *ndkPrebuiltObject) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
@@ -1915,14 +1929,14 @@
 }
 
 func (c *ndkPrebuiltLibrary) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCDeps, objFiles []string) {
+	deps CCPathDeps, objFiles common.Paths) {
 	// A null build step, but it sets up the output path.
 	if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
 		ctx.ModuleErrorf("NDK prebuilts must have an ndk_lib prefixed name")
 	}
 
-	includeDirs := pathtools.PrefixPaths(c.Properties.Export_include_dirs, common.ModuleSrcDir(ctx))
-	c.exportFlags = []string{common.JoinWithPrefix(includeDirs, "-isystem ")}
+	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
+	c.exportFlags = []string{common.JoinWithPrefix(includeDirs.Strings(), "-isystem ")}
 
 	c.out = ndkPrebuiltModuleToPath(ctx, flags.Toolchain, flags.Toolchain.ShlibSuffix(),
 		c.Properties.Sdk_version)
@@ -1960,7 +1974,7 @@
 	return NewCCLibrary(&module.CCLibrary, module, common.DeviceSupported)
 }
 
-func getNdkStlLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, stl string) string {
+func getNdkStlLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, stl string) common.SourcePath {
 	gccVersion := toolchain.GccVersion()
 	var libDir string
 	switch stl {
@@ -1973,22 +1987,22 @@
 	}
 
 	if libDir != "" {
-		ndkSrcRoot := ctx.AConfig().SrcDir() + "/prebuilts/ndk/current/sources"
-		return fmt.Sprintf("%s/%s/%s", ndkSrcRoot, libDir, ctx.Arch().Abi)
+		ndkSrcRoot := "prebuilts/ndk/current/sources"
+		return common.PathForSource(ctx, ndkSrcRoot).Join(ctx, libDir, ctx.Arch().Abi[0])
 	}
 
 	ctx.ModuleErrorf("Unknown NDK STL: %s", stl)
-	return ""
+	return common.PathForSource(ctx, "")
 }
 
 func (c *ndkPrebuiltStl) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCDeps, objFiles []string) {
+	deps CCPathDeps, objFiles common.Paths) {
 	// A null build step, but it sets up the output path.
 	if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
 		ctx.ModuleErrorf("NDK prebuilts must have an ndk_lib prefixed name")
 	}
 
-	includeDirs := pathtools.PrefixPaths(c.Properties.Export_include_dirs, common.ModuleSrcDir(ctx))
+	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
 	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
 
 	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
@@ -2000,7 +2014,7 @@
 	stlName := strings.TrimSuffix(libName, "_shared")
 	stlName = strings.TrimSuffix(stlName, "_static")
 	libDir := getNdkStlLibDir(ctx, flags.Toolchain, stlName)
-	c.out = libDir + "/" + libName + libExt
+	c.out = libDir.Join(ctx, libName+libExt)
 }
 
 func linkageMutator(mctx common.AndroidBottomUpMutatorContext) {
diff --git a/cc/gen.go b/cc/gen.go
index be50d75..035f40e 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -19,18 +19,15 @@
 // functions.
 
 import (
-	"path/filepath"
-
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 
 	"android/soong/common"
 )
 
 func init() {
-	pctx.StaticVariable("lexCmd", "${SrcDir}/prebuilts/misc/${HostPrebuiltTag}/flex/flex-2.5.39")
-	pctx.StaticVariable("yaccCmd", "${SrcDir}/prebuilts/misc/${HostPrebuiltTag}/bison/bison")
-	pctx.StaticVariable("yaccDataDir", "${SrcDir}/external/bison/data")
+	pctx.SourcePathVariable("lexCmd", "prebuilts/misc/${HostPrebuiltTag}/flex/flex-2.5.39")
+	pctx.SourcePathVariable("yaccCmd", "prebuilts/misc/${HostPrebuiltTag}/bison/bison")
+	pctx.SourcePathVariable("yaccDataDir", "external/bison/data")
 }
 
 var (
@@ -51,49 +48,45 @@
 		})
 )
 
-func genYacc(ctx common.AndroidModuleContext, yaccFile, yaccFlags string) (cppFile, headerFile string) {
-	cppFile = common.SrcDirRelPath(ctx, yaccFile)
-	cppFile = filepath.Join(common.ModuleGenDir(ctx), cppFile)
-	cppFile = pathtools.ReplaceExtension(cppFile, "cpp")
-	hppFile := pathtools.ReplaceExtension(cppFile, "hpp")
-	headerFile = pathtools.ReplaceExtension(cppFile, "h")
+func genYacc(ctx common.AndroidModuleContext, yaccFile common.Path, yaccFlags string) (cppFile, headerFile common.ModuleGenPath) {
+	cppFile = common.GenPathWithExt(ctx, yaccFile, "cpp")
+	hppFile := common.GenPathWithExt(ctx, yaccFile, "hpp")
+	headerFile = common.GenPathWithExt(ctx, yaccFile, "h")
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:    yacc,
-		Outputs: []string{cppFile, headerFile},
-		Inputs:  []string{yaccFile},
+		Outputs: common.WritablePaths{cppFile, headerFile},
+		Input:   yaccFile,
 		Args: map[string]string{
 			"yaccFlags": yaccFlags,
-			"cppFile":   cppFile,
-			"hppFile":   hppFile,
-			"hFile":     headerFile,
+			"cppFile":   cppFile.String(),
+			"hppFile":   hppFile.String(),
+			"hFile":     headerFile.String(),
 		},
 	})
 
 	return cppFile, headerFile
 }
 
-func genLex(ctx common.AndroidModuleContext, lexFile string) (cppFile string) {
-	cppFile = common.SrcDirRelPath(ctx, lexFile)
-	cppFile = filepath.Join(common.ModuleGenDir(ctx), cppFile)
-	cppFile = pathtools.ReplaceExtension(cppFile, "cpp")
+func genLex(ctx common.AndroidModuleContext, lexFile common.Path) (cppFile common.ModuleGenPath) {
+	cppFile = common.GenPathWithExt(ctx, lexFile, "cpp")
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:    lex,
-		Outputs: []string{cppFile},
-		Inputs:  []string{lexFile},
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
+		Rule:   lex,
+		Output: cppFile,
+		Input:  lexFile,
 	})
 
 	return cppFile
 }
 
-func genSources(ctx common.AndroidModuleContext, srcFiles []string,
-	buildFlags builderFlags) ([]string, []string) {
+func genSources(ctx common.AndroidModuleContext, srcFiles common.Paths,
+	buildFlags builderFlags) (common.Paths, common.Paths) {
 
-	var deps []string
+	var deps common.Paths
 
 	for i, srcFile := range srcFiles {
-		switch filepath.Ext(srcFile) {
+		switch srcFile.Ext() {
 		case ".y", ".yy":
 			cppFile, headerFile := genYacc(ctx, srcFile, buildFlags.yaccFlags)
 			srcFiles[i] = cppFile
diff --git a/cc/mips64_device.go b/cc/mips64_device.go
index 5aa5bc3..44ca4f8 100644
--- a/cc/mips64_device.go
+++ b/cc/mips64_device.go
@@ -90,7 +90,7 @@
 
 	pctx.StaticVariable("mips64GccVersion", mips64GccVersion)
 
-	pctx.StaticVariable("mips64GccRoot",
+	pctx.SourcePathVariable("mips64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mips64GccVersion}")
 
 	pctx.StaticVariable("mips64GccTriple", "mips64el-linux-android")
diff --git a/cc/mips_device.go b/cc/mips_device.go
index 8b47f8a..ba6b81a 100644
--- a/cc/mips_device.go
+++ b/cc/mips_device.go
@@ -121,7 +121,7 @@
 
 	pctx.StaticVariable("mipsGccVersion", mipsGccVersion)
 
-	pctx.StaticVariable("mipsGccRoot",
+	pctx.SourcePathVariable("mipsGccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mipsGccVersion}")
 
 	pctx.StaticVariable("mipsGccTriple", "mips64el-linux-android")
diff --git a/cc/util.go b/cc/util.go
index efc89f0..2fc717b 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -24,12 +24,12 @@
 
 // Efficiently converts a list of include directories to a single string
 // of cflags with -I prepended to each directory.
-func includeDirsToFlags(dirs []string) string {
-	return common.JoinWithPrefix(dirs, "-I")
+func includeDirsToFlags(dirs common.Paths) string {
+	return common.JoinWithPrefix(dirs.Strings(), "-I")
 }
 
-func includeFilesToFlags(dirs []string) string {
-	return common.JoinWithPrefix(dirs, "-include ")
+func includeFilesToFlags(files common.Paths) string {
+	return common.JoinWithPrefix(files.Strings(), "-include ")
 }
 
 func ldDirsToFlags(dirs []string) string {
diff --git a/cc/x86_64_device.go b/cc/x86_64_device.go
index ba190a0..b4f96b9 100644
--- a/cc/x86_64_device.go
+++ b/cc/x86_64_device.go
@@ -137,7 +137,7 @@
 
 	pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
 
-	pctx.StaticVariable("x86_64GccRoot",
+	pctx.SourcePathVariable("x86_64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
 
 	pctx.StaticVariable("x86_64GccTriple", "x86_64-linux-android")
diff --git a/cc/x86_darwin_host.go b/cc/x86_darwin_host.go
index 108bb85..9693de6 100644
--- a/cc/x86_darwin_host.go
+++ b/cc/x86_darwin_host.go
@@ -83,8 +83,8 @@
 	pctx.StaticVariable("macSdkRoot", "${macSdkPath}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk")
 
 	pctx.StaticVariable("darwinGccVersion", darwinGccVersion)
-	pctx.StaticVariable("darwinGccRoot",
-		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/host/i686-apple-darwin-${darwinGccVersion}")
+	pctx.SourcePathVariable("darwinGccRoot",
+		"prebuilts/gcc/${HostPrebuiltTag}/host/i686-apple-darwin-${darwinGccVersion}")
 
 	pctx.StaticVariable("darwinGccTriple", "i686-apple-darwin11")
 
diff --git a/cc/x86_device.go b/cc/x86_device.go
index 6dfbd6a..a44b293 100644
--- a/cc/x86_device.go
+++ b/cc/x86_device.go
@@ -139,7 +139,7 @@
 
 	pctx.StaticVariable("x86GccVersion", x86GccVersion)
 
-	pctx.StaticVariable("x86GccRoot",
+	pctx.SourcePathVariable("x86GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
 
 	pctx.StaticVariable("x86GccTriple", "x86_64-linux-android")
diff --git a/cc/x86_linux_host.go b/cc/x86_linux_host.go
index 98bceef..a3c50d1 100644
--- a/cc/x86_linux_host.go
+++ b/cc/x86_linux_host.go
@@ -108,8 +108,8 @@
 func init() {
 	pctx.StaticVariable("linuxGccVersion", linuxGccVersion)
 
-	pctx.StaticVariable("linuxGccRoot",
-		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc2.15-${linuxGccVersion}")
+	pctx.SourcePathVariable("linuxGccRoot",
+		"prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc2.15-${linuxGccVersion}")
 
 	pctx.StaticVariable("linuxGccTriple", "x86_64-linux")
 
diff --git a/cc/x86_windows_host.go b/cc/x86_windows_host.go
index 5f06bec..8556a64 100644
--- a/cc/x86_windows_host.go
+++ b/cc/x86_windows_host.go
@@ -68,8 +68,8 @@
 func init() {
 	pctx.StaticVariable("windowsGccVersion", windowsGccVersion)
 
-	pctx.StaticVariable("windowsGccRoot",
-		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-w64-mingw32-${windowsGccVersion}")
+	pctx.SourcePathVariable("windowsGccRoot",
+		"prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-w64-mingw32-${windowsGccVersion}")
 
 	pctx.StaticVariable("windowsGccTriple", "x86_64-w64-mingw32")
 
diff --git a/common/androidmk.go b/common/androidmk.go
index 5dd422d..06aa30c 100644
--- a/common/androidmk.go
+++ b/common/androidmk.go
@@ -37,11 +37,11 @@
 
 type AndroidMkData struct {
 	Class      string
-	OutputFile string
+	OutputFile OptionalPath
 
 	Custom func(w io.Writer, name, prefix string)
 
-	Extra func(name, prefix, outputFile string, arch Arch) []string
+	Extra func(name, prefix string, outputFile Path, arch Arch) []string
 }
 
 func AndroidMkSingleton() blueprint.Singleton {
@@ -55,7 +55,7 @@
 	hasBPDir := make(map[string]bool)
 	bpDirs := []string{}
 
-	ctx.SetNinjaBuildDir(pctx, filepath.Join(ctx.Config().(Config).BuildDir(), ".."))
+	ctx.SetNinjaBuildDir(pctx, filepath.Join(ctx.Config().(Config).buildDir, ".."))
 
 	ctx.VisitAllModules(func(module blueprint.Module) {
 		if _, ok := module.(AndroidModule); ok {
@@ -72,28 +72,13 @@
 
 	// Gather list of eligible Android modules for translation
 	androidMkModules := make(map[blueprint.Module]bool)
-	srcDir := ctx.Config().(Config).SrcDir()
-	intermediatesDir := filepath.Join(ctx.Config().(Config).IntermediatesDir(), "androidmk")
 	sort.Strings(bpDirs)
 	for _, bpDir := range bpDirs {
-		mkFile := filepath.Join(srcDir, bpDir, "Android.mk")
-
-		files, err := Glob(ctx, intermediatesDir, mkFile, nil)
-		if err != nil {
-			ctx.Errorf("glob: %s", err.Error())
-			continue
-		}
-
-		// Existing Android.mk file, use that instead
-		if len(files) > 0 {
-			for _, file := range files {
-				ctx.AddNinjaFileDeps(file)
+		mkFile := OptionalPathForSource(ctx, "androidmk", bpDir, "Android.mk")
+		if !mkFile.Valid() {
+			for _, mod := range dirModules[bpDir] {
+				androidMkModules[mod] = true
 			}
-			continue
-		}
-
-		for _, mod := range dirModules[bpDir] {
-			androidMkModules[mod] = true
 		}
 	}
 
@@ -110,16 +95,19 @@
 		}
 	}
 
-	transMk := filepath.Join(ctx.Config().(Config).BuildDir(), "Android.mk")
+	transMk := PathForOutput(ctx, "Android.mk")
+	if ctx.Failed() {
+		return
+	}
 
-	err := translateAndroidMk(ctx, transMk, androidMkModulesList)
+	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
 
 	ctx.Build(pctx, blueprint.BuildParams{
 		Rule:     blueprint.Phony,
-		Outputs:  []string{transMk},
+		Outputs:  []string{transMk.String()},
 		Optional: true,
 	})
 }
@@ -177,7 +165,7 @@
 
 	type archSrc struct {
 		arch  Arch
-		src   string
+		src   Path
 		extra []string
 	}
 
@@ -211,6 +199,10 @@
 			return
 		}
 
+		if !data.OutputFile.Valid() {
+			return
+		}
+
 		hC := hostClass{
 			host:     amod.HostOrDevice() == Host,
 			class:    data.Class,
@@ -219,7 +211,7 @@
 
 		src := archSrc{
 			arch: arch,
-			src:  data.OutputFile,
+			src:  data.OutputFile.Path(),
 		}
 
 		if data.Extra != nil {
@@ -242,7 +234,7 @@
 
 		printed := make(map[string]bool)
 		for _, src := range archSrcs {
-			io.WriteString(w, "LOCAL_SRC_FILES_"+src.arch.ArchType.String()+" := "+src.src+"\n")
+			io.WriteString(w, "LOCAL_SRC_FILES_"+src.arch.ArchType.String()+" := "+src.src.String()+"\n")
 
 			for _, extra := range src.extra {
 				if !printed[extra] {
diff --git a/common/config.go b/common/config.go
index c67023e..7f6ee65 100644
--- a/common/config.go
+++ b/common/config.go
@@ -20,6 +20,7 @@
 	"os"
 	"path/filepath"
 	"runtime"
+	"strings"
 	"sync"
 )
 
@@ -38,8 +39,6 @@
 
 type Config struct {
 	*config
-
-	dontCreateNinjaFile bool
 }
 
 // A config object represents the entire build configuration for Android.
@@ -142,8 +141,24 @@
 		},
 	}
 
+	// Sanity check the build and source directories. This won't catch strange
+	// configurations with symlinks, but at least checks the obvious cases.
+	absBuildDir, err := filepath.Abs(buildDir)
+	if err != nil {
+		return Config{}, err
+	}
+
+	absSrcDir, err := filepath.Abs(srcDir)
+	if err != nil {
+		return Config{}, err
+	}
+
+	if strings.HasPrefix(absSrcDir, absBuildDir) {
+		return Config{}, fmt.Errorf("Build dir must not contain source directory")
+	}
+
 	// Load any configurable options from the configuration file
-	err := loadConfig(config.config)
+	err = loadConfig(config.config)
 	if err != nil {
 		return Config{}, err
 	}
@@ -159,18 +174,6 @@
 	return config, nil
 }
 
-func (c *config) SrcDir() string {
-	return c.srcDir
-}
-
-func (c *config) BuildDir() string {
-	return c.buildDir
-}
-
-func (c *config) IntermediatesDir() string {
-	return filepath.Join(c.BuildDir(), ".intermediates")
-}
-
 func (c *config) RemoveAbandonedFiles() bool {
 	return false
 }
@@ -238,37 +241,7 @@
 	return false
 }
 
-// DeviceOut returns the path to out directory for device targets
-func (c *config) DeviceOut() string {
-	return filepath.Join(c.BuildDir(), "target/product", c.DeviceName())
-}
-
-// HostOut returns the path to out directory for host targets
-func (c *config) HostOut() string {
-	return filepath.Join(c.BuildDir(), "host", c.PrebuiltOS())
-}
-
-// HostBin returns the path to bin directory for host targets
-func (c *config) HostBin() string {
-	return filepath.Join(c.HostOut(), "bin")
-}
-
-// HostBinTool returns the path to a host tool in the bin directory for host targets
-func (c *config) HostBinTool(tool string) (string, error) {
-	return filepath.Join(c.HostBin(), tool), nil
-}
-
-// HostJavaDir returns the path to framework directory for host targets
-func (c *config) HostJavaDir() string {
-	return filepath.Join(c.HostOut(), "framework")
-}
-
-// HostJavaTool returns the path to a host tool in the frameworks directory for host targets
-func (c *config) HostJavaTool(tool string) (string, error) {
-	return filepath.Join(c.HostJavaDir(), tool), nil
-}
-
-func (c *config) ResourceOverlays() []string {
+func (c *config) ResourceOverlays() []SourcePath {
 	return nil
 }
 
@@ -296,10 +269,10 @@
 	return "nosdcard"
 }
 
-func (c *config) DefaultAppCertificateDir() string {
-	return filepath.Join(c.SrcDir(), "build/target/product/security")
+func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
+	return PathForSource(ctx, "build/target/product/security")
 }
 
-func (c *config) DefaultAppCertificate() string {
-	return filepath.Join(c.DefaultAppCertificateDir(), "testkey")
+func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
+	return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
 }
diff --git a/common/defs.go b/common/defs.go
index 7b2a706..9e185e4 100644
--- a/common/defs.go
+++ b/common/defs.go
@@ -20,13 +20,11 @@
 )
 
 var (
-	pctx = blueprint.NewPackageContext("android/soong/common")
+	pctx = NewPackageContext("android/soong/common")
 
 	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
 		Config.CpPreserveSymlinksFlags)
 
-	srcDir = pctx.VariableConfigMethod("srcDir", Config.SrcDir)
-
 	// A phony rule that is not the built-in Ninja phony rule.  The built-in
 	// phony rule has special behavior that is sometimes not desired.  See the
 	// Ninja docs for more details.
diff --git a/common/env.go b/common/env.go
index 8694c28..478fffc 100644
--- a/common/env.go
+++ b/common/env.go
@@ -15,8 +15,6 @@
 package common
 
 import (
-	"path/filepath"
-
 	"android/soong"
 	"android/soong/env"
 
@@ -43,12 +41,15 @@
 func (c *envSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
 	envDeps := ctx.Config().(Config).EnvDeps()
 
-	envFile := filepath.Join(ctx.Config().(Config).BuildDir(), ".soong.environment")
+	envFile := PathForOutput(ctx, ".soong.environment")
+	if ctx.Failed() {
+		return
+	}
 
-	err := env.WriteEnvFile(envFile, envDeps)
+	err := env.WriteEnvFile(envFile.String(), envDeps)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
 
-	ctx.AddNinjaFileDeps(envFile)
+	ctx.AddNinjaFileDeps(envFile.String())
 }
diff --git a/common/module.go b/common/module.go
index 113768a..36710c5 100644
--- a/common/module.go
+++ b/common/module.go
@@ -32,6 +32,19 @@
 	HostExecutable      = "host_executable"
 )
 
+type ModuleBuildParams struct {
+	Rule      blueprint.Rule
+	Output    WritablePath
+	Outputs   WritablePaths
+	Input     Path
+	Inputs    Paths
+	Implicit  Path
+	Implicits Paths
+	OrderOnly Paths
+	Default   bool
+	Args      map[string]string
+}
+
 type androidBaseContext interface {
 	Arch() Arch
 	HostOrDevice() HostOrDevice
@@ -52,12 +65,16 @@
 	blueprint.ModuleContext
 	androidBaseContext
 
-	ExpandSources(srcFiles, excludes []string) []string
-	Glob(outDir, globPattern string, excludes []string) []string
+	// Similar to Build, but takes Paths instead of []string,
+	// and performs more verification.
+	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
 
-	InstallFile(installPath, srcPath string, deps ...string) string
-	InstallFileName(installPath, name, srcPath string, deps ...string) string
-	CheckbuildFile(srcPath string)
+	ExpandSources(srcFiles, excludes []string) Paths
+	Glob(outDir, globPattern string, excludes []string) Paths
+
+	InstallFile(installPath string, srcPath Path, deps ...Path) Path
+	InstallFileName(installPath, name string, srcPath Path, deps ...Path) Path
+	CheckbuildFile(srcPath Path)
 }
 
 type AndroidModule interface {
@@ -196,8 +213,8 @@
 	archProperties          []*archProperties
 
 	noAddressSanitizer bool
-	installFiles       []string
-	checkbuildFiles    []string
+	installFiles       Paths
+	checkbuildFiles    Paths
 
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
@@ -254,9 +271,9 @@
 }
 
 func (a *AndroidModuleBase) computeInstallDeps(
-	ctx blueprint.ModuleContext) []string {
+	ctx blueprint.ModuleContext) Paths {
 
-	result := []string{}
+	result := Paths{}
 	ctx.VisitDepsDepthFirstIf(isFileInstaller,
 		func(m blueprint.Module) {
 			fileInstaller := m.(fileInstaller)
@@ -267,7 +284,7 @@
 	return result
 }
 
-func (a *AndroidModuleBase) filesToInstall() []string {
+func (a *AndroidModuleBase) filesToInstall() Paths {
 	return a.installFiles
 }
 
@@ -280,8 +297,8 @@
 		return
 	}
 
-	allInstalledFiles := []string{}
-	allCheckbuildFiles := []string{}
+	allInstalledFiles := Paths{}
+	allCheckbuildFiles := Paths{}
 	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
 		a := module.(AndroidModule).base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
@@ -295,7 +312,7 @@
 		ctx.Build(pctx, blueprint.BuildParams{
 			Rule:      blueprint.Phony,
 			Outputs:   []string{name},
-			Implicits: allInstalledFiles,
+			Implicits: allInstalledFiles.Strings(),
 		})
 		deps = append(deps, name)
 		a.installTarget = name
@@ -306,7 +323,7 @@
 		ctx.Build(pctx, blueprint.BuildParams{
 			Rule:      blueprint.Phony,
 			Outputs:   []string{name},
-			Implicits: allCheckbuildFiles,
+			Implicits: allCheckbuildFiles.Strings(),
 			Optional:  true,
 		})
 		deps = append(deps, name)
@@ -371,9 +388,9 @@
 type androidModuleContext struct {
 	blueprint.ModuleContext
 	androidBaseContextImpl
-	installDeps     []string
-	installFiles    []string
-	checkbuildFiles []string
+	installDeps     Paths
+	installFiles    Paths
+	checkbuildFiles Paths
 }
 
 func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params blueprint.BuildParams) {
@@ -381,6 +398,30 @@
 	a.ModuleContext.Build(pctx, params)
 }
 
+func (a *androidModuleContext) ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams) {
+	bparams := blueprint.BuildParams{
+		Rule:      params.Rule,
+		Outputs:   params.Outputs.Strings(),
+		Inputs:    params.Inputs.Strings(),
+		Implicits: params.Implicits.Strings(),
+		OrderOnly: params.OrderOnly.Strings(),
+		Args:      params.Args,
+		Optional:  !params.Default,
+	}
+
+	if params.Output != nil {
+		bparams.Outputs = append(bparams.Outputs, params.Output.String())
+	}
+	if params.Input != nil {
+		bparams.Inputs = append(bparams.Inputs, params.Input.String())
+	}
+	if params.Implicit != nil {
+		bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
+	}
+
+	a.ModuleContext.Build(pctx, bparams)
+}
+
 func (a *androidBaseContextImpl) Arch() Arch {
 	return a.arch
 }
@@ -413,31 +454,19 @@
 	return a.config
 }
 
-func (a *androidModuleContext) InstallFileName(installPath, name, srcPath string,
-	deps ...string) string {
+func (a *androidModuleContext) InstallFileName(installPath, name string, srcPath Path,
+	deps ...Path) Path {
 
-	config := a.AConfig()
-	var fullInstallPath string
-	if a.hod.Device() {
-		// TODO: replace unset with a device name once we have device targeting
-		fullInstallPath = filepath.Join(config.DeviceOut(), "system",
-			installPath, name)
-	} else {
-		// TODO
-		if a.ht == Windows {
-			fullInstallPath = filepath.Join(config.BuildDir(), "host", "windows-x86", installPath, name)
-		} else {
-			fullInstallPath = filepath.Join(config.HostOut(), installPath, name)
-		}
-	}
+	fullInstallPath := PathForModuleInstall(a, installPath, name)
 
 	deps = append(deps, a.installDeps...)
 
-	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+	a.ModuleBuild(pctx, ModuleBuildParams{
 		Rule:      Cp,
-		Outputs:   []string{fullInstallPath},
-		Inputs:    []string{srcPath},
-		OrderOnly: deps,
+		Output:    fullInstallPath,
+		Input:     srcPath,
+		OrderOnly: Paths(deps),
+		Default:   true,
 	})
 
 	a.installFiles = append(a.installFiles, fullInstallPath)
@@ -445,16 +474,16 @@
 	return fullInstallPath
 }
 
-func (a *androidModuleContext) InstallFile(installPath, srcPath string, deps ...string) string {
-	return a.InstallFileName(installPath, filepath.Base(srcPath), srcPath, deps...)
+func (a *androidModuleContext) InstallFile(installPath string, srcPath Path, deps ...Path) Path {
+	return a.InstallFileName(installPath, filepath.Base(srcPath.String()), srcPath, deps...)
 }
 
-func (a *androidModuleContext) CheckbuildFile(srcPath string) {
+func (a *androidModuleContext) CheckbuildFile(srcPath Path) {
 	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
 }
 
 type fileInstaller interface {
-	filesToInstall() []string
+	filesToInstall() Paths
 }
 
 func isFileInstaller(m blueprint.Module) bool {
@@ -476,8 +505,8 @@
 	return -1
 }
 
-func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) []string {
-	prefix := ModuleSrcDir(ctx)
+func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+	prefix := PathForModuleSrc(ctx).String()
 	for i, e := range excludes {
 		j := findStringInSlice(e, srcFiles)
 		if j != -1 {
@@ -487,32 +516,24 @@
 		excludes[i] = filepath.Join(prefix, e)
 	}
 
-	for i, srcFile := range srcFiles {
-		srcFiles[i] = filepath.Join(prefix, srcFile)
-	}
-
-	if !hasGlob(srcFiles) {
-		return srcFiles
-	}
-
-	globbedSrcFiles := make([]string, 0, len(srcFiles))
+	globbedSrcFiles := make(Paths, 0, len(srcFiles))
 	for _, s := range srcFiles {
 		if glob.IsGlob(s) {
-			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob("src_glob", s, excludes)...)
+			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob("src_glob", filepath.Join(prefix, s), excludes)...)
 		} else {
-			globbedSrcFiles = append(globbedSrcFiles, s)
+			globbedSrcFiles = append(globbedSrcFiles, PathForModuleSrc(ctx, s))
 		}
 	}
 
 	return globbedSrcFiles
 }
 
-func (ctx *androidModuleContext) Glob(outDir, globPattern string, excludes []string) []string {
-	ret, err := Glob(ctx, filepath.Join(ModuleOutDir(ctx), outDir), globPattern, excludes)
+func (ctx *androidModuleContext) Glob(outDir, globPattern string, excludes []string) Paths {
+	ret, err := Glob(ctx, PathForModuleOut(ctx, outDir).String(), globPattern, excludes)
 	if err != nil {
 		ctx.ModuleErrorf("glob: %s", err.Error())
 	}
-	return ret
+	return pathsForModuleSrcFromFullPath(ctx, ret)
 }
 
 func init() {
diff --git a/common/package_ctx.go b/common/package_ctx.go
new file mode 100644
index 0000000..cd18b65
--- /dev/null
+++ b/common/package_ctx.go
@@ -0,0 +1,127 @@
+// Copyright 2015 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 common
+
+import (
+	"fmt"
+
+	"github.com/google/blueprint"
+)
+
+// AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
+// some android-specific helper functions.
+type AndroidPackageContext struct {
+	blueprint.PackageContext
+}
+
+func NewPackageContext(pkgPath string) AndroidPackageContext {
+	return AndroidPackageContext{blueprint.NewPackageContext(pkgPath)}
+}
+
+// configErrorWrapper can be used with Path functions when a Context is not
+// available. A Config can be provided, and errors are stored as a list for
+// later retrieval.
+//
+// The most common use here will be with VariableFunc, where only a config is
+// provided, and an error should be returned.
+type configErrorWrapper struct {
+	config Config
+	errors []error
+}
+
+var _ PathContext = &configErrorWrapper{}
+var _ errorfContext = &configErrorWrapper{}
+
+func (e *configErrorWrapper) Config() interface{} {
+	return e.config
+}
+func (e *configErrorWrapper) Errorf(format string, args ...interface{}) {
+	e.errors = append(e.errors, fmt.Errorf(format, args...))
+}
+
+// SourcePathVariable returns a Variable whose value is the source directory
+// appended with the supplied path. It may only be called during a Go package's
+// initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) SourcePathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{config.(Config), []error{}}
+		p := safePathForSource(ctx, path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// HostBinVariable returns a Variable whose value is the path to a host tool
+// in the bin directory for host targets. It may only be called during a Go
+// package's initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{config.(Config), []error{}}
+		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "bin", path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// HostJavaToolVariable returns a Variable whose value is the path to a host
+// tool in the frameworks directory for host targets. It may only be called
+// during a Go package's initialization - either from the init() function or as
+// part of a package-scoped variable's initialization.
+func (p AndroidPackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{config.(Config), []error{}}
+		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// IntermediatesPathVariable returns a Variable whose value is the intermediate
+// directory appended with the supplied path. It may only be called during a Go
+// package's initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{config.(Config), []error{}}
+		p := PathForIntermediates(ctx, path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// PrefixedPathsForSourceVariable returns a Variable whose value is the
+// list of source paths prefixed with the supplied prefix. It may only be
+// called during a Go package's initialization - either from the init()
+// function or as part of a package-scoped variable's initialization.
+func (p AndroidPackageContext) PrefixedPathsForSourceVariable(name, prefix string, paths []string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{config.(Config), []error{}}
+		paths := PathsForSource(ctx, paths)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return JoinWithPrefix(paths.Strings(), prefix), nil
+	})
+}
diff --git a/common/paths.go b/common/paths.go
index d92dcf9..8a085ea 100644
--- a/common/paths.go
+++ b/common/paths.go
@@ -18,103 +18,571 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"reflect"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
-// ModuleOutDir returns the path to the module-specific output directory.
-func ModuleOutDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ctx.AConfig().IntermediatesDir(),
-		ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+// PathContext is the subset of a (Module|Singleton)Context required by the
+// Path methods.
+type PathContext interface {
+	Config() interface{}
 }
 
-// ModuleSrcDir returns the path of the directory that all source file paths are
-// specified relative to.
-func ModuleSrcDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ctx.AConfig().SrcDir(), ctx.ModuleDir())
+var _ PathContext = blueprint.SingletonContext(nil)
+var _ PathContext = blueprint.ModuleContext(nil)
+
+// errorfContext is the interface containing the Errorf method matching the
+// Errorf method in blueprint.SingletonContext.
+type errorfContext interface {
+	Errorf(format string, args ...interface{})
 }
 
-// ModuleBinDir returns the path to the module- and architecture-specific binary
-// output directory.
-func ModuleBinDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "bin")
+var _ errorfContext = blueprint.SingletonContext(nil)
+
+// moduleErrorf is the interface containing the ModuleErrorf method matching
+// the ModuleErrorf method in blueprint.ModuleContext.
+type moduleErrorf interface {
+	ModuleErrorf(format string, args ...interface{})
 }
 
-// ModuleLibDir returns the path to the module- and architecture-specific
-// library output directory.
-func ModuleLibDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "lib")
+var _ moduleErrorf = blueprint.ModuleContext(nil)
+
+// pathConfig returns the android Config interface associated to the context.
+// Panics if the context isn't affiliated with an android build.
+func pathConfig(ctx PathContext) Config {
+	if ret, ok := ctx.Config().(Config); ok {
+		return ret
+	}
+	panic("Paths may only be used on Soong builds")
 }
 
-// ModuleGenDir returns the module directory for generated files
-// path.
-func ModuleGenDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "gen")
-}
-
-// ModuleObjDir returns the module- and architecture-specific object directory
-// path.
-func ModuleObjDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "obj")
-}
-
-// ModuleGoPackageDir returns the module-specific package root directory path.
-// This directory is where the final package .a files are output and where
-// dependent modules search for this package via -I arguments.
-func ModuleGoPackageDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "pkg")
-}
-
-// ModuleIncludeDir returns the module-specific public include directory path.
-func ModuleIncludeDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "include")
-}
-
-// ModuleProtoDir returns the module-specific public proto include directory path.
-func ModuleProtoDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "proto")
-}
-
-func ModuleJSCompiledDir(ctx AndroidModuleContext) string {
-	return filepath.Join(ModuleOutDir(ctx), "js")
-}
-
-// CheckModuleSrcDirsExist logs an error on a property if any of the directories relative to the
-// Blueprints file don't exist.
-func CheckModuleSrcDirsExist(ctx AndroidModuleContext, dirs []string, prop string) {
-	for _, dir := range dirs {
-		fullDir := filepath.Join(ModuleSrcDir(ctx), dir)
-		if _, err := os.Stat(fullDir); err != nil {
-			if os.IsNotExist(err) {
-				ctx.PropertyErrorf(prop, "module source directory %q does not exist", dir)
-			} else {
-				ctx.PropertyErrorf(prop, "%s", err.Error())
-			}
-		}
+// reportPathError will register an error with the attached context. It
+// attempts ctx.ModuleErrorf for a better error message first, then falls
+// back to ctx.Errorf.
+func reportPathError(ctx PathContext, format string, args ...interface{}) {
+	if mctx, ok := ctx.(moduleErrorf); ok {
+		mctx.ModuleErrorf(format, args...)
+	} else if ectx, ok := ctx.(errorfContext); ok {
+		ectx.Errorf(format, args...)
+	} else {
+		panic(fmt.Sprintf(format, args...))
 	}
 }
 
-// CheckModuleSrcDirsExist logs an error on a property if any of the directories relative to the
-// top of the source tree don't exist.
-func CheckSrcDirsExist(ctx AndroidModuleContext, dirs []string, prop string) {
-	for _, dir := range dirs {
-		fullDir := filepath.Join(ctx.AConfig().SrcDir(), dir)
-		if _, err := os.Stat(fullDir); err != nil {
-			if os.IsNotExist(err) {
-				ctx.PropertyErrorf(prop, "top-level source directory %q does not exist", dir)
-			} else {
-				ctx.PropertyErrorf(prop, "%s", err.Error())
-			}
-		}
+type Path interface {
+	// Returns the path in string form
+	String() string
+
+	// Returns the current file extension of the path
+	Ext() string
+}
+
+// WritablePath is a type of path that can be used as an output for build rules.
+type WritablePath interface {
+	Path
+
+	writablePath()
+}
+
+type genPathProvider interface {
+	genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath
+}
+type objPathProvider interface {
+	objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath
+}
+type resPathProvider interface {
+	resPathWithName(ctx AndroidModuleContext, name string) ModuleResPath
+}
+
+// GenPathWithExt derives a new file path in ctx's generated sources directory
+// from the current path, but with the new extension.
+func GenPathWithExt(ctx AndroidModuleContext, p Path, ext string) ModuleGenPath {
+	if path, ok := p.(genPathProvider); ok {
+		return path.genPathWithExt(ctx, ext)
+	}
+	reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleGen(ctx)
+}
+
+// ObjPathWithExt derives a new file path in ctx's object directory from the
+// current path, but with the new extension.
+func ObjPathWithExt(ctx AndroidModuleContext, p Path, subdir, ext string) ModuleObjPath {
+	if path, ok := p.(objPathProvider); ok {
+		return path.objPathWithExt(ctx, subdir, ext)
+	}
+	reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleObj(ctx)
+}
+
+// ResPathWithName derives a new path in ctx's output resource directory, using
+// the current path to create the directory name, and the `name` argument for
+// the filename.
+func ResPathWithName(ctx AndroidModuleContext, p Path, name string) ModuleResPath {
+	if path, ok := p.(resPathProvider); ok {
+		return path.resPathWithName(ctx, name)
+	}
+	reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleRes(ctx)
+}
+
+// OptionalPath is a container that may or may not contain a valid Path.
+type OptionalPath struct {
+	valid bool
+	path  Path
+}
+
+// OptionalPathForPath returns an OptionalPath containing the path.
+func OptionalPathForPath(path Path) OptionalPath {
+	if path == nil {
+		return OptionalPath{}
+	}
+	return OptionalPath{valid: true, path: path}
+}
+
+// Valid returns whether there is a valid path
+func (p OptionalPath) Valid() bool {
+	return p.valid
+}
+
+// Path returns the Path embedded in this OptionalPath. You must be sure that
+// there is a valid path, since this method will panic if there is not.
+func (p OptionalPath) Path() Path {
+	if !p.valid {
+		panic("Requesting an invalid path")
+	}
+	return p.path
+}
+
+// String returns the string version of the Path, or "" if it isn't valid.
+func (p OptionalPath) String() string {
+	if p.valid {
+		return p.path.String()
+	} else {
+		return ""
 	}
 }
 
-// Returns a path relative to the top level source directory.  Panics if path is not inside the
-// top level source directory.
-func SrcDirRelPath(ctx AndroidModuleContext, path string) string {
-	srcDir := ctx.AConfig().SrcDir()
-	relPath, err := filepath.Rel(srcDir, path)
+// Paths is a slice of Path objects, with helpers to operate on the collection.
+type Paths []Path
+
+// PathsForSource returns Paths rooted from SrcDir
+func PathsForSource(ctx PathContext, paths []string) Paths {
+	ret := make(Paths, len(paths))
+	for i, path := range paths {
+		ret[i] = PathForSource(ctx, path)
+	}
+	return ret
+}
+
+// PathsForModuleSrc returns Paths rooted from the module's local source
+// directory
+func PathsForModuleSrc(ctx AndroidModuleContext, paths []string) Paths {
+	ret := make(Paths, len(paths))
+	for i, path := range paths {
+		ret[i] = PathForModuleSrc(ctx, path)
+	}
+	return ret
+}
+
+// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
+// source directory, but strip the local source directory from the beginning of
+// each string.
+func pathsForModuleSrcFromFullPath(ctx AndroidModuleContext, paths []string) Paths {
+	prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
+	ret := make(Paths, 0, len(paths))
+	for _, p := range paths {
+		path := filepath.Clean(p)
+		if !strings.HasPrefix(path, prefix) {
+			reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
+			continue
+		}
+		ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
+	}
+	return ret
+}
+
+// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
+// local source directory. If none are provided, use the default if it exists.
+func PathsWithOptionalDefaultForModuleSrc(ctx AndroidModuleContext, input []string, def string) Paths {
+	if len(input) > 0 {
+		return PathsForModuleSrc(ctx, input)
+	}
+	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
+	// is created, we're run again.
+	path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
+	return ctx.Glob("default", path, []string{})
+}
+
+// Strings returns the Paths in string form
+func (p Paths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
+// WritablePaths is a slice of WritablePaths, used for multiple outputs.
+type WritablePaths []WritablePath
+
+// Strings returns the string forms of the writable paths.
+func (p WritablePaths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
+type basePath struct {
+	path   string
+	config Config
+}
+
+func (p basePath) Ext() string {
+	return filepath.Ext(p.path)
+}
+
+// SourcePath is a Path representing a file path rooted from SrcDir
+type SourcePath struct {
+	basePath
+}
+
+var _ Path = SourcePath{}
+
+// safePathForSource is for paths that we expect are safe -- only for use by go
+// code that is embedding ninja variables in paths
+func safePathForSource(ctx PathContext, path string) SourcePath {
+	p := validateSafePath(ctx, path)
+	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(ret.String())
 	if err != nil {
-		panic(fmt.Errorf("%q is not inside %q: %s", path, srcDir, err.Error()))
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return ret
 	}
 
-	return relPath
+	return ret
+}
+
+// PathForSource returns a SourcePath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// doesn't escape the source dir, or is contained in the build dir. On error, it
+// will return a usable, but invalid SourcePath, and report a ModuleError.
+func PathForSource(ctx PathContext, paths ...string) SourcePath {
+	p := validatePath(ctx, paths...)
+	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(ret.String())
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return ret
+	}
+
+	if _, err = os.Stat(ret.String()); err != nil {
+		if os.IsNotExist(err) {
+			reportPathError(ctx, "source path %s does not exist", ret)
+		} else {
+			reportPathError(ctx, "%s: %s", ret, err.Error())
+		}
+	}
+	return ret
+}
+
+// OptionalPathForSource returns an OptionalPath with the SourcePath if the
+// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
+// so that the ninja file will be regenerated if the state of the path changes.
+func OptionalPathForSource(ctx blueprint.SingletonContext, intermediates string, paths ...string) OptionalPath {
+	p := validatePath(ctx, paths...)
+	path := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(path.String())
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return OptionalPath{}
+	}
+
+	// Use glob to produce proper dependencies, even though we only want
+	// a single file.
+	files, err := Glob(ctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
+	if err != nil {
+		reportPathError(ctx, "glob: %s", err.Error())
+		return OptionalPath{}
+	}
+
+	if len(files) == 0 {
+		return OptionalPath{}
+	}
+	return OptionalPathForPath(path)
+}
+
+func (p SourcePath) String() string {
+	return filepath.Join(p.config.srcDir, p.path)
+}
+
+// Join creates a new SourcePath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
+	path := validatePath(ctx, paths...)
+	return PathForSource(ctx, p.path, path)
+}
+
+// OverlayPath returns the overlay for `path' if it exists. This assumes that the
+// SourcePath is the path to a resource overlay directory.
+func (p SourcePath) OverlayPath(ctx AndroidModuleContext, path Path) OptionalPath {
+	var relDir string
+	if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
+		relDir = moduleSrcPath.sourcePath.path
+	} else if srcPath, ok := path.(SourcePath); ok {
+		relDir = srcPath.path
+	} else {
+		reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+		return OptionalPath{}
+	}
+	dir := filepath.Join(p.config.srcDir, p.path, relDir)
+	// Use Glob so that we are run again if the directory is added.
+	paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
+	if err != nil {
+		reportPathError(ctx, "glob: %s", err.Error())
+		return OptionalPath{}
+	}
+	if len(paths) == 0 {
+		return OptionalPath{}
+	}
+	relPath, err := filepath.Rel(p.config.srcDir, paths[0])
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	return OptionalPathForPath(PathForSource(ctx, relPath))
+}
+
+// OutputPath is a Path representing a file path rooted from the build directory
+type OutputPath struct {
+	basePath
+}
+
+var _ Path = OutputPath{}
+
+// PathForOutput returns an OutputPath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// does not escape the build dir. On error, it will return a usable, but invalid
+// OutputPath, and report a ModuleError.
+func PathForOutput(ctx PathContext, paths ...string) OutputPath {
+	path := validatePath(ctx, paths...)
+	return OutputPath{basePath{path, pathConfig(ctx)}}
+}
+
+func (p OutputPath) writablePath() {}
+
+func (p OutputPath) String() string {
+	return filepath.Join(p.config.buildDir, 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 {
+	path := validatePath(ctx, paths...)
+	return PathForOutput(ctx, p.path, path)
+}
+
+// PathForIntermediates returns an OutputPath representing the top-level
+// intermediates directory.
+func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
+	path := validatePath(ctx, paths...)
+	return PathForOutput(ctx, ".intermediates", path)
+}
+
+// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
+type ModuleSrcPath struct {
+	basePath
+	sourcePath SourcePath
+	moduleDir  string
+}
+
+var _ Path = ModuleSrcPath{}
+var _ genPathProvider = ModuleSrcPath{}
+var _ objPathProvider = ModuleSrcPath{}
+var _ resPathProvider = ModuleSrcPath{}
+
+// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
+// module's local source directory.
+func PathForModuleSrc(ctx AndroidModuleContext, paths ...string) ModuleSrcPath {
+	path := validatePath(ctx, paths...)
+	return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
+}
+
+// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
+// valid path if p is non-nil.
+func OptionalPathForModuleSrc(ctx AndroidModuleContext, p *string) OptionalPath {
+	if p == nil {
+		return OptionalPath{}
+	}
+	return OptionalPathForPath(PathForModuleSrc(ctx, *p))
+}
+
+func (p ModuleSrcPath) String() string {
+	return p.sourcePath.String()
+}
+
+func (p ModuleSrcPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
+	return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) resPathWithName(ctx AndroidModuleContext, name string) ModuleResPath {
+	// TODO: Use full directory if the new ctx is not the current ctx?
+	return PathForModuleRes(ctx, p.path, name)
+}
+
+// ModuleOutPath is a Path representing a module's output directory.
+type ModuleOutPath struct {
+	OutputPath
+}
+
+var _ Path = ModuleOutPath{}
+
+// PathForModuleOut returns a Path representing the paths... under the module's
+// output directory.
+func PathForModuleOut(ctx AndroidModuleContext, paths ...string) ModuleOutPath {
+	p := validatePath(ctx, paths...)
+	return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
+}
+
+// ModuleGenPath is a Path representing the 'gen' directory in a module's output
+// directory. Mainly used for generated sources.
+type ModuleGenPath struct {
+	ModuleOutPath
+	path string
+}
+
+var _ Path = ModuleGenPath{}
+var _ genPathProvider = ModuleGenPath{}
+var _ objPathProvider = ModuleGenPath{}
+
+// PathForModuleGen returns a Path representing the paths... under the module's
+// `gen' directory.
+func PathForModuleGen(ctx AndroidModuleContext, paths ...string) ModuleGenPath {
+	p := validatePath(ctx, paths...)
+	return ModuleGenPath{
+		PathForModuleOut(ctx, "gen", p),
+		p,
+	}
+}
+
+func (p ModuleGenPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
+	// TODO: make a different path for local vs remote generated files?
+	return PathForModuleGen(ctx, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleGenPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+// ModuleObjPath is a Path representing the 'obj' directory in a module's output
+// directory. Used for compiled objects.
+type ModuleObjPath struct {
+	ModuleOutPath
+}
+
+var _ Path = ModuleObjPath{}
+
+// PathForModuleObj returns a Path representing the paths... under the module's
+// 'obj' directory.
+func PathForModuleObj(ctx AndroidModuleContext, paths ...string) ModuleObjPath {
+	p := validatePath(ctx, paths...)
+	return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
+}
+
+// ModuleResPath is a a Path representing the 'res' directory in a module's
+// output directory.
+type ModuleResPath struct {
+	ModuleOutPath
+}
+
+var _ Path = ModuleResPath{}
+
+// PathForModuleRes returns a Path representing the paths... under the module's
+// 'res' directory.
+func PathForModuleRes(ctx AndroidModuleContext, paths ...string) ModuleResPath {
+	p := validatePath(ctx, paths...)
+	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
+}
+
+// PathForModuleInstall returns a Path representing the install path for the
+// module appended with paths...
+func PathForModuleInstall(ctx AndroidModuleContext, paths ...string) OutputPath {
+	var outPaths []string
+	if ctx.Device() {
+		outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), "system"}
+	} else {
+		outPaths = []string{"host", ctx.HostType().String() + "-x86"}
+	}
+	outPaths = append(outPaths, paths...)
+	return PathForOutput(ctx, outPaths...)
+}
+
+// validateSafePath validates a path that we trust (may contain ninja variables).
+// Ensures that it does not attempt to leave the containing directory.
+func validateSafePath(ctx PathContext, paths ...string) string {
+	// TODO: filepath.Join isn't necessarily correct with embedded ninja
+	// variables. '..' may remove the entire ninja variable, even if it
+	// will be expanded to multiple nested directories.
+	p := filepath.Join(paths...)
+	if p == ".." || strings.HasPrefix(p, "../") || strings.HasPrefix(p, "/") {
+		reportPathError(ctx, "Path is outside directory: %s", p)
+		return ""
+	}
+	return p
+}
+
+// validatePath validates that a path does not include ninja variables, and does
+// not attempt to leave the containing directory.
+func validatePath(ctx PathContext, paths ...string) string {
+	for _, path := range paths {
+		if strings.Contains(path, "$") {
+			reportPathError(ctx, "Path contains invalid character($): %s", path)
+			return ""
+		}
+	}
+	return validateSafePath(ctx, paths...)
 }
diff --git a/common/paths_test.go b/common/paths_test.go
new file mode 100644
index 0000000..16ede0d
--- /dev/null
+++ b/common/paths_test.go
@@ -0,0 +1,167 @@
+// Copyright 2015 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 common
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+type strsTestCase struct {
+	in  []string
+	out string
+	err []error
+}
+
+var commonValidatePathTestCases = []strsTestCase{
+	{
+		in:  []string{""},
+		out: "",
+	},
+	{
+		in:  []string{"a/b"},
+		out: "a/b",
+	},
+	{
+		in:  []string{"a/b", "c"},
+		out: "a/b/c",
+	},
+	{
+		in:  []string{"a/.."},
+		out: ".",
+	},
+	{
+		in:  []string{"."},
+		out: ".",
+	},
+	{
+		in:  []string{".."},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ..")},
+	},
+	{
+		in:  []string{"../a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../a")},
+	},
+	{
+		in:  []string{"b/../../a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../a")},
+	},
+	{
+		in:  []string{"/a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: /a")},
+	},
+}
+
+var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+	{
+		in:  []string{"$host/../$a"},
+		out: "$a",
+	},
+}...)
+
+var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+	{
+		in:  []string{"$host/../$a"},
+		out: "",
+		err: []error{errors.New("Path contains invalid character($): $host/../$a")},
+	},
+	{
+		in:  []string{"$host/.."},
+		out: "",
+		err: []error{errors.New("Path contains invalid character($): $host/..")},
+	},
+}...)
+
+func TestValidateSafePath(t *testing.T) {
+	for _, testCase := range validateSafePathTestCases {
+		ctx := &configErrorWrapper{}
+		out := validateSafePath(ctx, testCase.in...)
+		check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+	}
+}
+
+func TestValidatePath(t *testing.T) {
+	for _, testCase := range validatePathTestCases {
+		ctx := &configErrorWrapper{}
+		out := validatePath(ctx, testCase.in...)
+		check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+	}
+}
+
+func TestOptionalPath(t *testing.T) {
+	var path OptionalPath
+	checkInvalidOptionalPath(t, path)
+
+	path = OptionalPathForPath(nil)
+	checkInvalidOptionalPath(t, path)
+}
+
+func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
+	if path.Valid() {
+		t.Errorf("Uninitialized OptionalPath should not be valid")
+	}
+	if path.String() != "" {
+		t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
+	}
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath")
+		}
+	}()
+	path.Path()
+}
+
+func check(t *testing.T, testType, testString string,
+	got interface{}, err []error,
+	expected interface{}, expectedErr []error) {
+
+	printedTestCase := false
+	e := func(s string, expected, got interface{}) {
+		if !printedTestCase {
+			t.Errorf("test case %s: %s", testType, testString)
+			printedTestCase = true
+		}
+		t.Errorf("incorrect %s", s)
+		t.Errorf("  expected: %s", p(expected))
+		t.Errorf("       got: %s", p(got))
+	}
+
+	if !reflect.DeepEqual(expectedErr, err) {
+		e("errors:", expectedErr, err)
+	}
+
+	if !reflect.DeepEqual(expected, got) {
+		e("output:", expected, got)
+	}
+}
+
+func p(in interface{}) string {
+	if v, ok := in.([]interface{}); ok {
+		s := make([]string, len(v))
+		for i := range v {
+			s[i] = fmt.Sprintf("%#v", v[i])
+		}
+		return "[" + strings.Join(s, ", ") + "]"
+	} else {
+		return fmt.Sprintf("%#v", in)
+	}
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 76b4f16..b554357 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -15,10 +15,7 @@
 package genrule
 
 import (
-	"path/filepath"
-
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 
 	"android/soong"
 	"android/soong/common"
@@ -32,20 +29,20 @@
 }
 
 var (
-	pctx = blueprint.NewPackageContext("android/soong/genrule")
+	pctx = common.NewPackageContext("android/soong/genrule")
 )
 
 func init() {
-	pctx.VariableConfigMethod("srcDir", common.Config.SrcDir)
-	pctx.VariableConfigMethod("hostBin", common.Config.HostBin)
+	pctx.SourcePathVariable("srcDir", "")
+	pctx.HostBinToolVariable("hostBin", "")
 }
 
 type SourceFileGenerator interface {
-	GeneratedSourceFiles() []string
+	GeneratedSourceFiles() common.Paths
 }
 
 type HostToolProvider interface {
-	HostToolPath() string
+	HostToolPath() common.OptionalPath
 }
 
 type generatorProperties struct {
@@ -68,20 +65,20 @@
 
 	tasks taskFunc
 
-	deps []string
+	deps common.Paths
 	rule blueprint.Rule
 
-	outputFiles []string
+	outputFiles common.Paths
 }
 
 type taskFunc func(ctx common.AndroidModuleContext) []generateTask
 
 type generateTask struct {
-	in  []string
-	out string
+	in  common.Paths
+	out common.ModuleGenPath
 }
 
-func (g *generator) GeneratedSourceFiles() []string {
+func (g *generator) GeneratedSourceFiles() common.Paths {
 	return g.outputFiles
 }
 
@@ -104,8 +101,8 @@
 	ctx.VisitDirectDeps(func(module blueprint.Module) {
 		if t, ok := module.(HostToolProvider); ok {
 			p := t.HostToolPath()
-			if p != "" {
-				g.deps = append(g.deps, p)
+			if p.Valid() {
+				g.deps = append(g.deps, p.Path())
 			} else {
 				ctx.ModuleErrorf("host tool %q missing output file", ctx.OtherModuleName(module))
 			}
@@ -120,12 +117,11 @@
 }
 
 func (g *generator) generateSourceFile(ctx common.AndroidModuleContext, task generateTask) {
-
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.ModuleBuild(pctx, common.ModuleBuildParams{
 		Rule:      g.rule,
+		Output:    task.out,
 		Inputs:    task.in,
 		Implicits: g.deps,
-		Outputs:   []string{task.out},
 	})
 
 	g.outputFiles = append(g.outputFiles, task.out)
@@ -148,9 +144,10 @@
 		srcFiles := ctx.ExpandSources(properties.Srcs, nil)
 		tasks := make([]generateTask, 0, len(srcFiles))
 		for _, in := range srcFiles {
-			out := pathtools.ReplaceExtension(in, properties.Output_extension)
-			out = filepath.Join(common.ModuleGenDir(ctx), out)
-			tasks = append(tasks, generateTask{[]string{in}, out})
+			tasks = append(tasks, generateTask{
+				in:  common.Paths{in},
+				out: common.GenPathWithExt(ctx, in, properties.Output_extension),
+			})
 		}
 		return tasks
 	}
@@ -173,7 +170,7 @@
 		return []generateTask{
 			{
 				in:  ctx.ExpandSources(properties.Srcs, nil),
-				out: filepath.Join(common.ModuleGenDir(ctx), properties.Out),
+				out: properties.Out,
 			},
 		}
 	}
@@ -186,5 +183,5 @@
 	Srcs []string
 
 	// name of the output file that will be generated
-	Out string
+	Out common.ModuleGenPath
 }
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})
 		}
 	}