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/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)
+			}
+		}
+	}
 }