Add support for building NDK modules.

Change-Id: I2c5ede530e40a635e26ae45950580ef450e7dcc6
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index b79e821..fa25a9f 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -17,13 +17,15 @@
 )
 
 var stringProperties = map[string]string{
-	"LOCAL_MODULE":        "name",
-	"LOCAL_MODULE_STEM":   "stem",
-	"LOCAL_MODULE_CLASS":  "class",
-	"LOCAL_CXX_STL":       "stl",
-	"LOCAL_STRIP_MODULE":  "strip",
-	"LOCAL_MULTILIB":      "compile_multilib",
-	"LOCAL_ARM_MODE_HACK": "instruction_set",
+	"LOCAL_MODULE":          "name",
+	"LOCAL_MODULE_STEM":     "stem",
+	"LOCAL_MODULE_CLASS":    "class",
+	"LOCAL_CXX_STL":         "stl",
+	"LOCAL_STRIP_MODULE":    "strip",
+	"LOCAL_MULTILIB":        "compile_multilib",
+	"LOCAL_ARM_MODE_HACK":   "instruction_set",
+	"LOCAL_SDK_VERSION":     "sdk_version",
+	"LOCAL_NDK_STL_VARIANT": "stl",
 }
 
 var listProperties = map[string]string{
diff --git a/cc/arm64_device.go b/cc/arm64_device.go
index ac0d94a..309d01d 100644
--- a/cc/arm64_device.go
+++ b/cc/arm64_device.go
@@ -88,6 +88,10 @@
 
 var toolchainArm64Singleton = &toolchainArm64{}
 
+func (t *toolchainArm64) Name() string {
+	return "arm64"
+}
+
 func (t *toolchainArm64) GccRoot() string {
 	return "${arm64GccRoot}"
 }
@@ -96,6 +100,10 @@
 	return "${arm64GccTriple}"
 }
 
+func (t *toolchainArm64) GccVersion() string {
+	return "${arm64GccVersion}"
+}
+
 func (t *toolchainArm64) Cflags() string {
 	return "${arm64Cflags} ${arm64IncludeFlags}"
 }
diff --git a/cc/arm_device.go b/cc/arm_device.go
index e736509..bc59ab8 100644
--- a/cc/arm_device.go
+++ b/cc/arm_device.go
@@ -232,6 +232,10 @@
 	cflags, ldflags, clangCflags string
 }
 
+func (t *toolchainArm) Name() string {
+	return "arm"
+}
+
 func (t *toolchainArm) GccRoot() string {
 	return "${armGccRoot}"
 }
@@ -240,6 +244,10 @@
 	return "${armGccTriple}"
 }
 
+func (t *toolchainArm) GccVersion() string {
+	return "${armGccVersion}"
+}
+
 func (t *toolchainArm) Cflags() string {
 	return t.cflags
 }
diff --git a/cc/cc.go b/cc/cc.go
index 2c729fe..2583c86 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -146,7 +146,7 @@
 
 	// export_include_dirs: list of directories relative to the Blueprints file that will
 	// be added to the include path using -I for any module that links against this module
-	Export_include_dirs []string
+	Export_include_dirs []string `android:"arch_variant"`
 
 	// clang_cflags: list of module-specific flags that will be used for C and C++ compiles when
 	// compiling with clang
@@ -350,6 +350,23 @@
 	return factory(arch.ArchVariant, arch.CpuVariant)
 }
 
+func addNdkStlDepNames(ctx common.AndroidBaseContext, stl string, depNames CCDeps) CCDeps {
+	if stl == "ndk_system" {
+		// TODO: Make a system STL prebuilt for the NDK.
+		// The system STL doesn't have a prebuilt (it uses the system's libstdc++), but it does have
+		// its own includes. The includes are handled in ccBase.Flags().
+		return depNames
+	}
+
+	if strings.HasSuffix(stl, "_static") {
+		depNames.StaticLibs = append(depNames.StaticLibs, stl)
+	} else {
+		depNames.SharedLibs = append(depNames.SharedLibs, stl)
+	}
+
+	return depNames
+}
+
 func (c *ccBase) DepNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
 	depNames.WholeStaticLibs = append(depNames.WholeStaticLibs, c.properties.Whole_static_libs...)
 	depNames.StaticLibs = append(depNames.StaticLibs, c.properties.Static_libs...)
@@ -369,6 +386,14 @@
 		depNames.SharedLibs = append(depNames.SharedLibs, "libstdc++", "libstlport")
 	case "stlport_static":
 		depNames.StaticLibs = append(depNames.StaticLibs, "libstdc++", "libstlport_static")
+	case "":
+		// None or error.
+	default:
+		if !strings.HasPrefix(stl, "ndk_") {
+			panic("unexpected case")
+		}
+
+		depNames = addNdkStlDepNames(ctx, stl, depNames)
 	}
 
 	return depNames
@@ -533,6 +558,14 @@
 }
 
 func (c *ccBase) stl(ctx common.AndroidBaseContext) string {
+	if c.properties.Sdk_version != "" {
+		stl := c.properties.Stl
+		if stl == "" {
+			return "ndk_system"
+		}
+		return "ndk_lib" + stl
+	}
+
 	switch c.properties.Stl {
 	case "libc++", "libc++_static",
 		"stlport", "stlport_static",
@@ -542,8 +575,6 @@
 		return ""
 	case "":
 		return "libc++" // TODO: mingw needs libstdc++
-	case "ndk":
-		panic("TODO: stl: ndk")
 	default:
 		ctx.ModuleErrorf("stl: %q is not a supported STL", c.properties.Stl)
 		return ""
@@ -572,8 +603,6 @@
 				"${SrcDir}/bionic/libstdc++/include",
 				"${SrcDir}/bionic")
 		}
-	case "ndk":
-		panic("TODO")
 	case "libstdc++":
 		// Using bionic's basic libstdc++. Not actually an STL. Only around until the
 		// tree is in good enough shape to not need it.
@@ -581,14 +610,19 @@
 		if ctx.Device() {
 			flags.IncludeDirs = append(flags.IncludeDirs, "${SrcDir}/bionic/libstdc++/include")
 		}
+	case "ndk_system":
+		ndkSrcRoot := ctx.Config().(Config).SrcDir() + "/prebuilts/ndk/current/sources/"
+		flags.IncludeDirs = append(flags.IncludeDirs, ndkSrcRoot + "cxx-stl/system/include")
+	case "ndk_c++_shared", "ndk_c++_static":
+		// TODO(danalbert): This really shouldn't be here...
+		flags.CppFlags = append(flags.CppFlags, "-std=c++11")
 	case "":
+		// None or error.
 		if ctx.Host() {
 			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
 			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
 			flags.LdLibs = append(flags.LdLibs, "-lc", "-lm")
 		}
-	default:
-		panic(fmt.Errorf("Unknown stl: %q", stl))
 	}
 
 	return flags
@@ -766,6 +800,17 @@
 
 		if ctx.Host() {
 			return []string{}
+		} else if c.properties.Sdk_version != "" {
+			version := c.properties.Sdk_version
+			libs := []string{
+				"ndk_libc." + version,
+				"ndk_libm." + version,
+			}
+
+			if c.properties.Sdk_version != "" && c.stl(ctx) == "ndk_system" {
+				libs = append([]string{"libstdc++"}, libs...)
+			}
+			return libs
 		} else {
 			return []string{"libc", "libm"}
 		}
@@ -1295,6 +1340,131 @@
 	// Toolchain libraries do not get installed.
 }
 
+// NDK prebuilt libraries.
+//
+// These differ from regular prebuilts in that they aren't stripped and usually aren't installed
+// 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.Config().(Config).SrcDir(), version, toolchain.Name())
+}
+
+type ndkPrebuiltLibrary struct {
+	CCLibrary
+}
+
+func (*ndkPrebuiltLibrary) AndroidDynamicDependencies(
+	ctx common.AndroidDynamicDependerModuleContext) []string {
+
+	// NDK libraries can't have any dependencies
+	return nil
+}
+
+func (*ndkPrebuiltLibrary) DepNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
+	// NDK libraries can't have any dependencies
+	return CCDeps{}
+}
+
+func NdkPrebuiltLibraryFactory() (blueprint.Module, []interface{}) {
+	module := &ndkPrebuiltLibrary{}
+	module.LibraryProperties.BuildShared = true
+	return NewCCLibrary(&module.CCLibrary, module, common.DeviceSupported)
+}
+
+func (c *ndkPrebuiltLibrary) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
+	deps CCDeps, objFiles []string) {
+	// 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")
+	}
+
+	c.exportIncludeDirs = pathtools.PrefixPaths(c.properties.Export_include_dirs,
+		common.ModuleSrcDir(ctx))
+
+	// NDK prebuilt libraries are named like: ndk_LIBNAME.SDK_VERSION.
+	// We want to translate to just LIBNAME.
+	libName := strings.Split(strings.TrimPrefix(ctx.ModuleName(), "ndk_"), ".")[0]
+	libDir := getNdkLibDir(ctx, flags.Toolchain, c.properties.Sdk_version)
+	c.out = filepath.Join(libDir, libName + sharedLibraryExtension)
+}
+
+func (c *ndkPrebuiltLibrary) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
+	// Toolchain libraries do not get installed.
+}
+
+// The NDK STLs are slightly different from the prebuilt system libraries:
+//     * Are not specific to each platform version.
+//     * The libraries are not in a predictable location for each STL.
+
+type ndkPrebuiltStl struct {
+	ndkPrebuiltLibrary
+}
+
+type ndkPrebuiltStaticStl struct {
+	ndkPrebuiltStl
+}
+
+type ndkPrebuiltSharedStl struct {
+	ndkPrebuiltStl
+}
+
+func NdkPrebuiltSharedStlFactory() (blueprint.Module, []interface{}) {
+	module := &ndkPrebuiltSharedStl{}
+	module.LibraryProperties.BuildShared = true
+	return NewCCLibrary(&module.CCLibrary, module, common.DeviceSupported)
+}
+
+func NdkPrebuiltStaticStlFactory() (blueprint.Module, []interface{}) {
+	module := &ndkPrebuiltStaticStl{}
+	module.LibraryProperties.BuildStatic = true
+	return NewCCLibrary(&module.CCLibrary, module, common.DeviceSupported)
+}
+
+func getNdkStlLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, stl string) string {
+	gccVersion := toolchain.GccVersion()
+	var libDir string
+	switch stl {
+	case "libstlport":
+		libDir = "cxx-stl/stlport/libs"
+	case "libc++":
+		libDir = "cxx-stl/llvm-libc++/libs"
+	case "libgnustl":
+		libDir = fmt.Sprintf("cxx-stl/gnu-libstdc++/%s/libs", gccVersion)
+	}
+
+	if libDir != "" {
+		ndkSrcRoot := ctx.Config().(Config).SrcDir() + "/prebuilts/ndk/current/sources"
+		return fmt.Sprintf("%s/%s/%s", ndkSrcRoot, libDir, ctx.Arch().Abi)
+	}
+
+	ctx.ModuleErrorf("Unknown NDK STL: %s", stl)
+	return ""
+}
+
+func (c *ndkPrebuiltStl) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
+	deps CCDeps, objFiles []string) {
+	// 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")
+	}
+
+	c.exportIncludeDirs = pathtools.PrefixPaths(c.properties.Export_include_dirs,
+		common.ModuleSrcDir(ctx))
+
+	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
+	libExt := sharedLibraryExtension
+	if c.LibraryProperties.BuildStatic {
+		libExt = staticLibraryExtension
+	}
+
+	stlName := strings.TrimSuffix(libName, "_shared")
+	stlName = strings.TrimSuffix(stlName, "_static")
+	libDir := getNdkStlLibDir(ctx, flags.Toolchain, stlName)
+	c.out = libDir + "/" + libName + libExt
+}
+
 func LinkageMutator(mctx blueprint.EarlyMutatorContext) {
 	if c, ok := mctx.Module().(ccLibraryInterface); ok {
 		var modules []blueprint.Module
diff --git a/cc/toolchain.go b/cc/toolchain.go
index e4d23bd..d188845 100644
--- a/cc/toolchain.go
+++ b/cc/toolchain.go
@@ -34,8 +34,11 @@
 }
 
 type Toolchain interface {
+	Name() string
+
 	GccRoot() string
 	GccTriple() string
+	GccVersion() string
 	Cflags() string
 	Cppflags() string
 	Ldflags() string
diff --git a/cc/x86_linux_host.go b/cc/x86_linux_host.go
index 795620b..94b4a72 100644
--- a/cc/x86_linux_host.go
+++ b/cc/x86_linux_host.go
@@ -148,6 +148,14 @@
 	toolchainLinux
 }
 
+func (t *toolchainLinuxX86) Name() string {
+	return "x86"
+}
+
+func (t *toolchainLinuxX8664) Name() string {
+	return "x86_64"
+}
+
 func (t *toolchainLinux) GccRoot() string {
 	return "${linuxGccRoot}"
 }
@@ -156,6 +164,10 @@
 	return "${linuxGccTriple}"
 }
 
+func (t *toolchainLinux) GccVersion() string {
+	return "${linuxGccVersion}"
+}
+
 func (t *toolchainLinuxX86) Cflags() string {
 	return "${linuxCflags} ${linuxX86Cflags}"
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index cb579e9..c1f8243 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -47,6 +47,9 @@
 	ctx.RegisterModuleType("cc_test", cc.CCTestFactory)
 
 	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	ctx.RegisterModuleType("ndk_prebuilt_library", cc.NdkPrebuiltLibraryFactory)
+	ctx.RegisterModuleType("ndk_prebuilt_static_stl", cc.NdkPrebuiltStaticStlFactory)
+	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
 
 	ctx.RegisterModuleType("cc_library_host_static", cc.CCLibraryHostStaticFactory)
 	ctx.RegisterModuleType("cc_library_host_shared", cc.CCLibraryHostSharedFactory)
diff --git a/common/arch.go b/common/arch.go
index a43aa72..02212ad 100644
--- a/common/arch.go
+++ b/common/arch.go
@@ -117,6 +117,7 @@
 	ArchType     ArchType
 	ArchVariant  string
 	CpuVariant   string
+	Abi          string
 }
 
 func (a Arch) String() string {
@@ -234,12 +235,14 @@
 		ArchType:     Arm,
 		ArchVariant:  "armv7-a-neon",
 		CpuVariant:   "cortex-a15",
+		Abi:          "armeabi-v7a",
 	}
 	arm64Arch = Arch{
 		HostOrDevice: Device,
 		ArchType:     Arm64,
 		ArchVariant:  "armv8-a",
 		CpuVariant:   "denver",
+		Abi:          "arm64-v8a",
 	}
 	hostArch = Arch{
 		HostOrDevice: Host,
diff --git a/root.bp b/root.bp
index 247c96c..760e5dc 100644
--- a/root.bp
+++ b/root.bp
@@ -5,5 +5,6 @@
     "bionic/*",
     "external/*",
     "libnativehelper",
+    "prebuilts/ndk",
     "system/core/*",
 ]