Refactor cc modules to use decorators instead of inheritance

For example , instead of trying to have libraryLinker inherit from
baseLinker and libraryCompiler inherit from baseCompiler, create a
single decorator object that wraps both baseLinker and baseCompiler.

Test: Builds, no unexpected changes to build.ninja
Change-Id: I2468adaea8466c203a240259ba5694b8b1df7a52
diff --git a/cc/androidmk.go b/cc/androidmk.go
index df44a4c..4986387 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -26,6 +26,23 @@
 
 type AndroidMkContext interface {
 	Target() android.Target
+	subAndroidMk(*android.AndroidMkData, interface{})
+}
+
+type subAndroidMkProvider interface {
+	AndroidMk(AndroidMkContext, *android.AndroidMkData)
+}
+
+func (c *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+	if c.subAndroidMkOnce == nil {
+		c.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+	}
+	if androidmk, ok := obj.(subAndroidMkProvider); ok {
+		if !c.subAndroidMkOnce[androidmk] {
+			c.subAndroidMkOnce[androidmk] = true
+			androidmk.AndroidMk(c, data)
+		}
+	}
 }
 
 func (c *Module) AndroidMk() (ret android.AndroidMkData, err error) {
@@ -50,43 +67,27 @@
 		return nil
 	})
 
-	callSubAndroidMk := func(obj interface{}) {
-		if obj != nil {
-			if androidmk, ok := obj.(interface {
-				AndroidMk(AndroidMkContext, *android.AndroidMkData)
-			}); ok {
-				androidmk.AndroidMk(c, &ret)
-			}
-		}
-	}
-
 	for _, feature := range c.features {
-		callSubAndroidMk(feature)
+		c.subAndroidMk(&ret, feature)
 	}
 
-	callSubAndroidMk(c.compiler)
-	callSubAndroidMk(c.linker)
-	if c.linker.installable() {
-		callSubAndroidMk(c.installer)
-	}
+	c.subAndroidMk(&ret, c.compiler)
+	c.subAndroidMk(&ret, c.linker)
+	c.subAndroidMk(&ret, c.installer)
 
 	return ret, nil
 }
 
-func (library *baseLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	if !library.static() {
+		ctx.subAndroidMk(ret, &library.stripper)
+	}
+
 	if library.static() {
 		ret.Class = "STATIC_LIBRARIES"
 	} else {
 		ret.Class = "SHARED_LIBRARIES"
 	}
-}
-
-func (library *libraryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	library.baseLinker.AndroidMk(ctx, ret)
-
-	if !library.static() {
-		library.stripper.AndroidMk(ctx, ret)
-	}
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
 		var exportedIncludes []string
@@ -106,6 +107,10 @@
 
 		return nil
 	})
+
+	if !library.static() {
+		ctx.subAndroidMk(ret, library.baseInstaller)
+	}
 }
 
 func (object *objectLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
@@ -119,30 +124,39 @@
 	}
 }
 
-func (binary *binaryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	binary.stripper.AndroidMk(ctx, ret)
+func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, &binary.stripper)
 
 	ret.Class = "EXECUTABLES"
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
 		fmt.Fprintln(w, "LOCAL_CXX_STL := none")
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
-		if binary.static() {
+		if Bool(binary.Properties.Static_executable) {
 			fmt.Fprintln(w, "LOCAL_FORCE_STATIC_EXECUTABLE := true")
 		}
 		return nil
 	})
 }
 
-func (test *testBinaryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	test.binaryLinker.AndroidMk(ctx, ret)
-	if Bool(test.testLinker.Properties.Test_per_src) {
-		ret.SubName = "_" + test.binaryLinker.Properties.Stem
+func (benchmark *benchmarkDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, benchmark.binaryDecorator)
+	ctx.subAndroidMk(ret, benchmark.baseInstaller)
+}
+
+func (test *testBinary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, test.binaryDecorator)
+	ctx.subAndroidMk(ret, test.baseInstaller)
+	if Bool(test.Properties.Test_per_src) {
+		ret.SubName = "_" + test.binaryDecorator.Properties.Stem
 	}
 }
 
-func (library *toolchainLibraryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	library.baseLinker.AndroidMk(ctx, ret)
+func (test *testLibrary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, test.libraryDecorator)
+}
 
+func (library *toolchainLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ret.Class = "STATIC_LIBRARIES"
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
 		fmt.Fprintln(w, "LOCAL_CXX_STL := none")
@@ -185,13 +199,11 @@
 	})
 }
 
-func (c *stubCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (c *stubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.SubName = "." + strconv.Itoa(c.properties.ApiLevel)
-}
 
-func (installer *stubInstaller) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
-		path, file := filepath.Split(installer.installPath)
+		path, file := filepath.Split(c.installPath)
 		stem := strings.TrimSuffix(file, filepath.Ext(file))
 		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
diff --git a/cc/binary.go b/cc/binary.go
index fd64cda..2dbce66 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -16,6 +16,7 @@
 
 import (
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong"
 	"android/soong/android"
@@ -42,13 +43,13 @@
 
 // Module factory for binaries
 func binaryFactory() (blueprint.Module, []interface{}) {
-	module := NewBinary(android.HostAndDeviceSupported)
+	module, _ := NewBinary(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
 // Module factory for host binaries
 func binaryHostFactory() (blueprint.Module, []interface{}) {
-	module := NewBinary(android.HostSupported)
+	module, _ := NewBinary(android.HostSupported)
 	return module.Init()
 }
 
@@ -56,8 +57,8 @@
 // Executables
 //
 
-type binaryLinker struct {
-	baseLinker
+type binaryDecorator struct {
+	*baseLinker
 	stripper
 
 	Properties BinaryLinkerProperties
@@ -65,24 +66,16 @@
 	hostToolPath android.OptionalPath
 }
 
-var _ linker = (*binaryLinker)(nil)
+var _ linker = (*binaryDecorator)(nil)
 
-func (binary *binaryLinker) linkerProps() []interface{} {
+func (binary *binaryDecorator) linkerProps() []interface{} {
 	return append(binary.baseLinker.linkerProps(),
 		&binary.Properties,
 		&binary.stripper.StripProperties)
 
 }
 
-func (binary *binaryLinker) buildStatic() bool {
-	return binary.baseLinker.staticBinary()
-}
-
-func (binary *binaryLinker) buildShared() bool {
-	return !binary.baseLinker.staticBinary()
-}
-
-func (binary *binaryLinker) getStem(ctx BaseModuleContext) string {
+func (binary *binaryDecorator) getStem(ctx BaseModuleContext) string {
 	stem := ctx.ModuleName()
 	if binary.Properties.Stem != "" {
 		stem = binary.Properties.Stem
@@ -91,22 +84,22 @@
 	return stem + binary.Properties.Suffix
 }
 
-func (binary *binaryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+func (binary *binaryDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
 	if ctx.Device() {
 		if !Bool(binary.baseLinker.Properties.Nocrt) {
 			if !ctx.sdk() {
-				if binary.buildStatic() {
+				if binary.static() {
 					deps.CrtBegin = "crtbegin_static"
 				} else {
 					deps.CrtBegin = "crtbegin_dynamic"
 				}
 				deps.CrtEnd = "crtend_android"
 			} else {
-				if binary.buildStatic() {
+				if binary.static() {
 					deps.CrtBegin = "ndk_crtbegin_static." + ctx.sdkVersion()
 				} else {
-					if Bool(binary.Properties.Static_executable) {
+					if binary.static() {
 						deps.CrtBegin = "ndk_crtbegin_static." + ctx.sdkVersion()
 					} else {
 						deps.CrtBegin = "ndk_crtbegin_dynamic." + ctx.sdkVersion()
@@ -116,7 +109,7 @@
 			}
 		}
 
-		if binary.buildStatic() {
+		if binary.static() {
 			if inList("libc++_static", deps.StaticLibs) {
 				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc", "libdl")
 			}
@@ -130,55 +123,55 @@
 		}
 	}
 
-	if binary.buildShared() && inList("libc", deps.StaticLibs) {
+	if !binary.static() && 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 (*binaryLinker) installable() bool {
+func (binary *binaryDecorator) isDependencyRoot() bool {
 	return true
 }
 
-func (binary *binaryLinker) isDependencyRoot() bool {
-	return true
-}
-
-func NewBinary(hod android.HostOrDeviceSupported) *Module {
+func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
 	module := newModule(hod, android.MultilibFirst)
-	module.compiler = &baseCompiler{}
-	module.linker = &binaryLinker{}
-	module.installer = &baseInstaller{
-		dir: "bin",
+	binary := &binaryDecorator{
+		baseLinker: NewBaseLinker(),
 	}
-	return module
+	module.compiler = NewBaseCompiler()
+	module.linker = binary
+	module.installer = NewBaseInstaller("bin", "", InstallInSystem)
+	return module, binary
 }
 
-func (binary *binaryLinker) linkerInit(ctx BaseModuleContext) {
+func (binary *binaryDecorator) linkerInit(ctx BaseModuleContext) {
 	binary.baseLinker.linkerInit(ctx)
 
-	static := Bool(binary.Properties.Static_executable)
 	if ctx.Host() {
 		if ctx.Os() == android.Linux {
 			if binary.Properties.Static_executable == nil && Bool(ctx.AConfig().ProductVariables.HostStaticBinaries) {
-				static = true
+				binary.Properties.Static_executable = proptools.BoolPtr(true)
 			}
 		} else {
 			// Static executables are not supported on Darwin or Windows
-			static = false
+			binary.Properties.Static_executable = nil
 		}
 	}
-	if static {
-		binary.dynamicProperties.VariantIsStatic = true
-		binary.dynamicProperties.VariantIsStaticBinary = true
-	}
 }
 
-func (binary *binaryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+func (binary *binaryDecorator) static() bool {
+	return Bool(binary.Properties.Static_executable)
+}
+
+func (binary *binaryDecorator) staticBinary() bool {
+	return binary.static()
+}
+
+func (binary *binaryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = binary.baseLinker.linkerFlags(ctx, flags)
 
-	if ctx.Host() && !binary.staticBinary() {
+	if ctx.Host() && !binary.static() {
 		flags.LdFlags = append(flags.LdFlags, "-pie")
 		if ctx.Os() == android.Windows {
 			flags.LdFlags = append(flags.LdFlags, "-Wl,-e_mainCRTStartup")
@@ -193,7 +186,7 @@
 	}
 
 	if ctx.Device() {
-		if binary.buildStatic() {
+		if binary.static() {
 			// 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,
@@ -225,7 +218,7 @@
 			)
 		}
 	} else {
-		if binary.staticBinary() {
+		if binary.static() {
 			flags.LdFlags = append(flags.LdFlags, "-static")
 		}
 		if ctx.Darwin() {
@@ -236,7 +229,7 @@
 	return flags
 }
 
-func (binary *binaryLinker) link(ctx ModuleContext,
+func (binary *binaryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
 
 	fileName := binary.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
@@ -277,6 +270,6 @@
 	return ret
 }
 
-func (binary *binaryLinker) HostToolPath() android.OptionalPath {
+func (binary *binaryDecorator) HostToolPath() android.OptionalPath {
 	return binary.hostToolPath
 }
diff --git a/cc/cc.go b/cc/cc.go
index 8af2b8c..b381897 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -35,8 +35,6 @@
 func init() {
 	soong.RegisterModuleType("cc_defaults", defaultsFactory)
 
-	soong.RegisterModuleType("toolchain_library", toolchainLibraryFactory)
-
 	// 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
 	// functions will run first.
@@ -189,7 +187,6 @@
 
 	link(ctx ModuleContext, flags Flags, deps PathDeps, objFiles android.Paths) android.Path
 	appendLdflags([]string)
-	installable() bool
 }
 
 type installer interface {
@@ -252,6 +249,8 @@
 	outputFile android.OptionalPath
 
 	cachedToolchain config.Toolchain
+
+	subAndroidMkOnce map[subAndroidMkProvider]bool
 }
 
 func (c *Module) Init() (blueprint.Module, []interface{}) {
@@ -283,6 +282,17 @@
 	return android.InitDefaultableModule(c, c, props...)
 }
 
+// Returns true for dependency roots (binaries)
+// TODO(ccross): also handle dlopenable libraries
+func (c *Module) isDependencyRoot() bool {
+	if root, ok := c.linker.(interface {
+		isDependencyRoot() bool
+	}); ok {
+		return root.isDependencyRoot()
+	}
+	return false
+}
+
 type baseModuleContext struct {
 	android.BaseContext
 	moduleContextImpl
@@ -322,25 +332,21 @@
 }
 
 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 static, ok := ctx.mod.linker.(interface {
+		static() bool
+	}); ok {
+		return static.static()
 	}
-	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()))
-	}
+	return false
 }
 
 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 static, ok := ctx.mod.linker.(interface {
+		staticBinary() bool
+	}); ok {
+		return static.staticBinary()
 	}
-	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()))
-	}
+	return false
 }
 
 func (ctx *moduleContextImpl) noDefaultCompilerFlags() bool {
@@ -453,7 +459,7 @@
 		}
 		c.outputFile = android.OptionalPathForPath(outputFile)
 
-		if c.installer != nil && c.linker.installable() {
+		if c.installer != nil {
 			c.installer.install(ctx, outputFile)
 			if ctx.Failed() {
 				return
@@ -680,7 +686,7 @@
 			// Platform code can link to anything
 			return
 		}
-		if _, ok := to.linker.(*toolchainLibraryLinker); ok {
+		if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
 			// These are always allowed
 			return
 		}
@@ -692,7 +698,7 @@
 			// These are allowed, but don't set sdk_version
 			return
 		}
-		if _, ok := to.linker.(*stubLinker); ok {
+		if _, ok := to.linker.(*stubDecorator); ok {
 			// These aren't real libraries, but are the stub shared libraries that are included in
 			// the NDK.
 			return
@@ -794,7 +800,7 @@
 
 		if tag == reuseObjTag {
 			depPaths.ObjFiles = append(depPaths.ObjFiles,
-				cc.compiler.(*libraryCompiler).reuseObjFiles...)
+				cc.compiler.(libraryInterface).reuseObjs()...)
 			return
 		}
 
@@ -824,8 +830,8 @@
 			depPtr = &depPaths.LateStaticLibs
 		case wholeStaticDepTag:
 			depPtr = &depPaths.WholeStaticLibs
-			staticLib, _ := cc.linker.(libraryInterface)
-			if staticLib == nil || !staticLib.static() {
+			staticLib, ok := cc.linker.(libraryInterface)
+			if !ok || !staticLib.static() {
 				ctx.ModuleErrorf("module %q not a static library", name)
 				return
 			}
@@ -882,11 +888,11 @@
 		&BaseProperties{},
 		&BaseCompilerProperties{},
 		&BaseLinkerProperties{},
-		&LibraryCompilerProperties{},
+		&LibraryProperties{},
 		&FlagExporterProperties{},
-		&LibraryLinkerProperties{},
 		&BinaryLinkerProperties{},
-		&TestLinkerProperties{},
+		&TestProperties{},
+		&TestBinaryProperties{},
 		&UnusedProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
@@ -899,58 +905,6 @@
 	return android.InitDefaultsModule(module, module, propertyStructs...)
 }
 
-//
-// Device libraries shipped with gcc
-//
-
-type toolchainLibraryLinker struct {
-	baseLinker
-}
-
-var _ baseLinkerInterface = (*toolchainLibraryLinker)(nil)
-
-func (*toolchainLibraryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
-	// toolchain libraries can't have any dependencies
-	return deps
-}
-
-func (*toolchainLibraryLinker) buildStatic() bool {
-	return true
-}
-
-func (*toolchainLibraryLinker) buildShared() bool {
-	return false
-}
-
-func toolchainLibraryFactory() (blueprint.Module, []interface{}) {
-	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	module.compiler = &baseCompiler{}
-	module.linker = &toolchainLibraryLinker{}
-	module.Properties.Clang = proptools.BoolPtr(false)
-	return module.Init()
-}
-
-func (library *toolchainLibraryLinker) link(ctx ModuleContext,
-	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
-
-	libName := ctx.ModuleName() + staticLibraryExtension
-	outputFile := android.PathForModuleOut(ctx, libName)
-
-	if flags.Clang {
-		ctx.ModuleErrorf("toolchain_library must use GCC, not Clang")
-	}
-
-	CopyGccLib(ctx, libName, flagsToBuilderFlags(flags), outputFile)
-
-	ctx.CheckbuildFile(outputFile)
-
-	return outputFile
-}
-
-func (*toolchainLibraryLinker) installable() bool {
-	return false
-}
-
 // lastUniqueElements returns all unique elements of a slice, keeping the last copy of each
 // modifies the slice contents in place, and returns a subslice of the original slice
 func lastUniqueElements(list []string) []string {
diff --git a/cc/compiler.go b/cc/compiler.go
index 0182491..4a7bba9 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -89,6 +89,10 @@
 	} `android:"arch_variant"`
 }
 
+func NewBaseCompiler() *baseCompiler {
+	return &baseCompiler{}
+}
+
 type baseCompiler struct {
 	Properties BaseCompilerProperties
 }
@@ -311,7 +315,7 @@
 	pathDeps := deps.GeneratedHeaders
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
 	// Compile files listed in c.Properties.Srcs into objects
-	objFiles := compiler.compileObjs(ctx, flags, "",
+	objFiles := compileObjs(ctx, flags, "",
 		compiler.Properties.Srcs, compiler.Properties.Exclude_srcs,
 		deps.GeneratedSources, pathDeps)
 
@@ -323,7 +327,7 @@
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func (compiler *baseCompiler) compileObjs(ctx android.ModuleContext, flags Flags,
+func compileObjs(ctx android.ModuleContext, flags Flags,
 	subdir string, srcFiles, excludes []string, extraSrcs, deps android.Paths) android.Paths {
 
 	buildFlags := flagsToBuilderFlags(flags)
diff --git a/cc/installer.go b/cc/installer.go
index 9a1e1fa..a133bf2 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -30,12 +30,27 @@
 	Symlinks []string `android:"arch_variant"`
 }
 
+type installLocation int
+
+const (
+	InstallInSystem installLocation = 0
+	InstallInData                   = iota
+)
+
+func NewBaseInstaller(dir, dir64 string, location installLocation) *baseInstaller {
+	return &baseInstaller{
+		dir:      dir,
+		dir64:    dir64,
+		location: location,
+	}
+}
+
 type baseInstaller struct {
 	Properties InstallerProperties
 
-	dir   string
-	dir64 string
-	data  bool
+	dir      string
+	dir64    string
+	location installLocation
 
 	path android.OutputPath
 }
@@ -62,5 +77,5 @@
 }
 
 func (installer *baseInstaller) inData() bool {
-	return installer.data
+	return installer.location == InstallInData
 }
diff --git a/cc/library.go b/cc/library.go
index 2ae18e9..14ceb28 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -23,33 +23,22 @@
 	"android/soong/android"
 )
 
-type LibraryCompilerProperties struct {
+type LibraryProperties 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 FlagExporterProperties struct {
-	// 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"`
-}
-
-type LibraryLinkerProperties struct {
-	Static struct {
 		Enabled           *bool    `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"`
+
 		Enabled           *bool    `android:"arch_variant"`
 		Whole_static_libs []string `android:"arch_variant"`
 		Static_libs       []string `android:"arch_variant"`
@@ -69,6 +58,21 @@
 	Unique_host_soname *bool
 
 	VariantName string `blueprint:"mutated"`
+
+	// Build a static variant
+	BuildStatic bool `blueprint:"mutated"`
+	// Build a shared variant
+	BuildShared bool `blueprint:"mutated"`
+	// This variant is shared
+	VariantIsShared bool `blueprint:"mutated"`
+	// This variant is static
+	VariantIsStatic bool `blueprint:"mutated"`
+}
+
+type FlagExporterProperties struct {
+	// 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"`
 }
 
 func init() {
@@ -82,31 +86,31 @@
 // Module factory for combined static + shared libraries, device by default but with possible host
 // support
 func libraryFactory() (blueprint.Module, []interface{}) {
-	module := NewLibrary(android.HostAndDeviceSupported, true, true)
+	module, _ := NewLibrary(android.HostAndDeviceSupported, true, true)
 	return module.Init()
 }
 
 // Module factory for static libraries
 func libraryStaticFactory() (blueprint.Module, []interface{}) {
-	module := NewLibrary(android.HostAndDeviceSupported, false, true)
+	module, _ := NewLibrary(android.HostAndDeviceSupported, false, true)
 	return module.Init()
 }
 
 // Module factory for shared libraries
 func librarySharedFactory() (blueprint.Module, []interface{}) {
-	module := NewLibrary(android.HostAndDeviceSupported, true, false)
+	module, _ := NewLibrary(android.HostAndDeviceSupported, true, false)
 	return module.Init()
 }
 
 // Module factory for host static libraries
 func libraryHostStaticFactory() (blueprint.Module, []interface{}) {
-	module := NewLibrary(android.HostSupported, false, true)
+	module, _ := NewLibrary(android.HostSupported, false, true)
 	return module.Init()
 }
 
 // Module factory for host shared libraries
 func libraryHostSharedFactory() (blueprint.Module, []interface{}) {
-	module := NewLibrary(android.HostSupported, true, false)
+	module, _ := NewLibrary(android.HostSupported, true, false)
 	return module.Init()
 }
 
@@ -137,76 +141,17 @@
 
 var _ exportedFlagsProducer = (*flagExporter)(nil)
 
-type libraryCompiler struct {
-	baseCompiler
-
-	linker     *libraryLinker
-	Properties LibraryCompilerProperties
+// libraryDecorator wraps baseCompiler, baseLinker and baseInstaller to provide library-specific
+// functionality: static vs. shared linkage, reusing object files for shared libraries
+type libraryDecorator struct {
+	Properties LibraryProperties
 
 	// For reusing static library objects for shared library
 	reuseObjFiles android.Paths
-}
 
-var _ compiler = (*libraryCompiler)(nil)
-
-func (library *libraryCompiler) compilerProps() []interface{} {
-	props := library.baseCompiler.compilerProps()
-	return append(props, &library.Properties)
-}
-
-func (library *libraryCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags = library.baseCompiler.compilerFlags(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
-	// errors.
-	if ctx.Os() != android.Windows {
-		flags.CFlags = append(flags.CFlags, "-fPIC")
-	}
-
-	if library.linker.static() {
-		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
-	} else {
-		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
-	}
-
-	return flags
-}
-
-func (library *libraryCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
-	var objFiles android.Paths
-
-	objFiles = library.baseCompiler.compile(ctx, flags, deps)
-	library.reuseObjFiles = objFiles
-
-	pathDeps := deps.GeneratedHeaders
-	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
-
-	if library.linker.static() {
-		objFiles = append(objFiles, library.compileObjs(ctx, flags, android.DeviceStaticLibrary,
-			library.Properties.Static.Srcs, library.Properties.Static.Exclude_srcs,
-			nil, pathDeps)...)
-	} else {
-		objFiles = append(objFiles, library.compileObjs(ctx, flags, android.DeviceSharedLibrary,
-			library.Properties.Shared.Srcs, library.Properties.Shared.Exclude_srcs,
-			nil, pathDeps)...)
-	}
-
-	return objFiles
-}
-
-type libraryLinker struct {
-	baseLinker
 	flagExporter
 	stripper
 
-	Properties LibraryLinkerProperties
-
-	dynamicProperties struct {
-		BuildStatic bool `blueprint:"mutated"`
-		BuildShared bool `blueprint:"mutated"`
-	}
-
 	// If we're used as a whole_static_lib, our missing dependencies need
 	// to be given
 	wholeStaticMissingDeps []string
@@ -217,43 +162,40 @@
 	// Uses the module's name if empty, but can be overridden. Does not include
 	// shlib suffix.
 	libName string
+
+	sanitize *sanitize
+
+	// Decorated interafaces
+	*baseCompiler
+	*baseLinker
+	*baseInstaller
 }
 
-var _ linker = (*libraryLinker)(nil)
-
-type libraryInterface interface {
-	getWholeStaticMissingDeps() []string
-	static() bool
-	objs() android.Paths
-}
-
-func (library *libraryLinker) linkerProps() []interface{} {
-	props := library.baseLinker.linkerProps()
+func (library *libraryDecorator) linkerProps() []interface{} {
+	var props []interface{}
+	props = append(props, library.baseLinker.linkerProps()...)
 	return append(props,
 		&library.Properties,
-		&library.dynamicProperties,
 		&library.flagExporter.Properties,
 		&library.stripper.StripProperties)
 }
 
-func (library *libraryLinker) getLibName(ctx ModuleContext) string {
-	name := library.libName
-	if name == "" {
-		name = ctx.ModuleName()
-	}
-
-	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
-		if !strings.HasSuffix(name, "-host") {
-			name = name + "-host"
-		}
-	}
-
-	return name + library.Properties.VariantName
-}
-
-func (library *libraryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = library.baseLinker.linkerFlags(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
+	// errors.
+	if ctx.Os() != android.Windows {
+		flags.CFlags = append(flags.CFlags, "-fPIC")
+	}
+
+	if library.static() {
+		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
+	} else {
+		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
+	}
+
 	if !library.static() {
 		libName := library.getLibName(ctx)
 		// GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
@@ -288,10 +230,73 @@
 	return flags
 }
 
-func (library *libraryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
-	deps = library.baseLinker.linkerDeps(ctx, deps)
+func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
+	var objFiles android.Paths
+
+	objFiles = library.baseCompiler.compile(ctx, flags, deps)
+	library.reuseObjFiles = objFiles
+
+	pathDeps := deps.GeneratedHeaders
+	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
+
 	if library.static() {
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Static.Whole_static_libs...)
+		objFiles = append(objFiles, compileObjs(ctx, flags, android.DeviceStaticLibrary,
+			library.Properties.Static.Srcs, library.Properties.Static.Exclude_srcs,
+			nil, pathDeps)...)
+	} else {
+		objFiles = append(objFiles, compileObjs(ctx, flags, android.DeviceSharedLibrary,
+			library.Properties.Shared.Srcs, library.Properties.Shared.Exclude_srcs,
+			nil, pathDeps)...)
+	}
+
+	return objFiles
+}
+
+type libraryInterface interface {
+	getWholeStaticMissingDeps() []string
+	static() bool
+	objs() android.Paths
+	reuseObjs() android.Paths
+
+	// Returns true if the build options for the module have selected a static or shared build
+	buildStatic() bool
+	buildShared() bool
+
+	// Sets whether a specific variant is static or shared
+	setStatic(bool)
+}
+
+func (library *libraryDecorator) getLibName(ctx ModuleContext) string {
+	name := library.libName
+	if name == "" {
+		name = ctx.ModuleName()
+	}
+
+	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
+		if !strings.HasSuffix(name, "-host") {
+			name = name + "-host"
+		}
+	}
+
+	return name + library.Properties.VariantName
+}
+
+func (library *libraryDecorator) linkerInit(ctx BaseModuleContext) {
+	location := InstallInSystem
+	if library.sanitize.inData() {
+		location = InstallInData
+	}
+	library.baseInstaller.location = location
+
+	library.baseLinker.linkerInit(ctx)
+}
+
+func (library *libraryDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = library.baseLinker.linkerDeps(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 {
@@ -312,7 +317,7 @@
 	return deps
 }
 
-func (library *libraryLinker) linkStatic(ctx ModuleContext,
+func (library *libraryDecorator) linkStatic(ctx ModuleContext,
 	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
 
 	library.objFiles = append(android.Paths{}, deps.WholeStaticLibObjFiles...)
@@ -334,7 +339,7 @@
 	return outputFile
 }
 
-func (library *libraryLinker) linkShared(ctx ModuleContext,
+func (library *libraryDecorator) linkShared(ctx ModuleContext,
 	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
 
 	var linkerDeps android.Paths
@@ -397,7 +402,7 @@
 	return ret
 }
 
-func (library *libraryLinker) link(ctx ModuleContext,
+func (library *libraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
 
 	objFiles = append(objFiles, deps.ObjFiles...)
@@ -415,64 +420,92 @@
 	return out
 }
 
-func (library *libraryLinker) buildStatic() bool {
-	return library.dynamicProperties.BuildStatic &&
+func (library *libraryDecorator) buildStatic() bool {
+	return library.Properties.BuildStatic &&
 		(library.Properties.Static.Enabled == nil || *library.Properties.Static.Enabled)
 }
 
-func (library *libraryLinker) buildShared() bool {
-	return library.dynamicProperties.BuildShared &&
+func (library *libraryDecorator) buildShared() bool {
+	return library.Properties.BuildShared &&
 		(library.Properties.Shared.Enabled == nil || *library.Properties.Shared.Enabled)
 }
 
-func (library *libraryLinker) getWholeStaticMissingDeps() []string {
+func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
 	return library.wholeStaticMissingDeps
 }
 
-func (library *libraryLinker) installable() bool {
-	return !library.static()
-}
-
-func (library *libraryLinker) objs() android.Paths {
+func (library *libraryDecorator) objs() android.Paths {
 	return library.objFiles
 }
 
-type libraryInstaller struct {
-	baseInstaller
-
-	linker   *libraryLinker
-	sanitize *sanitize
+func (library *libraryDecorator) reuseObjs() android.Paths {
+	return library.reuseObjFiles
 }
 
-func (library *libraryInstaller) install(ctx ModuleContext, file android.Path) {
-	if !library.linker.static() {
+func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
+	if !ctx.static() {
 		library.baseInstaller.install(ctx, file)
 	}
 }
 
-func (library *libraryInstaller) inData() bool {
-	return library.baseInstaller.inData() || library.sanitize.inData()
+func (library *libraryDecorator) static() bool {
+	return library.Properties.VariantIsStatic
 }
 
-func NewLibrary(hod android.HostOrDeviceSupported, shared, static bool) *Module {
+func (library *libraryDecorator) setStatic(static bool) {
+	library.Properties.VariantIsStatic = static
+}
+
+func NewLibrary(hod android.HostOrDeviceSupported, shared, static bool) (*Module, *libraryDecorator) {
 	module := newModule(hod, android.MultilibBoth)
 
-	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",
+	library := &libraryDecorator{
+		Properties: LibraryProperties{
+			BuildShared: shared,
+			BuildStatic: static,
 		},
-		linker:   linker,
-		sanitize: module.sanitize,
+		baseCompiler:  NewBaseCompiler(),
+		baseLinker:    NewBaseLinker(),
+		baseInstaller: NewBaseInstaller("lib", "lib64", InstallInSystem),
+		sanitize:      module.sanitize,
 	}
 
-	return module
+	module.compiler = library
+	module.linker = library
+	module.installer = library
+
+	return module, library
+}
+
+func linkageMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
+		if library, ok := m.linker.(libraryInterface); ok {
+			var modules []blueprint.Module
+			if library.buildStatic() && library.buildShared() {
+				modules = mctx.CreateLocalVariations("static", "shared")
+				static := modules[0].(*Module)
+				shared := modules[1].(*Module)
+
+				static.linker.(libraryInterface).setStatic(true)
+				shared.linker.(libraryInterface).setStatic(false)
+
+				if staticCompiler, ok := static.compiler.(*libraryDecorator); ok {
+					sharedCompiler := shared.compiler.(*libraryDecorator)
+					if len(staticCompiler.Properties.Static.Cflags) == 0 &&
+						len(sharedCompiler.Properties.Shared.Cflags) == 0 {
+						// Optimize out compiling common .o files twice for static+shared libraries
+						mctx.AddInterVariantDependency(reuseObjTag, shared, static)
+						sharedCompiler.baseCompiler.Properties.Srcs = nil
+						sharedCompiler.baseCompiler.Properties.Generated_sources = nil
+					}
+				}
+			} else if library.buildStatic() {
+				modules = mctx.CreateLocalVariations("static")
+				modules[0].(*Module).linker.(libraryInterface).setStatic(true)
+			} else if library.buildShared() {
+				modules = mctx.CreateLocalVariations("shared")
+				modules[0].(*Module).linker.(libraryInterface).setStatic(false)
+			}
+		}
+	}
 }
diff --git a/cc/linker.go b/cc/linker.go
index 9767755..df19de7 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -14,6 +14,11 @@
 
 package cc
 
+import (
+	"android/soong/android"
+	"fmt"
+)
+
 // This file contains the basic functionality for linking against static libraries and shared
 // libraries.  Final linking into libraries or executables is handled in library.go, binary.go, etc.
 
@@ -67,14 +72,15 @@
 	Nocrt *bool `android:"arch_variant"`
 }
 
+func NewBaseLinker() *baseLinker {
+	return &baseLinker{}
+}
+
 // 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"`
-		RunPaths              []string `blueprint:"mutated"`
+		RunPaths []string `blueprint:"mutated"`
 	}
 }
 
@@ -84,9 +90,9 @@
 
 func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
 	if ctx.toolchain().Is64Bit() {
-		linker.dynamicProperties.RunPaths = []string{"../lib64", "lib64"}
+		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
 	} else {
-		linker.dynamicProperties.RunPaths = []string{"../lib", "lib"}
+		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib", "lib")
 	}
 }
 
@@ -113,7 +119,7 @@
 			deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc")
 		}
 
-		if !linker.static() {
+		if !ctx.static() {
 			if linker.Properties.System_shared_libs != nil {
 				deps.LateSharedLibs = append(deps.LateSharedLibs,
 					linker.Properties.System_shared_libs...)
@@ -158,7 +164,7 @@
 
 	flags.LdFlags = append(flags.LdFlags, linker.Properties.Ldflags...)
 
-	if ctx.Host() && !linker.static() {
+	if ctx.Host() {
 		rpath_prefix := `\$$ORIGIN/`
 		if ctx.Darwin() {
 			rpath_prefix = "@loader_path/"
@@ -178,37 +184,7 @@
 	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
-}
-
-func (linker *baseLinker) isDependencyRoot() bool {
-	return false
-}
-
-type baseLinkerInterface interface {
-	// Returns true if the build options for the module have selected a static or shared build
-	buildStatic() bool
-	buildShared() bool
-
-	// Sets whether a specific variant is static or shared
-	setStatic(bool)
-
-	// Returns whether a specific variant is a static library or binary
-	static() bool
-
-	// Returns whether a module is a static binary
-	staticBinary() bool
-
-	// Returns true for dependency roots (binaries)
-	// TODO(ccross): also handle dlopenable libraries
-	isDependencyRoot() bool
+func (linker *baseLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
+	panic(fmt.Errorf("baseLinker doesn't know how to link"))
 }
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 1dd02de..891b49a 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -89,12 +89,13 @@
 	ApiLevel int `blueprint:"mutated"`
 }
 
-type stubCompiler struct {
-	baseCompiler
+type stubDecorator struct {
+	*libraryDecorator
 
 	properties libraryProperties
 
-	linker *stubLinker
+	versionScriptPath android.ModuleGenPath
+	installPath       string
 }
 
 // OMG GO
@@ -106,7 +107,7 @@
 	}
 }
 
-func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubCompiler) {
+func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubDecorator) {
 	minVersion := 9 // Minimum version supported by the NDK.
 	// TODO(danalbert): Use PlatformSdkVersion when possible.
 	// This is an interesting case because for the moment we actually need 24
@@ -152,19 +153,19 @@
 
 	modules := mctx.CreateVariations(versionStrs...)
 	for i, module := range modules {
-		module.(*Module).compiler.(*stubCompiler).properties.ApiLevel = firstGenVersion + i
+		module.(*Module).compiler.(*stubDecorator).properties.ApiLevel = firstGenVersion + i
 	}
 }
 
 func ndkApiMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
-		if compiler, ok := m.compiler.(*stubCompiler); ok {
+		if compiler, ok := m.compiler.(*stubDecorator); ok {
 			generateStubApiVariants(mctx, compiler)
 		}
 	}
 }
 
-func (c *stubCompiler) compilerInit(ctx BaseModuleContext) {
+func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
 	c.baseCompiler.compilerInit(ctx)
 
 	name := strings.TrimSuffix(ctx.ModuleName(), ".ndk")
@@ -176,7 +177,7 @@
 	ndkMigratedLibs = append(ndkMigratedLibs, name)
 }
 
-func (c *stubCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
+func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
 	arch := ctx.Arch().ArchType.String()
 
 	if !strings.HasSuffix(ctx.ModuleName(), ndkLibrarySuffix) {
@@ -189,7 +190,7 @@
 	stubSrcPath := android.PathForModuleGen(ctx, stubSrcName)
 	versionScriptName := fileBase + ".map"
 	versionScriptPath := android.PathForModuleGen(ctx, versionScriptName)
-	c.linker.versionScriptPath = versionScriptPath
+	c.versionScriptPath = versionScriptPath
 	symbolFilePath := android.PathForModuleSrc(ctx, c.properties.Symbol_file)
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:    genStubSrc,
@@ -218,47 +219,31 @@
 	excludeSrcs := []string{}
 	extraSrcs := []android.Path{stubSrcPath}
 	extraDeps := []android.Path{}
-	return c.baseCompiler.compileObjs(ctx, flags, subdir, srcs, excludeSrcs,
+	return compileObjs(ctx, flags, subdir, srcs, excludeSrcs,
 		extraSrcs, extraDeps)
 }
 
-type stubLinker struct {
-	libraryLinker
-
-	versionScriptPath android.ModuleGenPath
-}
-
-func (linker *stubLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+func (linker *stubDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
 	return Deps{}
 }
 
-func (linker *stubLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	linker.libraryLinker.libName = strings.TrimSuffix(ctx.ModuleName(),
+func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	stub.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(),
 		ndkLibrarySuffix)
-	return linker.libraryLinker.linkerFlags(ctx, flags)
+	return stub.libraryDecorator.linkerFlags(ctx, flags)
 }
 
-func (linker *stubLinker) link(ctx ModuleContext, flags Flags, deps PathDeps,
+func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objFiles android.Paths) android.Path {
 
-	linkerScriptFlag := "-Wl,--version-script," + linker.versionScriptPath.String()
+	linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
 	flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
-	return linker.libraryLinker.link(ctx, flags, deps, objFiles)
+	return stub.libraryDecorator.link(ctx, flags, deps, objFiles)
 }
 
-type stubInstaller struct {
-	baseInstaller
-
-	compiler *stubCompiler
-
-	installPath string
-}
-
-var _ installer = (*stubInstaller)(nil)
-
-func (installer *stubInstaller) install(ctx ModuleContext, path android.Path) {
+func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
 	arch := ctx.Target().Arch.ArchType.Name
-	apiLevel := installer.compiler.properties.ApiLevel
+	apiLevel := stub.properties.ApiLevel
 
 	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
 	// architectures it's just installed to lib.
@@ -269,32 +254,26 @@
 
 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
 		"platforms/android-%d/arch-%s/usr/%s", apiLevel, arch, libDir))
-	installer.installPath = ctx.InstallFile(installDir, path).String()
+	stub.installPath = ctx.InstallFile(installDir, path).String()
 }
 
 func newStubLibrary() *Module {
-	module := newModule(android.DeviceSupported, android.MultilibBoth)
+	module, library := NewLibrary(android.DeviceSupported, true, false)
 	module.stl = nil
+	module.sanitize = nil
+	library.StripProperties.Strip.None = true
 
-	linker := &stubLinker{}
-	linker.dynamicProperties.BuildShared = true
-	linker.dynamicProperties.BuildStatic = false
-	linker.stripper.StripProperties.Strip.None = true
-	module.linker = linker
-
-	compiler := &stubCompiler{}
-	compiler.linker = linker
-	module.compiler = compiler
-	module.installer = &stubInstaller{baseInstaller{
-		dir:   "lib",
-		dir64: "lib64",
-	}, compiler, ""}
+	stub := &stubDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = stub
+	module.linker = stub
+	module.installer = stub
 
 	return module
 }
 
 func ndkLibraryFactory() (blueprint.Module, []interface{}) {
 	module := newStubLibrary()
-	return android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth,
-		&module.compiler.(*stubCompiler).properties)
+	return module.Init()
 }
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 2b24507..407a026 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -70,7 +70,11 @@
 
 func ndkPrebuiltObjectFactory() (blueprint.Module, []interface{}) {
 	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	module.linker = &ndkPrebuiltObjectLinker{}
+	module.linker = &ndkPrebuiltObjectLinker{
+		objectLinker: objectLinker{
+			baseLinker: NewBaseLinker(),
+		},
+	}
 	module.Properties.HideFromMake = true
 	return module.Init()
 }
@@ -86,14 +90,11 @@
 }
 
 type ndkPrebuiltLibraryLinker struct {
-	libraryLinker
+	*libraryDecorator
 }
 
-var _ baseLinkerInterface = (*ndkPrebuiltLibraryLinker)(nil)
-var _ exportedFlagsProducer = (*libraryLinker)(nil)
-
 func (ndk *ndkPrebuiltLibraryLinker) linkerProps() []interface{} {
-	return append(ndk.libraryLinker.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties)
+	return append(ndk.libraryDecorator.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties)
 }
 
 func (*ndkPrebuiltLibraryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
@@ -102,10 +103,14 @@
 }
 
 func ndkPrebuiltLibraryFactory() (blueprint.Module, []interface{}) {
-	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	linker := &ndkPrebuiltLibraryLinker{}
-	linker.dynamicProperties.BuildShared = true
+	module, library := NewLibrary(android.DeviceSupported, true, false)
+	linker := &ndkPrebuiltLibraryLinker{
+		libraryDecorator: library,
+	}
+	module.compiler = nil
 	module.linker = linker
+	module.installer = nil
+	module.stl = nil
 	module.Properties.HideFromMake = true
 	return module.Init()
 }
@@ -128,19 +133,29 @@
 }
 
 func ndkPrebuiltSharedStlFactory() (blueprint.Module, []interface{}) {
-	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	linker := &ndkPrebuiltStlLinker{}
-	linker.dynamicProperties.BuildShared = true
+	module, library := NewLibrary(android.DeviceSupported, true, false)
+	linker := &ndkPrebuiltStlLinker{
+		ndkPrebuiltLibraryLinker: ndkPrebuiltLibraryLinker{
+			libraryDecorator: library,
+		},
+	}
+	module.compiler = nil
 	module.linker = linker
+	module.installer = nil
 	module.Properties.HideFromMake = true
 	return module.Init()
 }
 
 func ndkPrebuiltStaticStlFactory() (blueprint.Module, []interface{}) {
-	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	linker := &ndkPrebuiltStlLinker{}
-	linker.dynamicProperties.BuildStatic = true
+	module, library := NewLibrary(android.DeviceSupported, false, true)
+	linker := &ndkPrebuiltStlLinker{
+		ndkPrebuiltLibraryLinker: ndkPrebuiltLibraryLinker{
+			libraryDecorator: library,
+		},
+	}
+	module.compiler = nil
 	module.linker = linker
+	module.installer = nil
 	module.Properties.HideFromMake = true
 	return module.Init()
 }
@@ -177,7 +192,7 @@
 
 	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
 	libExt := flags.Toolchain.ShlibSuffix()
-	if ndk.dynamicProperties.BuildStatic {
+	if ndk.Properties.BuildStatic {
 		libExt = staticLibraryExtension
 	}
 
@@ -186,40 +201,3 @@
 	libDir := getNdkStlLibDir(ctx, flags.Toolchain, stlName)
 	return libDir.Join(ctx, libName+libExt)
 }
-
-func linkageMutator(mctx android.BottomUpMutatorContext) {
-	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")
-					static := modules[0].(*Module)
-					shared := modules[1].(*Module)
-
-					static.linker.(baseLinkerInterface).setStatic(true)
-					shared.linker.(baseLinkerInterface).setStatic(false)
-
-					if staticCompiler, ok := static.compiler.(*libraryCompiler); ok {
-						sharedCompiler := shared.compiler.(*libraryCompiler)
-						if len(staticCompiler.Properties.Static.Cflags) == 0 &&
-							len(sharedCompiler.Properties.Shared.Cflags) == 0 {
-							// Optimize out compiling common .o files twice for static+shared libraries
-							mctx.AddInterVariantDependency(reuseObjTag, shared, static)
-							sharedCompiler.baseCompiler.Properties.Srcs = nil
-							sharedCompiler.baseCompiler.Properties.Generated_sources = nil
-						}
-					}
-				} 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()))
-				}
-			}
-		}
-	}
-}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 2eae360..04dce7b 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -96,7 +96,7 @@
 
 	ctx.VisitAllModules(func(module blueprint.Module) {
 		if m, ok := module.(*Module); ok {
-			if installer, ok := m.installer.(*stubInstaller); ok {
+			if installer, ok := m.installer.(*stubDecorator); ok {
 				installPaths = append(installPaths, installer.installPath)
 			}
 		}
diff --git a/cc/object.go b/cc/object.go
index fbb7b7f..c9f0a06 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -32,13 +32,16 @@
 }
 
 type objectLinker struct {
+	*baseLinker
 	Properties ObjectLinkerProperties
 }
 
 func objectFactory() (blueprint.Module, []interface{}) {
 	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	module.compiler = &baseCompiler{}
-	module.linker = &objectLinker{}
+	module.linker = &objectLinker{
+		baseLinker: NewBaseLinker(),
+	}
+	module.compiler = NewBaseCompiler()
 	return module.Init()
 }
 
@@ -84,7 +87,3 @@
 	ctx.CheckbuildFile(outputFile)
 	return outputFile
 }
-
-func (*objectLinker) installable() bool {
-	return false
-}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index e7f021a..fb7cb37 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -345,7 +345,7 @@
 func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
 		if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
-			if d, ok := c.linker.(baseLinkerInterface); ok && d.isDependencyRoot() && c.sanitize.Sanitizer(t) {
+			if c.isDependencyRoot() && c.sanitize.Sanitizer(t) {
 				modules := mctx.CreateVariations(t.String())
 				modules[0].(*Module).sanitize.SetSanitizer(t, true)
 				if mctx.AConfig().EmbeddedInMake() && !c.Host() {
diff --git a/cc/test.go b/cc/test.go
index d9b8571..3b89a10 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -24,10 +24,12 @@
 	"android/soong/android"
 )
 
-type TestLinkerProperties struct {
+type TestProperties struct {
 	// if set, build against the gtest library. Defaults to true.
 	Gtest bool
+}
 
+type TestBinaryProperties struct {
 	// 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
@@ -71,29 +73,50 @@
 	return module.Init()
 }
 
+type testPerSrc interface {
+	testPerSrc() bool
+	srcs() []string
+	setSrc(string, string)
+}
+
+func (test *testBinary) testPerSrc() bool {
+	return Bool(test.Properties.Test_per_src)
+}
+
+func (test *testBinary) srcs() []string {
+	return test.baseCompiler.Properties.Srcs
+}
+
+func (test *testBinary) setSrc(name, src string) {
+	test.baseCompiler.Properties.Srcs = []string{src}
+	test.binaryDecorator.Properties.Stem = name
+}
+
+var _ testPerSrc = (*testBinary)(nil)
+
 func testPerSrcMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
-		if test, ok := m.linker.(*testBinaryLinker); ok {
-			if Bool(test.testLinker.Properties.Test_per_src) {
-				testNames := make([]string, len(m.compiler.(*baseCompiler).Properties.Srcs))
-				for i, src := range m.compiler.(*baseCompiler).Properties.Srcs {
+		if test, ok := m.linker.(testPerSrc); ok {
+			if test.testPerSrc() && len(test.srcs()) > 0 {
+				testNames := make([]string, len(test.srcs()))
+				for i, src := range test.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.(*testBinaryLinker).binaryLinker.Properties.Stem = testNames[i]
+				for i, src := range test.srcs() {
+					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
 				}
 			}
 		}
 	}
 }
 
-type testLinker struct {
-	Properties TestLinkerProperties
+type testDecorator struct {
+	Properties TestProperties
+	linker     *baseLinker
 }
 
-func (test *testLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	if !test.Properties.Gtest {
 		return flags
 	}
@@ -119,7 +142,7 @@
 	return flags
 }
 
-func (test *testLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+func (test *testDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
 	if test.Properties.Gtest {
 		if ctx.sdk() && ctx.Device() {
 			switch ctx.selectedStl() {
@@ -134,123 +157,156 @@
 			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
 		}
 	}
+
 	return deps
 }
 
-type testBinaryLinker struct {
-	testLinker
-	binaryLinker
-}
-
-func (test *testBinaryLinker) linkerInit(ctx BaseModuleContext) {
-	test.binaryLinker.linkerInit(ctx)
+func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
 	runpath := "../../lib"
 	if ctx.toolchain().Is64Bit() {
 		runpath += "64"
 	}
-	test.dynamicProperties.RunPaths = append([]string{runpath}, test.dynamicProperties.RunPaths...)
+	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
 }
 
-func (test *testBinaryLinker) linkerProps() []interface{} {
-	return append(test.binaryLinker.linkerProps(), &test.testLinker.Properties)
+func (test *testDecorator) linkerProps() []interface{} {
+	return []interface{}{&test.Properties}
 }
 
-func (test *testBinaryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags = test.binaryLinker.linkerFlags(ctx, flags)
-	flags = test.testLinker.linkerFlags(ctx, flags)
-	return flags
+func NewTestInstaller() *baseInstaller {
+	return NewBaseInstaller("nativetest", "nativetest64", InstallInData)
 }
 
-func (test *testBinaryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
-	deps = test.testLinker.linkerDeps(ctx, deps)
-	deps = test.binaryLinker.linkerDeps(ctx, deps)
+type testBinary struct {
+	testDecorator
+	*binaryDecorator
+	*baseCompiler
+	*baseInstaller
+	Properties TestBinaryProperties
+}
+
+func (test *testBinary) linkerProps() []interface{} {
+	props := append(test.testDecorator.linkerProps(), test.binaryDecorator.linkerProps()...)
+	props = append(props, &test.Properties)
+	return props
+}
+
+func (test *testBinary) linkerInit(ctx BaseModuleContext) {
+	test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
+	test.binaryDecorator.linkerInit(ctx)
+}
+
+func (test *testBinary) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = test.testDecorator.linkerDeps(ctx, deps)
+	deps = test.binaryDecorator.linkerDeps(ctx, deps)
 	return deps
 }
 
-type testLibraryLinker struct {
-	testLinker
-	*libraryLinker
-}
-
-func (test *testLibraryLinker) linkerProps() []interface{} {
-	return append(test.libraryLinker.linkerProps(), &test.testLinker.Properties)
-}
-
-func (test *testLibraryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags = test.libraryLinker.linkerFlags(ctx, flags)
-	flags = test.testLinker.linkerFlags(ctx, flags)
+func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = test.binaryDecorator.linkerFlags(ctx, flags)
+	flags = test.testDecorator.linkerFlags(ctx, flags)
 	return flags
 }
 
-func (test *testLibraryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
-	deps = test.testLinker.linkerDeps(ctx, deps)
-	deps = test.libraryLinker.linkerDeps(ctx, deps)
-	return deps
-}
-
-type testInstaller struct {
-	baseInstaller
-}
-
-func (installer *testInstaller) install(ctx ModuleContext, file android.Path) {
-	installer.dir = filepath.Join(installer.dir, ctx.ModuleName())
-	installer.dir64 = filepath.Join(installer.dir64, ctx.ModuleName())
-	installer.baseInstaller.install(ctx, file)
+func (test *testBinary) install(ctx ModuleContext, file android.Path) {
+	test.baseInstaller.dir = filepath.Join("nativetest", ctx.ModuleName())
+	test.baseInstaller.dir64 = filepath.Join("nativetest64", ctx.ModuleName())
+	test.baseInstaller.install(ctx, file)
 }
 
 func NewTest(hod android.HostOrDeviceSupported) *Module {
-	module := newModule(hod, android.MultilibBoth)
-	module.compiler = &baseCompiler{}
-	linker := &testBinaryLinker{}
-	linker.testLinker.Properties.Gtest = true
-	module.linker = linker
-	module.installer = &testInstaller{
-		baseInstaller: baseInstaller{
-			dir:   "nativetest",
-			dir64: "nativetest64",
-			data:  true,
+	module, binary := NewBinary(hod)
+	module.multilib = android.MultilibBoth
+
+	test := &testBinary{
+		testDecorator: testDecorator{
+			linker: binary.baseLinker,
 		},
+		binaryDecorator: binary,
+		baseCompiler:    NewBaseCompiler(),
+		baseInstaller:   NewTestInstaller(),
 	}
+	test.testDecorator.Properties.Gtest = true
+	module.compiler = test
+	module.linker = test
+	module.installer = test
 	return module
 }
 
+type testLibrary struct {
+	testDecorator
+	*libraryDecorator
+}
+
+func (test *testLibrary) linkerProps() []interface{} {
+	return append(test.testDecorator.linkerProps(), test.libraryDecorator.linkerProps()...)
+}
+
+func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
+	test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
+	test.libraryDecorator.linkerInit(ctx)
+}
+
+func (test *testLibrary) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = test.testDecorator.linkerDeps(ctx, deps)
+	deps = test.libraryDecorator.linkerDeps(ctx, deps)
+	return deps
+}
+
+func (test *testLibrary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = test.libraryDecorator.linkerFlags(ctx, flags)
+	flags = test.testDecorator.linkerFlags(ctx, flags)
+	return flags
+}
+
 func NewTestLibrary(hod android.HostOrDeviceSupported) *Module {
-	module := NewLibrary(android.HostAndDeviceSupported, false, true)
-	linker := &testLibraryLinker{
-		libraryLinker: module.linker.(*libraryLinker),
-	}
-	linker.testLinker.Properties.Gtest = true
-	module.linker = linker
-	module.installer = &testInstaller{
-		baseInstaller: baseInstaller{
-			dir:   "nativetest",
-			dir64: "nativetest64",
-			data:  true,
+	module, library := NewLibrary(android.HostAndDeviceSupported, false, true)
+	test := &testLibrary{
+		testDecorator: testDecorator{
+			linker: library.baseLinker,
 		},
+		libraryDecorator: library,
 	}
+	test.testDecorator.Properties.Gtest = true
+	module.linker = test
+	module.installer = nil
 	return module
 }
 
-type benchmarkLinker struct {
-	testBinaryLinker
+type benchmarkDecorator struct {
+	*binaryDecorator
+	*baseInstaller
 }
 
-func (benchmark *benchmarkLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
-	deps = benchmark.testBinaryLinker.linkerDeps(ctx, deps)
+func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
+	runpath := "../../lib"
+	if ctx.toolchain().Is64Bit() {
+		runpath += "64"
+	}
+	benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
+	benchmark.binaryDecorator.linkerInit(ctx)
+}
+
+func (benchmark *benchmarkDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+	deps = benchmark.binaryDecorator.linkerDeps(ctx, deps)
 	deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark")
 	return deps
 }
 
+func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
+	benchmark.baseInstaller.dir = filepath.Join("nativetest", ctx.ModuleName())
+	benchmark.baseInstaller.dir64 = filepath.Join("nativetest64", ctx.ModuleName())
+}
+
 func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
-	module := newModule(hod, android.MultilibFirst)
-	module.compiler = &baseCompiler{}
-	module.linker = &benchmarkLinker{}
-	module.installer = &testInstaller{
-		baseInstaller: baseInstaller{
-			dir:   "nativetest",
-			dir64: "nativetest64",
-			data:  true,
-		},
+	module, binary := NewBinary(hod)
+	module.multilib = android.MultilibBoth
+
+	benchmark := &benchmarkDecorator{
+		binaryDecorator: binary,
+		baseInstaller:   NewTestInstaller(),
 	}
+	module.linker = benchmark
+	module.installer = benchmark
 	return module
 }
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
new file mode 100644
index 0000000..0548da9
--- /dev/null
+++ b/cc/toolchain_library.go
@@ -0,0 +1,76 @@
+// 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 (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong"
+	"android/soong/android"
+)
+
+//
+// Device libraries shipped with gcc
+//
+
+func init() {
+	soong.RegisterModuleType("toolchain_library", toolchainLibraryFactory)
+}
+
+type toolchainLibraryDecorator struct {
+	*libraryDecorator
+}
+
+func (*toolchainLibraryDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+	// toolchain libraries can't have any dependencies
+	return deps
+}
+
+func toolchainLibraryFactory() (blueprint.Module, []interface{}) {
+	module, library := NewLibrary(android.DeviceSupported, false, true)
+	toolchainLibrary := &toolchainLibraryDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = toolchainLibrary
+	module.linker = toolchainLibrary
+	module.Properties.Clang = proptools.BoolPtr(false)
+	module.stl = nil
+	module.sanitize = nil
+	module.installer = nil
+	return module.Init()
+}
+
+func (library *toolchainLibraryDecorator) compile(ctx ModuleContext, flags Flags,
+	deps PathDeps) android.Paths {
+	return nil
+}
+
+func (library *toolchainLibraryDecorator) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objFiles android.Paths) android.Path {
+
+	libName := ctx.ModuleName() + staticLibraryExtension
+	outputFile := android.PathForModuleOut(ctx, libName)
+
+	if flags.Clang {
+		ctx.ModuleErrorf("toolchain_library must use GCC, not Clang")
+	}
+
+	CopyGccLib(ctx, libName, flagsToBuilderFlags(flags), outputFile)
+
+	ctx.CheckbuildFile(outputFile)
+
+	return outputFile
+}