// Copyright 2020 The Android Open Source Project
//
// 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

// This file contains image variant related things, including image mutator functions, utility
// functions to determine where a module is installed, etc.

import (
	"fmt"
	"reflect"
	"strings"

	"android/soong/android"
	"android/soong/snapshot"
)

var _ android.ImageInterface = (*Module)(nil)

type ImageVariantType string

const (
	coreImageVariant          ImageVariantType = "core"
	vendorImageVariant        ImageVariantType = "vendor"
	productImageVariant       ImageVariantType = "product"
	ramdiskImageVariant       ImageVariantType = "ramdisk"
	vendorRamdiskImageVariant ImageVariantType = "vendor_ramdisk"
	recoveryImageVariant      ImageVariantType = "recovery"
	hostImageVariant          ImageVariantType = "host"
)

const (
	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
	// against the VNDK.
	VendorVariationPrefix = "vendor."

	// ProductVariationPrefix is the variant prefix used for /product code that compiles
	// against the VNDK.
	ProductVariationPrefix = "product."
)

func (ctx *moduleContext) ProductSpecific() bool {
	return ctx.ModuleContext.ProductSpecific() || ctx.mod.productSpecificModuleContext()
}

func (ctx *moduleContext) SocSpecific() bool {
	return ctx.ModuleContext.SocSpecific() || ctx.mod.socSpecificModuleContext()
}

func (ctx *moduleContext) DeviceSpecific() bool {
	return ctx.ModuleContext.DeviceSpecific() || ctx.mod.deviceSpecificModuleContext()
}

func (ctx *moduleContextImpl) inProduct() bool {
	return ctx.mod.InProduct()
}

func (ctx *moduleContextImpl) inVendor() bool {
	return ctx.mod.InVendor()
}

func (ctx *moduleContextImpl) inRamdisk() bool {
	return ctx.mod.InRamdisk()
}

func (ctx *moduleContextImpl) inVendorRamdisk() bool {
	return ctx.mod.InVendorRamdisk()
}

func (ctx *moduleContextImpl) inRecovery() bool {
	return ctx.mod.InRecovery()
}

func (c *Module) productSpecificModuleContext() bool {
	// Additionally check if this module is inProduct() that means it is a "product" variant of a
	// module. As well as product specific modules, product variants must be installed to /product.
	return c.InProduct()
}

func (c *Module) socSpecificModuleContext() bool {
	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
	// unless they have "odm_available: true".
	return c.HasVendorVariant() && c.InVendor() && !c.VendorVariantToOdm()
}

func (c *Module) deviceSpecificModuleContext() bool {
	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
	return c.InVendor() && c.VendorVariantToOdm()
}

// Returns true when this module is configured to have core and vendor variants.
func (c *Module) HasVendorVariant() bool {
	return Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Odm_available)
}

// Returns true when this module creates a vendor variant and wants to install the vendor variant
// to the odm partition.
func (c *Module) VendorVariantToOdm() bool {
	return Bool(c.VendorProperties.Odm_available)
}

// Returns true when this module is configured to have core and product variants.
func (c *Module) HasProductVariant() bool {
	return Bool(c.VendorProperties.Product_available)
}

// Returns true when this module is configured to have core and either product or vendor variants.
func (c *Module) HasNonSystemVariants() bool {
	return c.HasVendorVariant() || c.HasProductVariant()
}

// Returns true if the module is "product" variant. Usually these modules are installed in /product
func (c *Module) InProduct() bool {
	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
}

// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
func (c *Module) InVendor() bool {
	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
}

func (c *Module) InRamdisk() bool {
	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
}

func (c *Module) InVendorRamdisk() bool {
	return c.ModuleBase.InVendorRamdisk() || c.ModuleBase.InstallInVendorRamdisk()
}

func (c *Module) InRecovery() bool {
	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
}

func (c *Module) OnlyInRamdisk() bool {
	return c.ModuleBase.InstallInRamdisk()
}

func (c *Module) OnlyInVendorRamdisk() bool {
	return c.ModuleBase.InstallInVendorRamdisk()
}

func (c *Module) OnlyInRecovery() bool {
	return c.ModuleBase.InstallInRecovery()
}

func visitPropsAndCompareVendorAndProductProps(v reflect.Value) bool {
	if v.Kind() != reflect.Struct {
		return true
	}
	for i := 0; i < v.NumField(); i++ {
		prop := v.Field(i)
		if prop.Kind() == reflect.Struct && v.Type().Field(i).Name == "Target" {
			vendor_prop := prop.FieldByName("Vendor")
			product_prop := prop.FieldByName("Product")
			if vendor_prop.Kind() != reflect.Struct && product_prop.Kind() != reflect.Struct {
				// Neither Target.Vendor nor Target.Product is defined
				continue
			}
			if vendor_prop.Kind() != reflect.Struct || product_prop.Kind() != reflect.Struct ||
				!reflect.DeepEqual(vendor_prop.Interface(), product_prop.Interface()) {
				// If only one of either Target.Vendor or Target.Product is
				// defined or they have different values, it fails the build
				// since VNDK must have the same properties for both vendor
				// and product variants.
				return false
			}
		} else if !visitPropsAndCompareVendorAndProductProps(prop) {
			// Visit the substructures to find Target.Vendor and Target.Product
			return false
		}
	}
	return true
}

// In the case of VNDK, vendor and product variants must have the same properties.
// VNDK installs only one file and shares it for both vendor and product modules on
// runtime. We may not define different versions of a VNDK lib for each partition.
// This function is used only for the VNDK modules that is available to both vendor
// and product partitions.
func (c *Module) compareVendorAndProductProps() bool {
	if !c.IsVndk() && !Bool(c.VendorProperties.Product_available) {
		panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
	}
	for _, properties := range c.GetProperties() {
		if !visitPropsAndCompareVendorAndProductProps(reflect.ValueOf(properties).Elem()) {
			return false
		}
	}
	return true
}

// ImageMutatableModule provides a common image mutation interface for  LinkableInterface modules.
type ImageMutatableModule interface {
	android.Module
	LinkableInterface

	// AndroidModuleBase returns the android.ModuleBase for this module
	AndroidModuleBase() *android.ModuleBase

	// VendorAvailable returns true if this module is available on the vendor image.
	VendorAvailable() bool

	// OdmAvailable returns true if this module is available on the odm image.
	OdmAvailable() bool

	// ProductAvailable returns true if this module is available on the product image.
	ProductAvailable() bool

	// RamdiskAvailable returns true if this module is available on the ramdisk image.
	RamdiskAvailable() bool

	// RecoveryAvailable returns true if this module is available on the recovery image.
	RecoveryAvailable() bool

	// VendorRamdiskAvailable returns true if this module is available on the vendor ramdisk image.
	VendorRamdiskAvailable() bool

	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
	IsSnapshotPrebuilt() bool

	// SnapshotVersion returns the snapshot version for this module.
	SnapshotVersion(mctx android.BaseModuleContext) string

	// SdkVersion returns the SDK version for this module.
	SdkVersion() string

	// ExtraVariants returns the list of extra variants this module requires.
	ExtraVariants() []string

	// AppendExtraVariant returns an extra variant to the list of extra variants this module requires.
	AppendExtraVariant(extraVariant string)

	// SetRamdiskVariantNeeded sets whether the Ramdisk Variant is needed.
	SetRamdiskVariantNeeded(b bool)

	// SetVendorRamdiskVariantNeeded sets whether the Vendor Ramdisk Variant is needed.
	SetVendorRamdiskVariantNeeded(b bool)

	// SetRecoveryVariantNeeded sets whether the Recovery Variant is needed.
	SetRecoveryVariantNeeded(b bool)

	// SetCoreVariantNeeded sets whether the Core Variant is needed.
	SetCoreVariantNeeded(b bool)
}

var _ ImageMutatableModule = (*Module)(nil)

func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
	m.CheckVndkProperties(mctx)
	MutateImage(mctx, m)
}

// CheckVndkProperties checks whether the VNDK-related properties are set correctly.
// If properties are not set correctly, results in a module context property error.
func (m *Module) CheckVndkProperties(mctx android.BaseModuleContext) {
	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
	productSpecific := mctx.ProductSpecific()

	if vndkdep := m.vndkdep; vndkdep != nil {
		if vndkdep.isVndk() {
			if vendorSpecific || productSpecific {
				if !vndkdep.isVndkExt() {
					mctx.PropertyErrorf("vndk",
						"must set `extends: \"...\"` to vndk extension")
				} else if Bool(m.VendorProperties.Vendor_available) {
					mctx.PropertyErrorf("vendor_available",
						"must not set at the same time as `vndk: {extends: \"...\"}`")
				} else if Bool(m.VendorProperties.Product_available) {
					mctx.PropertyErrorf("product_available",
						"must not set at the same time as `vndk: {extends: \"...\"}`")
				}
			} else {
				if vndkdep.isVndkExt() {
					mctx.PropertyErrorf("vndk",
						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
						m.getVndkExtendsModuleName())
				}
				if !Bool(m.VendorProperties.Vendor_available) {
					mctx.PropertyErrorf("vndk",
						"vendor_available must be set to true when `vndk: {enabled: true}`")
				}
				if Bool(m.VendorProperties.Product_available) {
					// If a VNDK module creates both product and vendor variants, they
					// must have the same properties since they share a single VNDK
					// library on runtime.
					if !m.compareVendorAndProductProps() {
						mctx.ModuleErrorf("product properties must have the same values with the vendor properties for VNDK modules")
					}
				}
			}
		} else {
			if vndkdep.isVndkSp() {
				mctx.PropertyErrorf("vndk",
					"must set `enabled: true` to set `support_system_process: true`")
			}
			if vndkdep.isVndkExt() {
				mctx.PropertyErrorf("vndk",
					"must set `enabled: true` to set `extends: %q`",
					m.getVndkExtendsModuleName())
			}
		}
	}
}

func (m *Module) VendorAvailable() bool {
	return Bool(m.VendorProperties.Vendor_available)
}

func (m *Module) OdmAvailable() bool {
	return Bool(m.VendorProperties.Odm_available)
}

func (m *Module) ProductAvailable() bool {
	return Bool(m.VendorProperties.Product_available)
}

func (m *Module) RamdiskAvailable() bool {
	return Bool(m.Properties.Ramdisk_available)
}

func (m *Module) VendorRamdiskAvailable() bool {
	return Bool(m.Properties.Vendor_ramdisk_available)
}

func (m *Module) AndroidModuleBase() *android.ModuleBase {
	return &m.ModuleBase
}

func (m *Module) RecoveryAvailable() bool {
	return Bool(m.Properties.Recovery_available)
}

func (m *Module) ExtraVariants() []string {
	return m.Properties.ExtraVersionedImageVariations
}

func (m *Module) AppendExtraVariant(extraVariant string) {
	m.Properties.ExtraVersionedImageVariations = append(m.Properties.ExtraVersionedImageVariations, extraVariant)
}

func (m *Module) SetRamdiskVariantNeeded(b bool) {
	m.Properties.RamdiskVariantNeeded = b
}

func (m *Module) SetVendorRamdiskVariantNeeded(b bool) {
	m.Properties.VendorRamdiskVariantNeeded = b
}

func (m *Module) SetRecoveryVariantNeeded(b bool) {
	m.Properties.RecoveryVariantNeeded = b
}

func (m *Module) SetCoreVariantNeeded(b bool) {
	m.Properties.CoreVariantNeeded = b
}

func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
	if snapshot, ok := m.linker.(SnapshotInterface); ok {
		return snapshot.Version()
	} else {
		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
		// Should we be panicking here instead?
		return ""
	}
}

func (m *Module) KernelHeadersDecorator() bool {
	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
		return true
	}
	return false
}

// MutateImage handles common image mutations for ImageMutatableModule interfaces.
func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) {
	// Validation check
	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
	productSpecific := mctx.ProductSpecific()

	if m.VendorAvailable() {
		if vendorSpecific {
			mctx.PropertyErrorf("vendor_available",
				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
		}
		if m.OdmAvailable() {
			mctx.PropertyErrorf("vendor_available",
				"doesn't make sense at the same time as `odm_available: true`")
		}
	}

	if m.OdmAvailable() {
		if vendorSpecific {
			mctx.PropertyErrorf("odm_available",
				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
		}
	}

	if m.ProductAvailable() {
		if productSpecific {
			mctx.PropertyErrorf("product_available",
				"doesn't make sense at the same time as `product_specific: true`")
		}
		if vendorSpecific {
			mctx.PropertyErrorf("product_available",
				"cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
		}
	}

	var coreVariantNeeded bool = false
	var ramdiskVariantNeeded bool = false
	var vendorRamdiskVariantNeeded bool = false
	var recoveryVariantNeeded bool = false

	var vendorVariants []string
	var productVariants []string

	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
	recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion()
	usingRecoverySnapshot := recoverySnapshotVersion != "current" &&
		recoverySnapshotVersion != ""
	needVndkVersionVendorVariantForLlndk := false
	if boardVndkVersion != "" {
		boardVndkApiLevel, err := android.ApiLevelFromUser(mctx, boardVndkVersion)
		if err == nil && !boardVndkApiLevel.IsPreview() {
			// VNDK snapshot newer than v30 has LLNDK stub libraries.
			// Only the VNDK version less than or equal to v30 requires generating the vendor
			// variant of the VNDK version from the source tree.
			needVndkVersionVendorVariantForLlndk = boardVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, "30"))
		}
	}
	if boardVndkVersion == "current" {
		boardVndkVersion = platformVndkVersion
	}
	if productVndkVersion == "current" {
		productVndkVersion = platformVndkVersion
	}

	if m.NeedsLlndkVariants() {
		// This is an LLNDK library.  The implementation of the library will be on /system,
		// and vendor and product variants will be created with LLNDK stubs.
		// The LLNDK libraries need vendor variants even if there is no VNDK.
		coreVariantNeeded = true
		if platformVndkVersion != "" {
			vendorVariants = append(vendorVariants, platformVndkVersion)
			productVariants = append(productVariants, platformVndkVersion)
		}
		// Generate vendor variants for boardVndkVersion only if the VNDK snapshot does not
		// provide the LLNDK stub libraries.
		if needVndkVersionVendorVariantForLlndk {
			vendorVariants = append(vendorVariants, boardVndkVersion)
		}
		if productVndkVersion != "" {
			productVariants = append(productVariants, productVndkVersion)
		}
	} else if m.NeedsVendorPublicLibraryVariants() {
		// A vendor public library has the implementation on /vendor, with stub variants
		// for system and product.
		coreVariantNeeded = true
		vendorVariants = append(vendorVariants, boardVndkVersion)
		if platformVndkVersion != "" {
			productVariants = append(productVariants, platformVndkVersion)
		}
		if productVndkVersion != "" {
			productVariants = append(productVariants, productVndkVersion)
		}
	} else if boardVndkVersion == "" {
		// If the device isn't compiling against the VNDK, we always
		// use the core mode.
		coreVariantNeeded = true
	} else if m.IsSnapshotPrebuilt() {
		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
		// PRODUCT_EXTRA_VNDK_VERSIONS.
		if m.InstallInRecovery() {
			recoveryVariantNeeded = true
		} else {
			vendorVariants = append(vendorVariants, m.SnapshotVersion(mctx))
		}
	} else if m.HasNonSystemVariants() && !m.IsVndkExt() {
		// This will be available to /system unless it is product_specific
		// which will be handled later.
		coreVariantNeeded = true

		// We assume that modules under proprietary paths are compatible for
		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
		// PLATFORM_VNDK_VERSION.
		if m.HasVendorVariant() {
			if snapshot.IsVendorProprietaryModule(mctx) {
				vendorVariants = append(vendorVariants, boardVndkVersion)
			} else {
				vendorVariants = append(vendorVariants, platformVndkVersion)
			}
		}

		// product_available modules are available to /product.
		if m.HasProductVariant() {
			productVariants = append(productVariants, platformVndkVersion)
			// VNDK is always PLATFORM_VNDK_VERSION
			if !m.IsVndk() {
				productVariants = append(productVariants, productVndkVersion)
			}
		}
	} else if vendorSpecific && m.SdkVersion() == "" {
		// This will be available in /vendor (or /odm) only

		// kernel_headers is a special module type whose exported headers
		// are coming from DeviceKernelHeaders() which is always vendor
		// dependent. They'll always have both vendor variants.
		// For other modules, we assume that modules under proprietary
		// paths are compatible for BOARD_VNDK_VERSION. The other modules
		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
		if m.KernelHeadersDecorator() {
			vendorVariants = append(vendorVariants,
				platformVndkVersion,
				boardVndkVersion,
			)
		} else if snapshot.IsVendorProprietaryModule(mctx) {
			vendorVariants = append(vendorVariants, boardVndkVersion)
		} else {
			vendorVariants = append(vendorVariants, platformVndkVersion)
		}
	} else {
		// This is either in /system (or similar: /data), or is a
		// module built with the NDK. Modules built with the NDK
		// will be restricted using the existing link type checks.
		coreVariantNeeded = true
	}

	if boardVndkVersion != "" && productVndkVersion != "" {
		if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
			// The module has "product_specific: true" that does not create core variant.
			coreVariantNeeded = false
			productVariants = append(productVariants, productVndkVersion)
		}
	} else {
		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
		// restriction to use system libs.
		// No product variants defined in this case.
		productVariants = []string{}
	}

	if m.RamdiskAvailable() {
		ramdiskVariantNeeded = true
	}

	if m.AndroidModuleBase().InstallInRamdisk() {
		ramdiskVariantNeeded = true
		coreVariantNeeded = false
	}

	if m.VendorRamdiskAvailable() {
		vendorRamdiskVariantNeeded = true
	}

	if m.AndroidModuleBase().InstallInVendorRamdisk() {
		vendorRamdiskVariantNeeded = true
		coreVariantNeeded = false
	}

	if m.RecoveryAvailable() {
		recoveryVariantNeeded = true
	}

	if m.AndroidModuleBase().InstallInRecovery() {
		recoveryVariantNeeded = true
		coreVariantNeeded = false
	}

	// If using a snapshot, the recovery variant under AOSP directories is not needed,
	// except for kernel headers, which needs all variants.
	if !m.KernelHeadersDecorator() &&
		!m.IsSnapshotPrebuilt() &&
		usingRecoverySnapshot &&
		!snapshot.IsRecoveryProprietaryModule(mctx) {
		recoveryVariantNeeded = false
	}

	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
		m.AppendExtraVariant(VendorVariationPrefix + variant)
	}

	for _, variant := range android.FirstUniqueStrings(productVariants) {
		m.AppendExtraVariant(ProductVariationPrefix + variant)
	}

	m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
	m.SetVendorRamdiskVariantNeeded(vendorRamdiskVariantNeeded)
	m.SetRecoveryVariantNeeded(recoveryVariantNeeded)
	m.SetCoreVariantNeeded(coreVariantNeeded)

	// Disable the module if no variants are needed.
	if !ramdiskVariantNeeded &&
		!recoveryVariantNeeded &&
		!coreVariantNeeded &&
		len(m.ExtraVariants()) == 0 {
		m.Disable()
	}
}

func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
	return c.Properties.CoreVariantNeeded
}

func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
	return c.Properties.RamdiskVariantNeeded
}

func (c *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
	return c.Properties.VendorRamdiskVariantNeeded
}

func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
	return false
}

func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
	return c.Properties.RecoveryVariantNeeded
}

func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
	return c.Properties.ExtraVersionedImageVariations
}

func squashVendorSrcs(m *Module) {
	if lib, ok := m.compiler.(*libraryDecorator); ok {
		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
			lib.baseCompiler.Properties.Target.Vendor.Srcs...)

		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
			lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)

		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
			lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
	}
}

func squashProductSrcs(m *Module) {
	if lib, ok := m.compiler.(*libraryDecorator); ok {
		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
			lib.baseCompiler.Properties.Target.Product.Srcs...)

		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
			lib.baseCompiler.Properties.Target.Product.Exclude_srcs...)

		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
			lib.baseCompiler.Properties.Target.Product.Exclude_generated_sources...)
	}
}

func squashRecoverySrcs(m *Module) {
	if lib, ok := m.compiler.(*libraryDecorator); ok {
		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
			lib.baseCompiler.Properties.Target.Recovery.Srcs...)

		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)

		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
			lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
	}
}

func squashVendorRamdiskSrcs(m *Module) {
	if lib, ok := m.compiler.(*libraryDecorator); ok {
		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs...)
	}
}

func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
	m := module.(*Module)
	if variant == android.RamdiskVariation {
		m.MakeAsPlatform()
	} else if variant == android.VendorRamdiskVariation {
		m.MakeAsPlatform()
		squashVendorRamdiskSrcs(m)
	} else if variant == android.RecoveryVariation {
		m.MakeAsPlatform()
		squashRecoverySrcs(m)
	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
		m.Properties.ImageVariationPrefix = VendorVariationPrefix
		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
		squashVendorSrcs(m)

		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
		// Hide other vendor variants to avoid collision.
		vndkVersion := ctx.DeviceConfig().VndkVersion()
		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
			m.Properties.HideFromMake = true
			m.HideFromMake()
		}
	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
		m.Properties.ImageVariationPrefix = ProductVariationPrefix
		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
		squashProductSrcs(m)
	}

	if c.NeedsVendorPublicLibraryVariants() &&
		(variant == android.CoreVariation || strings.HasPrefix(variant, ProductVariationPrefix)) {
		c.VendorProperties.IsVendorPublicLibrary = true
	}
}
