diff --git a/android/androidmk.go b/android/androidmk.go index a670656b7..6ab4a2431 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -12,6 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// This file offers AndroidMkEntriesProvider, which individual modules implement to output +// Android.mk entries that contain information about the modules built through Soong. Kati reads +// and combines them with the legacy Make-based module definitions to produce the complete view of +// the source tree, which makes this a critical point of Make-Soong interoperability. +// +// Naturally, Soong-only builds do not rely on this mechanism. + package android import ( @@ -36,8 +43,8 @@ func RegisterAndroidMkBuildComponents(ctx RegistrationContext) { ctx.RegisterSingletonType("androidmk", AndroidMkSingleton) } -// Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to -// use the Custom function. +// Deprecated: Use AndroidMkEntriesProvider instead, especially if you're not going to use the +// Custom function. It's easier to use and test. type AndroidMkDataProvider interface { AndroidMk() AndroidMkData BaseModuleName() string @@ -63,37 +70,82 @@ type AndroidMkData struct { type AndroidMkExtraFunc func(w io.Writer, outputFile Path) -// Allows modules to customize their Android*.mk output. +// Interface for modules to declare their Android.mk outputs. Note that every module needs to +// implement this in order to be included in the final Android-.mk output, even if +// they only need to output the common set of entries without any customizations. type AndroidMkEntriesProvider interface { + // Returns AndroidMkEntries objects that contain all basic info plus extra customization data + // if needed. This is the core func to implement. + // Note that one can return multiple objects. For example, java_library may return an additional + // AndroidMkEntries object for its hostdex sub-module. AndroidMkEntries() []AndroidMkEntries + // Modules don't need to implement this as it's already implemented by ModuleBase. + // AndroidMkEntries uses BaseModuleName() instead of ModuleName() because certain modules + // e.g. Prebuilts, override the Name() func and return modified names. + // If a different name is preferred, use SubName or OverrideName in AndroidMkEntries. BaseModuleName() string } +// The core data struct that modules use to provide their Android.mk data. type AndroidMkEntries struct { - Class string - SubName string - OverrideName string - DistFiles TaggedDistFiles - OutputFile OptionalPath - Disabled bool - Include string - Required []string - Host_required []string + // Android.mk class string, e.g EXECUTABLES, JAVA_LIBRARIES, ETC + Class string + // Optional suffix to append to the module name. Useful when a module wants to return multiple + // AndroidMkEntries objects. For example, when a java_library returns an additional entry for + // its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a + // different name than the parent's. + SubName string + // If set, this value overrides the base module name. SubName is still appended. + OverrideName string + // Dist files to output + DistFiles TaggedDistFiles + // The output file for Kati to process and/or install. If absent, the module is skipped. + OutputFile OptionalPath + // If true, the module is skipped and does not appear on the final Android-.mk + // file. Useful when a module needs to be skipped conditionally. + Disabled bool + // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_prebuilt.mk + // If not set, $(BUILD_SYSTEM)/prebuilt.mk is used. + Include string + // Required modules that need to be built and included in the final build output when building + // this module. + Required []string + // Required host modules that need to be built and included in the final build output when + // building this module. + Host_required []string + // Required device modules that need to be built and included in the final build output when + // building this module. Target_required []string header bytes.Buffer footer bytes.Buffer + // Funcs to append additional Android.mk entries or modify the common ones. Multiple funcs are + // accepted so that common logic can be factored out as a shared func. ExtraEntries []AndroidMkExtraEntriesFunc + // Funcs to add extra lines to the module's Android.mk output. Unlike AndroidMkExtraEntriesFunc, + // which simply sets Make variable values, this can be used for anything since it can write any + // Make statements directly to the final Android-*.mk file. + // Primarily used to call macros or declare/update Make targets. ExtraFooters []AndroidMkExtraFootersFunc - EntryMap map[string][]string + // A map that holds the up-to-date Make variable values. Can be accessed from tests. + EntryMap map[string][]string + // A list of EntryMap keys in insertion order. This serves a few purposes: + // 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this, + // the outputted Android-*.mk file may change even though there have been no content changes. + // 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR), + // without worrying about the variables being mixed up in the actual mk file. + // 3. Makes troubleshooting and spotting errors easier. entryOrder []string } type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries) type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries) +// Utility funcs to manipulate Android.mk variable entries. + +// SetString sets a Make variable with the given name to the given value. func (a *AndroidMkEntries) SetString(name, value string) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -101,6 +153,7 @@ func (a *AndroidMkEntries) SetString(name, value string) { a.EntryMap[name] = []string{value} } +// SetPath sets a Make variable with the given name to the given path string. func (a *AndroidMkEntries) SetPath(name string, path Path) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -108,12 +161,15 @@ func (a *AndroidMkEntries) SetPath(name string, path Path) { a.EntryMap[name] = []string{path.String()} } +// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid. +// It is a no-op if the given path is invalid. func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) { if path.Valid() { a.SetPath(name, path.Path()) } } +// AddPath appends the given path string to a Make variable with the given name. func (a *AndroidMkEntries) AddPath(name string, path Path) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -121,12 +177,15 @@ func (a *AndroidMkEntries) AddPath(name string, path Path) { a.EntryMap[name] = append(a.EntryMap[name], path.String()) } +// AddOptionalPath appends the given path string to a Make variable with the given name if it is +// valid. It is a no-op if the given path is invalid. func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) { if path.Valid() { a.AddPath(name, path.Path()) } } +// SetPaths sets a Make variable with the given name to a slice of the given path strings. func (a *AndroidMkEntries) SetPaths(name string, paths Paths) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -134,12 +193,15 @@ func (a *AndroidMkEntries) SetPaths(name string, paths Paths) { a.EntryMap[name] = paths.Strings() } +// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings +// only if there are a non-zero amount of paths. func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) { if len(paths) > 0 { a.SetPaths(name, paths) } } +// AddPaths appends the given path strings to a Make variable with the given name. func (a *AndroidMkEntries) AddPaths(name string, paths Paths) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -147,6 +209,8 @@ func (a *AndroidMkEntries) AddPaths(name string, paths Paths) { a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...) } +// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true. +// It is a no-op if the given flag is false. func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) { if flag { if _, ok := a.EntryMap[name]; !ok { @@ -156,6 +220,7 @@ func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) { } } +// SetBool sets a Make variable with the given name to if the given bool flag value. func (a *AndroidMkEntries) SetBool(name string, flag bool) { if _, ok := a.EntryMap[name]; !ok { a.entryOrder = append(a.entryOrder, name) @@ -167,6 +232,7 @@ func (a *AndroidMkEntries) SetBool(name string, flag bool) { } } +// AddStrings appends the given strings to a Make variable with the given name. func (a *AndroidMkEntries) AddStrings(name string, value ...string) { if len(value) == 0 { return @@ -368,6 +434,8 @@ func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string { return generateDistContributionsForMake(distContributions) } +// fillInEntries goes through the common variable processing and calls the extra data funcs to +// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file. func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) { a.EntryMap = make(map[string][]string) amod := mod.(Module).base() @@ -488,6 +556,8 @@ func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod bluep } } +// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the +// given Writer object. func (a *AndroidMkEntries) write(w io.Writer) { if a.Disabled { return @@ -508,6 +578,8 @@ func (a *AndroidMkEntries) FooterLinesForTests() []string { return strings.Split(string(a.footer.Bytes()), "\n") } +// AndroidMkSingleton is a singleton to collect Android.mk data from all modules and dump them into +// the final Android-.mk file output. func AndroidMkSingleton() Singleton { return &androidMkSingleton{} } @@ -515,6 +587,7 @@ func AndroidMkSingleton() Singleton { type androidMkSingleton struct{} func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) { + // Skip if Soong wasn't invoked from Make. if !ctx.Config().KatiEnabled() { return } @@ -525,6 +598,8 @@ func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) { androidMkModulesList = append(androidMkModulesList, module) }) + // Sort the module list by the module names to eliminate random churns, which may erroneously + // invoke additional build processes. sort.SliceStable(androidMkModulesList, func(i, j int) bool { return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j]) }) @@ -617,6 +692,8 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.M } } +// A simple, special Android.mk entry output func to make it possible to build blueprint tools using +// m by making them phony targets. func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module, goBinary bootstrap.GoBinaryTool) error { @@ -649,6 +726,8 @@ func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprin data.Target_required = data.Entries.Target_required } +// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider +// instead. func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module, provider AndroidMkDataProvider) error { @@ -695,6 +774,8 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Mod return nil } +// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider +// instead. func WriteAndroidMkData(w io.Writer, data AndroidMkData) { if data.Disabled { return @@ -742,11 +823,14 @@ func shouldSkipAndroidMkProcessing(module *ModuleBase) bool { module.Os() == LinuxBionic } +// A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how +// to use this func. func AndroidMkDataPaths(data []DataPath) []string { var testFiles []string for _, d := range data { rel := d.SrcPath.Rel() path := d.SrcPath.String() + // LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path. if !strings.HasSuffix(path, rel) { panic(fmt.Errorf("path %q does not end with %q", path, rel)) }