Merge "Improve BUILDDIR handling with symlinks"
diff --git a/Android.bp b/Android.bp
index b6f6a63..b4928f8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -124,6 +124,7 @@
         "cc/cc.go",
         "cc/clang.go",
         "cc/gen.go",
+        "cc/stl.go",
         "cc/toolchain.go",
         "cc/util.go",
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 08cdbdc..aa5c4dc 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -22,16 +22,50 @@
 	"android/soong/common"
 )
 
-func (c *CCLibrary) AndroidMk() (ret common.AndroidMkData, err error) {
-	if c.static() {
+func (c *Module) AndroidMk() (ret common.AndroidMkData, err error) {
+	ret.OutputFile = c.outputFile
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile common.Path) (err error) {
+		if len(c.deps.SharedLibs) > 0 {
+			fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(c.deps.SharedLibs, " "))
+		}
+		return nil
+	})
+
+	callSubAndroidMk := func(obj interface{}) {
+		if obj != nil {
+			if androidmk, ok := obj.(interface {
+				AndroidMk(*common.AndroidMkData)
+			}); ok {
+				androidmk.AndroidMk(&ret)
+			}
+		}
+	}
+
+	for _, feature := range c.features {
+		callSubAndroidMk(feature)
+	}
+
+	callSubAndroidMk(c.compiler)
+	callSubAndroidMk(c.linker)
+	callSubAndroidMk(c.installer)
+
+	return ret, nil
+}
+
+func (library *baseLinker) AndroidMk(ret *common.AndroidMkData) {
+	if library.static() {
 		ret.Class = "STATIC_LIBRARIES"
 	} else {
 		ret.Class = "SHARED_LIBRARIES"
 	}
-	ret.OutputFile = c.outputFile()
-	ret.Extra = func(w io.Writer, outputFile common.Path) error {
-		exportedIncludes := []string{}
-		for _, flag := range c.exportedFlags() {
+}
+
+func (library *libraryLinker) AndroidMk(ret *common.AndroidMkData) {
+	library.baseLinker.AndroidMk(ret)
+
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile common.Path) error {
+		exportedIncludes := library.exportedFlags()
+		for _, flag := range library.exportedFlags() {
 			if flag != "" {
 				exportedIncludes = append(exportedIncludes, strings.TrimPrefix(flag, "-I"))
 			}
@@ -40,49 +74,36 @@
 			fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DIRS :=", strings.Join(exportedIncludes, " "))
 		}
 
-		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX :=", outputFile.Ext())
-		if len(c.savedDepNames.SharedLibs) > 0 {
-			fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES :=", strings.Join(c.savedDepNames.SharedLibs, " "))
-		}
-
-		if c.Properties.Relative_install_path != "" {
-			fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", c.Properties.Relative_install_path)
-		}
+		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
 
 		// These are already included in LOCAL_SHARED_LIBRARIES
 		fmt.Fprintln(w, "LOCAL_CXX_STL := none")
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
 
 		return nil
-	}
-	return
+	})
 }
 
-func (c *ccObject) AndroidMk() (ret common.AndroidMkData, err error) {
-	ret.OutputFile = c.outputFile()
+func (object *objectLinker) AndroidMk(ret *common.AndroidMkData) {
 	ret.Custom = func(w io.Writer, name, prefix string) error {
-		out := c.outputFile().Path()
+		out := ret.OutputFile.Path()
 
 		fmt.Fprintln(w, "\n$("+prefix+"OUT_INTERMEDIATE_LIBRARIES)/"+name+objectExtension+":", out.String(), "| $(ACP)")
 		fmt.Fprintln(w, "\t$(copy-file-to-target)")
 
 		return nil
 	}
-	return
 }
 
-func (c *CCBinary) AndroidMk() (ret common.AndroidMkData, err error) {
+func (binary *binaryLinker) AndroidMk(ret *common.AndroidMkData) {
 	ret.Class = "EXECUTABLES"
-	ret.Extra = func(w io.Writer, outputFile common.Path) error {
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile common.Path) error {
 		fmt.Fprintln(w, "LOCAL_CXX_STL := none")
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
-		fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES :=", strings.Join(c.savedDepNames.SharedLibs, " "))
-		if c.Properties.Relative_install_path != "" {
-			fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", c.Properties.Relative_install_path)
-		}
-
 		return nil
-	}
-	ret.OutputFile = c.outputFile()
-	return
+	})
+}
+
+func (test *testLinker) AndroidMk(ret *common.AndroidMkData) {
+	ret.Disabled = true
 }
diff --git a/cc/cc.go b/cc/cc.go
index 33fc01e..d20a197 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -32,26 +32,26 @@
 )
 
 func init() {
-	soong.RegisterModuleType("cc_library_static", CCLibraryStaticFactory)
-	soong.RegisterModuleType("cc_library_shared", CCLibrarySharedFactory)
-	soong.RegisterModuleType("cc_library", CCLibraryFactory)
-	soong.RegisterModuleType("cc_object", CCObjectFactory)
-	soong.RegisterModuleType("cc_binary", CCBinaryFactory)
-	soong.RegisterModuleType("cc_test", CCTestFactory)
-	soong.RegisterModuleType("cc_benchmark", CCBenchmarkFactory)
-	soong.RegisterModuleType("cc_defaults", CCDefaultsFactory)
+	soong.RegisterModuleType("cc_library_static", libraryStaticFactory)
+	soong.RegisterModuleType("cc_library_shared", librarySharedFactory)
+	soong.RegisterModuleType("cc_library", libraryFactory)
+	soong.RegisterModuleType("cc_object", objectFactory)
+	soong.RegisterModuleType("cc_binary", binaryFactory)
+	soong.RegisterModuleType("cc_test", testFactory)
+	soong.RegisterModuleType("cc_benchmark", benchmarkFactory)
+	soong.RegisterModuleType("cc_defaults", defaultsFactory)
 
-	soong.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
-	soong.RegisterModuleType("ndk_prebuilt_library", NdkPrebuiltLibraryFactory)
-	soong.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
-	soong.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
-	soong.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
+	soong.RegisterModuleType("toolchain_library", toolchainLibraryFactory)
+	soong.RegisterModuleType("ndk_prebuilt_library", ndkPrebuiltLibraryFactory)
+	soong.RegisterModuleType("ndk_prebuilt_object", ndkPrebuiltObjectFactory)
+	soong.RegisterModuleType("ndk_prebuilt_static_stl", ndkPrebuiltStaticStlFactory)
+	soong.RegisterModuleType("ndk_prebuilt_shared_stl", ndkPrebuiltSharedStlFactory)
 
-	soong.RegisterModuleType("cc_library_host_static", CCLibraryHostStaticFactory)
-	soong.RegisterModuleType("cc_library_host_shared", CCLibraryHostSharedFactory)
-	soong.RegisterModuleType("cc_binary_host", CCBinaryHostFactory)
-	soong.RegisterModuleType("cc_test_host", CCTestHostFactory)
-	soong.RegisterModuleType("cc_benchmark_host", CCBenchmarkHostFactory)
+	soong.RegisterModuleType("cc_library_host_static", libraryHostStaticFactory)
+	soong.RegisterModuleType("cc_library_host_shared", libraryHostSharedFactory)
+	soong.RegisterModuleType("cc_binary_host", binaryHostFactory)
+	soong.RegisterModuleType("cc_test_host", testHostFactory)
+	soong.RegisterModuleType("cc_benchmark_host", benchmarkHostFactory)
 
 	// LinkageMutator must be registered after common.ArchMutator, but that is guaranteed by
 	// the Go initialization order because this package depends on common, so common's init
@@ -169,37 +169,9 @@
 	pctx.StaticVariable("clangPath", "${clangBase}/${HostPrebuiltTag}/${clangVersion}/bin")
 }
 
-type CCModuleContext common.AndroidBaseContext
-
-// Building C/C++ code is handled by objects that satisfy this interface via composition
-type CCModuleType interface {
-	common.AndroidModule
-
-	// Modify property values after parsing Blueprints file but before starting dependency
-	// resolution or build rule generation
-	ModifyProperties(CCModuleContext)
-
-	// Modify the ccFlags
-	flags(common.AndroidModuleContext, CCFlags) CCFlags
-
-	// Return list of dependency names for use in depsMutator
-	depNames(common.AndroidBaseContext, CCDeps) CCDeps
-
-	// Add dynamic dependencies
-	depsMutator(common.AndroidBottomUpMutatorContext)
-
-	// Compile objects into final module
-	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() common.OptionalPath
-}
-
-type CCDeps struct {
-	StaticLibs, SharedLibs, LateStaticLibs, WholeStaticLibs []string
+type Deps struct {
+	SharedLibs, LateSharedLibs                  []string
+	StaticLibs, LateStaticLibs, WholeStaticLibs []string
 
 	ObjFiles common.Paths
 
@@ -208,8 +180,9 @@
 	CrtBegin, CrtEnd string
 }
 
-type CCPathDeps struct {
-	StaticLibs, SharedLibs, LateStaticLibs, WholeStaticLibs common.Paths
+type PathDeps struct {
+	SharedLibs, LateSharedLibs                  common.Paths
+	StaticLibs, LateStaticLibs, WholeStaticLibs common.Paths
 
 	ObjFiles               common.Paths
 	WholeStaticLibObjFiles common.Paths
@@ -219,7 +192,7 @@
 	CrtBegin, CrtEnd common.OptionalPath
 }
 
-type CCFlags struct {
+type Flags struct {
 	GlobalFlags []string // Flags that apply to C, C++, and assembly source files
 	AsFlags     []string // Flags that apply to assembly source files
 	CFlags      []string // Flags that apply to C and C++ source files
@@ -231,10 +204,11 @@
 	Nocrt     bool
 	Toolchain Toolchain
 	Clang     bool
+
+	RequiredInstructionSet string
 }
 
-// Properties used to compile all C or C++ modules
-type CCBaseProperties struct {
+type BaseCompilerProperties struct {
 	// list of source files used to compile the C/C++ module.  May be .c, .cpp, or .S files.
 	Srcs []string `android:"arch_variant"`
 
@@ -254,12 +228,17 @@
 	// list of module-specific flags that will be used for .S compiles
 	Asflags []string `android:"arch_variant"`
 
+	// list of module-specific flags that will be used for C and C++ compiles when
+	// compiling with clang
+	Clang_cflags []string `android:"arch_variant"`
+
+	// list of module-specific flags that will be used for .S compiles when
+	// compiling with clang
+	Clang_asflags []string `android:"arch_variant"`
+
 	// list of module-specific flags that will be used for .y and .yy compiles
 	Yaccflags []string
 
-	// list of module-specific flags that will be used for all link steps
-	Ldflags []string `android:"arch_variant"`
-
 	// the instruction set architecture to use to compile the C/C++
 	// module.
 	Instruction_set string `android:"arch_variant"`
@@ -285,23 +264,17 @@
 	// If possible, don't use this.
 	Local_include_files []string `android:"arch_variant"`
 
-	// 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 `android:"arch_variant"`
+	// pass -frtti instead of -fno-rtti
+	Rtti *bool
 
-	// list of module-specific flags that will be used for C and C++ compiles when
-	// compiling with clang
-	Clang_cflags []string `android:"arch_variant"`
+	Debug, Release struct {
+		// list of module-specific flags that will be used for C and C++ compiles in debug or
+		// release builds
+		Cflags []string `android:"arch_variant"`
+	} `android:"arch_variant"`
+}
 
-	// list of module-specific flags that will be used for .S compiles when
-	// compiling with clang
-	Clang_asflags []string `android:"arch_variant"`
-
-	// list of system libraries that will be dynamically linked to
-	// shared library and executable modules.  If unset, generally defaults to libc
-	// and libm.  Set to [] to prevent linking against libc and libm.
-	System_shared_libs []string
-
+type BaseLinkerProperties struct {
 	// list of modules whose object files should be linked into this module
 	// in their entirety.  For static library modules, all of the .o files from the intermediate
 	// directory of the dependency will be linked into this modules .a file.  For a shared library,
@@ -314,54 +287,116 @@
 	// list of modules that should be dynamically linked into this module.
 	Shared_libs []string `android:"arch_variant"`
 
+	// list of module-specific flags that will be used for all link steps
+	Ldflags []string `android:"arch_variant"`
+
+	// don't insert default compiler flags into asflags, cflags,
+	// cppflags, conlyflags, ldflags, or include_dirs
+	No_default_compiler_flags *bool
+
+	// list of system libraries that will be dynamically linked to
+	// shared library and executable modules.  If unset, generally defaults to libc
+	// and libm.  Set to [] to prevent linking against libc and libm.
+	System_shared_libs []string
+
 	// allow the module to contain undefined symbols.  By default,
 	// modules cannot contain undefined symbols that are not satisified by their immediate
 	// dependencies.  Set this flag to true to remove --no-undefined from the linker flags.
 	// This flag should only be necessary for compiling low-level libraries like libc.
 	Allow_undefined_symbols *bool
 
-	// don't link in crt_begin and crt_end.  This flag should only be necessary for
-	// compiling crt or libc.
-	Nocrt *bool `android:"arch_variant"`
-
 	// don't link in libgcc.a
 	No_libgcc *bool
 
-	// don't insert default compiler flags into asflags, cflags,
-	// cppflags, conlyflags, ldflags, or include_dirs
-	No_default_compiler_flags *bool
-
-	// compile module with clang instead of gcc
-	Clang *bool `android:"arch_variant"`
-
-	// pass -frtti instead of -fno-rtti
-	Rtti *bool
-
 	// -l arguments to pass to linker for host-provided shared libraries
 	Host_ldlibs []string `android:"arch_variant"`
+}
 
-	// select the STL library to use.  Possible values are "libc++", "libc++_static",
-	// "stlport", "stlport_static", "ndk", "libstdc++", or "none".  Leave blank to select the
-	// default
-	Stl string
-
-	// Set for combined shared/static libraries to prevent compiling object files a second time
-	SkipCompileObjs bool `blueprint:"mutated"`
-
-	Debug, Release struct {
-		// list of module-specific flags that will be used for C and C++ compiles in debug or
-		// release builds
-		Cflags []string `android:"arch_variant"`
+type LibraryCompilerProperties struct {
+	Static struct {
+		Srcs         []string `android:"arch_variant"`
+		Exclude_srcs []string `android:"arch_variant"`
+		Cflags       []string `android:"arch_variant"`
 	} `android:"arch_variant"`
+	Shared struct {
+		Srcs         []string `android:"arch_variant"`
+		Exclude_srcs []string `android:"arch_variant"`
+		Cflags       []string `android:"arch_variant"`
+	} `android:"arch_variant"`
+}
+
+type LibraryLinkerProperties struct {
+	Static struct {
+		Whole_static_libs []string `android:"arch_variant"`
+		Static_libs       []string `android:"arch_variant"`
+		Shared_libs       []string `android:"arch_variant"`
+	} `android:"arch_variant"`
+	Shared struct {
+		Whole_static_libs []string `android:"arch_variant"`
+		Static_libs       []string `android:"arch_variant"`
+		Shared_libs       []string `android:"arch_variant"`
+	} `android:"arch_variant"`
+
+	// local file name to pass to the linker as --version_script
+	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"`
+	// local file name to pass to the linker as -force_symbols_not_weak_list
+	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"`
+
+	// 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 `android:"arch_variant"`
+
+	// don't link in crt_begin and crt_end.  This flag should only be necessary for
+	// compiling crt or libc.
+	Nocrt *bool `android:"arch_variant"`
+}
+
+type BinaryLinkerProperties struct {
+	// compile executable with -static
+	Static_executable *bool
+
+	// set the name of the output
+	Stem string `android:"arch_variant"`
+
+	// append to the name of the output
+	Suffix string `android:"arch_variant"`
+
+	// if set, add an extra objcopy --prefix-symbols= step
+	Prefix_symbols string
+}
+
+type TestLinkerProperties struct {
+	// if set, build against the gtest library. Defaults to true.
+	Gtest bool
+
+	// Create a separate binary for each source file.  Useful when there is
+	// global state that can not be torn down and reset between each test suite.
+	Test_per_src *bool
+}
+
+// Properties used to compile all C or C++ modules
+type BaseProperties struct {
+	// compile module with clang instead of gcc
+	Clang *bool `android:"arch_variant"`
 
 	// Minimum sdk version supported when compiling against the ndk
 	Sdk_version string
 
+	// don't insert default compiler flags into asflags, cflags,
+	// cppflags, conlyflags, ldflags, or include_dirs
+	No_default_compiler_flags *bool
+}
+
+type InstallerProperties struct {
 	// install to a subdirectory of the default install path for the module
 	Relative_install_path string
 }
 
-type CCUnusedProperties struct {
+type UnusedProperties struct {
 	Native_coverage  *bool
 	Required         []string
 	Sanitize         []string `android:"arch_variant"`
@@ -370,163 +405,522 @@
 	Tags             []string
 }
 
-// CCBase contains the properties and members used by all C/C++ module types, and implements
-// the blueprint.Module interface.  It expects to be embedded into an outer specialization struct,
-// and uses a ccModuleType interface to that struct to create the build steps.
-type CCBase struct {
+type ModuleContextIntf interface {
+	module() *Module
+	static() bool
+	staticBinary() bool
+	clang() bool
+	toolchain() Toolchain
+	noDefaultCompilerFlags() bool
+	sdk() bool
+	sdkVersion() string
+}
+
+type ModuleContext interface {
+	common.AndroidModuleContext
+	ModuleContextIntf
+}
+
+type BaseModuleContext interface {
+	common.AndroidBaseContext
+	ModuleContextIntf
+}
+
+type Customizer interface {
+	CustomizeProperties(BaseModuleContext)
+	Properties() []interface{}
+}
+
+type feature interface {
+	begin(ctx BaseModuleContext)
+	deps(ctx BaseModuleContext, deps Deps) Deps
+	flags(ctx ModuleContext, flags Flags) Flags
+	props() []interface{}
+}
+
+type compiler interface {
+	feature
+	compile(ctx ModuleContext, flags Flags) common.Paths
+}
+
+type linker interface {
+	feature
+	link(ctx ModuleContext, flags Flags, deps PathDeps, objFiles common.Paths) common.Path
+}
+
+type installer interface {
+	props() []interface{}
+	install(ctx ModuleContext, path common.Path)
+	inData() bool
+}
+
+// Module contains the properties and members used by all C/C++ module types, and implements
+// the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
+// to construct the output file.  Behavior can be customized with a Customizer interface
+type Module struct {
 	common.AndroidModuleBase
 	common.DefaultableModule
-	module CCModuleType
 
-	Properties CCBaseProperties
+	Properties BaseProperties
+	unused     UnusedProperties
 
-	unused CCUnusedProperties
+	// initialize before calling Init
+	hod      common.HostOrDeviceSupported
+	multilib common.Multilib
 
-	installPath string
+	// delegates, initialize before calling Init
+	customizer Customizer
+	features   []feature
+	compiler   compiler
+	linker     linker
+	installer  installer
 
-	savedDepNames CCDeps
+	deps       Deps
+	outputFile common.OptionalPath
+
+	cachedToolchain Toolchain
 }
 
-func newCCBase(base *CCBase, module CCModuleType, hod common.HostOrDeviceSupported,
-	multilib common.Multilib, props ...interface{}) (blueprint.Module, []interface{}) {
+func (c *Module) Init() (blueprint.Module, []interface{}) {
+	props := []interface{}{&c.Properties, &c.unused}
+	if c.customizer != nil {
+		props = append(props, c.customizer.Properties()...)
+	}
+	if c.compiler != nil {
+		props = append(props, c.compiler.props()...)
+	}
+	if c.linker != nil {
+		props = append(props, c.linker.props()...)
+	}
+	if c.installer != nil {
+		props = append(props, c.installer.props()...)
+	}
+	for _, feature := range c.features {
+		props = append(props, feature.props()...)
+	}
 
-	base.module = module
+	_, props = common.InitAndroidArchModule(c, c.hod, c.multilib, props...)
 
-	props = append(props, &base.Properties, &base.unused)
-
-	_, props = common.InitAndroidArchModule(module, hod, multilib, props...)
-
-	return common.InitDefaultableModule(module, base, props...)
+	return common.InitDefaultableModule(c, c, props...)
 }
 
-func (c *CCBase) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
-	toolchain := c.findToolchain(ctx)
+type baseModuleContext struct {
+	common.AndroidBaseContext
+	moduleContextImpl
+}
+
+type moduleContext struct {
+	common.AndroidModuleContext
+	moduleContextImpl
+}
+
+type moduleContextImpl struct {
+	mod *Module
+	ctx BaseModuleContext
+}
+
+func (ctx *moduleContextImpl) module() *Module {
+	return ctx.mod
+}
+
+func (ctx *moduleContextImpl) clang() bool {
+	return ctx.mod.clang(ctx.ctx)
+}
+
+func (ctx *moduleContextImpl) toolchain() Toolchain {
+	return ctx.mod.toolchain(ctx.ctx)
+}
+
+func (ctx *moduleContextImpl) static() bool {
+	if ctx.mod.linker == nil {
+		panic(fmt.Errorf("static called on module %q with no linker", ctx.ctx.ModuleName()))
+	}
+	if linker, ok := ctx.mod.linker.(baseLinkerInterface); ok {
+		return linker.static()
+	} else {
+		panic(fmt.Errorf("static called on module %q that doesn't use base linker", ctx.ctx.ModuleName()))
+	}
+}
+
+func (ctx *moduleContextImpl) staticBinary() bool {
+	if ctx.mod.linker == nil {
+		panic(fmt.Errorf("staticBinary called on module %q with no linker", ctx.ctx.ModuleName()))
+	}
+	if linker, ok := ctx.mod.linker.(baseLinkerInterface); ok {
+		return linker.staticBinary()
+	} else {
+		panic(fmt.Errorf("staticBinary called on module %q that doesn't use base linker", ctx.ctx.ModuleName()))
+	}
+}
+
+func (ctx *moduleContextImpl) noDefaultCompilerFlags() bool {
+	return Bool(ctx.mod.Properties.No_default_compiler_flags)
+}
+
+func (ctx *moduleContextImpl) sdk() bool {
+	return ctx.mod.Properties.Sdk_version != ""
+}
+
+func (ctx *moduleContextImpl) sdkVersion() string {
+	return ctx.mod.Properties.Sdk_version
+}
+
+func newBaseModule(hod common.HostOrDeviceSupported, multilib common.Multilib) *Module {
+	return &Module{
+		hod:      hod,
+		multilib: multilib,
+	}
+}
+
+func newModule(hod common.HostOrDeviceSupported, multilib common.Multilib) *Module {
+	module := newBaseModule(hod, multilib)
+	module.features = []feature{
+		&stlFeature{},
+	}
+	return module
+}
+
+func (c *Module) GenerateAndroidBuildActions(actx common.AndroidModuleContext) {
+	ctx := &moduleContext{
+		AndroidModuleContext: actx,
+		moduleContextImpl: moduleContextImpl{
+			mod: c,
+		},
+	}
+	ctx.ctx = ctx
+
+	flags := Flags{
+		Toolchain: c.toolchain(ctx),
+		Clang:     c.clang(ctx),
+	}
+
+	if c.compiler != nil {
+		flags = c.compiler.flags(ctx, flags)
+	}
+	if c.linker != nil {
+		flags = c.linker.flags(ctx, flags)
+	}
+	for _, feature := range c.features {
+		flags = feature.flags(ctx, flags)
+	}
 	if ctx.Failed() {
 		return
 	}
 
-	flags := c.collectFlags(ctx, toolchain)
-	if ctx.Failed() {
-		return
-	}
+	flags.CFlags, _ = filterList(flags.CFlags, illegalFlags)
+	flags.CppFlags, _ = filterList(flags.CppFlags, illegalFlags)
+	flags.ConlyFlags, _ = filterList(flags.ConlyFlags, illegalFlags)
 
-	deps := c.depsToPaths(ctx, c.savedDepNames)
+	// Optimization to reduce size of build.ninja
+	// Replace the long list of flags for each file with a module-local variable
+	ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
+	ctx.Variable(pctx, "cppflags", strings.Join(flags.CppFlags, " "))
+	ctx.Variable(pctx, "asflags", strings.Join(flags.AsFlags, " "))
+	flags.CFlags = []string{"$cflags"}
+	flags.CppFlags = []string{"$cppflags"}
+	flags.AsFlags = []string{"$asflags"}
+
+	deps := c.depsToPaths(actx, c.deps)
 	if ctx.Failed() {
 		return
 	}
 
 	flags.CFlags = append(flags.CFlags, deps.Cflags...)
 
-	objFiles := c.compileObjs(ctx, flags)
-	if ctx.Failed() {
-		return
+	var objFiles common.Paths
+	if c.compiler != nil {
+		objFiles = c.compiler.compile(ctx, flags)
+		if ctx.Failed() {
+			return
+		}
 	}
 
-	generatedObjFiles := c.compileGeneratedObjs(ctx, flags)
-	if ctx.Failed() {
-		return
-	}
+	if c.linker != nil {
+		outputFile := c.linker.link(ctx, flags, deps, objFiles)
+		if ctx.Failed() {
+			return
+		}
+		c.outputFile = common.OptionalPathForPath(outputFile)
 
-	objFiles = append(objFiles, generatedObjFiles...)
-
-	c.ccModuleType().compileModule(ctx, flags, deps, objFiles)
-	if ctx.Failed() {
-		return
-	}
-
-	c.ccModuleType().installModule(ctx, flags)
-	if ctx.Failed() {
-		return
+		if c.installer != nil {
+			c.installer.install(ctx, outputFile)
+			if ctx.Failed() {
+				return
+			}
+		}
 	}
 }
 
-func (c *CCBase) ccModuleType() CCModuleType {
-	return c.module
-}
-
-func (c *CCBase) findToolchain(ctx common.AndroidModuleContext) Toolchain {
-	arch := ctx.Arch()
-	hod := ctx.HostOrDevice()
-	ht := ctx.HostType()
-	factory := toolchainFactories[hod][ht][arch.ArchType]
-	if factory == nil {
-		ctx.ModuleErrorf("Toolchain not found for %s %s arch %q", hod.String(), ht.String(), arch.String())
-		return nil
+func (c *Module) toolchain(ctx BaseModuleContext) Toolchain {
+	if c.cachedToolchain == nil {
+		arch := ctx.Arch()
+		hod := ctx.HostOrDevice()
+		ht := ctx.HostType()
+		factory := toolchainFactories[hod][ht][arch.ArchType]
+		if factory == nil {
+			ctx.ModuleErrorf("Toolchain not found for %s %s arch %q", hod.String(), ht.String(), arch.String())
+			return nil
+		}
+		c.cachedToolchain = factory(arch)
 	}
-	return factory(arch)
+	return c.cachedToolchain
 }
 
-func (c *CCBase) ModifyProperties(ctx CCModuleContext) {
-}
-
-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...)
-	depNames.SharedLibs = append(depNames.SharedLibs, c.Properties.Shared_libs...)
-
-	return depNames
-}
-
-func (c *CCBase) depsMutator(ctx common.AndroidBottomUpMutatorContext) {
-	c.savedDepNames = c.module.depNames(ctx, CCDeps{})
-	c.savedDepNames.WholeStaticLibs = lastUniqueElements(c.savedDepNames.WholeStaticLibs)
-	c.savedDepNames.StaticLibs = lastUniqueElements(c.savedDepNames.StaticLibs)
-	c.savedDepNames.SharedLibs = lastUniqueElements(c.savedDepNames.SharedLibs)
-
-	staticLibs := c.savedDepNames.WholeStaticLibs
-	staticLibs = append(staticLibs, c.savedDepNames.StaticLibs...)
-	staticLibs = append(staticLibs, c.savedDepNames.LateStaticLibs...)
-	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, staticLibs...)
-
-	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, c.savedDepNames.SharedLibs...)
-
-	ctx.AddDependency(ctx.Module(), c.savedDepNames.ObjFiles.Strings()...)
-	if c.savedDepNames.CrtBegin != "" {
-		ctx.AddDependency(ctx.Module(), c.savedDepNames.CrtBegin)
+func (c *Module) begin(ctx BaseModuleContext) {
+	if c.compiler != nil {
+		c.compiler.begin(ctx)
 	}
-	if c.savedDepNames.CrtEnd != "" {
-		ctx.AddDependency(ctx.Module(), c.savedDepNames.CrtEnd)
+	if c.linker != nil {
+		c.linker.begin(ctx)
+	}
+	for _, feature := range c.features {
+		feature.begin(ctx)
+	}
+}
+
+func (c *Module) depsMutator(actx common.AndroidBottomUpMutatorContext) {
+	ctx := &baseModuleContext{
+		AndroidBaseContext: actx,
+		moduleContextImpl: moduleContextImpl{
+			mod: c,
+		},
+	}
+	ctx.ctx = ctx
+
+	if c.customizer != nil {
+		c.customizer.CustomizeProperties(ctx)
+	}
+
+	c.begin(ctx)
+
+	c.deps = Deps{}
+
+	if c.compiler != nil {
+		c.deps = c.compiler.deps(ctx, c.deps)
+	}
+	if c.linker != nil {
+		c.deps = c.linker.deps(ctx, c.deps)
+	}
+	for _, feature := range c.features {
+		c.deps = feature.deps(ctx, c.deps)
+	}
+
+	c.deps.WholeStaticLibs = lastUniqueElements(c.deps.WholeStaticLibs)
+	c.deps.StaticLibs = lastUniqueElements(c.deps.StaticLibs)
+	c.deps.LateStaticLibs = lastUniqueElements(c.deps.LateStaticLibs)
+	c.deps.SharedLibs = lastUniqueElements(c.deps.SharedLibs)
+	c.deps.LateSharedLibs = lastUniqueElements(c.deps.LateSharedLibs)
+
+	staticLibs := c.deps.WholeStaticLibs
+	staticLibs = append(staticLibs, c.deps.StaticLibs...)
+	staticLibs = append(staticLibs, c.deps.LateStaticLibs...)
+	actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, staticLibs...)
+
+	sharedLibs := c.deps.SharedLibs
+	sharedLibs = append(sharedLibs, c.deps.LateSharedLibs...)
+	actx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, sharedLibs...)
+
+	actx.AddDependency(ctx.module(), c.deps.ObjFiles.Strings()...)
+	if c.deps.CrtBegin != "" {
+		actx.AddDependency(ctx.module(), c.deps.CrtBegin)
+	}
+	if c.deps.CrtEnd != "" {
+		actx.AddDependency(ctx.module(), c.deps.CrtEnd)
 	}
 }
 
 func depsMutator(ctx common.AndroidBottomUpMutatorContext) {
-	if c, ok := ctx.Module().(CCModuleType); ok {
-		c.ModifyProperties(ctx)
+	if c, ok := ctx.Module().(*Module); ok {
 		c.depsMutator(ctx)
 	}
 }
 
-// Create a ccFlags struct that collects the compile flags from global values,
-// per-target values, module type values, and per-module Blueprints properties
-func (c *CCBase) collectFlags(ctx common.AndroidModuleContext, toolchain Toolchain) CCFlags {
-	flags := CCFlags{
-		CFlags:     c.Properties.Cflags,
-		CppFlags:   c.Properties.Cppflags,
-		ConlyFlags: c.Properties.Conlyflags,
-		LdFlags:    c.Properties.Ldflags,
-		AsFlags:    c.Properties.Asflags,
-		YaccFlags:  c.Properties.Yaccflags,
-		Nocrt:      Bool(c.Properties.Nocrt),
-		Toolchain:  toolchain,
-		Clang:      Bool(c.Properties.Clang),
+func (c *Module) clang(ctx BaseModuleContext) bool {
+	clang := Bool(c.Properties.Clang)
+
+	if c.Properties.Clang == nil {
+		if ctx.Host() {
+			clang = true
+		}
+
+		if ctx.Device() && ctx.AConfig().DeviceUsesClang() {
+			clang = true
+		}
 	}
 
+	if !c.toolchain(ctx).ClangSupported() {
+		clang = false
+	}
+
+	return clang
+}
+
+func (c *Module) depsToPathsFromList(ctx common.AndroidModuleContext,
+	names []string) (modules []common.AndroidModule,
+	outputFiles common.Paths, exportedFlags []string) {
+
+	for _, n := range names {
+		found := false
+		ctx.VisitDirectDeps(func(m blueprint.Module) {
+			otherName := ctx.OtherModuleName(m)
+			if otherName != n {
+				return
+			}
+
+			if a, ok := m.(*Module); ok {
+				if !a.Enabled() {
+					ctx.ModuleErrorf("depends on disabled module %q", otherName)
+					return
+				}
+				if a.HostOrDevice() != ctx.HostOrDevice() {
+					ctx.ModuleErrorf("host/device mismatch between %q and %q", ctx.ModuleName(),
+						otherName)
+					return
+				}
+
+				if outputFile := a.outputFile; outputFile.Valid() {
+					if found {
+						ctx.ModuleErrorf("multiple modules satisified dependency on %q", otherName)
+						return
+					}
+					outputFiles = append(outputFiles, outputFile.Path())
+					modules = append(modules, a)
+					if i, ok := a.linker.(exportedFlagsProducer); ok {
+						exportedFlags = append(exportedFlags, i.exportedFlags()...)
+					}
+					found = true
+				} else {
+					ctx.ModuleErrorf("module %q missing output file", otherName)
+					return
+				}
+			} else {
+				ctx.ModuleErrorf("module %q not an android module", otherName)
+				return
+			}
+		})
+		if !found && !inList(n, ctx.GetMissingDependencies()) {
+			ctx.ModuleErrorf("unsatisified dependency on %q", n)
+		}
+	}
+
+	return modules, outputFiles, exportedFlags
+}
+
+// Convert dependency names to paths.  Takes a Deps containing names and returns a PathDeps
+// containing paths
+func (c *Module) depsToPaths(ctx common.AndroidModuleContext, deps Deps) PathDeps {
+	var depPaths PathDeps
+	var newCflags []string
+
+	var wholeStaticLibModules []common.AndroidModule
+
+	wholeStaticLibModules, depPaths.WholeStaticLibs, newCflags =
+		c.depsToPathsFromList(ctx, deps.WholeStaticLibs)
+	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
+	depPaths.ReexportedCflags = append(depPaths.ReexportedCflags, newCflags...)
+
+	for _, am := range wholeStaticLibModules {
+		if m, ok := am.(*Module); ok {
+			if staticLib, ok := m.linker.(*libraryLinker); ok && staticLib.static() {
+				if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
+					postfix := " (required by " + ctx.OtherModuleName(m) + ")"
+					for i := range missingDeps {
+						missingDeps[i] += postfix
+					}
+					ctx.AddMissingDependencies(missingDeps)
+				}
+				depPaths.WholeStaticLibObjFiles =
+					append(depPaths.WholeStaticLibObjFiles, staticLib.objFiles...)
+			} else {
+				ctx.ModuleErrorf("module %q not a static library", ctx.OtherModuleName(m))
+			}
+		} else {
+			ctx.ModuleErrorf("module %q not an android module", ctx.OtherModuleName(m))
+		}
+	}
+
+	_, depPaths.StaticLibs, newCflags = c.depsToPathsFromList(ctx, deps.StaticLibs)
+	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
+
+	_, depPaths.LateStaticLibs, newCflags = c.depsToPathsFromList(ctx, deps.LateStaticLibs)
+	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
+
+	_, depPaths.SharedLibs, newCflags = c.depsToPathsFromList(ctx, deps.SharedLibs)
+	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
+
+	_, depPaths.LateSharedLibs, newCflags = c.depsToPathsFromList(ctx, deps.LateSharedLibs)
+	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
+
+	ctx.VisitDirectDeps(func(bm blueprint.Module) {
+		if m, ok := bm.(*Module); ok {
+			otherName := ctx.OtherModuleName(m)
+			if otherName == deps.CrtBegin {
+				depPaths.CrtBegin = m.outputFile
+			} else if otherName == deps.CrtEnd {
+				depPaths.CrtEnd = m.outputFile
+			} else {
+				output := m.outputFile
+				if output.Valid() {
+					depPaths.ObjFiles = append(depPaths.ObjFiles, output.Path())
+				} else {
+					ctx.ModuleErrorf("module %s did not provide an output file", otherName)
+				}
+			}
+		}
+	})
+
+	return depPaths
+}
+
+func (c *Module) InstallInData() bool {
+	if c.installer == nil {
+		return false
+	}
+	return c.installer.inData()
+}
+
+// Compiler
+
+type baseCompiler struct {
+	Properties BaseCompilerProperties
+}
+
+var _ compiler = (*baseCompiler)(nil)
+
+func (compiler *baseCompiler) props() []interface{} {
+	return []interface{}{&compiler.Properties}
+}
+
+func (compiler *baseCompiler) begin(ctx BaseModuleContext)                {}
+func (compiler *baseCompiler) deps(ctx BaseModuleContext, deps Deps) Deps { return deps }
+
+// Create a Flags struct that collects the compile flags from global values,
+// per-target values, module type values, and per-module Blueprints properties
+func (compiler *baseCompiler) flags(ctx ModuleContext, flags Flags) Flags {
+	toolchain := ctx.toolchain()
+
+	flags.CFlags = append(flags.CFlags, compiler.Properties.Cflags...)
+	flags.CppFlags = append(flags.CppFlags, compiler.Properties.Cppflags...)
+	flags.ConlyFlags = append(flags.ConlyFlags, compiler.Properties.Conlyflags...)
+	flags.AsFlags = append(flags.AsFlags, compiler.Properties.Asflags...)
+	flags.YaccFlags = append(flags.YaccFlags, compiler.Properties.Yaccflags...)
+
 	// Include dir cflags
-	rootIncludeDirs := common.PathsForSource(ctx, c.Properties.Include_dirs)
-	localIncludeDirs := common.PathsForModuleSrc(ctx, c.Properties.Local_include_dirs)
+	rootIncludeDirs := common.PathsForSource(ctx, compiler.Properties.Include_dirs)
+	localIncludeDirs := common.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
 	flags.GlobalFlags = append(flags.GlobalFlags,
 		includeDirsToFlags(localIncludeDirs),
 		includeDirsToFlags(rootIncludeDirs))
 
-	rootIncludeFiles := common.PathsForSource(ctx, c.Properties.Include_files)
-	localIncludeFiles := common.PathsForModuleSrc(ctx, c.Properties.Local_include_files)
+	rootIncludeFiles := common.PathsForSource(ctx, compiler.Properties.Include_files)
+	localIncludeFiles := common.PathsForModuleSrc(ctx, compiler.Properties.Local_include_files)
 
 	flags.GlobalFlags = append(flags.GlobalFlags,
 		includeFilesToFlags(rootIncludeFiles),
 		includeFilesToFlags(localIncludeFiles))
 
-	if !Bool(c.Properties.No_default_compiler_flags) {
-		if c.Properties.Sdk_version == "" || ctx.Host() {
+	if !ctx.noDefaultCompilerFlags() {
+		if !ctx.sdk() || ctx.Host() {
 			flags.GlobalFlags = append(flags.GlobalFlags,
 				"${commonGlobalIncludes}",
 				toolchain.IncludeFlags(),
@@ -540,21 +934,10 @@
 		}...)
 	}
 
-	if c.Properties.Clang == nil {
-		if ctx.Host() {
-			flags.Clang = true
-		}
-
-		if ctx.Device() && ctx.AConfig().DeviceUsesClang() {
-			flags.Clang = true
-		}
+	instructionSet := compiler.Properties.Instruction_set
+	if flags.RequiredInstructionSet != "" {
+		instructionSet = flags.RequiredInstructionSet
 	}
-
-	if !toolchain.ClangSupported() {
-		flags.Clang = false
-	}
-
-	instructionSet := c.Properties.Instruction_set
 	instructionSetFlags, err := toolchain.InstructionSetFlags(instructionSet)
 	if flags.Clang {
 		instructionSetFlags, err = toolchain.ClangInstructionSetFlags(instructionSet)
@@ -564,12 +947,12 @@
 	}
 
 	// TODO: debug
-	flags.CFlags = append(flags.CFlags, c.Properties.Release.Cflags...)
+	flags.CFlags = append(flags.CFlags, compiler.Properties.Release.Cflags...)
 
 	if flags.Clang {
 		flags.CFlags = clangFilterUnknownCflags(flags.CFlags)
-		flags.CFlags = append(flags.CFlags, c.Properties.Clang_cflags...)
-		flags.AsFlags = append(flags.AsFlags, c.Properties.Clang_asflags...)
+		flags.CFlags = append(flags.CFlags, compiler.Properties.Clang_cflags...)
+		flags.AsFlags = append(flags.AsFlags, compiler.Properties.Clang_asflags...)
 		flags.CppFlags = clangFilterUnknownCflags(flags.CppFlags)
 		flags.ConlyFlags = clangFilterUnknownCflags(flags.ConlyFlags)
 		flags.LdFlags = clangFilterUnknownCflags(flags.LdFlags)
@@ -582,11 +965,7 @@
 		flags.LdFlags = append(flags.LdFlags, target, gccPrefix)
 	}
 
-	if !Bool(c.Properties.No_default_compiler_flags) {
-		if ctx.Device() && !Bool(c.Properties.Allow_undefined_symbols) {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
-		}
-
+	if !ctx.noDefaultCompilerFlags() {
 		flags.GlobalFlags = append(flags.GlobalFlags, instructionSetFlags)
 
 		if flags.Clang {
@@ -611,7 +990,7 @@
 		}
 
 		if ctx.Device() {
-			if Bool(c.Properties.Rtti) {
+			if Bool(compiler.Properties.Rtti) {
 				flags.CppFlags = append(flags.CppFlags, "-frtti")
 			} else {
 				flags.CppFlags = append(flags.CppFlags, "-fno-rtti")
@@ -622,14 +1001,8 @@
 
 		if flags.Clang {
 			flags.CppFlags = append(flags.CppFlags, toolchain.ClangCppflags())
-			flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
 		} else {
 			flags.CppFlags = append(flags.CppFlags, toolchain.Cppflags())
-			flags.LdFlags = append(flags.LdFlags, toolchain.Ldflags())
-		}
-
-		if ctx.Host() {
-			flags.LdFlags = append(flags.LdFlags, c.Properties.Host_ldlibs...)
 		}
 	}
 
@@ -640,9 +1013,7 @@
 	}
 	flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainLdflags())
 
-	flags = c.ccModuleType().flags(ctx, flags)
-
-	if c.Properties.Sdk_version == "" {
+	if !ctx.sdk() {
 		if ctx.Host() && !flags.Clang {
 			// The host GCC doesn't support C++14 (and is deprecated, so likely
 			// never will). Build these modules with C++11.
@@ -652,10 +1023,6 @@
 		}
 	}
 
-	flags.CFlags, _ = filterList(flags.CFlags, illegalFlags)
-	flags.CppFlags, _ = filterList(flags.CppFlags, illegalFlags)
-	flags.ConlyFlags, _ = filterList(flags.ConlyFlags, illegalFlags)
-
 	// We can enforce some rules more strictly in the code we own. strict
 	// indicates if this is code that we can be stricter with. If we have
 	// rules that we want to apply to *our* code (but maybe can't for
@@ -672,27 +1039,36 @@
 		flags.CFlags = append(flags.CFlags, "-DANDROID_STRICT")
 	}
 
-	// Optimization to reduce size of build.ninja
-	// Replace the long list of flags for each file with a module-local variable
-	ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
-	ctx.Variable(pctx, "cppflags", strings.Join(flags.CppFlags, " "))
-	ctx.Variable(pctx, "asflags", strings.Join(flags.AsFlags, " "))
-	flags.CFlags = []string{"$cflags"}
-	flags.CppFlags = []string{"$cppflags"}
-	flags.AsFlags = []string{"$asflags"}
-
 	return flags
 }
 
-func (c *CCBase) flags(ctx common.AndroidModuleContext, flags CCFlags) CCFlags {
-	return flags
+func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags) common.Paths {
+	// Compile files listed in c.Properties.Srcs into objects
+	objFiles := compiler.compileObjs(ctx, flags, "", compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
+	if ctx.Failed() {
+		return nil
+	}
+
+	var genSrcs common.Paths
+	ctx.VisitDirectDeps(func(module blueprint.Module) {
+		if gen, ok := module.(genrule.SourceFileGenerator); ok {
+			genSrcs = append(genSrcs, gen.GeneratedSourceFiles()...)
+		}
+	})
+
+	if len(genSrcs) != 0 {
+		genObjs := TransformSourceToObj(ctx, "", genSrcs, flagsToBuilderFlags(flags), nil)
+		objFiles = append(objFiles, genObjs...)
+	}
+
+	return objFiles
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func (c *CCBase) customCompileObjs(ctx common.AndroidModuleContext, flags CCFlags,
+func (compiler *baseCompiler) compileObjs(ctx common.AndroidModuleContext, flags Flags,
 	subdir string, srcFiles, excludes []string) common.Paths {
 
-	buildFlags := ccFlagsToBuilderFlags(flags)
+	buildFlags := flagsToBuilderFlags(flags)
 
 	inputFiles := ctx.ExpandSources(srcFiles, excludes)
 	srcPaths, deps := genSources(ctx, inputFiles, buildFlags)
@@ -700,385 +1076,100 @@
 	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) common.Paths {
-
-	if c.Properties.SkipCompileObjs {
-		return nil
-	}
-
-	return c.customCompileObjs(ctx, flags, "", c.Properties.Srcs, c.Properties.Exclude_srcs)
-}
-
-// Compile generated source files from dependencies
-func (c *CCBase) compileGeneratedObjs(ctx common.AndroidModuleContext, flags CCFlags) common.Paths {
-	var srcs common.Paths
-
-	if c.Properties.SkipCompileObjs {
-		return nil
-	}
-
-	ctx.VisitDirectDeps(func(module blueprint.Module) {
-		if gen, ok := module.(genrule.SourceFileGenerator); ok {
-			srcs = append(srcs, gen.GeneratedSourceFiles()...)
-		}
-	})
-
-	if len(srcs) == 0 {
-		return nil
-	}
-
-	return TransformSourceToObj(ctx, "", srcs, ccFlagsToBuilderFlags(flags), nil)
-}
-
-func (c *CCBase) outputFile() common.OptionalPath {
-	return common.OptionalPath{}
-}
-
-func (c *CCBase) depsToPathsFromList(ctx common.AndroidModuleContext,
-	names []string) (modules []common.AndroidModule,
-	outputFiles common.Paths, exportedFlags []string) {
-
-	for _, n := range names {
-		found := false
-		ctx.VisitDirectDeps(func(m blueprint.Module) {
-			otherName := ctx.OtherModuleName(m)
-			if otherName != n {
-				return
-			}
-
-			if a, ok := m.(CCModuleType); ok {
-				if !a.Enabled() {
-					// If a cc_library host+device module depends on a library that exists as both
-					// cc_library_shared and cc_library_host_shared, it will end up with two
-					// dependencies with the same name, one of which is marked disabled for each
-					// of host and device.  Ignore the disabled one.
-					return
-				}
-				if a.HostOrDevice() != ctx.HostOrDevice() {
-					ctx.ModuleErrorf("host/device mismatch between %q and %q", ctx.ModuleName(),
-						otherName)
-					return
-				}
-
-				if outputFile := a.outputFile(); outputFile.Valid() {
-					if found {
-						ctx.ModuleErrorf("multiple modules satisified dependency on %q", otherName)
-						return
-					}
-					outputFiles = append(outputFiles, outputFile.Path())
-					modules = append(modules, a)
-					if i, ok := a.(ccExportedFlagsProducer); ok {
-						exportedFlags = append(exportedFlags, i.exportedFlags()...)
-					}
-					found = true
-				} else {
-					ctx.ModuleErrorf("module %q missing output file", otherName)
-					return
-				}
-			} else {
-				ctx.ModuleErrorf("module %q not an android module", otherName)
-				return
-			}
-		})
-		if !found && !inList(n, ctx.GetMissingDependencies()) {
-			ctx.ModuleErrorf("unsatisified dependency on %q", n)
-		}
-	}
-
-	return modules, outputFiles, exportedFlags
-}
-
-// 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) CCPathDeps {
-	var depPaths CCPathDeps
-	var newCflags []string
-
-	var wholeStaticLibModules []common.AndroidModule
-
-	wholeStaticLibModules, depPaths.WholeStaticLibs, newCflags =
-		c.depsToPathsFromList(ctx, depNames.WholeStaticLibs)
-	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
-	depPaths.ReexportedCflags = append(depPaths.ReexportedCflags, newCflags...)
-
-	for _, m := range wholeStaticLibModules {
-		if staticLib, ok := m.(ccLibraryInterface); ok && staticLib.static() {
-			if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
-				postfix := " (required by " + ctx.OtherModuleName(m) + ")"
-				for i := range missingDeps {
-					missingDeps[i] += postfix
-				}
-				ctx.AddMissingDependencies(missingDeps)
-			}
-			depPaths.WholeStaticLibObjFiles =
-				append(depPaths.WholeStaticLibObjFiles, staticLib.allObjFiles()...)
-		} else {
-			ctx.ModuleErrorf("module %q not a static library", ctx.OtherModuleName(m))
-		}
-	}
-
-	_, depPaths.StaticLibs, newCflags = c.depsToPathsFromList(ctx, depNames.StaticLibs)
-	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
-
-	_, depPaths.LateStaticLibs, newCflags = c.depsToPathsFromList(ctx, depNames.LateStaticLibs)
-	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
-
-	_, depPaths.SharedLibs, newCflags = c.depsToPathsFromList(ctx, depNames.SharedLibs)
-	depPaths.Cflags = append(depPaths.Cflags, newCflags...)
-
-	ctx.VisitDirectDeps(func(m blueprint.Module) {
-		if obj, ok := m.(ccObjectProvider); ok {
-			otherName := ctx.OtherModuleName(m)
-			if otherName == depNames.CrtBegin {
-				if !Bool(c.Properties.Nocrt) {
-					depPaths.CrtBegin = obj.object().outputFile()
-				}
-			} else if otherName == depNames.CrtEnd {
-				if !Bool(c.Properties.Nocrt) {
-					depPaths.CrtEnd = obj.object().outputFile()
-				}
-			} else {
-				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)
-				}
-			}
-		}
-	})
-
-	return depPaths
-}
-
-type ccLinkedProperties struct {
-	VariantIsShared       bool `blueprint:"mutated"`
-	VariantIsStatic       bool `blueprint:"mutated"`
-	VariantIsStaticBinary bool `blueprint:"mutated"`
-}
-
-// CCLinked contains the properties and members used by libraries and executables
-type CCLinked struct {
-	CCBase
-	dynamicProperties ccLinkedProperties
-}
-
-func newCCDynamic(dynamic *CCLinked, module CCModuleType, hod common.HostOrDeviceSupported,
-	multilib common.Multilib, props ...interface{}) (blueprint.Module, []interface{}) {
-
-	props = append(props, &dynamic.dynamicProperties)
-
-	return newCCBase(&dynamic.CCBase, module, hod, multilib, props...)
-}
-
-func (c *CCLinked) systemSharedLibs(ctx common.AndroidBaseContext) []string {
-	if c.Properties.System_shared_libs != nil {
-		return c.Properties.System_shared_libs
-	} else if ctx.Device() && c.Properties.Sdk_version == "" {
-		return []string{"libc", "libm"}
-	} else {
-		return nil
+// baseLinker provides support for shared_libs, static_libs, and whole_static_libs properties
+type baseLinker struct {
+	Properties        BaseLinkerProperties
+	dynamicProperties struct {
+		VariantIsShared       bool `blueprint:"mutated"`
+		VariantIsStatic       bool `blueprint:"mutated"`
+		VariantIsStaticBinary bool `blueprint:"mutated"`
 	}
 }
 
-func (c *CCLinked) stl(ctx common.AndroidBaseContext) string {
-	if c.Properties.Sdk_version != "" && ctx.Device() {
-		switch c.Properties.Stl {
-		case "":
-			return "ndk_system"
-		case "c++_shared", "c++_static",
-			"stlport_shared", "stlport_static",
-			"gnustl_static":
-			return "ndk_lib" + c.Properties.Stl
-		default:
-			ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", c.Properties.Stl)
-			return ""
-		}
-	}
+func (linker *baseLinker) begin(ctx BaseModuleContext) {}
 
-	if ctx.HostType() == common.Windows {
-		switch c.Properties.Stl {
-		case "libc++", "libc++_static", "libstdc++", "":
-			// libc++ is not supported on mingw
-			return "libstdc++"
-		case "none":
-			return ""
-		default:
-			ctx.ModuleErrorf("stl: %q is not a supported STL", c.Properties.Stl)
-			return ""
-		}
-	} else {
-		switch c.Properties.Stl {
-		case "libc++", "libc++_static",
-			"libstdc++":
-			return c.Properties.Stl
-		case "none":
-			return ""
-		case "":
-			if c.static() {
-				return "libc++_static"
-			} else {
-				return "libc++"
-			}
-		default:
-			ctx.ModuleErrorf("stl: %q is not a supported STL", c.Properties.Stl)
-			return ""
-		}
-	}
+func (linker *baseLinker) props() []interface{} {
+	return []interface{}{&linker.Properties, &linker.dynamicProperties}
 }
 
-var hostDynamicGccLibs, hostStaticGccLibs map[common.HostType][]string
-
-func init() {
-	hostDynamicGccLibs = map[common.HostType][]string{
-		common.Linux:  []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
-		common.Darwin: []string{"-lc", "-lSystem"},
-		common.Windows: []string{"-lmsvcr110", "-lmingw32", "-lgcc", "-lmoldname",
-			"-lmingwex", "-lmsvcrt", "-ladvapi32", "-lshell32", "-luser32",
-			"-lkernel32", "-lmingw32", "-lgcc", "-lmoldname", "-lmingwex",
-			"-lmsvcrt"},
-	}
-	hostStaticGccLibs = map[common.HostType][]string{
-		common.Linux:   []string{"-Wl,--start-group", "-lgcc", "-lgcc_eh", "-lc", "-Wl,--end-group"},
-		common.Darwin:  []string{"NO_STATIC_HOST_BINARIES_ON_DARWIN"},
-		common.Windows: []string{"NO_STATIC_HOST_BINARIES_ON_WINDOWS"},
-	}
-}
-
-func (c *CCLinked) flags(ctx common.AndroidModuleContext, flags CCFlags) CCFlags {
-	stl := c.stl(ctx)
-	if ctx.Failed() {
-		return flags
-	}
-
-	switch stl {
-	case "libc++", "libc++_static":
-		flags.CFlags = append(flags.CFlags, "-D_USING_LIBCXX")
-		if ctx.Host() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
-			flags.LdFlags = append(flags.LdFlags, "-lpthread", "-lm")
-			if c.staticBinary() {
-				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.HostType()]...)
-			} else {
-				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.HostType()]...)
-			}
-		} else {
-			if ctx.Arch().ArchType == common.Arm {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
-			}
-		}
-	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.
-		// Host builds will use GNU libstdc++.
-		if ctx.Device() {
-			flags.CFlags = append(flags.CFlags, "-I"+common.PathForSource(ctx, "bionic/libstdc++/include").String())
-		} else {
-			// Host builds will use the system C++. libc++ on Darwin, GNU libstdc++ everywhere else
-			flags.CppFlags = append(flags.CppFlags, flags.Toolchain.SystemCppCppflags())
-			flags.LdFlags = append(flags.LdFlags, flags.Toolchain.SystemCppLdflags())
-		}
-	case "ndk_system":
-		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")
-	case "ndk_libstlport_shared", "ndk_libstlport_static", "ndk_libgnustl_static":
-		// Nothing
-	case "":
-		// None or error.
-		if ctx.Host() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
-			if c.staticBinary() {
-				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.HostType()]...)
-			} else {
-				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.HostType()]...)
-			}
-		}
-	default:
-		panic(fmt.Errorf("Unknown stl in CCLinked.Flags: %q", stl))
-	}
-
-	return flags
-}
-
-func (c *CCLinked) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
-	depNames = c.CCBase.depNames(ctx, depNames)
-
-	stl := c.stl(ctx)
-	if ctx.Failed() {
-		return depNames
-	}
-
-	switch stl {
-	case "libstdc++":
-		if ctx.Device() {
-			depNames.SharedLibs = append(depNames.SharedLibs, stl)
-		}
-	case "libc++", "libc++_static":
-		if stl == "libc++" {
-			depNames.SharedLibs = append(depNames.SharedLibs, stl)
-		} else {
-			depNames.StaticLibs = append(depNames.StaticLibs, stl)
-		}
-		if ctx.Device() {
-			if ctx.Arch().ArchType == common.Arm {
-				depNames.StaticLibs = append(depNames.StaticLibs, "libunwind_llvm")
-			}
-			if c.staticBinary() {
-				depNames.StaticLibs = append(depNames.StaticLibs, "libdl")
-			} else {
-				depNames.SharedLibs = append(depNames.SharedLibs, "libdl")
-			}
-		}
-	case "":
-		// None or error.
-	case "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().
-		depNames.SharedLibs = append([]string{"libstdc++"}, depNames.SharedLibs...)
-	case "ndk_libc++_shared", "ndk_libstlport_shared":
-		depNames.SharedLibs = append(depNames.SharedLibs, stl)
-	case "ndk_libc++_static", "ndk_libstlport_static", "ndk_libgnustl_static":
-		depNames.StaticLibs = append(depNames.StaticLibs, stl)
-	default:
-		panic(fmt.Errorf("Unknown stl in CCLinked.depNames: %q", stl))
-	}
+func (linker *baseLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
+	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
 
 	if ctx.ModuleName() != "libcompiler_rt-extras" {
-		depNames.StaticLibs = append(depNames.StaticLibs, "libcompiler_rt-extras")
+		deps.StaticLibs = append(deps.StaticLibs, "libcompiler_rt-extras")
 	}
 
 	if ctx.Device() {
 		// libgcc and libatomic have to be last on the command line
-		depNames.LateStaticLibs = append(depNames.LateStaticLibs, "libatomic")
-		if !Bool(c.Properties.No_libgcc) {
-			depNames.LateStaticLibs = append(depNames.LateStaticLibs, "libgcc")
+		deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
+		if !Bool(linker.Properties.No_libgcc) {
+			deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc")
 		}
 
-		if !c.static() {
-			depNames.SharedLibs = append(depNames.SharedLibs, c.systemSharedLibs(ctx)...)
+		if !linker.static() {
+			if linker.Properties.System_shared_libs != nil {
+				deps.LateSharedLibs = append(deps.LateSharedLibs,
+					linker.Properties.System_shared_libs...)
+			} else if !ctx.sdk() {
+				deps.LateSharedLibs = append(deps.LateSharedLibs, "libc", "libm")
+			}
 		}
 
-		if c.Properties.Sdk_version != "" {
-			version := c.Properties.Sdk_version
-			depNames.SharedLibs = append(depNames.SharedLibs,
+		if ctx.sdk() {
+			version := ctx.sdkVersion()
+			deps.SharedLibs = append(deps.SharedLibs,
 				"ndk_libc."+version,
 				"ndk_libm."+version,
 			)
 		}
 	}
 
-	return depNames
+	return deps
 }
 
-// ccLinkedInterface interface is used on ccLinked to deal with static or shared variants
-type ccLinkedInterface interface {
+func (linker *baseLinker) flags(ctx ModuleContext, flags Flags) Flags {
+	toolchain := ctx.toolchain()
+
+	flags.LdFlags = append(flags.LdFlags, linker.Properties.Ldflags...)
+
+	if !ctx.noDefaultCompilerFlags() {
+		if ctx.Device() && !Bool(linker.Properties.Allow_undefined_symbols) {
+			flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
+		}
+
+		if flags.Clang {
+			flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
+		} else {
+			flags.LdFlags = append(flags.LdFlags, toolchain.Ldflags())
+		}
+
+		if ctx.Host() {
+			flags.LdFlags = append(flags.LdFlags, linker.Properties.Host_ldlibs...)
+		}
+	}
+
+	if !flags.Clang {
+		flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainLdflags())
+	}
+
+	return flags
+}
+
+func (linker *baseLinker) static() bool {
+	return linker.dynamicProperties.VariantIsStatic
+}
+
+func (linker *baseLinker) staticBinary() bool {
+	return linker.dynamicProperties.VariantIsStaticBinary
+}
+
+func (linker *baseLinker) setStatic(static bool) {
+	linker.dynamicProperties.VariantIsStatic = static
+}
+
+type baseLinkerInterface interface {
 	// Returns true if the build options for the module have selected a static or shared build
 	buildStatic() bool
 	buildShared() bool
@@ -1093,172 +1184,63 @@
 	staticBinary() bool
 }
 
-var _ ccLinkedInterface = (*CCLibrary)(nil)
-var _ ccLinkedInterface = (*CCBinary)(nil)
-
-func (c *CCLinked) static() bool {
-	return c.dynamicProperties.VariantIsStatic
-}
-
-func (c *CCLinked) staticBinary() bool {
-	return c.dynamicProperties.VariantIsStaticBinary
-}
-
-func (c *CCLinked) setStatic(static bool) {
-	c.dynamicProperties.VariantIsStatic = static
-}
-
-type ccExportedFlagsProducer interface {
+type exportedFlagsProducer interface {
 	exportedFlags() []string
 }
 
+type baseInstaller struct {
+	Properties InstallerProperties
+
+	dir   string
+	dir64 string
+	data  bool
+
+	path common.Path
+}
+
+var _ installer = (*baseInstaller)(nil)
+
+func (installer *baseInstaller) props() []interface{} {
+	return []interface{}{&installer.Properties}
+}
+
+func (installer *baseInstaller) install(ctx ModuleContext, file common.Path) {
+	subDir := installer.dir
+	if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
+		subDir = installer.dir64
+	}
+	dir := common.PathForModuleInstall(ctx, subDir, installer.Properties.Relative_install_path)
+	installer.path = ctx.InstallFile(dir, file)
+}
+
+func (installer *baseInstaller) inData() bool {
+	return installer.data
+}
+
 //
 // Combined static+shared libraries
 //
 
-type CCLibraryProperties struct {
-	BuildStatic bool `blueprint:"mutated"`
-	BuildShared bool `blueprint:"mutated"`
-	Static      struct {
-		Srcs              []string `android:"arch_variant"`
-		Exclude_srcs      []string `android:"arch_variant"`
-		Cflags            []string `android:"arch_variant"`
-		Whole_static_libs []string `android:"arch_variant"`
-		Static_libs       []string `android:"arch_variant"`
-		Shared_libs       []string `android:"arch_variant"`
-	} `android:"arch_variant"`
-	Shared struct {
-		Srcs              []string `android:"arch_variant"`
-		Exclude_srcs      []string `android:"arch_variant"`
-		Cflags            []string `android:"arch_variant"`
-		Whole_static_libs []string `android:"arch_variant"`
-		Static_libs       []string `android:"arch_variant"`
-		Shared_libs       []string `android:"arch_variant"`
-	} `android:"arch_variant"`
+type libraryCompiler struct {
+	baseCompiler
 
-	// local file name to pass to the linker as --version_script
-	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"`
-	// local file name to pass to the linker as -force_symbols_not_weak_list
-	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"`
-}
+	linker     *libraryLinker
+	Properties LibraryCompilerProperties
 
-type CCLibrary struct {
-	CCLinked
-
-	reuseFrom     ccLibraryInterface
+	// For reusing static library objects for shared library
+	reuseFrom     *libraryCompiler
 	reuseObjFiles common.Paths
-	objFiles      common.Paths
-	exportFlags   []string
-	out           common.Path
-	systemLibs    []string
-
-	// If we're used as a whole_static_lib, our missing dependencies need
-	// to be given
-	wholeStaticMissingDeps []string
-
-	LibraryProperties CCLibraryProperties
 }
 
-func (c *CCLibrary) buildStatic() bool {
-	return c.LibraryProperties.BuildStatic
+var _ compiler = (*libraryCompiler)(nil)
+
+func (library *libraryCompiler) props() []interface{} {
+	props := library.baseCompiler.props()
+	return append(props, &library.Properties)
 }
 
-func (c *CCLibrary) buildShared() bool {
-	return c.LibraryProperties.BuildShared
-}
-
-type ccLibraryInterface interface {
-	ccLinkedInterface
-	ccLibrary() *CCLibrary
-	setReuseFrom(ccLibraryInterface)
-	getReuseFrom() ccLibraryInterface
-	getReuseObjFiles() common.Paths
-	allObjFiles() common.Paths
-	getWholeStaticMissingDeps() []string
-}
-
-var _ ccLibraryInterface = (*CCLibrary)(nil)
-
-func (c *CCLibrary) ccLibrary() *CCLibrary {
-	return c
-}
-
-func NewCCLibrary(library *CCLibrary, module CCModuleType,
-	hod common.HostOrDeviceSupported) (blueprint.Module, []interface{}) {
-
-	return newCCDynamic(&library.CCLinked, module, hod, common.MultilibBoth,
-		&library.LibraryProperties)
-}
-
-func CCLibraryFactory() (blueprint.Module, []interface{}) {
-	module := &CCLibrary{}
-
-	module.LibraryProperties.BuildShared = true
-	module.LibraryProperties.BuildStatic = true
-
-	return NewCCLibrary(module, module, common.HostAndDeviceSupported)
-}
-
-func (c *CCLibrary) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
-	depNames = c.CCLinked.depNames(ctx, depNames)
-	if c.static() {
-		depNames.WholeStaticLibs = append(depNames.WholeStaticLibs, c.LibraryProperties.Static.Whole_static_libs...)
-		depNames.StaticLibs = append(depNames.StaticLibs, c.LibraryProperties.Static.Static_libs...)
-		depNames.SharedLibs = append(depNames.SharedLibs, c.LibraryProperties.Static.Shared_libs...)
-	} else {
-		if ctx.Device() {
-			if c.Properties.Sdk_version == "" {
-				depNames.CrtBegin = "crtbegin_so"
-				depNames.CrtEnd = "crtend_so"
-			} else {
-				depNames.CrtBegin = "ndk_crtbegin_so." + c.Properties.Sdk_version
-				depNames.CrtEnd = "ndk_crtend_so." + c.Properties.Sdk_version
-			}
-		}
-		depNames.WholeStaticLibs = append(depNames.WholeStaticLibs, c.LibraryProperties.Shared.Whole_static_libs...)
-		depNames.StaticLibs = append(depNames.StaticLibs, c.LibraryProperties.Shared.Static_libs...)
-		depNames.SharedLibs = append(depNames.SharedLibs, c.LibraryProperties.Shared.Shared_libs...)
-	}
-
-	c.systemLibs = c.systemSharedLibs(ctx)
-
-	return depNames
-}
-
-func (c *CCLibrary) outputFile() common.OptionalPath {
-	return common.OptionalPathForPath(c.out)
-}
-
-func (c *CCLibrary) getReuseObjFiles() common.Paths {
-	return c.reuseObjFiles
-}
-
-func (c *CCLibrary) setReuseFrom(reuseFrom ccLibraryInterface) {
-	c.reuseFrom = reuseFrom
-}
-
-func (c *CCLibrary) getReuseFrom() ccLibraryInterface {
-	return c.reuseFrom
-}
-
-func (c *CCLibrary) allObjFiles() common.Paths {
-	return c.objFiles
-}
-
-func (c *CCLibrary) getWholeStaticMissingDeps() []string {
-	return c.wholeStaticMissingDeps
-}
-
-func (c *CCLibrary) exportedFlags() []string {
-	return c.exportFlags
-}
-
-func (c *CCLibrary) flags(ctx common.AndroidModuleContext, flags CCFlags) CCFlags {
-	flags = c.CCLinked.flags(ctx, flags)
+func (library *libraryCompiler) flags(ctx ModuleContext, flags Flags) Flags {
+	flags = library.baseCompiler.flags(ctx, flags)
 
 	// MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
 	// all code is position independent, and then those warnings get promoted to
@@ -1267,13 +1249,71 @@
 		flags.CFlags = append(flags.CFlags, "-fPIC")
 	}
 
-	if c.static() {
-		flags.CFlags = append(flags.CFlags, c.LibraryProperties.Static.Cflags...)
+	if library.linker.static() {
+		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
 	} else {
-		flags.CFlags = append(flags.CFlags, c.LibraryProperties.Shared.Cflags...)
+		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
 	}
 
-	if !c.static() {
+	return flags
+}
+
+func (library *libraryCompiler) compile(ctx ModuleContext, flags Flags) common.Paths {
+	var objFiles common.Paths
+
+	if library.reuseFrom != library && library.reuseFrom.Properties.Static.Cflags == nil &&
+		library.Properties.Shared.Cflags == nil {
+		objFiles = append(common.Paths(nil), library.reuseFrom.reuseObjFiles...)
+	} else {
+		objFiles = library.baseCompiler.compile(ctx, flags)
+		library.reuseObjFiles = objFiles
+	}
+
+	if library.linker.static() {
+		objFiles = append(objFiles, library.compileObjs(ctx, flags, common.DeviceStaticLibrary,
+			library.Properties.Static.Srcs, library.Properties.Static.Exclude_srcs)...)
+	} else {
+		objFiles = append(objFiles, library.compileObjs(ctx, flags, common.DeviceSharedLibrary,
+			library.Properties.Shared.Srcs, library.Properties.Shared.Exclude_srcs)...)
+	}
+
+	return objFiles
+}
+
+type libraryLinker struct {
+	baseLinker
+
+	Properties LibraryLinkerProperties
+
+	dynamicProperties struct {
+		BuildStatic bool `blueprint:"mutated"`
+		BuildShared bool `blueprint:"mutated"`
+	}
+
+	exportFlags []string
+
+	// If we're used as a whole_static_lib, our missing dependencies need
+	// to be given
+	wholeStaticMissingDeps []string
+
+	// For whole_static_libs
+	objFiles common.Paths
+}
+
+var _ linker = (*libraryLinker)(nil)
+var _ exportedFlagsProducer = (*libraryLinker)(nil)
+
+func (library *libraryLinker) props() []interface{} {
+	props := library.baseLinker.props()
+	return append(props, &library.Properties, &library.dynamicProperties)
+}
+
+func (library *libraryLinker) flags(ctx ModuleContext, flags Flags) Flags {
+	flags = library.baseLinker.flags(ctx, flags)
+
+	flags.Nocrt = Bool(library.Properties.Nocrt)
+
+	if !library.static() {
 		libName := ctx.ModuleName()
 		// GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
 		sharedFlag := "-Wl,-shared"
@@ -1305,56 +1345,69 @@
 	return flags
 }
 
-func (c *CCLibrary) compileStaticLibrary(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
+func (library *libraryLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = library.baseLinker.deps(ctx, deps)
+	if library.static() {
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Static.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
+	} else {
+		if ctx.Device() && !Bool(library.Properties.Nocrt) {
+			if !ctx.sdk() {
+				deps.CrtBegin = "crtbegin_so"
+				deps.CrtEnd = "crtend_so"
+			} else {
+				deps.CrtBegin = "ndk_crtbegin_so." + ctx.sdkVersion()
+				deps.CrtEnd = "ndk_crtend_so." + ctx.sdkVersion()
+			}
+		}
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Shared.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
+	}
 
-	staticFlags := flags
-	objFilesStatic := c.customCompileObjs(ctx, staticFlags, common.DeviceStaticLibrary,
-		c.LibraryProperties.Static.Srcs, c.LibraryProperties.Static.Exclude_srcs)
+	return deps
+}
 
-	objFiles = append(objFiles, objFilesStatic...)
+func (library *libraryLinker) exportedFlags() []string {
+	return library.exportFlags
+}
+
+func (library *libraryLinker) linkStatic(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
+
 	objFiles = append(objFiles, deps.WholeStaticLibObjFiles...)
+	library.objFiles = objFiles
 
 	outputFile := common.PathForModuleOut(ctx, ctx.ModuleName()+staticLibraryExtension)
 
 	if ctx.Darwin() {
-		TransformDarwinObjToStaticLib(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
+		TransformDarwinObjToStaticLib(ctx, objFiles, flagsToBuilderFlags(flags), outputFile)
 	} else {
-		TransformObjToStaticLib(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
+		TransformObjToStaticLib(ctx, objFiles, flagsToBuilderFlags(flags), outputFile)
 	}
 
-	c.wholeStaticMissingDeps = ctx.GetMissingDependencies()
-
-	c.objFiles = objFiles
-	c.out = outputFile
-
-	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
-	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
-	c.exportFlags = append(c.exportFlags, deps.ReexportedCflags...)
+	library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
 
 	ctx.CheckbuildFile(outputFile)
+
+	return outputFile
 }
 
-func (c *CCLibrary) compileSharedLibrary(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
-
-	sharedFlags := flags
-	objFilesShared := c.customCompileObjs(ctx, sharedFlags, common.DeviceSharedLibrary,
-		c.LibraryProperties.Shared.Srcs, c.LibraryProperties.Shared.Exclude_srcs)
-
-	objFiles = append(objFiles, objFilesShared...)
+func (library *libraryLinker) linkShared(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
 
 	outputFile := common.PathForModuleOut(ctx, ctx.ModuleName()+flags.Toolchain.ShlibSuffix())
 
 	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)
+	versionScript := common.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
+	unexportedSymbols := common.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
+	forceNotWeakSymbols := common.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_not_weak_list)
+	forceWeakSymbols := common.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_weak_list)
 	if !ctx.Darwin() {
 		if versionScript.Valid() {
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,--version-script,"+versionScript.String())
+			flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
 			linkerDeps = append(linkerDeps, versionScript.Path())
 		}
 		if unexportedSymbols.Valid() {
@@ -1371,100 +1424,128 @@
 			ctx.PropertyErrorf("version_script", "Not supported on Darwin")
 		}
 		if unexportedSymbols.Valid() {
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
+			flags.LdFlags = append(flags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
 			linkerDeps = append(linkerDeps, unexportedSymbols.Path())
 		}
 		if forceNotWeakSymbols.Valid() {
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
+			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceNotWeakSymbols.Path())
 		}
 		if forceWeakSymbols.Valid() {
-			sharedFlags.LdFlags = append(sharedFlags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
+			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
 		}
 	}
 
-	TransformObjToDynamicBinary(ctx, objFiles, deps.SharedLibs, deps.StaticLibs,
-		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, false,
-		ccFlagsToBuilderFlags(sharedFlags), outputFile)
+	sharedLibs := deps.SharedLibs
+	sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
 
-	c.out = outputFile
-	includeDirs := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
-	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
-	c.exportFlags = append(c.exportFlags, deps.ReexportedCflags...)
+	TransformObjToDynamicBinary(ctx, objFiles, sharedLibs,
+		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
+		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, flagsToBuilderFlags(flags), outputFile)
+
+	return outputFile
 }
 
-func (c *CCLibrary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
+func (library *libraryLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
 
-	// Reuse the object files from the matching static library if it exists
-	if c.getReuseFrom().ccLibrary() == c {
-		c.reuseObjFiles = objFiles
+	var out common.Path
+	if library.static() {
+		out = library.linkStatic(ctx, flags, deps, objFiles)
 	} else {
-		if c.getReuseFrom().ccLibrary().LibraryProperties.Static.Cflags == nil &&
-			c.LibraryProperties.Shared.Cflags == nil {
-			objFiles = append(common.Paths(nil), c.getReuseFrom().getReuseObjFiles()...)
-		}
+		out = library.linkShared(ctx, flags, deps, objFiles)
 	}
 
-	if c.static() {
-		c.compileStaticLibrary(ctx, flags, deps, objFiles)
-	} else {
-		c.compileSharedLibrary(ctx, flags, deps, objFiles)
+	includeDirs := common.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)
+	library.exportFlags = []string{includeDirsToFlags(includeDirs)}
+	library.exportFlags = append(library.exportFlags, deps.ReexportedCflags...)
+
+	return out
+}
+
+func (library *libraryLinker) buildStatic() bool {
+	return library.dynamicProperties.BuildStatic
+}
+
+func (library *libraryLinker) buildShared() bool {
+	return library.dynamicProperties.BuildShared
+}
+
+func (library *libraryLinker) getWholeStaticMissingDeps() []string {
+	return library.wholeStaticMissingDeps
+}
+
+type libraryInstaller struct {
+	baseInstaller
+
+	linker *libraryLinker
+}
+
+func (library *libraryInstaller) install(ctx ModuleContext, file common.Path) {
+	if !library.linker.static() {
+		library.baseInstaller.install(ctx, file)
 	}
 }
 
-func (c *CCLibrary) installStaticLibrary(ctx common.AndroidModuleContext, flags CCFlags) {
-	// Static libraries do not get installed.
-}
+func NewLibrary(hod common.HostOrDeviceSupported, shared, static bool) *Module {
+	module := newModule(hod, common.MultilibBoth)
 
-func (c *CCLibrary) installSharedLibrary(ctx common.AndroidModuleContext, flags CCFlags) {
-	installDir := "lib"
-	if flags.Toolchain.Is64Bit() {
-		installDir = "lib64"
+	linker := &libraryLinker{}
+	linker.dynamicProperties.BuildShared = shared
+	linker.dynamicProperties.BuildStatic = static
+	module.linker = linker
+
+	module.compiler = &libraryCompiler{
+		linker: linker,
+	}
+	module.installer = &libraryInstaller{
+		baseInstaller: baseInstaller{
+			dir:   "lib",
+			dir64: "lib64",
+		},
+		linker: linker,
 	}
 
-	ctx.InstallFile(common.PathForModuleInstall(ctx, installDir, c.Properties.Relative_install_path), c.out)
+	return module
 }
 
-func (c *CCLibrary) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	if c.static() {
-		c.installStaticLibrary(ctx, flags)
-	} else {
-		c.installSharedLibrary(ctx, flags)
-	}
+func libraryFactory() (blueprint.Module, []interface{}) {
+	module := NewLibrary(common.HostAndDeviceSupported, true, true)
+	return module.Init()
 }
 
 //
 // Objects (for crt*.o)
 //
 
-type ccObjectProvider interface {
-	object() *ccObject
+type objectLinker struct {
 }
 
-type ccObject struct {
-	CCBase
-	out common.OptionalPath
+func objectFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	module.compiler = &baseCompiler{}
+	module.linker = &objectLinker{}
+	return module.Init()
 }
 
-func (c *ccObject) object() *ccObject {
-	return c
+func (*objectLinker) props() []interface{} {
+	return nil
 }
 
-func CCObjectFactory() (blueprint.Module, []interface{}) {
-	module := &ccObject{}
+func (*objectLinker) begin(ctx BaseModuleContext) {}
 
-	return newCCBase(&module.CCBase, module, common.DeviceSupported, common.MultilibBoth)
-}
-
-func (*ccObject) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
+func (*objectLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
 	// object files can't have any dynamic dependencies
-	return CCDeps{}
+	return deps
 }
 
-func (c *ccObject) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
+func (*objectLinker) flags(ctx ModuleContext, flags Flags) Flags {
+	return flags
+}
+
+func (object *objectLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
 
 	objFiles = append(objFiles, deps.ObjFiles...)
 
@@ -1473,128 +1554,115 @@
 		outputFile = objFiles[0]
 	} else {
 		output := common.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
-		TransformObjsToObj(ctx, objFiles, ccFlagsToBuilderFlags(flags), output)
+		TransformObjsToObj(ctx, objFiles, flagsToBuilderFlags(flags), output)
 		outputFile = output
 	}
 
-	c.out = common.OptionalPathForPath(outputFile)
-
 	ctx.CheckbuildFile(outputFile)
+	return outputFile
 }
 
-func (c *ccObject) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	// Object files do not get installed.
-}
-
-func (c *ccObject) outputFile() common.OptionalPath {
-	return c.out
-}
-
-var _ ccObjectProvider = (*ccObject)(nil)
-
 //
 // Executables
 //
 
-type CCBinaryProperties struct {
-	// compile executable with -static
-	Static_executable *bool
+type binaryLinker struct {
+	baseLinker
 
-	// set the name of the output
-	Stem string `android:"arch_variant"`
+	Properties BinaryLinkerProperties
 
-	// append to the name of the output
-	Suffix string `android:"arch_variant"`
-
-	// if set, add an extra objcopy --prefix-symbols= step
-	Prefix_symbols string
+	hostToolPath common.OptionalPath
 }
 
-type CCBinary struct {
-	CCLinked
-	out              common.Path
-	installFile      common.Path
-	BinaryProperties CCBinaryProperties
+var _ linker = (*binaryLinker)(nil)
+
+func (binary *binaryLinker) props() []interface{} {
+	return append(binary.baseLinker.props(), &binary.Properties)
 }
 
-func (c *CCBinary) buildStatic() bool {
-	return Bool(c.BinaryProperties.Static_executable)
+func (binary *binaryLinker) buildStatic() bool {
+	return Bool(binary.Properties.Static_executable)
 }
 
-func (c *CCBinary) buildShared() bool {
-	return !Bool(c.BinaryProperties.Static_executable)
+func (binary *binaryLinker) buildShared() bool {
+	return !Bool(binary.Properties.Static_executable)
 }
 
-func (c *CCBinary) getStem(ctx common.AndroidModuleContext) string {
+func (binary *binaryLinker) getStem(ctx BaseModuleContext) string {
 	stem := ctx.ModuleName()
-	if c.BinaryProperties.Stem != "" {
-		stem = c.BinaryProperties.Stem
+	if binary.Properties.Stem != "" {
+		stem = binary.Properties.Stem
 	}
 
-	return stem + c.BinaryProperties.Suffix
+	return stem + binary.Properties.Suffix
 }
 
-func (c *CCBinary) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
-	depNames = c.CCLinked.depNames(ctx, depNames)
+func (binary *binaryLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = binary.baseLinker.deps(ctx, deps)
 	if ctx.Device() {
-		if c.Properties.Sdk_version == "" {
-			if Bool(c.BinaryProperties.Static_executable) {
-				depNames.CrtBegin = "crtbegin_static"
+		if !ctx.sdk() {
+			if Bool(binary.Properties.Static_executable) {
+				deps.CrtBegin = "crtbegin_static"
 			} else {
-				depNames.CrtBegin = "crtbegin_dynamic"
+				deps.CrtBegin = "crtbegin_dynamic"
 			}
-			depNames.CrtEnd = "crtend_android"
+			deps.CrtEnd = "crtend_android"
 		} else {
-			if Bool(c.BinaryProperties.Static_executable) {
-				depNames.CrtBegin = "ndk_crtbegin_static." + c.Properties.Sdk_version
+			if Bool(binary.Properties.Static_executable) {
+				deps.CrtBegin = "ndk_crtbegin_static." + ctx.sdkVersion()
 			} else {
-				depNames.CrtBegin = "ndk_crtbegin_dynamic." + c.Properties.Sdk_version
+				deps.CrtBegin = "ndk_crtbegin_dynamic." + ctx.sdkVersion()
 			}
-			depNames.CrtEnd = "ndk_crtend_android." + c.Properties.Sdk_version
+			deps.CrtEnd = "ndk_crtend_android." + ctx.sdkVersion()
 		}
 
-		if Bool(c.BinaryProperties.Static_executable) {
-			if c.stl(ctx) == "libc++_static" {
-				depNames.StaticLibs = append(depNames.StaticLibs, "libm", "libc", "libdl")
+		if Bool(binary.Properties.Static_executable) {
+			if inList("libc++_static", deps.StaticLibs) {
+				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc", "libdl")
 			}
 			// static libraries libcompiler_rt, libc and libc_nomalloc need to be linked with
 			// --start-group/--end-group along with libgcc.  If they are in deps.StaticLibs,
 			// move them to the beginning of deps.LateStaticLibs
 			var groupLibs []string
-			depNames.StaticLibs, groupLibs = filterList(depNames.StaticLibs,
+			deps.StaticLibs, groupLibs = filterList(deps.StaticLibs,
 				[]string{"libc", "libc_nomalloc", "libcompiler_rt"})
-			depNames.LateStaticLibs = append(groupLibs, depNames.LateStaticLibs...)
+			deps.LateStaticLibs = append(groupLibs, deps.LateStaticLibs...)
 		}
 	}
-	return depNames
+
+	if !Bool(binary.Properties.Static_executable) && inList("libc", deps.StaticLibs) {
+		ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
+			"from static libs or set static_executable: true")
+	}
+	return deps
 }
 
-func NewCCBinary(binary *CCBinary, module CCModuleType,
-	hod common.HostOrDeviceSupported, multilib common.Multilib,
-	props ...interface{}) (blueprint.Module, []interface{}) {
-
-	props = append(props, &binary.BinaryProperties)
-
-	return newCCDynamic(&binary.CCLinked, module, hod, multilib, props...)
+func NewBinary(hod common.HostOrDeviceSupported) *Module {
+	module := newModule(hod, common.MultilibFirst)
+	module.compiler = &baseCompiler{}
+	module.linker = &binaryLinker{}
+	module.installer = &baseInstaller{
+		dir: "bin",
+	}
+	return module
 }
 
-func CCBinaryFactory() (blueprint.Module, []interface{}) {
-	module := &CCBinary{}
-
-	return NewCCBinary(module, module, common.HostAndDeviceSupported, common.MultilibFirst)
+func binaryFactory() (blueprint.Module, []interface{}) {
+	module := NewBinary(common.HostAndDeviceSupported)
+	return module.Init()
 }
 
-func (c *CCBinary) ModifyProperties(ctx CCModuleContext) {
+func (binary *binaryLinker) ModifyProperties(ctx ModuleContext) {
 	if ctx.Darwin() {
-		c.BinaryProperties.Static_executable = proptools.BoolPtr(false)
+		binary.Properties.Static_executable = proptools.BoolPtr(false)
 	}
-	if Bool(c.BinaryProperties.Static_executable) {
-		c.dynamicProperties.VariantIsStaticBinary = true
+	if Bool(binary.Properties.Static_executable) {
+		binary.dynamicProperties.VariantIsStaticBinary = true
 	}
 }
 
-func (c *CCBinary) flags(ctx common.AndroidModuleContext, flags CCFlags) CCFlags {
-	flags = c.CCLinked.flags(ctx, flags)
+func (binary *binaryLinker) flags(ctx ModuleContext, flags Flags) Flags {
+	flags = binary.baseLinker.flags(ctx, flags)
 
 	if ctx.Host() {
 		flags.LdFlags = append(flags.LdFlags, "-pie")
@@ -1611,7 +1679,7 @@
 	}
 
 	if ctx.Device() {
-		if Bool(c.BinaryProperties.Static_executable) {
+		if Bool(binary.Properties.Static_executable) {
 			// Clang driver needs -static to create static executable.
 			// However, bionic/linker uses -shared to overwrite.
 			// Linker for x86 targets does not allow coexistance of -static and -shared,
@@ -1629,7 +1697,7 @@
 		} else {
 			linker := "/system/bin/linker"
 			if flags.Toolchain.Is64Bit() {
-				linker = "/system/bin/linker64"
+				linker += "64"
 			}
 
 			flags.LdFlags = append(flags.LdFlags,
@@ -1648,86 +1716,69 @@
 	return flags
 }
 
-func (c *CCBinary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
+func (binary *binaryLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
 
-	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 := common.PathForModuleOut(ctx, binary.getStem(ctx)+flags.Toolchain.ExecutableSuffix())
+	if ctx.HostOrDevice().Host() {
+		binary.hostToolPath = common.OptionalPathForPath(outputFile)
 	}
+	ret := outputFile
 
-	outputFile := common.PathForModuleOut(ctx, c.getStem(ctx)+flags.Toolchain.ExecutableSuffix())
-	c.out = outputFile
-	if c.BinaryProperties.Prefix_symbols != "" {
+	if binary.Properties.Prefix_symbols != "" {
 		afterPrefixSymbols := outputFile
-		outputFile = common.PathForModuleOut(ctx, c.getStem(ctx)+".intermediate")
-		TransformBinaryPrefixSymbols(ctx, c.BinaryProperties.Prefix_symbols, outputFile,
-			ccFlagsToBuilderFlags(flags), afterPrefixSymbols)
+		outputFile = common.PathForModuleOut(ctx, binary.getStem(ctx)+".intermediate")
+		TransformBinaryPrefixSymbols(ctx, binary.Properties.Prefix_symbols, outputFile,
+			flagsToBuilderFlags(flags), afterPrefixSymbols)
 	}
 
 	var linkerDeps common.Paths
 
-	TransformObjToDynamicBinary(ctx, objFiles, deps.SharedLibs, deps.StaticLibs,
+	sharedLibs := deps.SharedLibs
+	sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
+
+	TransformObjToDynamicBinary(ctx, objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
-		ccFlagsToBuilderFlags(flags), outputFile)
+		flagsToBuilderFlags(flags), outputFile)
+
+	return ret
 }
 
-func (c *CCBinary) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	c.installFile = ctx.InstallFile(common.PathForModuleInstall(ctx, "bin", c.Properties.Relative_install_path), c.out)
+func (binary *binaryLinker) HostToolPath() common.OptionalPath {
+	return binary.hostToolPath
 }
 
-func (c *CCBinary) HostToolPath() common.OptionalPath {
-	if c.HostOrDevice().Host() {
-		return common.OptionalPathForPath(c.installFile)
-	}
-	return common.OptionalPath{}
-}
-
-func (c *CCBinary) binary() *CCBinary {
-	return c
-}
-
-type testPerSrc interface {
-	binary() *CCBinary
-	testPerSrc() bool
-}
-
-var _ testPerSrc = (*CCTest)(nil)
-
 func testPerSrcMutator(mctx common.AndroidBottomUpMutatorContext) {
-	if test, ok := mctx.Module().(testPerSrc); ok {
-		if test.testPerSrc() {
-			testNames := make([]string, len(test.binary().Properties.Srcs))
-			for i, src := range test.binary().Properties.Srcs {
-				testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
-			}
-			tests := mctx.CreateLocalVariations(testNames...)
-			for i, src := range test.binary().Properties.Srcs {
-				tests[i].(testPerSrc).binary().Properties.Srcs = []string{src}
-				tests[i].(testPerSrc).binary().BinaryProperties.Stem = testNames[i]
+	if m, ok := mctx.Module().(*Module); ok {
+		if test, ok := m.linker.(*testLinker); ok {
+			if Bool(test.Properties.Test_per_src) {
+				testNames := make([]string, len(m.compiler.(*baseCompiler).Properties.Srcs))
+				for i, src := range m.compiler.(*baseCompiler).Properties.Srcs {
+					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
+				}
+				tests := mctx.CreateLocalVariations(testNames...)
+				for i, src := range m.compiler.(*baseCompiler).Properties.Srcs {
+					tests[i].(*Module).compiler.(*baseCompiler).Properties.Srcs = []string{src}
+					tests[i].(*Module).linker.(*testLinker).binaryLinker.Properties.Stem = testNames[i]
+				}
 			}
 		}
 	}
 }
 
-type CCTestProperties struct {
-	// if set, build against the gtest library. Defaults to true.
-	Gtest bool
-
-	// Create a separate binary for each source file.  Useful when there is
-	// global state that can not be torn down and reset between each test suite.
-	Test_per_src *bool
+type testLinker struct {
+	binaryLinker
+	Properties TestLinkerProperties
 }
 
-type CCTest struct {
-	CCBinary
-
-	TestProperties CCTestProperties
+func (test *testLinker) props() []interface{} {
+	return append(test.binaryLinker.props(), &test.Properties)
 }
 
-func (c *CCTest) flags(ctx common.AndroidModuleContext, flags CCFlags) CCFlags {
-	flags = c.CCBinary.flags(ctx, flags)
-	if !c.TestProperties.Gtest {
+func (test *testLinker) flags(ctx ModuleContext, flags Flags) Flags {
+	flags = test.binaryLinker.flags(ctx, flags)
+
+	if !test.Properties.Gtest {
 		return flags
 	}
 
@@ -1752,175 +1803,159 @@
 	return flags
 }
 
-func (c *CCTest) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
-	if c.TestProperties.Gtest {
-		depNames.StaticLibs = append(depNames.StaticLibs, "libgtest_main", "libgtest")
+func (test *testLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
+	if test.Properties.Gtest {
+		deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
 	}
-	depNames = c.CCBinary.depNames(ctx, depNames)
-	return depNames
+	deps = test.binaryLinker.deps(ctx, deps)
+	return deps
 }
 
-func (c *CCTest) InstallInData() bool {
-	return true
+type testInstaller struct {
+	baseInstaller
 }
 
-func (c *CCTest) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	installDir := "nativetest"
-	if flags.Toolchain.Is64Bit() {
-		installDir = "nativetest64"
+func (installer *testInstaller) install(ctx ModuleContext, file common.Path) {
+	installer.dir = filepath.Join(installer.dir, ctx.ModuleName())
+	installer.dir64 = filepath.Join(installer.dir64, ctx.ModuleName())
+	installer.baseInstaller.install(ctx, file)
+}
+
+func NewTest(hod common.HostOrDeviceSupported) *Module {
+	module := newModule(hod, common.MultilibBoth)
+	module.compiler = &baseCompiler{}
+	linker := &testLinker{}
+	linker.Properties.Gtest = true
+	module.linker = linker
+	module.installer = &testInstaller{
+		baseInstaller: baseInstaller{
+			dir:   "nativetest",
+			dir64: "nativetest64",
+			data:  true,
+		},
 	}
-	ctx.InstallFile(common.PathForModuleInstall(ctx, installDir, ctx.ModuleName()), c.out)
+	return module
 }
 
-func (c *CCTest) testPerSrc() bool {
-	return Bool(c.TestProperties.Test_per_src)
+func testFactory() (blueprint.Module, []interface{}) {
+	module := NewTest(common.HostAndDeviceSupported)
+	return module.Init()
 }
 
-func NewCCTest(test *CCTest, module CCModuleType,
-	hod common.HostOrDeviceSupported, props ...interface{}) (blueprint.Module, []interface{}) {
-
-	props = append(props, &test.TestProperties)
-
-	return NewCCBinary(&test.CCBinary, module, hod, common.MultilibBoth, props...)
+type benchmarkLinker struct {
+	binaryLinker
 }
 
-func CCTestFactory() (blueprint.Module, []interface{}) {
-	module := &CCTest{}
-	module.TestProperties.Gtest = true
-
-	return NewCCTest(module, module, common.HostAndDeviceSupported)
+func (benchmark *benchmarkLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = benchmark.binaryLinker.deps(ctx, deps)
+	deps.StaticLibs = append(deps.StaticLibs, "libbenchmark", "libbase")
+	return deps
 }
 
-type CCBenchmark struct {
-	CCBinary
-}
-
-func (c *CCBenchmark) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
-	depNames = c.CCBinary.depNames(ctx, depNames)
-	depNames.StaticLibs = append(depNames.StaticLibs, "libbenchmark", "libbase")
-	return depNames
-}
-
-func (c *CCBenchmark) InstallInData() bool {
-	return true
-}
-
-func (c *CCBenchmark) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	if ctx.Device() {
-		installDir := "nativetest"
-		if flags.Toolchain.Is64Bit() {
-			installDir = "nativetest64"
-		}
-		ctx.InstallFile(common.PathForModuleInstall(ctx, installDir, ctx.ModuleName()), c.out)
-	} else {
-		c.CCBinary.installModule(ctx, flags)
+func NewBenchmark(hod common.HostOrDeviceSupported) *Module {
+	module := newModule(hod, common.MultilibFirst)
+	module.compiler = &baseCompiler{}
+	module.linker = &benchmarkLinker{}
+	module.installer = &baseInstaller{
+		dir:   "nativetest",
+		dir64: "nativetest64",
+		data:  true,
 	}
+	return module
 }
 
-func NewCCBenchmark(test *CCBenchmark, module CCModuleType,
-	hod common.HostOrDeviceSupported, props ...interface{}) (blueprint.Module, []interface{}) {
-
-	return NewCCBinary(&test.CCBinary, module, hod, common.MultilibFirst, props...)
-}
-
-func CCBenchmarkFactory() (blueprint.Module, []interface{}) {
-	module := &CCBenchmark{}
-
-	return NewCCBenchmark(module, module, common.HostAndDeviceSupported)
+func benchmarkFactory() (blueprint.Module, []interface{}) {
+	module := NewBenchmark(common.HostAndDeviceSupported)
+	return module.Init()
 }
 
 //
 // Static library
 //
 
-func CCLibraryStaticFactory() (blueprint.Module, []interface{}) {
-	module := &CCLibrary{}
-	module.LibraryProperties.BuildStatic = true
-
-	return NewCCLibrary(module, module, common.HostAndDeviceSupported)
+func libraryStaticFactory() (blueprint.Module, []interface{}) {
+	module := NewLibrary(common.HostAndDeviceSupported, false, true)
+	return module.Init()
 }
 
 //
 // Shared libraries
 //
 
-func CCLibrarySharedFactory() (blueprint.Module, []interface{}) {
-	module := &CCLibrary{}
-	module.LibraryProperties.BuildShared = true
-
-	return NewCCLibrary(module, module, common.HostAndDeviceSupported)
+func librarySharedFactory() (blueprint.Module, []interface{}) {
+	module := NewLibrary(common.HostAndDeviceSupported, true, false)
+	return module.Init()
 }
 
 //
 // Host static library
 //
 
-func CCLibraryHostStaticFactory() (blueprint.Module, []interface{}) {
-	module := &CCLibrary{}
-	module.LibraryProperties.BuildStatic = true
-
-	return NewCCLibrary(module, module, common.HostSupported)
+func libraryHostStaticFactory() (blueprint.Module, []interface{}) {
+	module := NewLibrary(common.HostSupported, false, true)
+	return module.Init()
 }
 
 //
 // Host Shared libraries
 //
 
-func CCLibraryHostSharedFactory() (blueprint.Module, []interface{}) {
-	module := &CCLibrary{}
-	module.LibraryProperties.BuildShared = true
-
-	return NewCCLibrary(module, module, common.HostSupported)
+func libraryHostSharedFactory() (blueprint.Module, []interface{}) {
+	module := NewLibrary(common.HostSupported, true, false)
+	return module.Init()
 }
 
 //
 // Host Binaries
 //
 
-func CCBinaryHostFactory() (blueprint.Module, []interface{}) {
-	module := &CCBinary{}
-
-	return NewCCBinary(module, module, common.HostSupported, common.MultilibFirst)
+func binaryHostFactory() (blueprint.Module, []interface{}) {
+	module := NewBinary(common.HostSupported)
+	return module.Init()
 }
 
 //
 // Host Tests
 //
 
-func CCTestHostFactory() (blueprint.Module, []interface{}) {
-	module := &CCTest{}
-	return NewCCTest(module, module, common.HostSupported)
+func testHostFactory() (blueprint.Module, []interface{}) {
+	module := NewTest(common.HostSupported)
+	return module.Init()
 }
 
 //
 // Host Benchmarks
 //
 
-func CCBenchmarkHostFactory() (blueprint.Module, []interface{}) {
-	module := &CCBenchmark{}
-	return NewCCBinary(&module.CCBinary, module, common.HostSupported, common.MultilibFirst)
+func benchmarkHostFactory() (blueprint.Module, []interface{}) {
+	module := NewBenchmark(common.HostSupported)
+	return module.Init()
 }
 
 //
 // Defaults
 //
-type CCDefaults struct {
+type Defaults struct {
 	common.AndroidModuleBase
 	common.DefaultsModule
 }
 
-func (*CCDefaults) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+func (*Defaults) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
 }
 
-func CCDefaultsFactory() (blueprint.Module, []interface{}) {
-	module := &CCDefaults{}
+func defaultsFactory() (blueprint.Module, []interface{}) {
+	module := &Defaults{}
 
 	propertyStructs := []interface{}{
-		&CCBaseProperties{},
-		&CCLibraryProperties{},
-		&CCBinaryProperties{},
-		&CCTestProperties{},
-		&CCUnusedProperties{},
+		&BaseProperties{},
+		&BaseCompilerProperties{},
+		&BaseLinkerProperties{},
+		&LibraryCompilerProperties{},
+		&LibraryLinkerProperties{},
+		&BinaryLinkerProperties{},
+		&TestLinkerProperties{},
+		&UnusedProperties{},
+		&StlProperties{},
 	}
 
 	_, propertyStructs = common.InitAndroidArchModule(module, common.HostAndDeviceDefault,
@@ -1933,27 +1968,35 @@
 // Device libraries shipped with gcc
 //
 
-type toolchainLibrary struct {
-	CCLibrary
+type toolchainLibraryLinker struct {
+	baseLinker
 }
 
-func (*toolchainLibrary) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
+var _ baseLinkerInterface = (*toolchainLibraryLinker)(nil)
+
+func (*toolchainLibraryLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
 	// toolchain libraries can't have any dependencies
-	return CCDeps{}
+	return deps
 }
 
-func ToolchainLibraryFactory() (blueprint.Module, []interface{}) {
-	module := &toolchainLibrary{}
+func (*toolchainLibraryLinker) buildStatic() bool {
+	return true
+}
 
-	module.LibraryProperties.BuildStatic = true
+func (*toolchainLibraryLinker) buildShared() bool {
+	return false
+}
+
+func toolchainLibraryFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	module.compiler = &baseCompiler{}
+	module.linker = &toolchainLibraryLinker{}
 	module.Properties.Clang = proptools.BoolPtr(false)
-
-	return newCCBase(&module.CCBase, module, common.DeviceSupported, common.MultilibBoth,
-		&module.LibraryProperties)
+	return module.Init()
 }
 
-func (c *toolchainLibrary) compileModule(ctx common.AndroidModuleContext,
-	flags CCFlags, deps CCPathDeps, objFiles common.Paths) {
+func (library *toolchainLibraryLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles common.Paths) common.Path {
 
 	libName := ctx.ModuleName() + staticLibraryExtension
 	outputFile := common.PathForModuleOut(ctx, libName)
@@ -1962,15 +2005,11 @@
 		ctx.ModuleErrorf("toolchain_library must use GCC, not Clang")
 	}
 
-	CopyGccLib(ctx, libName, ccFlagsToBuilderFlags(flags), outputFile)
-
-	c.out = outputFile
+	CopyGccLib(ctx, libName, flagsToBuilderFlags(flags), outputFile)
 
 	ctx.CheckbuildFile(outputFile)
-}
 
-func (c *toolchainLibrary) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	// Toolchain libraries do not get installed.
+	return outputFile
 }
 
 // NDK prebuilt libraries.
@@ -1994,95 +2033,94 @@
 	return dir.Join(ctx, name+ext)
 }
 
-type ndkPrebuiltObject struct {
-	ccObject
+type ndkPrebuiltObjectLinker struct {
+	objectLinker
 }
 
-func (*ndkPrebuiltObject) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
+func (*ndkPrebuiltObjectLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
 	// NDK objects can't have any dependencies
-	return CCDeps{}
+	return deps
 }
 
-func NdkPrebuiltObjectFactory() (blueprint.Module, []interface{}) {
-	module := &ndkPrebuiltObject{}
-	return newCCBase(&module.CCBase, module, common.DeviceSupported, common.MultilibBoth)
+func ndkPrebuiltObjectFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	module.linker = &ndkPrebuiltObjectLinker{}
+	return module.Init()
 }
 
-func (c *ndkPrebuiltObject) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCPathDeps, objFiles common.Paths) {
+func (c *ndkPrebuiltObjectLinker) link(ctx ModuleContext, flags Flags,
+	deps PathDeps, objFiles common.Paths) common.Path {
 	// 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 = common.OptionalPathForPath(ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, c.Properties.Sdk_version))
+	return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, ctx.sdkVersion())
 }
 
-func (c *ndkPrebuiltObject) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	// Objects do not get installed.
+type ndkPrebuiltLibraryLinker struct {
+	libraryLinker
+	Properties struct {
+		Export_include_dirs []string `android:"arch_variant"`
+	}
 }
 
-var _ ccObjectProvider = (*ndkPrebuiltObject)(nil)
+var _ baseLinkerInterface = (*ndkPrebuiltLibraryLinker)(nil)
+var _ exportedFlagsProducer = (*libraryLinker)(nil)
 
-type ndkPrebuiltLibrary struct {
-	CCLibrary
+func (ndk *ndkPrebuiltLibraryLinker) props() []interface{} {
+	return []interface{}{&ndk.Properties}
 }
 
-func (*ndkPrebuiltLibrary) depNames(ctx common.AndroidBaseContext, depNames CCDeps) CCDeps {
+func (*ndkPrebuiltLibraryLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
 	// NDK libraries can't have any dependencies
-	return CCDeps{}
+	return deps
 }
 
-func NdkPrebuiltLibraryFactory() (blueprint.Module, []interface{}) {
-	module := &ndkPrebuiltLibrary{}
-	module.LibraryProperties.BuildShared = true
-	return NewCCLibrary(&module.CCLibrary, module, common.DeviceSupported)
+func ndkPrebuiltLibraryFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	linker := &ndkPrebuiltLibraryLinker{}
+	linker.dynamicProperties.BuildShared = true
+	module.linker = linker
+	return module.Init()
 }
 
-func (c *ndkPrebuiltLibrary) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCPathDeps, objFiles common.Paths) {
+func (ndk *ndkPrebuiltLibraryLinker) link(ctx ModuleContext, flags Flags,
+	deps PathDeps, objFiles common.Paths) common.Path {
 	// 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 := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
-	c.exportFlags = []string{common.JoinWithPrefix(includeDirs.Strings(), "-isystem ")}
+	includeDirs := common.PathsForModuleSrc(ctx, ndk.Properties.Export_include_dirs)
+	ndk.exportFlags = []string{common.JoinWithPrefix(includeDirs.Strings(), "-isystem ")}
 
-	c.out = ndkPrebuiltModuleToPath(ctx, flags.Toolchain, flags.Toolchain.ShlibSuffix(),
-		c.Properties.Sdk_version)
-}
-
-func (c *ndkPrebuiltLibrary) installModule(ctx common.AndroidModuleContext, flags CCFlags) {
-	// NDK prebuilt libraries do not get installed.
+	return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, flags.Toolchain.ShlibSuffix(),
+		ctx.sdkVersion())
 }
 
 // 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 ndkPrebuiltStlLinker struct {
+	ndkPrebuiltLibraryLinker
 }
 
-type ndkPrebuiltStaticStl struct {
-	ndkPrebuiltStl
+func ndkPrebuiltSharedStlFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	linker := &ndkPrebuiltStlLinker{}
+	linker.dynamicProperties.BuildShared = true
+	module.linker = linker
+	return module.Init()
 }
 
-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 ndkPrebuiltStaticStlFactory() (blueprint.Module, []interface{}) {
+	module := newBaseModule(common.DeviceSupported, common.MultilibBoth)
+	linker := &ndkPrebuiltStlLinker{}
+	linker.dynamicProperties.BuildStatic = true
+	module.linker = linker
+	return module.Init()
 }
 
 func getNdkStlLibDir(ctx common.AndroidModuleContext, toolchain Toolchain, stl string) common.SourcePath {
@@ -2106,49 +2144,53 @@
 	return common.PathForSource(ctx, "")
 }
 
-func (c *ndkPrebuiltStl) compileModule(ctx common.AndroidModuleContext, flags CCFlags,
-	deps CCPathDeps, objFiles common.Paths) {
+func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags,
+	deps PathDeps, objFiles common.Paths) common.Path {
 	// 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 := common.PathsForModuleSrc(ctx, c.Properties.Export_include_dirs)
-	c.exportFlags = []string{includeDirsToFlags(includeDirs)}
+	includeDirs := common.PathsForModuleSrc(ctx, ndk.Properties.Export_include_dirs)
+	ndk.exportFlags = []string{includeDirsToFlags(includeDirs)}
 
 	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
 	libExt := flags.Toolchain.ShlibSuffix()
-	if c.LibraryProperties.BuildStatic {
+	if ndk.dynamicProperties.BuildStatic {
 		libExt = staticLibraryExtension
 	}
 
 	stlName := strings.TrimSuffix(libName, "_shared")
 	stlName = strings.TrimSuffix(stlName, "_static")
 	libDir := getNdkStlLibDir(ctx, flags.Toolchain, stlName)
-	c.out = libDir.Join(ctx, libName+libExt)
+	return libDir.Join(ctx, libName+libExt)
 }
 
 func linkageMutator(mctx common.AndroidBottomUpMutatorContext) {
-	if c, ok := mctx.Module().(ccLinkedInterface); ok {
-		var modules []blueprint.Module
-		if c.buildStatic() && c.buildShared() {
-			modules = mctx.CreateLocalVariations("static", "shared")
-			modules[0].(ccLinkedInterface).setStatic(true)
-			modules[1].(ccLinkedInterface).setStatic(false)
-		} else if c.buildStatic() {
-			modules = mctx.CreateLocalVariations("static")
-			modules[0].(ccLinkedInterface).setStatic(true)
-		} else if c.buildShared() {
-			modules = mctx.CreateLocalVariations("shared")
-			modules[0].(ccLinkedInterface).setStatic(false)
-		} else {
-			panic(fmt.Errorf("ccLibrary %q not static or shared", mctx.ModuleName()))
-		}
+	if m, ok := mctx.Module().(*Module); ok {
+		if m.linker != nil {
+			if linker, ok := m.linker.(baseLinkerInterface); ok {
+				var modules []blueprint.Module
+				if linker.buildStatic() && linker.buildShared() {
+					modules = mctx.CreateLocalVariations("static", "shared")
+					modules[0].(*Module).linker.(baseLinkerInterface).setStatic(true)
+					modules[1].(*Module).linker.(baseLinkerInterface).setStatic(false)
+				} else if linker.buildStatic() {
+					modules = mctx.CreateLocalVariations("static")
+					modules[0].(*Module).linker.(baseLinkerInterface).setStatic(true)
+				} else if linker.buildShared() {
+					modules = mctx.CreateLocalVariations("shared")
+					modules[0].(*Module).linker.(baseLinkerInterface).setStatic(false)
+				} else {
+					panic(fmt.Errorf("library %q not static or shared", mctx.ModuleName()))
+				}
 
-		if _, ok := c.(ccLibraryInterface); ok {
-			reuseFrom := modules[0].(ccLibraryInterface)
-			for _, m := range modules {
-				m.(ccLibraryInterface).setReuseFrom(reuseFrom)
+				if _, ok := m.compiler.(*libraryCompiler); ok {
+					reuseFrom := modules[0].(*Module).compiler.(*libraryCompiler)
+					for _, m := range modules {
+						m.(*Module).compiler.(*libraryCompiler).reuseFrom = reuseFrom
+					}
+				}
 			}
 		}
 	}
diff --git a/cc/stl.go b/cc/stl.go
new file mode 100644
index 0000000..580570d
--- /dev/null
+++ b/cc/stl.go
@@ -0,0 +1,198 @@
+// Copyright 2016 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 cc
+
+import (
+	"android/soong/common"
+	"fmt"
+)
+
+type StlProperties struct {
+	// select the STL library to use.  Possible values are "libc++", "libc++_static",
+	// "stlport", "stlport_static", "ndk", "libstdc++", or "none".  Leave blank to select the
+	// default
+	Stl string
+
+	SelectedStl string `blueprint:"mutated"`
+}
+
+type stlFeature struct {
+	Properties StlProperties
+}
+
+var _ feature = (*stlFeature)(nil)
+
+func (stl *stlFeature) props() []interface{} {
+	return []interface{}{&stl.Properties}
+}
+
+func (stl *stlFeature) begin(ctx BaseModuleContext) {
+	stl.Properties.SelectedStl = func() string {
+		if ctx.sdk() && ctx.Device() {
+			switch stl.Properties.Stl {
+			case "":
+				return "ndk_system"
+			case "c++_shared", "c++_static",
+				"stlport_shared", "stlport_static",
+				"gnustl_static":
+				return "ndk_lib" + stl.Properties.Stl
+			default:
+				ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", stl.Properties.Stl)
+				return ""
+			}
+		} else if ctx.HostType() == common.Windows {
+			switch stl.Properties.Stl {
+			case "libc++", "libc++_static", "libstdc++", "":
+				// libc++ is not supported on mingw
+				return "libstdc++"
+			case "none":
+				return ""
+			default:
+				ctx.ModuleErrorf("stl: %q is not a supported STL", stl.Properties.Stl)
+				return ""
+			}
+		} else {
+			switch stl.Properties.Stl {
+			case "libc++", "libc++_static",
+				"libstdc++":
+				return stl.Properties.Stl
+			case "none":
+				return ""
+			case "":
+				if ctx.static() {
+					return "libc++_static"
+				} else {
+					return "libc++"
+				}
+			default:
+				ctx.ModuleErrorf("stl: %q is not a supported STL", stl.Properties.Stl)
+				return ""
+			}
+		}
+	}()
+}
+
+func (stl *stlFeature) deps(ctx BaseModuleContext, deps Deps) Deps {
+	switch stl.Properties.SelectedStl {
+	case "libstdc++":
+		if ctx.Device() {
+			deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
+		}
+	case "libc++", "libc++_static":
+		if stl.Properties.SelectedStl == "libc++" {
+			deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
+		} else {
+			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl)
+		}
+		if ctx.Device() {
+			if ctx.Arch().ArchType == common.Arm {
+				deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
+			}
+			if ctx.staticBinary() {
+				deps.StaticLibs = append(deps.StaticLibs, "libdl")
+			} else {
+				deps.SharedLibs = append(deps.SharedLibs, "libdl")
+			}
+		}
+	case "":
+		// None or error.
+	case "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().
+		deps.SharedLibs = append([]string{"libstdc++"}, deps.SharedLibs...)
+	case "ndk_libc++_shared", "ndk_libstlport_shared":
+		deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
+	case "ndk_libc++_static", "ndk_libstlport_static", "ndk_libgnustl_static":
+		deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl)
+	default:
+		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
+	}
+
+	return deps
+}
+
+func (stl *stlFeature) flags(ctx ModuleContext, flags Flags) Flags {
+	switch stl.Properties.SelectedStl {
+	case "libc++", "libc++_static":
+		flags.CFlags = append(flags.CFlags, "-D_USING_LIBCXX")
+		if ctx.Host() {
+			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
+			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
+			flags.LdFlags = append(flags.LdFlags, "-lpthread", "-lm")
+			if ctx.staticBinary() {
+				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.HostType()]...)
+			} else {
+				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.HostType()]...)
+			}
+		} else {
+			if ctx.Arch().ArchType == common.Arm {
+				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
+			}
+		}
+	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.
+		// Host builds will use GNU libstdc++.
+		if ctx.Device() {
+			flags.CFlags = append(flags.CFlags, "-I"+common.PathForSource(ctx, "bionic/libstdc++/include").String())
+		} else {
+			// Host builds will use the system C++. libc++ on Darwin, GNU libstdc++ everywhere else
+			flags.CppFlags = append(flags.CppFlags, flags.Toolchain.SystemCppCppflags())
+			flags.LdFlags = append(flags.LdFlags, flags.Toolchain.SystemCppLdflags())
+		}
+	case "ndk_system":
+		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")
+	case "ndk_libstlport_shared", "ndk_libstlport_static", "ndk_libgnustl_static":
+		// Nothing
+	case "":
+		// None or error.
+		if ctx.Host() {
+			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
+			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
+			if ctx.staticBinary() {
+				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.HostType()]...)
+			} else {
+				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.HostType()]...)
+			}
+		}
+	default:
+		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
+	}
+
+	return flags
+}
+
+var hostDynamicGccLibs, hostStaticGccLibs map[common.HostType][]string
+
+func init() {
+	hostDynamicGccLibs = map[common.HostType][]string{
+		common.Linux:  []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
+		common.Darwin: []string{"-lc", "-lSystem"},
+		common.Windows: []string{"-lmsvcr110", "-lmingw32", "-lgcc", "-lmoldname",
+			"-lmingwex", "-lmsvcrt", "-ladvapi32", "-lshell32", "-luser32",
+			"-lkernel32", "-lmingw32", "-lgcc", "-lmoldname", "-lmingwex",
+			"-lmsvcrt"},
+	}
+	hostStaticGccLibs = map[common.HostType][]string{
+		common.Linux:   []string{"-Wl,--start-group", "-lgcc", "-lgcc_eh", "-lc", "-Wl,--end-group"},
+		common.Darwin:  []string{"NO_STATIC_HOST_BINARIES_ON_DARWIN"},
+		common.Windows: []string{"NO_STATIC_HOST_BINARIES_ON_WINDOWS"},
+	}
+}
diff --git a/cc/util.go b/cc/util.go
index 2fc717b..c083744 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -72,7 +72,7 @@
 	return matches[1], nil
 }
 
-func ccFlagsToBuilderFlags(in CCFlags) builderFlags {
+func flagsToBuilderFlags(in Flags) builderFlags {
 	return builderFlags{
 		globalFlags: strings.Join(in.GlobalFlags, " "),
 		asFlags:     strings.Join(in.AsFlags, " "),
diff --git a/common/androidmk.go b/common/androidmk.go
index 9628a10..49380e0 100644
--- a/common/androidmk.go
+++ b/common/androidmk.go
@@ -39,10 +39,11 @@
 type AndroidMkData struct {
 	Class      string
 	OutputFile OptionalPath
+	Disabled   bool
 
 	Custom func(w io.Writer, name, prefix string) error
 
-	Extra func(w io.Writer, outputFile Path) error
+	Extra []func(w io.Writer, outputFile Path) error
 }
 
 func AndroidMkSingleton() blueprint.Singleton {
@@ -166,6 +167,10 @@
 		return data.Custom(w, name, prefix)
 	}
 
+	if data.Disabled {
+		return nil
+	}
+
 	if !data.OutputFile.Valid() {
 		return err
 	}
@@ -189,8 +194,8 @@
 		fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
 	}
 
-	if data.Extra != nil {
-		err = data.Extra(w, data.OutputFile.Path())
+	for _, extra := range data.Extra {
+		err = extra(w, data.OutputFile.Path())
 		if err != nil {
 			return err
 		}
diff --git a/common/arch.go b/common/arch.go
index ee1cfae..0e9c7fe 100644
--- a/common/arch.go
+++ b/common/arch.go
@@ -557,12 +557,14 @@
 	for _, properties := range base.generalProperties {
 		propertiesValue := reflect.ValueOf(properties)
 		if propertiesValue.Kind() != reflect.Ptr {
-			panic("properties must be a pointer to a struct")
+			panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
 		}
 
 		propertiesValue = propertiesValue.Elem()
 		if propertiesValue.Kind() != reflect.Struct {
-			panic("properties must be a pointer to a struct")
+			panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
 		}
 
 		archProperties := &archProperties{}