// Copyright 2019 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 android type ImageInterfaceContext interface { ArchModuleContext Module() Module ModuleErrorf(fmt string, args ...interface{}) PropertyErrorf(property, fmt string, args ...interface{}) DeviceSpecific() bool SocSpecific() bool ProductSpecific() bool SystemExtSpecific() bool Platform() bool Config() Config } // ImageInterface is implemented by modules that need to be split by the imageTransitionMutator. type ImageInterface interface { // ImageMutatorBegin is called before any other method in the ImageInterface. ImageMutatorBegin(ctx ImageInterfaceContext) // VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image). VendorVariantNeeded(ctx ImageInterfaceContext) bool // ProductVariantNeeded should return true if the module needs a product variant (installed on the product image). ProductVariantNeeded(ctx ImageInterfaceContext) bool // CoreVariantNeeded should return true if the module needs a core variant (installed on the system image). CoreVariantNeeded(ctx ImageInterfaceContext) bool // RamdiskVariantNeeded should return true if the module needs a ramdisk variant (installed on the // ramdisk partition). RamdiskVariantNeeded(ctx ImageInterfaceContext) bool // VendorRamdiskVariantNeeded should return true if the module needs a vendor ramdisk variant (installed on the // vendor ramdisk partition). VendorRamdiskVariantNeeded(ctx ImageInterfaceContext) bool // DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the // debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk). DebugRamdiskVariantNeeded(ctx ImageInterfaceContext) bool // RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the // recovery partition). RecoveryVariantNeeded(ctx ImageInterfaceContext) bool // ExtraImageVariations should return a list of the additional variations needed for the module. After the // variants are created the SetImageVariation method will be called on each newly created variant with the // its variation. ExtraImageVariations(ctx ImageInterfaceContext) []string // SetImageVariation is called for each newly created image variant. The receiver is the original // module, "variation" is the name of the newly created variant. "variation" is set on the receiver. SetImageVariation(ctx ImageInterfaceContext, variation string) } const ( // VendorVariation is the variant name used for /vendor code that does not // compile against the VNDK. VendorVariation string = "vendor" // ProductVariation is the variant name used for /product code that does not // compile against the VNDK. ProductVariation string = "product" // CoreVariation is the variant used for framework-private libraries, or // SDK libraries. (which framework-private libraries can use), which // will be installed to the system image. CoreVariation string = "" // RecoveryVariation means a module to be installed to recovery image. RecoveryVariation string = "recovery" // RamdiskVariation means a module to be installed to ramdisk image. RamdiskVariation string = "ramdisk" // VendorRamdiskVariation means a module to be installed to vendor ramdisk image. VendorRamdiskVariation string = "vendor_ramdisk" // DebugRamdiskVariation means a module to be installed to debug ramdisk image. DebugRamdiskVariation string = "debug_ramdisk" ) type imageInterfaceContextAdapter struct { IncomingTransitionContext kind moduleKind } var _ ImageInterfaceContext = (*imageInterfaceContextAdapter)(nil) func (e *imageInterfaceContextAdapter) Platform() bool { return e.kind == platformModule } func (e *imageInterfaceContextAdapter) DeviceSpecific() bool { return e.kind == deviceSpecificModule } func (e *imageInterfaceContextAdapter) SocSpecific() bool { return e.kind == socSpecificModule } func (e *imageInterfaceContextAdapter) ProductSpecific() bool { return e.kind == productSpecificModule } func (e *imageInterfaceContextAdapter) SystemExtSpecific() bool { return e.kind == systemExtSpecificModule } // imageMutatorBeginMutator calls ImageMutatorBegin on all modules that may have image variants. // This happens right before the imageTransitionMutator runs. It's needed to initialize these // modules so that they return the correct results for all the other ImageInterface methods, // which the imageTransitionMutator will call. Transition mutators should also not mutate modules // (except in their Mutate() function), which this method does, so we run it in a separate mutator // first. func imageMutatorBeginMutator(ctx BottomUpMutatorContext) { if m, ok := ctx.Module().(ImageInterface); ok && ctx.Os() == Android { m.ImageMutatorBegin(ctx) } } // imageTransitionMutator creates variants for modules that implement the ImageInterface that // allow them to build differently for each partition (recovery, core, vendor, etc.). type imageTransitionMutator struct{} func getImageVariations(ctx ImageInterfaceContext) []string { var variations []string if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok { if m.CoreVariantNeeded(ctx) { variations = append(variations, CoreVariation) } if m.RamdiskVariantNeeded(ctx) { variations = append(variations, RamdiskVariation) } if m.VendorRamdiskVariantNeeded(ctx) { variations = append(variations, VendorRamdiskVariation) } if m.DebugRamdiskVariantNeeded(ctx) { variations = append(variations, DebugRamdiskVariation) } if m.RecoveryVariantNeeded(ctx) { variations = append(variations, RecoveryVariation) } if m.VendorVariantNeeded(ctx) { variations = append(variations, VendorVariation) } if m.ProductVariantNeeded(ctx) { variations = append(variations, ProductVariation) } extraVariations := m.ExtraImageVariations(ctx) variations = append(variations, extraVariations...) } if len(variations) == 0 { variations = append(variations, "") } return variations } func (imageTransitionMutator) Split(ctx BaseModuleContext) []string { return getImageVariations(ctx) } func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { return sourceVariation } func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok { return CoreVariation } variations := getImageVariations(&imageInterfaceContextAdapter{ IncomingTransitionContext: ctx, kind: determineModuleKind(ctx.Module().base(), ctx), }) // If there's only 1 possible variation, use that. This is a holdover from when blueprint, // when adding dependencies, would use the only variant of a module regardless of its variations // if only 1 variant existed. if len(variations) == 1 { return variations[0] } return incomingVariation } func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { ctx.Module().base().setImageVariation(variation) if m, ok := ctx.Module().(ImageInterface); ok { m.SetImageVariation(ctx, variation) } }