diff options
130 files changed, 4605 insertions, 859 deletions
diff --git a/aconfig/build_flags/Android.bp b/aconfig/build_flags/Android.bp new file mode 100644 index 000000000..69e431606 --- /dev/null +++ b/aconfig/build_flags/Android.bp @@ -0,0 +1,23 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-aconfig-build_flags", + pkgPath: "android/soong/aconfig/build_flags", + deps: [ + "blueprint", + "blueprint-pathtools", + "sbox_proto", + "soong", + "soong-android", + ], + srcs: [ + "all_build_flag_declarations.go", + "declarations.go", + "init.go", + ], + testSrcs: [ + ], + pluginFor: ["soong_build"], +} diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go new file mode 100644 index 000000000..282c9dcf8 --- /dev/null +++ b/aconfig/build_flags/all_build_flag_declarations.go @@ -0,0 +1,78 @@ +// Copyright 2023 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 build_flags + +import ( + "android/soong/android" +) + +// A singleton module that collects all of the build flags declared in the +// tree into a single combined file for export to the external flag setting +// server (inside Google it's Gantry). +// +// Note that this is ALL build_declarations modules present in the tree, not just +// ones that are relevant to the product currently being built, so that that infra +// doesn't need to pull from multiple builds and merge them. +func AllBuildFlagDeclarationsFactory() android.Singleton { + return &allBuildFlagDeclarationsSingleton{} +} + +type allBuildFlagDeclarationsSingleton struct { + intermediateBinaryProtoPath android.OutputPath + intermediateTextProtoPath android.OutputPath +} + +func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) { + // Find all of the build_flag_declarations modules + var intermediateFiles android.Paths + ctx.VisitAllModules(func(module android.Module) { + decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey) + if !ok { + return + } + intermediateFiles = append(intermediateFiles, decl.IntermediateCacheOutputPath) + }) + + // Generate build action for build_flag (binary proto output) + this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb") + ctx.Build(pctx, android.BuildParams{ + Rule: allDeclarationsRule, + Inputs: intermediateFiles, + Output: this.intermediateBinaryProtoPath, + Description: "all_build_flag_declarations", + Args: map[string]string{ + "intermediates": android.JoinPathsWithPrefix(intermediateFiles, "--intermediate "), + }, + }) + ctx.Phony("all_build_flag_declarations", this.intermediateBinaryProtoPath) + + // Generate build action for build_flag (text proto output) + this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto") + ctx.Build(pctx, android.BuildParams{ + Rule: allDeclarationsRuleTextProto, + Input: this.intermediateBinaryProtoPath, + Output: this.intermediateTextProtoPath, + Description: "all_build_flag_declarations_textproto", + }) + ctx.Phony("all_build_flag_declarations_textproto", this.intermediateTextProtoPath) +} + +func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) { + ctx.DistForGoal("droid", this.intermediateBinaryProtoPath) + for _, goal := range []string{"docs", "droid", "sdk"} { + ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "build_flags/all_flags.pb") + ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "build_flags/all_flags.textproto") + } +} diff --git a/aconfig/build_flags/declarations.go b/aconfig/build_flags/declarations.go new file mode 100644 index 000000000..f6a6ee1a8 --- /dev/null +++ b/aconfig/build_flags/declarations.go @@ -0,0 +1,118 @@ +// Copyright 2023 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 build_flags + +import ( + "fmt" + "strings" + + "android/soong/android" + + "github.com/google/blueprint" +) + +type BuildFlagDeclarationsProviderData struct { + IntermediateCacheOutputPath android.WritablePath + IntermediateDumpOutputPath android.WritablePath +} + +var BuildFlagDeclarationsProviderKey = blueprint.NewProvider[BuildFlagDeclarationsProviderData]() + +type DeclarationsModule struct { + android.ModuleBase + android.DefaultableModuleBase + + // Properties for "aconfig_declarations" + properties struct { + // aconfig files, relative to this Android.bp file + Srcs []string `android:"path"` + } + + intermediatePath android.WritablePath +} + +func DeclarationsFactory() android.Module { + module := &DeclarationsModule{} + + android.InitAndroidModule(module) + android.InitDefaultableModule(module) + module.AddProperties(&module.properties) + + return module +} + +func (module *DeclarationsModule) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + // The default output of this module is the intermediates format, which is + // not installable and in a private format that no other rules can handle + // correctly. + return []android.Path{module.intermediatePath}, nil + default: + return nil, fmt.Errorf("unsupported build_flags_declarations module reference tag %q", tag) + } +} + +func joinAndPrefix(prefix string, values []string) string { + var sb strings.Builder + for _, v := range values { + sb.WriteString(prefix) + sb.WriteString(v) + } + return sb.String() +} + +func optionalVariable(prefix string, value string) string { + var sb strings.Builder + if value != "" { + sb.WriteString(prefix) + sb.WriteString(value) + } + return sb.String() +} + +func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Intermediate format + declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) + intermediateCacheFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.pb") + inputFiles := make([]android.Path, len(declarationFiles)) + copy(inputFiles, declarationFiles) + + // TODO(lamont): generate the rc_proto.FlagArtifacts message for the sources. + args := map[string]string{ + "release_version": ctx.Config().ReleaseVersion(), + "declarations": android.JoinPathsWithPrefix(declarationFiles, "--decl "), + } + ctx.Build(pctx, android.BuildParams{ + Rule: buildFlagRule, + Output: intermediateCacheFilePath, + Inputs: inputFiles, + Description: "build_flag_declarations", + Args: args, + }) + + intermediateDumpFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.textproto") + ctx.Build(pctx, android.BuildParams{ + Rule: buildFlagTextRule, + Output: intermediateDumpFilePath, + Input: intermediateCacheFilePath, + Description: "build_flag_declarations_text", + }) + + android.SetProvider(ctx, BuildFlagDeclarationsProviderKey, BuildFlagDeclarationsProviderData{ + IntermediateCacheOutputPath: intermediateCacheFilePath, + IntermediateDumpOutputPath: intermediateDumpFilePath, + }) +} diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go new file mode 100644 index 000000000..5907f4eb3 --- /dev/null +++ b/aconfig/build_flags/init.go @@ -0,0 +1,78 @@ +// Copyright 2023 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 build_flags + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +var ( + pctx = android.NewPackageContext("android/soong/aconfig/build_flags") + + // For build_flag_declarations: Generate cache file + buildFlagRule = pctx.AndroidStaticRule("build-flag-declarations", + blueprint.RuleParams{ + Command: `${buildFlagDeclarations} ` + + ` ${declarations}` + + ` --format pb` + + ` --output ${out}.tmp` + + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, + CommandDeps: []string{ + "${buildFlagDeclarations}", + }, + Restat: true, + }, "release_version", "declarations") + + buildFlagTextRule = pctx.AndroidStaticRule("build-flag-declarations-text", + blueprint.RuleParams{ + Command: `${buildFlagDeclarations} --format=textproto` + + ` --intermediate ${in}` + + ` --format textproto` + + ` --output ${out}.tmp` + + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, + CommandDeps: []string{ + "${buildFlagDeclarations}", + }, + Restat: true, + }) + + allDeclarationsRule = pctx.AndroidStaticRule("all-build-flag-declarations-dump", + blueprint.RuleParams{ + Command: `${buildFlagDeclarations} ${intermediates} --format pb --output ${out}`, + CommandDeps: []string{ + "${buildFlagDeclarations}", + }, + }, "intermediates") + + allDeclarationsRuleTextProto = pctx.AndroidStaticRule("All_build_flag_declarations_dump_textproto", + blueprint.RuleParams{ + Command: `${buildFlagDeclarations} --intermediate ${in} --format textproto --output ${out}`, + CommandDeps: []string{ + "${buildFlagDeclarations}", + }, + }) +) + +func init() { + RegisterBuildComponents(android.InitRegistrationContext) + pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations") +} + +func RegisterBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("build_flag_declarations", DeclarationsFactory) + ctx.RegisterParallelSingletonType("all_build_flag_declarations", AllBuildFlagDeclarationsFactory) +} diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go index 4c1782b8e..ee9891df1 100644 --- a/android/aconfig_providers.go +++ b/android/aconfig_providers.go @@ -43,14 +43,6 @@ type AconfigDeclarationsProviderData struct { var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]() -// This is used to collect the aconfig declarations info on the transitive closure, -// the data is keyed on the container. -type AconfigTransitiveDeclarationsInfo struct { - AconfigFiles map[string]Paths -} - -var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]() - type ModeInfo struct { Container string Mode string @@ -80,54 +72,15 @@ func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]Mo } } -// CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than -// we can do in ModuleBase. -func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) { - if *mergedAconfigFiles == nil { - *mergedAconfigFiles = make(map[string]Paths) - } - ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) { - if dep, _ := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); dep.IntermediateCacheOutputPath != nil { - (*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath) - return - } - if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok { - for container, v := range dep.AconfigFiles { - (*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...) - } - } - // We process these last, so that they determine the final value, eliminating any duplicates that we picked up - // from UpdateAndroidBuildActions. - if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok { - for container, v := range dep.AconfigFiles { - (*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...) - } - } - }) - - for _, container := range SortedKeys(*mergedAconfigFiles) { - aconfigFiles := (*mergedAconfigFiles)[container] - (*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, container, aconfigFiles, false) - } - - SetProvider(ctx, AconfigTransitiveDeclarationsInfoProvider, AconfigTransitiveDeclarationsInfo{ - AconfigFiles: *mergedAconfigFiles, - }) -} - -func SetAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) { - setAconfigFileMkEntries(m, entries, aconfigFiles) -} - type aconfigPropagatingDeclarationsInfo struct { AconfigFiles map[string]Paths ModeInfos map[string]ModeInfo } -var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]() +var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]() func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) { - if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok { + if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { for k, v := range dep.ModeInfos { msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n", module.Name(), container, k, v.Container, v.Mode) @@ -159,17 +112,12 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) } - if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok { + if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { for container, v := range dep.AconfigFiles { mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) } propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos) } - if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok { - for container, v := range dep.AconfigFiles { - mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) - } - } }) // We only need to set the provider if we have aconfig files. if len(mergedAconfigFiles) > 0 { @@ -178,15 +126,16 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true) } - SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{ + SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{ AconfigFiles: mergedAconfigFiles, ModeInfos: mergedModeInfos, }) + ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles) } } func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) { - info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey) + info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) // If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done. if !ok || len(info.AconfigFiles) == 0 { return @@ -217,7 +166,7 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries if len(*entries) == 0 { return } - info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey) + info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) if !ok || len(info.AconfigFiles) == 0 { return } @@ -225,7 +174,7 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries for idx, _ := range *entries { (*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries, func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { - setAconfigFileMkEntries(mod.base(), entries, info.AconfigFiles) + entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles)) }, ) @@ -255,10 +204,6 @@ func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, genera return Paths{output} } -func setAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) { - entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(m, aconfigFiles)) -} - func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) { // TODO(b/311155208): The default container here should be system. container := "system" diff --git a/android/apex.go b/android/apex.go index dc0aeed17..fcbd13ea2 100644 --- a/android/apex.go +++ b/android/apex.go @@ -37,11 +37,7 @@ var ( // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` type ApexInfo struct { // Name of the apex variation that this module (i.e. the apex variant of the module) is - // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that this name and the - // Soong module name of the APEX can be different. That happens when there is - // `override_apex` that overrides `apex`. In that case, both Soong modules have the same - // apex variation name which usually is `com.android.foo`. This name is also the `name` - // in the path `/apex/<name>` where this apex is activated on at runtime. + // mutated into, or "" for a platform (i.e. non-APEX) variant. // // Also note that a module can be included in multiple APEXes, in which case, the module is // mutated into one or more variants, each of which is for an APEX. The variants then can diff --git a/android/config.go b/android/config.go index f6711e61f..76c590ad9 100644 --- a/android/config.go +++ b/android/config.go @@ -2109,6 +2109,7 @@ var ( "RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS", "RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION", "RELEASE_APEX_CONTRIBUTIONS_PERMISSION", + "RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS", "RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING", "RELEASE_APEX_CONTRIBUTIONS_RESOLV", "RELEASE_APEX_CONTRIBUTIONS_SCHEDULING", diff --git a/android/filegroup.go b/android/filegroup.go index 86d7b4bbd..a8326d46a 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -56,9 +56,6 @@ type fileGroup struct { DefaultableModuleBase properties fileGroupProperties srcs Paths - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]Paths } var _ SourceFileProducer = (*fileGroup)(nil) @@ -97,7 +94,6 @@ func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) { fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path)) } SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()}) - CollectDependencyAconfigFiles(ctx, &fg.mergedAconfigFiles) var aconfigDeclarations []string var intermediateCacheOutputPaths Paths diff --git a/android/makevars.go b/android/makevars.go index e73645fdc..f92f4581e 100644 --- a/android/makevars.go +++ b/android/makevars.go @@ -473,7 +473,7 @@ func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestI # Values written by Soong to generate install rules that can be amended by Kati. - +EXTRA_INSTALL_ZIPS := `) preserveSymlinksFlag := "-d" @@ -507,9 +507,13 @@ func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestI if extraFiles := install.extraFiles; extraFiles != nil { fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String()) fmt.Fprintf(buf, "\t ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n") + fmt.Fprintf(buf, "EXTRA_INSTALL_ZIPS += %s:%s:%s\n", install.to.String(), extraFiles.dir.String(), extraFiles.zip.String()) } + fmt.Fprintln(buf) } + fmt.Fprintf(buf, ".KATI_READONLY := EXTRA_INSTALL_ZIPS\n") + fmt.Fprintf(buf, "$(KATI_visibility_prefix EXTRA_INSTALL_ZIPS,build/make/core/Makefile)\n") for _, symlink := range symlinks { fmt.Fprintf(buf, "%s:", symlink.to.String()) diff --git a/android/module.go b/android/module.go index 40a591007..c4cc5e617 100644 --- a/android/module.go +++ b/android/module.go @@ -902,6 +902,9 @@ type ModuleBase struct { installedInitRcPaths InstallPaths installedVintfFragmentsPaths InstallPaths + // Merged Aconfig files for all transitive deps. + aconfigFilePaths Paths + // set of dependency module:location mappings used to populate the license metadata for // apex containers. licenseInstallMap []string @@ -1066,7 +1069,8 @@ func addRequiredDeps(ctx BottomUpMutatorContext) { // TODO(jiyong): the Make-side does this only when the required module is a shared // library or a native test. bothInAndroid := ctx.Device() && target.Os.Class == Device - nativeArch := InList(ctx.Arch().ArchType.Multilib, []string{"lib32", "lib64"}) + nativeArch := InList(ctx.Arch().ArchType.Multilib, []string{"lib32", "lib64"}) && + InList(target.Arch.ArchType.Multilib, []string{"lib32", "lib64"}) sameBitness := ctx.Arch().ArchType.Multilib == target.Arch.ArchType.Multilib if bothInAndroid && nativeArch && !sameBitness { return @@ -1892,12 +1896,14 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } } - m.module.GenerateAndroidBuildActions(ctx) + // Call aconfigUpdateAndroidBuildActions to collect merged aconfig files before being used + // in m.module.GenerateAndroidBuildActions + aconfigUpdateAndroidBuildActions(ctx) if ctx.Failed() { return } - aconfigUpdateAndroidBuildActions(ctx) + m.module.GenerateAndroidBuildActions(ctx) if ctx.Failed() { return } @@ -1976,6 +1982,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) TargetDependencies: targetRequired, HostDependencies: hostRequired, Data: data, + Required: m.RequiredModuleNames(), } SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON) } @@ -2145,19 +2152,39 @@ func (e configurationEvalutor) EvaluateConfiguration(condition proptools.Configu ctx := e.ctx m := e.m switch condition.FunctionName() { - case "release_variable": + case "release_flag": if condition.NumArgs() != 1 { - ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", condition.NumArgs()) + ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs()) return proptools.ConfigurableValueUndefined() } - if v, ok := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]; ok { - return proptools.ConfigurableValueString(v) + if ty, ok := ctx.Config().productVariables.BuildFlagTypes[condition.Arg(0)]; ok { + v := ctx.Config().productVariables.BuildFlags[condition.Arg(0)] + switch ty { + case "unspecified", "obsolete": + return proptools.ConfigurableValueUndefined() + case "string": + return proptools.ConfigurableValueString(v) + case "bool": + return proptools.ConfigurableValueBool(v == "true") + default: + panic("unhandled release flag type: " + ty) + } } return proptools.ConfigurableValueUndefined() case "product_variable": - // TODO(b/323382414): Might add these on a case-by-case basis - ctx.OtherModulePropertyErrorf(m, property, "TODO(b/323382414): Product variables are not yet supported in selects") - return proptools.ConfigurableValueUndefined() + if condition.NumArgs() != 1 { + ctx.OtherModulePropertyErrorf(m, property, "product_variable requires 1 argument, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + variable := condition.Arg(0) + switch variable { + case "debuggable": + return proptools.ConfigurableValueBool(ctx.Config().Debuggable()) + default: + // TODO(b/323382414): Might add these on a case-by-case basis + ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable)) + return proptools.ConfigurableValueUndefined() + } case "soong_config_variable": if condition.NumArgs() != 2 { ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", condition.NumArgs()) diff --git a/android/module_context.go b/android/module_context.go index 605d3baeb..18adb3002 100644 --- a/android/module_context.go +++ b/android/module_context.go @@ -482,6 +482,10 @@ func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPat return m.packageFile(fullInstallPath, srcPath, false) } +func (m *moduleContext) getAconfigPaths() *Paths { + return &m.module.base().aconfigFilePaths +} + func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { licenseFiles := m.Module().EffectiveLicenseFiles() spec := PackagingSpec{ @@ -492,6 +496,8 @@ func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, e effectiveLicenseFiles: &licenseFiles, partition: fullInstallPath.partition, skipInstall: m.skipInstall(), + aconfigPaths: m.getAconfigPaths(), + archType: m.target.Arch.ArchType, } m.packagingSpecs = append(m.packagingSpecs, spec) return spec @@ -616,6 +622,8 @@ func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, src executable: false, partition: fullInstallPath.partition, skipInstall: m.skipInstall(), + aconfigPaths: m.getAconfigPaths(), + archType: m.target.Arch.ArchType, }) return fullInstallPath @@ -658,6 +666,8 @@ func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name str executable: false, partition: fullInstallPath.partition, skipInstall: m.skipInstall(), + aconfigPaths: m.getAconfigPaths(), + archType: m.target.Arch.ArchType, }) return fullInstallPath diff --git a/android/module_info_json.go b/android/module_info_json.go index 1c0a38e5a..ee552dce5 100644 --- a/android/module_info_json.go +++ b/android/module_info_json.go @@ -17,6 +17,7 @@ type CoreModuleInfoJSON struct { HostDependencies []string `json:"host_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET)) TargetDependencies []string `json:"target_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST)) Data []string `json:"data,omitempty"` // $(sort $(ALL_MODULES.$(m).TEST_DATA)) + Required []string `json:"required,omitempty"` // $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET)) } type ModuleInfoJSON struct { @@ -77,6 +78,7 @@ func encodeModuleInfoJSON(w io.Writer, moduleInfoJSON *ModuleInfoJSON) error { sortAndUnique(&moduleInfoJSONCopy.core.HostDependencies) sortAndUnique(&moduleInfoJSONCopy.core.TargetDependencies) sortAndUnique(&moduleInfoJSONCopy.core.Data) + sortAndUnique(&moduleInfoJSONCopy.core.Required) sortAndUnique(&moduleInfoJSONCopy.Class) sortAndUnique(&moduleInfoJSONCopy.Tags) diff --git a/android/packaging.go b/android/packaging.go index fe61da1e9..ae412e1bb 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -20,6 +20,7 @@ import ( "strings" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) // PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A @@ -48,6 +49,12 @@ type PackagingSpec struct { // is created via InstallFile or InstallSymlink) or a simple packaging (i.e. created via // PackageFile). skipInstall bool + + // Paths of aconfig files for the built artifact + aconfigPaths *Paths + + // ArchType of the module which produced this packaging spec + archType ArchType } func (p *PackagingSpec) Equals(other *PackagingSpec) bool { @@ -102,6 +109,11 @@ func (p *PackagingSpec) SkipInstall() bool { return p.skipInstall } +// Paths of aconfig files for the built artifact +func (p *PackagingSpec) GetAconfigPaths() Paths { + return *p.aconfigPaths +} + type PackageModule interface { Module packagingBase() *PackagingBase @@ -131,18 +143,24 @@ type PackagingBase struct { // for rare cases like when there's a dependency to a module which exists in certain repo // checkouts, this is needed. IgnoreMissingDependencies bool + + // If this is set to true by a module type inheriting PackagingBase, the deps property + // collects the first target only even with compile_multilib: true. + DepsCollectFirstTargetOnly bool } type depsProperty struct { // Modules to include in this package - Deps []string `android:"arch_variant"` + Deps proptools.Configurable[[]string] `android:"arch_variant"` } type packagingMultilibProperties struct { - First depsProperty `android:"arch_variant"` - Common depsProperty `android:"arch_variant"` - Lib32 depsProperty `android:"arch_variant"` - Lib64 depsProperty `android:"arch_variant"` + First depsProperty `android:"arch_variant"` + Common depsProperty `android:"arch_variant"` + Lib32 depsProperty `android:"arch_variant"` + Lib64 depsProperty `android:"arch_variant"` + Both depsProperty `android:"arch_variant"` + Prefer32 depsProperty `android:"arch_variant"` } type packagingArchProperties struct { @@ -153,8 +171,8 @@ type packagingArchProperties struct { } type PackagingProperties struct { - Deps []string `android:"arch_variant"` - Multilib packagingMultilibProperties `android:"arch_variant"` + Deps proptools.Configurable[[]string] `android:"arch_variant"` + Multilib packagingMultilibProperties `android:"arch_variant"` Arch packagingArchProperties } @@ -172,22 +190,55 @@ func (p *PackagingBase) packagingBase() *PackagingBase { // multi target, deps is selected for each of the targets and is NOT selected for the current // architecture which would be Common. func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string { + get := func(prop proptools.Configurable[[]string]) []string { + return prop.GetOrDefault(ctx, nil) + } + var ret []string if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 { - ret = append(ret, p.properties.Deps...) + ret = append(ret, get(p.properties.Deps)...) } else if arch.Multilib == "lib32" { - ret = append(ret, p.properties.Multilib.Lib32.Deps...) + ret = append(ret, get(p.properties.Multilib.Lib32.Deps)...) + // multilib.prefer32.deps are added for lib32 only when they support 32-bit arch + for _, dep := range get(p.properties.Multilib.Prefer32.Deps) { + if checkIfOtherModuleSupportsLib32(ctx, dep) { + ret = append(ret, dep) + } + } } else if arch.Multilib == "lib64" { - ret = append(ret, p.properties.Multilib.Lib64.Deps...) + ret = append(ret, get(p.properties.Multilib.Lib64.Deps)...) + // multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch + for _, dep := range get(p.properties.Multilib.Prefer32.Deps) { + if !checkIfOtherModuleSupportsLib32(ctx, dep) { + ret = append(ret, dep) + } + } } else if arch == Common { - ret = append(ret, p.properties.Multilib.Common.Deps...) + ret = append(ret, get(p.properties.Multilib.Common.Deps)...) } - for i, t := range ctx.MultiTargets() { - if t.Arch.ArchType == arch { - ret = append(ret, p.properties.Deps...) - if i == 0 { - ret = append(ret, p.properties.Multilib.First.Deps...) + if p.DepsCollectFirstTargetOnly { + if len(get(p.properties.Multilib.First.Deps)) > 0 { + ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead") + } + for i, t := range ctx.MultiTargets() { + if t.Arch.ArchType == arch { + ret = append(ret, get(p.properties.Multilib.Both.Deps)...) + if i == 0 { + ret = append(ret, get(p.properties.Deps)...) + } + } + } + } else { + if len(get(p.properties.Multilib.Both.Deps)) > 0 { + ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead") + } + for i, t := range ctx.MultiTargets() { + if t.Arch.ArchType == arch { + ret = append(ret, get(p.properties.Deps)...) + if i == 0 { + ret = append(ret, get(p.properties.Multilib.First.Deps)...) + } } } } @@ -195,20 +246,20 @@ func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []s if ctx.Arch().ArchType == Common { switch arch { case Arm64: - ret = append(ret, p.properties.Arch.Arm64.Deps...) + ret = append(ret, get(p.properties.Arch.Arm64.Deps)...) case Arm: - ret = append(ret, p.properties.Arch.Arm.Deps...) + ret = append(ret, get(p.properties.Arch.Arm.Deps)...) case X86_64: - ret = append(ret, p.properties.Arch.X86_64.Deps...) + ret = append(ret, get(p.properties.Arch.X86_64.Deps)...) case X86: - ret = append(ret, p.properties.Arch.X86.Deps...) + ret = append(ret, get(p.properties.Arch.X86.Deps)...) } } return FirstUniqueStrings(ret) } -func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target { +func getSupportedTargets(ctx BaseModuleContext) []Target { var ret []Target // The current and the common OS targets are always supported ret = append(ret, ctx.Target()) @@ -220,6 +271,28 @@ func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target { return ret } +// getLib32Target returns the 32-bit target from the list of targets this module supports. If this +// module doesn't support 32-bit target, nil is returned. +func getLib32Target(ctx BaseModuleContext) *Target { + for _, t := range getSupportedTargets(ctx) { + if t.Arch.ArchType.Multilib == "lib32" { + return &t + } + } + return nil +} + +// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists. +func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool { + t := getLib32Target(ctx) + if t == nil { + // This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit + // or not. + return false + } + return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep) +} + // PackagingItem is a marker interface for dependency tags. // Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip(). type PackagingItem interface { @@ -240,7 +313,7 @@ func (PackagingItemAlwaysDepTag) IsPackagingItem() bool { // See PackageModule.AddDeps func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) { - for _, t := range p.getSupportedTargets(ctx) { + for _, t := range getSupportedTargets(ctx) { for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) { if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) { continue @@ -252,11 +325,31 @@ func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.Dep func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec { m := make(map[string]PackagingSpec) + + var arches []ArchType + for _, target := range getSupportedTargets(ctx) { + arches = append(arches, target.Arch.ArchType) + } + + // filter out packaging specs for unsupported architecture + filterArch := func(ps PackagingSpec) bool { + for _, arch := range arches { + if arch == ps.archType { + return true + } + } + return false + } + ctx.VisitDirectDeps(func(child Module) { if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() { return } for _, ps := range child.TransitivePackagingSpecs() { + if !filterArch(ps) { + continue + } + if filter != nil { if !filter(ps) { continue diff --git a/android/packaging_test.go b/android/packaging_test.go index 383343723..19b46fefd 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -15,6 +15,7 @@ package android import ( + "strings" "testing" "github.com/google/blueprint" @@ -67,18 +68,15 @@ type packageTestModule struct { entries []string } -func packageMultiTargetTestModuleFactory() Module { +func packageTestModuleFactory(multiTarget bool, depsCollectFirstTargetOnly bool) Module { module := &packageTestModule{} InitPackageModule(module) - InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon) - module.AddProperties(&module.properties) - return module -} - -func packageTestModuleFactory() Module { - module := &packageTestModule{} - InitPackageModule(module) - InitAndroidArchModule(module, DeviceSupported, MultilibBoth) + module.DepsCollectFirstTargetOnly = depsCollectFirstTargetOnly + if multiTarget { + InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon) + } else { + InitAndroidArchModule(module, DeviceSupported, MultilibBoth) + } module.AddProperties(&module.properties) return module } @@ -98,17 +96,24 @@ func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile) } -func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) { +type testConfig struct { + multiTarget bool + depsCollectFirstTargetOnly bool + debuggable bool +} + +func runPackagingTest(t *testing.T, config testConfig, bp string, expected []string) { t.Helper() var archVariant string - var moduleFactory ModuleFactory - if multitarget { + if config.multiTarget { archVariant = "android_common" - moduleFactory = packageMultiTargetTestModuleFactory } else { archVariant = "android_arm64_armv8-a" - moduleFactory = packageTestModuleFactory + } + + moduleFactory := func() Module { + return packageTestModuleFactory(config.multiTarget, config.depsCollectFirstTargetOnly) } result := GroupFixturePreparers( @@ -117,6 +122,9 @@ func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []stri ctx.RegisterModuleType("component", componentTestModuleFactory) ctx.RegisterModuleType("package_module", moduleFactory) }), + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.Debuggable = proptools.BoolPtr(config.debuggable) + }), FixtureWithRootAndroidBp(bp), ).RunTest(t) @@ -128,8 +136,11 @@ func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []stri } func TestPackagingBaseMultiTarget(t *testing.T) { - multiTarget := true - runPackagingTest(t, multiTarget, + config := testConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -141,7 +152,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -158,7 +169,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -176,7 +187,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -199,7 +210,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -221,7 +232,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -252,8 +263,11 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } func TestPackagingBaseSingleTarget(t *testing.T) { - multiTarget := false - runPackagingTest(t, multiTarget, + config := testConfig{ + multiTarget: false, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -265,7 +279,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -282,7 +296,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -304,7 +318,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -325,7 +339,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -353,7 +367,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -374,8 +388,11 @@ func TestPackagingBaseSingleTarget(t *testing.T) { func TestPackagingWithSkipInstallDeps(t *testing.T) { // package -[dep]-> foo -[dep]-> bar -[dep]-> baz // Packaging should continue transitively through modules that are not installed. - multiTarget := false - runPackagingTest(t, multiTarget, + config := testConfig{ + multiTarget: false, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -398,3 +415,238 @@ func TestPackagingWithSkipInstallDeps(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar", "lib64/baz"}) } + +func TestPackagingWithDepsCollectFirstTargetOnly(t *testing.T) { + config := testConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: true, + } + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + compile_multilib: "32", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + lib32: { + deps: ["bar"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib32/bar", "lib64/foo"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + both: { + deps: ["bar"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib32/bar", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + component { + name: "baz", + } + + package_module { + name: "package", + deps: ["foo"], + arch: { + arm64: { + deps: ["bar"], + }, + x86_64: { + deps: ["baz"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib64/bar"}) +} + +func TestDebuggableDeps(t *testing.T) { + bp := ` + component { + name: "foo", + } + + component { + name: "bar", + deps: ["baz"], + } + + component { + name: "baz", + } + + package_module { + name: "package", + deps: ["foo"] + select(product_variable("debuggable"), { + true: ["bar"], + default: [], + }), + }` + testcases := []struct { + debuggable bool + expected []string + }{ + { + debuggable: true, + expected: []string{"lib64/foo", "lib64/bar", "lib64/baz"}, + }, + { + debuggable: false, + expected: []string{"lib64/foo"}, + }, + } + for _, tc := range testcases { + config := testConfig{ + debuggable: tc.debuggable, + } + runPackagingTest(t, config, bp, tc.expected) + } +} + +func TestPrefer32Deps(t *testing.T) { + bpTemplate := ` + component { + name: "foo", + compile_multilib: "both", // not needed but for clarity + } + + component { + name: "foo_32only", + compile_multilib: "prefer32", + } + + component { + name: "foo_64only", + compile_multilib: "64", + } + + package_module { + name: "package", + compile_multilib: "%COMPILE_MULTILIB%", + multilib: { + prefer32: { + deps: %DEPS%, + }, + }, + } + ` + + testcases := []struct { + compileMultilib string + deps []string + expected []string + }{ + { + compileMultilib: "first", + deps: []string{"foo", "foo_64only"}, + expected: []string{"lib64/foo", "lib64/foo_64only"}, + }, + { + compileMultilib: "64", + deps: []string{"foo", "foo_64only"}, + expected: []string{"lib64/foo", "lib64/foo_64only"}, + }, + { + compileMultilib: "32", + deps: []string{"foo", "foo_32only"}, + expected: []string{"lib32/foo", "lib32/foo_32only"}, + }, + { + compileMultilib: "both", + deps: []string{"foo", "foo_32only", "foo_64only"}, + expected: []string{"lib32/foo", "lib32/foo_32only", "lib64/foo_64only"}, + }, + } + for _, tc := range testcases { + config := testConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: true, + } + bp := strings.Replace(bpTemplate, "%COMPILE_MULTILIB%", tc.compileMultilib, -1) + bp = strings.Replace(bp, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1) + runPackagingTest(t, config, bp, tc.expected) + } +} diff --git a/android/selects_test.go b/android/selects_test.go index 56db4374a..87ba42a94 100644 --- a/android/selects_test.go +++ b/android/selects_test.go @@ -706,6 +706,29 @@ func TestSelects(t *testing.T) { `, expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string_list"`, }, + { + name: "Select in variable", + bp: ` + my_second_variable = ["after.cpp"] + my_variable = select(soong_config_variable("my_namespace", "my_variable"), { + "a": ["a.cpp"], + "b": ["b.cpp"], + default: ["c.cpp"], + }) + my_second_variable + my_module_type { + name: "foo", + my_string_list: ["before.cpp"] + my_variable, + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"before.cpp", "a.cpp", "after.cpp"}, + }, + vendorVars: map[string]map[string]string{ + "my_namespace": { + "my_variable": "a", + }, + }, + }, } for _, tc := range testCases { @@ -810,13 +833,21 @@ type selectsMockModule struct { properties selectsMockModuleProperties } +func optionalToPtr[T any](o proptools.ConfigurableOptional[T]) *T { + if o.IsEmpty() { + return nil + } + x := o.Get() + return &x +} + func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) { SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{ - my_bool: p.properties.My_bool.Get(ctx), - my_string: p.properties.My_string.Get(ctx), - my_string_list: p.properties.My_string_list.Get(ctx), - my_paths: p.properties.My_paths.Get(ctx), - replacing_string_list: p.properties.Replacing_string_list.Get(ctx), + my_bool: optionalToPtr(p.properties.My_bool.Get(ctx)), + my_string: optionalToPtr(p.properties.My_string.Get(ctx)), + my_string_list: optionalToPtr(p.properties.My_string_list.Get(ctx)), + my_paths: optionalToPtr(p.properties.My_paths.Get(ctx)), + replacing_string_list: optionalToPtr(p.properties.Replacing_string_list.Get(ctx)), my_nonconfigurable_bool: p.properties.My_nonconfigurable_bool, my_nonconfigurable_string: p.properties.My_nonconfigurable_string, my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list, diff --git a/android/util.go b/android/util.go index 698a85650..e21e66b88 100644 --- a/android/util.go +++ b/android/util.go @@ -24,6 +24,8 @@ import ( "sort" "strings" "sync" + + "github.com/google/blueprint/proptools" ) // CopyOf returns a new slice that has the same contents as s. @@ -302,6 +304,24 @@ func RemoveFromList(s string, list []string) (bool, []string) { return removed, result } +// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of +// each. It does not modify the input slice. The eq function should return true +// if two elements can be considered equal. +func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList { + k := 0 +outer: + for i := 0; i < len(list); i++ { + for j := 0; j < k; j++ { + if eq(list[i], list[j]) { + continue outer + } + } + list[k] = list[i] + k++ + } + return list[:k] +} + // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of // each. It does not modify the input slice. func FirstUniqueStrings(list []string) []string { @@ -524,25 +544,9 @@ func SplitFileExt(name string) (string, string, string) { return root, suffix, ext } -func shard[T ~[]E, E any](toShard T, shardSize int) []T { - if len(toShard) == 0 { - return nil - } - - ret := make([]T, 0, (len(toShard)+shardSize-1)/shardSize) - for len(toShard) > shardSize { - ret = append(ret, toShard[0:shardSize]) - toShard = toShard[shardSize:] - } - if len(toShard) > 0 { - ret = append(ret, toShard) - } - return ret -} - // ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths. func ShardPaths(paths Paths, shardSize int) []Paths { - return shard(paths, shardSize) + return proptools.ShardBySize(paths, shardSize) } // ShardString takes a string and returns a slice of strings where the length of each one is @@ -565,7 +569,7 @@ func ShardString(s string, shardSize int) []string { // ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize // elements. func ShardStrings(s []string, shardSize int) [][]string { - return shard(s, shardSize) + return proptools.ShardBySize(s, shardSize) } // CheckDuplicate checks if there are duplicates in given string list. diff --git a/android/variable.go b/android/variable.go index 419bd61de..a3fdafb77 100644 --- a/android/variable.go +++ b/android/variable.go @@ -492,6 +492,8 @@ type ProductVariables struct { BuildFlags map[string]string `json:",omitempty"` + BuildFlagTypes map[string]string `json:",omitempty"` + BuildFromSourceStub *bool `json:",omitempty"` BuildIgnoreApexContributionContents *bool `json:",omitempty"` diff --git a/apex/apex.go b/apex/apex.go index 40eb712ab..1dec61b6d 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -137,10 +137,6 @@ type apexBundleProperties struct { // Rust binaries with prefer_rlib:true add unnecessary dependencies. Unwanted_transitive_deps []string - // The minimum SDK version that this APEX must support at minimum. This is usually set to - // the SDK version that the APEX was first introduced. - Min_sdk_version *string - // Whether this APEX is considered updatable or not. When set to true, this will enforce // additional rules for making sure that the APEX is truly updatable. To be updatable, // min_sdk_version should be set as well. This will also disable the size optimizations like @@ -388,6 +384,10 @@ type overridableProperties struct { // Trim against a specific Dynamic Common Lib APEX Trim_against *string + + // The minimum SDK version that this APEX must support at minimum. This is usually set to + // the SDK version that the APEX was first introduced. + Min_sdk_version *string } type apexBundle struct { @@ -490,9 +490,6 @@ type apexBundle struct { javaApisUsedByModuleFile android.ModuleOutPath aconfigFiles []android.Path - - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths } // apexFileClass represents a type of file that can be included in APEX. @@ -702,7 +699,12 @@ var ( func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) { binVariations := target.Variations() libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}) + rustLibVariations := append( + target.Variations(), []blueprint.Variation{ + {Mutator: "rust_libraries", Variation: "dylib"}, + {Mutator: "link", Variation: ""}, + }..., + ) // Append "image" variation binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) @@ -1033,6 +1035,11 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { // be built for this apexBundle. apexVariationName := mctx.ModuleName() // could be com.android.foo + if overridable, ok := mctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" { + // use the overridden name com.mycompany.android.foo + apexVariationName = overridable.GetOverriddenBy() + } + a.properties.ApexVariationName = apexVariationName testApexes := []string{} if a.testApex { @@ -1097,7 +1104,7 @@ func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) { if !mctx.Module().Enabled(mctx) { return } - if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() { + if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting(mctx) { mctx.WalkDeps(func(child, parent android.Module) bool { // b/208656169 Do not propagate strict updatability linting to libcore/ // These libs are available on the classpath during compilation @@ -1191,8 +1198,9 @@ var ( } ) -func (a *apexBundle) checkStrictUpdatabilityLinting() bool { - return a.Updatable() && !android.InList(a.ApexVariationName(), skipStrictUpdatabilityLintAllowlist) +func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool { + // The allowlist contains the base apex name, so use that instead of the ApexVariationName + return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist) } // apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use @@ -1293,13 +1301,12 @@ type apexTransitionMutator struct{} func (a *apexTransitionMutator) Split(ctx android.BaseModuleContext) []string { // apexBundle itself is mutated so that it and its dependencies have the same apex variant. if ai, ok := ctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) { - return []string{ai.ApexVariationName()} - } else if o, ok := ctx.Module().(*OverrideApex); ok { - apexBundleName := o.GetOverriddenModuleName() - if apexBundleName == "" { - ctx.ModuleErrorf("base property is not set") + if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" { + return []string{overridable.GetOverriddenBy()} } - return []string{apexBundleName} + return []string{ai.ApexVariationName()} + } else if _, ok := ctx.Module().(*OverrideApex); ok { + return []string{ctx.ModuleName()} } return []string{""} } @@ -1312,9 +1319,12 @@ func (a *apexTransitionMutator) IncomingTransition(ctx android.IncomingTransitio if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() { return android.IncomingApexTransition(ctx, incomingVariation) } else if ai, ok := ctx.Module().(ApexInfoMutator); ok { + if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" { + return overridable.GetOverriddenBy() + } return ai.ApexVariationName() - } else if o, ok := ctx.Module().(*OverrideApex); ok { - return o.GetOverriddenModuleName() + } else if _, ok := ctx.Module().(*OverrideApex); ok { + return ctx.Module().Name() } return "" @@ -1639,7 +1649,8 @@ func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFil func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile { dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir()) - return newApexFile(ctx, outputFile, outputFile.Base(), dirInApex, etc, prebuilt) + makeModuleName := strings.ReplaceAll(filepath.Join(dirInApex, outputFile.Base()), "/", "_") + return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, prebuilt) } func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile { @@ -2326,7 +2337,7 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) { - if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider); ok { + if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigPropagatingProviderKey); ok { if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil { vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...) } @@ -2414,7 +2425,6 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } } - android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles) //////////////////////////////////////////////////////////////////////////////////////////// // 3) some fields in apexBundle struct are configured @@ -2586,9 +2596,6 @@ func BundleFactory() android.Module { type Defaults struct { android.ModuleBase android.DefaultsModuleBase - - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths } // apex_defaults provides defaultable properties to other apex modules. @@ -2611,10 +2618,6 @@ type OverrideApex struct { android.OverrideModuleBase } -func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) { - android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles) -} - func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) { // All the overrides happen in the base module. } @@ -2657,7 +2660,7 @@ func (a *apexBundle) minSdkVersionValue(ctx android.EarlyModuleContext) string { // Only override the minSdkVersion value on Apexes which already specify // a min_sdk_version (it's optional for non-updatable apexes), and that its // min_sdk_version value is lower than the one to override with. - minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version)) + minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version)) if minApiLevel.IsNone() { return "" } diff --git a/apex/apex_test.go b/apex/apex_test.go index 9a5c2b49b..965b4be24 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -5049,6 +5049,20 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding // is disabled. android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + + // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi + // file creation. + java.FixtureConfigureBootJars("platform:foo"), + android.FixtureModifyMockFS(func(fs android.MockFS) { + fs["platform/Android.bp"] = []byte(` + java_library { + name: "foo", + srcs: ["Test.java"], + compile_dex: true, + } + `) + fs["platform/Test.java"] = nil + }), ) checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { @@ -5143,7 +5157,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. - checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) + checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv @@ -5221,7 +5235,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. - checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) + checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv @@ -5410,7 +5424,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. - checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) + checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv @@ -5507,7 +5521,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. - checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) + checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv @@ -5620,7 +5634,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. - checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) + checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv @@ -5908,6 +5922,7 @@ func TestApexWithApps(t *testing.T) { srcs: ["foo/bar/MyClass.java"], sdk_version: "current", system_modules: "none", + use_embedded_native_libs: true, jni_libs: ["libjni"], stl: "none", apex_available: [ "myapex" ], @@ -6462,7 +6477,7 @@ func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) { t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) } - overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar").Rule("apexManifestRule") + overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule") overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"] if overrideBarActualDefaultVersion != barExpectedDefaultVersion { t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) @@ -6842,7 +6857,7 @@ func TestOverrideApex(t *testing.T) { `, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"})) originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule) - overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Module().(android.OverridableModule) + overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule) if originalVariant.GetOverriddenBy() != "" { t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy()) } @@ -6850,7 +6865,7 @@ func TestOverrideApex(t *testing.T) { t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy()) } - module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex") + module := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -8942,7 +8957,7 @@ func TestAllowedFiles(t *testing.T) { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } - rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Rule("diffApexContentRule") + rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Rule("diffApexContentRule") if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } @@ -11238,6 +11253,20 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { android.FixtureMergeMockFs(map[string][]byte{ "system/sepolicy/apex/com.android.foo-file_contexts": nil, }), + // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi + // file creation. + java.FixtureConfigureBootJars("platform:foo"), + android.FixtureModifyMockFS(func(fs android.MockFS) { + fs["platform/Android.bp"] = []byte(` + java_library { + name: "foo", + srcs: ["Test.java"], + compile_dex: true, + } + `) + fs["platform/Test.java"] = nil + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildFlags = map[string]string{ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions, @@ -11266,7 +11295,7 @@ func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { variation := func(moduleName string) string { ret := "android_common_com.android.foo" if moduleName == "com.google.android.foo" { - ret = "android_common_com.google.android.foo_com.android.foo" + ret = "android_common_com.google.android.foo_com.google.android.foo" } return ret } @@ -11531,3 +11560,118 @@ func TestAconfifDeclarationsValidation(t *testing.T) { "depend on java_aconfig_library not passed as an input", aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "quux")) } + +func TestMultiplePrebuiltsWithSameBase(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + prebuilts: ["myetc", "myetc2"], + min_sdk_version: "29", + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + prebuilt_etc { + name: "myetc", + src: "myprebuilt", + filename: "myfilename", + } + prebuilt_etc { + name: "myetc2", + sub_dir: "mysubdir", + src: "myprebuilt", + filename: "myfilename", + } + `, withFiles(android.MockFS{ + "packages/modules/common/build/allowed_deps.txt": nil, + })) + + ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) + data := android.AndroidMkDataForTest(t, ctx, ab) + var builder strings.Builder + data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) + androidMk := builder.String() + + android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_myfilename.myapex") + android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_mysubdir_myfilename.myapex") +} + +func TestApexMinSdkVersionOverride(t *testing.T) { + checkMinSdkVersion := func(t *testing.T, module android.TestingModule, expectedMinSdkVersion string) { + args := module.Rule("apexRule").Args + optFlags := args["opt_flags"] + if !strings.Contains(optFlags, "--min_sdk_version "+expectedMinSdkVersion) { + t.Errorf("%s: Expected min_sdk_version=%s, got: %s", module.Module(), expectedMinSdkVersion, optFlags) + } + } + + checkHasDep := func(t *testing.T, ctx *android.TestContext, m android.Module, wantDep android.Module) { + t.Helper() + found := false + ctx.VisitDirectDeps(m, func(dep blueprint.Module) { + if dep == wantDep { + found = true + } + }) + if !found { + t.Errorf("Could not find a dependency from %v to %v\n", m, wantDep) + } + } + + ctx := testApex(t, ` + apex { + name: "com.android.apex30", + min_sdk_version: "30", + key: "apex30.key", + java_libs: ["javalib"], + } + + java_library { + name: "javalib", + srcs: ["A.java"], + apex_available: ["com.android.apex30"], + min_sdk_version: "30", + sdk_version: "current", + } + + override_apex { + name: "com.mycompany.android.apex30", + base: "com.android.apex30", + } + + override_apex { + name: "com.mycompany.android.apex31", + base: "com.android.apex30", + min_sdk_version: "31", + } + + apex_key { + name: "apex30.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + `, android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.android.apex30-file_contexts": nil, + }), + ) + + baseModule := ctx.ModuleForTests("com.android.apex30", "android_common_com.android.apex30") + checkMinSdkVersion(t, baseModule, "30") + + // Override module, but uses same min_sdk_version + overridingModuleSameMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30") + javalibApex30Variant := ctx.ModuleForTests("javalib", "android_common_apex30") + checkMinSdkVersion(t, overridingModuleSameMinSdkVersion, "30") + checkHasDep(t, ctx, overridingModuleSameMinSdkVersion.Module(), javalibApex30Variant.Module()) + + // Override module, uses different min_sdk_version + overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31") + javalibApex31Variant := ctx.ModuleForTests("javalib", "android_common_apex31") + checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31") + checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module()) +} diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 2be9c10e1..9f1e1e16f 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -795,3 +795,127 @@ func TestNonBootJarInFragment(t *testing.T) { } `) } + +// Source and prebuilt apex provide different set of boot jars +func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) { + bp := ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: ["apex-fragment"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + bootclasspath_fragment { + name: "apex-fragment", + contents: ["foo", "bar"], + apex_available:[ "myapex" ], + hidden_api: { + split_packages: ["*"], + }, + } + + prebuilt_apex { + name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android + apex_name: "myapex", + source_apex_name: "myapex", + src: "myapex.apex", + exported_bootclasspath_fragments: ["apex-fragment"], + } + + java_import { + name: "foo", + jars: ["foo.jar"], + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + prebuilt_bootclasspath_fragment { + name: "apex-fragment", + contents: ["foo"], // Unlike the source fragment, this is missing bar + apex_available:[ "myapex" ], + hidden_api: { + annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", + metadata: "my-bootclasspath-fragment/metadata.csv", + index: "my-bootclasspath-fragment/index.csv", + stub_flags: "my-bootclasspath-fragment/stub-flags.csv", + all_flags: "my-bootclasspath-fragment/all-flags.csv", + }, + } + + apex_contributions { + name: "my_apex_contributions", + api_domain: "myapex", + contents: [%v], + } + ` + testCases := []struct { + desc string + configuredBootJars []string + apexContributionContents string + errorExpected bool + }{ + { + desc: "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds", + configuredBootJars: []string{"myapex:foo", "myapex:bar"}, + }, + { + desc: "Source apex is selected, and APEX_BOOT_JARS is missing bar", + configuredBootJars: []string{"myapex:foo"}, + errorExpected: true, + }, + { + desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build", + configuredBootJars: []string{"myapex:foo"}, + apexContributionContents: `"prebuilt_com.google.android.myapex"`, + }, + { + desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo", + configuredBootJars: []string{"myapex:bar"}, + apexContributionContents: `"prebuilt_com.google.android.myapex"`, + errorExpected: true, + }, + } + + for _, tc := range testCases { + fixture := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + java.FixtureConfigureApexBootJars(tc.configuredBootJars...), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions", + } + }), + ) + if tc.errorExpected { + fixture = fixture.ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`), + ) + } + fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents)) + } +} diff --git a/apex/prebuilt.go b/apex/prebuilt.go index ea847e15c..b2afa3933 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -503,9 +503,6 @@ type Prebuilt struct { inputApex android.Path provenanceMetaDataFile android.OutputPath - - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths } type ApexFileProperties struct { @@ -838,7 +835,21 @@ func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) { android.SetProvider(ctx, android.PrebuiltInfoProvider, info) } +// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global +// PRODUCT_APEX_BOOT_JARS +// This validation will only run on the apex which is active for this product/release_config +func validateApexClasspathFragments(ctx android.ModuleContext) { + ctx.VisitDirectDeps(func(m android.Module) { + if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists { + ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName) + } + }) +} + func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Validate contents of classpath fragments + validateApexClasspathFragments(ctx) + p.apexKeysPath = writeApexKeys(ctx, p) // TODO(jungjw): Check the key validity. p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path() @@ -881,8 +892,6 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...) p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile) } - - android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles) } func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath { @@ -1064,6 +1073,9 @@ func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) { } func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Validate contents of classpath fragments + validateApexClasspathFragments(ctx) + a.apexKeysPath = writeApexKeys(ctx, a) a.installFilename = a.InstallFilename() if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) { diff --git a/bin/aninja b/bin/aninja new file mode 100755 index 000000000..cceb79489 --- /dev/null +++ b/bin/aninja @@ -0,0 +1,25 @@ +#!/bin/bash -e + +# Copyright (C) 2022 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top +require_lunch + +cd $(gettop) +prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-${TARGET_PRODUCT}.ninja "$@" + diff --git a/bin/build-flag-declarations b/bin/build-flag-declarations new file mode 100755 index 000000000..222f0836a --- /dev/null +++ b/bin/build-flag-declarations @@ -0,0 +1,28 @@ +#!/bin/bash -eu +# +# Copyright 2017 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. + +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh +require_top + +# Save the current PWD for use in soong_ui +export ORIGINAL_PWD=${PWD} +export TOP=$(gettop) +source ${TOP}/build/soong/scripts/microfactory.bash + +soong_build_go build-flag-declarations android/soong/cmd/release_config/build_flag_declarations + +cd ${TOP} +exec "$(getoutdir)/build-flag-declarations" "$@" diff --git a/bin/cgrep b/bin/cgrep new file mode 100755 index 000000000..6c9130cd9 --- /dev/null +++ b/bin/cgrep @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hpp' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/ggrep b/bin/ggrep new file mode 100755 index 000000000..fce8c846a --- /dev/null +++ b/bin/ggrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.gradle' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/gogrep b/bin/gogrep new file mode 100755 index 000000000..0265ccf15 --- /dev/null +++ b/bin/gogrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.go' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/hmm b/bin/hmm new file mode 100755 index 000000000..c3d60fa4d --- /dev/null +++ b/bin/hmm @@ -0,0 +1,79 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +cat <<EOF + +Run "m help" for help with the build system itself. + +Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment: +- lunch: lunch <product_name>-<release_type>-<build_variant> + Selects <product_name> as the product to build, and <build_variant> as the variant to + build, and stores those selections in the environment to be read by subsequent + invocations of 'm' etc. +- tapas: tapas [<App1> <App2> ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] + Sets up the build environment for building unbundled apps (APKs). +- banchan: banchan <module1> [<module2> ...] \\ + [arm|x86|arm64|riscv64|x86_64|arm64_only|x86_64only] [eng|userdebug|user] + Sets up the build environment for building unbundled modules (APEXes). +- croot: Changes directory to the top of the tree, or a subdirectory thereof. +- m: Makes from the top of the tree. +- mm: Builds and installs all of the modules in the current directory, and their + dependencies. +- mmm: Builds and installs all of the modules in the supplied directories, and their + dependencies. + To limit the modules being built use the syntax: mmm dir/:target1,target2. +- mma: Same as 'mm' +- mmma: Same as 'mmm' +- provision: Flash device with all required partitions. Options will be passed on to fastboot. +- cgrep: Greps on all local C/C++ files. +- ggrep: Greps on all local Gradle files. +- gogrep: Greps on all local Go files. +- jgrep: Greps on all local Java files. +- jsongrep: Greps on all local Json files. +- ktgrep: Greps on all local Kotlin files. +- resgrep: Greps on all local res/*.xml files. +- mangrep: Greps on all local AndroidManifest.xml files. +- mgrep: Greps on all local Makefiles and *.bp files. +- owngrep: Greps on all local OWNERS files. +- rsgrep: Greps on all local Rust files. +- sepgrep: Greps on all local sepolicy files. +- sgrep: Greps on all local source files. +- tomlgrep: Greps on all local Toml files. +- pygrep: Greps on all local Python files. +- godir: Go to the directory containing a file. +- allmod: List all modules. +- gomod: Go to the directory containing a module. +- bmod: Get the Bazel label of a Soong module if it is converted with bp2build. +- pathmod: Get the directory containing a module. +- outmod: Gets the location of a module's installed outputs with a certain extension. +- dirmods: Gets the modules defined in a given directory. +- installmod: Adb installs a module's built APK. +- refreshmod: Refresh list of modules for allmod/gomod/pathmod/outmod/installmod. +- syswrite: Remount partitions (e.g. system.img) as writable, rebooting if necessary. + +Environment options: +- SANITIZE_HOST: Set to 'address' to use ASAN for all host modules. +- ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages. + +Look at build/make/envsetup for more functions: +EOF + local T=$(gettop) + local A="" + local i + for i in `(cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z]*\).*/\1/p" | sort | uniq`; do + A="$A $i" + done + echo $A diff --git a/bin/jgrep b/bin/jgrep new file mode 100755 index 000000000..afe70dbd0 --- /dev/null +++ b/bin/jgrep @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.java' \ + -o -name '*.kt' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/jsongrep b/bin/jsongrep new file mode 100755 index 000000000..6e14d0c0d --- /dev/null +++ b/bin/jsongrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.json' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/ktgrep b/bin/ktgrep new file mode 120000 index 000000000..9b5149136 --- /dev/null +++ b/bin/ktgrep @@ -0,0 +1 @@ +jgrep
\ No newline at end of file @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --all-modules --dir="$(pwd)" "$@" + +exit $? diff --git a/bin/mangrep b/bin/mangrep new file mode 100755 index 000000000..a3430006a --- /dev/null +++ b/bin/mangrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name 'AndroidManifest.xml' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/mgrep b/bin/mgrep new file mode 100755 index 000000000..793730d9c --- /dev/null +++ b/bin/mgrep @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name 'Makefile' \ + -o -name 'Makefile.*' \ + -o -name '*.make' \ + -o -name '*.mak' \ + -o -name '*.mk' \ + -o -name '*.bp' \ + \) -exec grep --color -n "$@" {} + +exit $? @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir-no-deps --dir="$(pwd)" "$@" + +exit $? diff --git a/bin/mma b/bin/mma new file mode 100755 index 000000000..6f1c9348d --- /dev/null +++ b/bin/mma @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir --dir="$(pwd)" "$@" + +exit $? diff --git a/bin/mmm b/bin/mmm new file mode 100755 index 000000000..ab3a6321c --- /dev/null +++ b/bin/mmm @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs-no-deps --dir="$(pwd)" "$@" + +exit $? diff --git a/bin/mmma b/bin/mmma new file mode 100755 index 000000000..d9190e575 --- /dev/null +++ b/bin/mmma @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs --dir="$(pwd)" "$@" + +exit $? diff --git a/bin/overrideflags b/bin/overrideflags new file mode 100755 index 000000000..e16537b69 --- /dev/null +++ b/bin/overrideflags @@ -0,0 +1,100 @@ +#!/bin/bash -e +# Copyright (C) 2023 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. + + +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +function print_help() { + echo -e "overrideflags is used to set default value for local build." + echo -e "\nOptions:" + echo -e "\t--release-config \tPath to release configuration directory. Required" + echo -e "\t--no-edit \tIf present, skip editing flag value file." + echo -e "\t-h/--help \tShow this help." +} + +function main() { + while (($# > 0)); do + case $1 in + --release-config) + if [[ $# -le 1 ]]; then + echo "--release-config requires a path" + return 1 + fi + local release_config_dir="$2" + shift 2 + ;; + --no-edit) + local no_edit="true" + shift 1 + ;; + -h|--help) + print_help + return + ;; + *) + echo "$1 is unrecognized" + print_help + return 1 + ;; + esac + done + + + + case $(uname -s) in + Darwin) + local host_arch=darwin-x86 + ;; + Linux) + local host_arch=linux-x86 + ;; + *) + >&2 echo Unknown host $(uname -s) + return + ;; + esac + + if [[ -z "${release_config_dir}" ]]; then + echo "Please provide release configuration path by --release-config" + exit 1 + elif [ ! -d "${release_config_dir}" ]; then + echo "${release_config_dir} is an invalid directory" + exit 1 + fi + local T="$(gettop)" + local aconfig_dir="${T}"/build/make/tools/aconfig/ + local overrideflag_py="${aconfig_dir}"/overrideflags/overrideflags.py + local overridefile="${release_config_dir}/aconfig/override_values.textproto" + + # Edit override file + if [[ -z "${no_edit}" ]]; then + editor="${EDITOR:-$(which vim)}" + + eval "${editor} ${overridefile}" + if [ $? -ne 0 ]; then + echo "Fail to set override values" + return 1 + fi + fi + + ${T}/prebuilts/build-tools/${host_arch}/bin/py3-cmd -u "${overrideflag_py}" \ + --overrides "${overridefile}" \ + --out "${release_config_dir}/aconfig" +} + + +main "$@" diff --git a/bin/owngrep b/bin/owngrep new file mode 100755 index 000000000..26ce6e874 --- /dev/null +++ b/bin/owngrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name 'OWNERS' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/pygrep b/bin/pygrep new file mode 100755 index 000000000..e072289bc --- /dev/null +++ b/bin/pygrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.py' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/rcgrep b/bin/rcgrep new file mode 100755 index 000000000..ff93e5102 --- /dev/null +++ b/bin/rcgrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.rc' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/refreshmod b/bin/refreshmod new file mode 100755 index 000000000..f51184624 --- /dev/null +++ b/bin/refreshmod @@ -0,0 +1,31 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# Update module-info.json in out. + +# Common script utilities +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh + +require_top + +if [ ! "$ANDROID_PRODUCT_OUT" ]; then + echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2 + return 1 +fi + +echo "Refreshing modules (building module-info.json)" >&2 + +_wrap_build $TOP/build/soong/soong_ui.bash --build-mode --all-modules --dir="$(pwd)" module-info diff --git a/bin/resgrep b/bin/resgrep new file mode 100755 index 000000000..600091fd4 --- /dev/null +++ b/bin/resgrep @@ -0,0 +1,19 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do + find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} + +done diff --git a/bin/rsgrep b/bin/rsgrep new file mode 100755 index 000000000..8c241518d --- /dev/null +++ b/bin/rsgrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.rs' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/sepgrep b/bin/sepgrep new file mode 100755 index 000000000..0e0d1ba8d --- /dev/null +++ b/bin/sepgrep @@ -0,0 +1,19 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \ + -exec grep --color -n -r --exclude-dir=\.git "$@" {} + +exit $? diff --git a/bin/sgrep b/bin/sgrep new file mode 100755 index 000000000..f18655369 --- /dev/null +++ b/bin/sgrep @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hpp' \ + -o -name '*.S' \ + -o -name '*.java' \ + -o -name '*.kt' \ + -o -name '*.xml' \ + -o -name '*.sh' \ + -o -name '*.mk' \ + -o -name '*.bp' \ + -o -name '*.aidl' \ + -o -name '*.vts' \ + -o -name '*.proto' \ + -o -name '*.rs' \ + -o -name '*.go' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/syswrite b/bin/syswrite new file mode 100755 index 000000000..46201e3da --- /dev/null +++ b/bin/syswrite @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright (C) 2024 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. + +# syswrite - disable verity, reboot if needed, and remount image +# Easy way to make system.img/etc writable + +adb wait-for-device && adb root && adb wait-for-device || exit 1 +if [[ $(adb disable-verity | grep -i "reboot") ]]; then + echo "rebooting" + adb reboot && adb wait-for-device && adb root && adb wait-for-device || exit 1 +fi +adb remount || exit 1 diff --git a/bin/tomlgrep b/bin/tomlgrep new file mode 100755 index 000000000..636ef22da --- /dev/null +++ b/bin/tomlgrep @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.toml' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/bin/treegrep b/bin/treegrep new file mode 100755 index 000000000..b83d41997 --- /dev/null +++ b/bin/treegrep @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright (C) 2022 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. + +find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \ + -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hpp' \ + -o -name '*.S' \ + -o -name '*.java' \ + -o -name '*.kt' \ + -o -name '*.xml' \ + \) -exec grep --color -n "$@" {} + +exit $? diff --git a/cc/Android.bp b/cc/Android.bp index 9ce89330e..3bbcaa92e 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -51,6 +51,7 @@ bootstrap_go_package { "vndk.go", "vndk_prebuilt.go", + "cmake_snapshot.go", "cmakelists.go", "compdb.go", "compiler.go", @@ -92,6 +93,7 @@ bootstrap_go_package { "binary_test.go", "cc_test.go", "cc_test_only_property_test.go", + "cmake_snapshot_test.go", "compiler_test.go", "gen_test.go", "genrule_test.go", @@ -109,5 +111,12 @@ bootstrap_go_package { "tidy_test.go", "vendor_public_library_test.go", ], + embedSrcs: [ + "cmake_ext_add_aidl_library.txt", + "cmake_ext_append_flags.txt", + "cmake_main.txt", + "cmake_module_aidl.txt", + "cmake_module_cc.txt", + ], pluginFor: ["soong_build"], } diff --git a/cc/androidmk.go b/cc/androidmk.go index df356df0e..0c6f97c34 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -125,8 +125,6 @@ func (c *Module) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("SOONG_SDK_VARIANT_MODULES", "$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))") } - android.SetAconfigFileMkEntries(c.AndroidModuleBase(), entries, c.mergedAconfigFiles) - entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", c.IsSkipInstall()) }, }, diff --git a/cc/binary.go b/cc/binary.go index 7aa8e20cc..3ff35de56 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -18,6 +18,7 @@ import ( "path/filepath" "android/soong/android" + "github.com/google/blueprint" ) @@ -425,6 +426,10 @@ func (binary *binaryDecorator) link(ctx ModuleContext, validations = append(validations, objs.tidyDepFiles...) linkerDeps = append(linkerDeps, flags.LdFlagsDeps...) + if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil { + deps.StaticLibs = append(deps.StaticLibs, generatedLib) + } + // Register link action. transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true, diff --git a/cc/builder.go b/cc/builder.go index e255cbebb..d817d8257 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -19,6 +19,7 @@ package cc // functions. import ( + "fmt" "path/filepath" "runtime" "strconv" @@ -45,18 +46,18 @@ var ( blueprint.RuleParams{ Depfile: "${out}.d", Deps: blueprint.DepsGCC, - Command: "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in", + Command: "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in$postCmd", CommandDeps: []string{"$ccCmd"}, }, - "ccCmd", "cFlags") + "ccCmd", "cFlags", "postCmd") // Rule to invoke gcc with given command and flags, but no dependencies. ccNoDeps = pctx.AndroidStaticRule("ccNoDeps", blueprint.RuleParams{ - Command: "$relPwd $ccCmd -c $cFlags -o $out $in", + Command: "$relPwd $ccCmd -c $cFlags -o $out $in$postCmd", CommandDeps: []string{"$ccCmd"}, }, - "ccCmd", "cFlags") + "ccCmd", "cFlags", "postCmd") // Rules to invoke ld to link binaries. Uses a .rsp file to list dependencies, as there may // be many. @@ -330,6 +331,15 @@ var ( CommandDeps: []string{"$cxxExtractor", "$kytheVnames"}, }, "cFlags") + + // Function pointer for producting staticlibs from rlibs. Corresponds to + // rust.TransformRlibstoStaticlib(), initialized in soong-rust (rust/builder.go init()) + // + // This is required since soong-rust depends on soong-cc, so soong-cc cannot depend on soong-rust + // without resulting in a circular dependency. Setting this function pointer in soong-rust allows + // soong-cc to call into this particular function. + TransformRlibstoStaticlib (func(ctx android.ModuleContext, mainSrc android.Path, deps []RustRlibDep, + outputFile android.WritablePath) android.Path) = nil ) func PwdPrefix() string { @@ -390,6 +400,7 @@ type builderFlags struct { gcovCoverage bool sAbiDump bool emitXrefs bool + clangVerify bool assemblerWithCpp bool // True if .s files should be processed with the c preprocessor. @@ -581,6 +592,7 @@ func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs var moduleToolingFlags string var ccCmd string + var postCmd string tidy := flags.tidy coverage := flags.gcovCoverage dump := flags.sAbiDump @@ -625,6 +637,10 @@ func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs ccCmd = "${config.ClangBin}/" + ccCmd + if flags.clangVerify { + postCmd = " && touch $$out" + } + var implicitOutputs android.WritablePaths if coverage { gcnoFile := android.ObjPathWithExt(ctx, subdir, srcFile, "gcno") @@ -641,8 +657,9 @@ func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs Implicits: cFlagsDeps, OrderOnly: pathDeps, Args: map[string]string{ - "cFlags": shareFlags("cFlags", moduleFlags), - "ccCmd": ccCmd, // short and not shared + "cFlags": shareFlags("cFlags", moduleFlags), + "ccCmd": ccCmd, // short and not shared + "postCmd": postCmd, }, }) @@ -778,6 +795,47 @@ func transformObjToStaticLib(ctx android.ModuleContext, } } +// Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty. +func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path { + if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 { + // This should only be reachable if a module defines static_rlibs and + // soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests). + panic(fmt.Errorf("TransformRlibstoStaticlib is not set and static_rlibs is defined in %s", ctx.ModuleName())) + } else if len(rlibDeps) == 0 { + return nil + } + + output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a") + stemFile := output.ReplaceExtension(ctx, "rs") + crateNames := []string{} + + // Collect crate names + for _, lib := range rlibDeps { + // Exclude libstd so this can support no_std builds. + if lib.CrateName != "libstd" { + crateNames = append(crateNames, lib.CrateName) + } + } + + // Deduplicate any crateNames just to be safe + crateNames = android.FirstUniqueStrings(crateNames) + + // Write the source file + android.WriteFileRule(ctx, stemFile, genRustStaticlibSrcFile(crateNames)) + + return TransformRlibstoStaticlib(ctx, stemFile, rlibDeps, output) +} + +func genRustStaticlibSrcFile(crateNames []string) string { + lines := []string{ + "// @Soong generated Source", + } + for _, crate := range crateNames { + lines = append(lines, fmt.Sprintf("extern crate %s;", crate)) + } + return strings.Join(lines, "\n") +} + // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries, // and shared libraries, to a shared library (.so) or dynamic executable func transformObjToDynamicBinary(ctx android.ModuleContext, @@ -100,6 +100,7 @@ type Deps struct { StaticLibs, LateStaticLibs, WholeStaticLibs []string HeaderLibs []string RuntimeLibs []string + Rlibs []string // UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to // prevent automatically exporting symbols. @@ -145,6 +146,17 @@ type Deps struct { LlndkHeaderLibs []string } +// A struct which to collect flags for rlib dependencies +type RustRlibDep struct { + LibPath android.Path // path to the rlib + LinkDirs []string // flags required for dependency (e.g. -L flags) + CrateName string // crateNames associated with rlibDeps +} + +func EqRustRlibDeps(a RustRlibDep, b RustRlibDep) bool { + return a.LibPath == b.LibPath +} + // PathDeps is a struct containing file paths to dependencies of a module. // It's constructed in depsToPath() by traversing the direct dependencies of the current module. // It's used to construct flags for various build statements (such as for compiling and linking). @@ -157,6 +169,8 @@ type PathDeps struct { SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths // Paths to .a files StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths + // Paths and crateNames for RustStaticLib dependencies + RustRlibDeps []RustRlibDep // Transitive static library dependencies of static libraries for use in ordering. TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path] @@ -185,6 +199,7 @@ type PathDeps struct { ReexportedFlags []string ReexportedGeneratedHeaders android.Paths ReexportedDeps android.Paths + ReexportedRustRlibDeps []RustRlibDep // Paths to crt*.o files CrtBegin, CrtEnd android.Paths @@ -245,6 +260,7 @@ type Flags struct { GcovCoverage bool // True if coverage files should be generated. SAbiDump bool // True if header abi dumps should be generated. EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe + ClangVerify bool // If true, append cflags "-Xclang -verify" and append "&& touch $out" to the clang command line. // The instruction set required for clang ("arm" or "thumb"). RequiredInstructionSet string @@ -298,6 +314,7 @@ type BaseProperties struct { AndroidMkSharedLibs []string `blueprint:"mutated"` AndroidMkStaticLibs []string `blueprint:"mutated"` + AndroidMkRlibs []string `blueprint:"mutated"` AndroidMkRuntimeLibs []string `blueprint:"mutated"` AndroidMkWholeStaticLibs []string `blueprint:"mutated"` AndroidMkHeaderLibs []string `blueprint:"mutated"` @@ -353,6 +370,10 @@ type BaseProperties struct { // for building binaries that are started before APEXes are activated. Bootstrap *bool + // Allows this module to be included in CMake release snapshots to be built outside of Android + // build system and source tree. + Cmake_snapshot_supported *bool + // Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant. // see soong/cc/config/vndk.go MustUseVendorVariant bool `blueprint:"mutated"` @@ -588,6 +609,7 @@ type compiler interface { compilerDeps(ctx DepsContext, deps Deps) Deps compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags compilerProps() []interface{} + baseCompilerProps() BaseCompilerProperties appendCflags([]string) appendAsflags([]string) @@ -602,6 +624,7 @@ type linker interface { linkerDeps(ctx DepsContext, deps Deps) Deps linkerFlags(ctx ModuleContext, flags Flags) Flags linkerProps() []interface{} + baseLinkerProps() BaseLinkerProperties useClangLld(actx ModuleContext) bool link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path @@ -654,6 +677,7 @@ const ( headerLibraryDependency = iota sharedLibraryDependency staticLibraryDependency + rlibLibraryDependency ) func (k libraryDependencyKind) String() string { @@ -664,6 +688,8 @@ func (k libraryDependencyKind) String() string { return "sharedLibraryDependency" case staticLibraryDependency: return "staticLibraryDependency" + case rlibLibraryDependency: + return "rlibLibraryDependency" default: panic(fmt.Errorf("unknown libraryDependencyKind %d", k)) } @@ -741,6 +767,11 @@ func (d libraryDependencyTag) static() bool { return d.Kind == staticLibraryDependency } +// rlib returns true if the libraryDependencyTag is tagging an rlib dependency. +func (d libraryDependencyTag) rlib() bool { + return d.Kind == rlibLibraryDependency +} + func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation { if d.shared() { return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency} @@ -907,9 +938,6 @@ type Module struct { hideApexVariantFromMake bool - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths - logtagsPaths android.Paths } @@ -1111,6 +1139,14 @@ func (c *Module) RustLibraryInterface() bool { return false } +func (c *Module) CrateName() string { + panic(fmt.Errorf("CrateName called on non-Rust module: %q", c.BaseModuleName())) +} + +func (c *Module) ExportedCrateLinkDirs() []string { + panic(fmt.Errorf("ExportedCrateLinkDirs called on non-Rust module: %q", c.BaseModuleName())) +} + func (c *Module) IsFuzzModule() bool { if _, ok := c.compiler.(*fuzzBinary); ok { return true @@ -1993,10 +2029,6 @@ func (c *Module) stubLibraryMultipleApexViolation(ctx android.ModuleContext) boo return false } -func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) { - android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles) -} - func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { ctx := moduleContextFromAndroidModuleContext(actx, c) @@ -2163,7 +2195,9 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()}) - android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles) + if Bool(c.Properties.Cmake_snapshot_supported) { + android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil)) + } c.maybeInstall(ctx, apexInfo) @@ -2308,6 +2342,7 @@ func (c *Module) deps(ctx DepsContext) Deps { deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs) deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs) + deps.Rlibs = android.LastUniqueStrings(deps.Rlibs) deps.LateStaticLibs = android.LastUniqueStrings(deps.LateStaticLibs) deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs) deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs) @@ -2615,6 +2650,15 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { }, depTag, lib) } + for _, lib := range deps.Rlibs { + depTag := libraryDependencyTag{Kind: rlibLibraryDependency} + actx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: ""}, + {Mutator: "rust_libraries", Variation: "rlib"}, + {Mutator: "rust_stdlinkage", Variation: "rlib-std"}, + }, depTag, lib) + } + // staticUnwinderDep is treated as staticDep for Q apexes // so that native libraries/binaries are linked with static unwinder // because Q libc doesn't have unwinder APIs @@ -3224,6 +3268,14 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { default: panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order)) } + + case libDepTag.rlib(): + rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()} + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep) + depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...) + depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...) + case libDepTag.static(): staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider) if !isStaticLib { @@ -3276,6 +3328,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order)) } } + + // We re-export the Rust static_rlibs so rlib dependencies don't need to be redeclared by cc_library_static dependents. + // E.g. libfoo (cc_library_static) depends on libfoo.ffi (a rust_ffi rlib), libbar depending on libfoo shouldn't have to also add libfoo.ffi to static_rlibs. + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...) + if libDepTag.unexportedSymbols { depPaths.LdFlags = append(depPaths.LdFlags, "-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base()) @@ -3328,6 +3386,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, depExporterInfo.SystemIncludeDirs...) depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, depExporterInfo.Deps...) depPaths.Flags = append(depPaths.Flags, depExporterInfo.Flags...) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...) + + // Only re-export RustRlibDeps for cc static libs + if c.static() { + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...) + } if libDepTag.reexportFlags { reexportExporter(depExporterInfo) @@ -3400,11 +3464,14 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs) depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs) depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps) + depPaths.RustRlibDeps = android.FirstUniqueFunc(depPaths.RustRlibDeps, EqRustRlibDeps) + depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs) depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs) depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags) depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps) depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders) + depPaths.ReexportedRustRlibDeps = android.FirstUniqueFunc(depPaths.ReexportedRustRlibDeps, EqRustRlibDeps) if c.sabi != nil { c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes) @@ -4063,9 +4130,6 @@ type Defaults struct { android.ModuleBase android.DefaultsModuleBase android.ApexModuleBase - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths } // cc_defaults provides a set of properties that can be inherited by other cc diff --git a/cc/cc_test.go b/cc/cc_test.go index 3d75bf5a6..026d291e0 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -3218,3 +3218,32 @@ func TestVendorSdkVersion(t *testing.T) { testSdkVersionFlag("libfoo", "30") testSdkVersionFlag("libbar", "29") } + +func TestClangVerify(t *testing.T) { + t.Parallel() + + ctx := testCc(t, ` + cc_library { + name: "lib_no_clang_verify", + srcs: ["libnocv.cc"], + } + + cc_library { + name: "lib_clang_verify", + srcs: ["libcv.cc"], + clang_verify: true, + } + `) + + module := ctx.ModuleForTests("lib_no_clang_verify", "android_arm64_armv8-a_shared") + + cFlags_no_cv := module.Rule("cc").Args["cFlags"] + if strings.Contains(cFlags_no_cv, "-Xclang") || strings.Contains(cFlags_no_cv, "-verify") { + t.Errorf("expected %q not in cflags, got %q", "-Xclang -verify", cFlags_no_cv) + } + + cFlags_cv := ctx.ModuleForTests("lib_clang_verify", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"] + if strings.Contains(cFlags_cv, "-Xclang") && strings.Contains(cFlags_cv, "-verify") { + t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv) + } +} diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt new file mode 100644 index 000000000..af5bdf6c0 --- /dev/null +++ b/cc/cmake_ext_add_aidl_library.txt @@ -0,0 +1,68 @@ +function(add_aidl_library NAME LANG AIDLROOT SOURCES AIDLFLAGS) + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + cmake_policy(SET CMP0116 NEW) + endif() + + # Strip trailing slash + get_filename_component(AIDLROOT_TRAILING "${AIDLROOT}" NAME) + if ("${AIDLROOT_TRAILING}" STREQUAL "") + get_filename_component(AIDLROOT "${AIDLROOT}foo" DIRECTORY) + endif() + + set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/.intermediates/${NAME}-source") + set(GEN_SOURCES) + foreach (SOURCE ${SOURCES}) + set(SOURCE_FULL ${AIDLROOT}/${SOURCE}) + get_filename_component(SOURCE_WLE ${SOURCE} NAME_WLE) + get_filename_component(SOURCE_SUBDIR ${SOURCE} DIRECTORY) + set(GEN_SOURCE "${GEN_DIR}/${SOURCE_SUBDIR}/${SOURCE_WLE}.cpp") + + file(READ "${SOURCE}" SOURCE_CONTENTS) + string(FIND "${SOURCE_CONTENTS}" "@VintfStability" VINTF_MATCH) + set(STABILITY_FLAG) + if (${VINTF_MATCH} GREATER_EQUAL 0) + set(STABILITY_FLAG --stability vintf) + endif() + + set(DEPFILE_ARG) + if (NOT ${CMAKE_GENERATOR} MATCHES "Unix Makefiles") + set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d") + endif() + + add_custom_command( + OUTPUT "${GEN_SOURCE}" + MAIN_DEPENDENCY "${SOURCE_FULL}" + ${DEPFILE_ARG} + COMMAND "${AIDL_BIN}" + ARGS + --lang=${LANG} + --include="${AIDLROOT}" + --dep="${GEN_SOURCE}.d" + --out="${GEN_DIR}" + --header_out="${GEN_DIR}/include" + --ninja + --structured + --min_sdk_version=current + ${STABILITY_FLAG} + ${AIDLFLAGS} + "${SOURCE_FULL}" + ) + list(APPEND GEN_SOURCES "${GEN_SOURCE}") + endforeach() + + add_library(${NAME} ${GEN_SOURCES}) + + target_include_directories(${NAME} + PUBLIC + "${GEN_DIR}/include" + ) + + if (${LANG} MATCHES "ndk") + set(BINDER_LIB_NAME "libbinder_ndk_sdk") + else() + set(BINDER_LIB_NAME "libbinder_sdk") + endif() + target_link_libraries(${NAME} + ${BINDER_LIB_NAME} + ) +endfunction() diff --git a/cc/cmake_ext_append_flags.txt b/cc/cmake_ext_append_flags.txt new file mode 100644 index 000000000..2cfb1aca9 --- /dev/null +++ b/cc/cmake_ext_append_flags.txt @@ -0,0 +1,10 @@ +include(CheckCXXCompilerFlag) + +macro(append_cxx_flags_if_supported VAR) + foreach(FLAG ${ARGN}) + check_cxx_compiler_flag(${FLAG} HAS_FLAG${FLAG}) + if(${HAS_FLAG${FLAG}}) + list(APPEND ${VAR} ${FLAG}) + endif() + endforeach() +endmacro() diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt new file mode 100644 index 000000000..deb1de1f8 --- /dev/null +++ b/cc/cmake_main.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.18) +project(<<.M.Name>> CXX) +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(AddAidlLibrary) +include(AppendCxxFlagsIfSupported) + +if (NOT ANDROID_BUILD_TOP) + set(ANDROID_BUILD_TOP "${CMAKE_CURRENT_SOURCE_DIR}") +endif() + +set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin") +if (NOT AIDL_BIN) + find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}") +endif() + +<<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>> + +<<range .Pprop.SystemPackages ->> +find_package(<<.>> REQUIRED) +<<end >> +<<range .Pprop.PregeneratedPackages ->> +add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL) +<<end>> +add_compile_options(${<<.M.Name>>_CFLAGS}) +<<range $moduleDir, $value := .ModuleDirs ->> +add_subdirectory(<<$moduleDir>>) +<<end>> diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt new file mode 100644 index 000000000..84755a32a --- /dev/null +++ b/cc/cmake_module_aidl.txt @@ -0,0 +1,11 @@ +# <<.M.Name>> + +<<setList .M.Name "_SRCS" "" (getAidlSources .M)>> + +<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>> + +add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>> + "${ANDROID_BUILD_TOP}/<<.Ctx.OtherModuleDir .M>>/<<(getCompilerProperties .M).AidlInterface.AidlRoot>>" + "${<<.M.Name>>_SRCS}" + "${<<.M.Name>>_AIDLFLAGS}") +add_library(android::<<.M.Name>> ALIAS <<.M.Name>>) diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt new file mode 100644 index 000000000..488e5e102 --- /dev/null +++ b/cc/cmake_module_cc.txt @@ -0,0 +1,35 @@ +<<$srcs := getSources .M>> +<<$includeDirs := getIncludeDirs .Ctx .M>> +<<$cflags := (getCompilerProperties .M).Cflags>> +<<$deps := mapLibraries .Ctx .M (concat5 +(getLinkerProperties .M).Whole_static_libs +(getLinkerProperties .M).Static_libs +(getLinkerProperties .M).Shared_libs +(getLinkerProperties .M).Header_libs +(getExtraLibs .M) +) .Pprop.LibraryMapping>> + +# <<.M.Name>> +<<if $srcs>> +<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (toStrings $srcs)>> +add_<<getModuleType .M>>(<<.M.Name>> ${<<.M.Name>>_SRCS}) +<<- else>> +add_<<getModuleType .M>>(<<.M.Name>> INTERFACE) +<<- end>> +add_<<getModuleType .M>>(android::<<.M.Name>> ALIAS <<.M.Name>>) +<<print "">> + +<<- if $includeDirs>> +<<setList .M.Name "_INCLUDES" "${ANDROID_BUILD_TOP}/" $includeDirs>> +target_include_directories(<<.M.Name>> <<if $srcs>>PUBLIC<<else>>INTERFACE<<end>> ${<<.M.Name>>_INCLUDES}) +<<end>> + +<<- if and $srcs $cflags>> +<<cflagsList .M.Name "_CFLAGS" $cflags .Snapshot.Properties.Unportable_flags .Snapshot.Properties.Cflags_ignored>> +target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS}) +<<end>> + +<<- if $deps>> +<<setList .M.Name "_DEPENDENCIES" "" $deps>> +target_link_libraries(<<.M.Name>> <<if not $srcs>>INTERFACE <<end ->> ${<<.M.Name>>_DEPENDENCIES}) +<<end>> diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go new file mode 100644 index 000000000..c21a46f04 --- /dev/null +++ b/cc/cmake_snapshot.go @@ -0,0 +1,544 @@ +// Copyright 2024 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 ( + "android/soong/android" + "bytes" + _ "embed" + "fmt" + "path/filepath" + "slices" + "sort" + "strings" + "text/template" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +const veryVerbose bool = false + +//go:embed cmake_main.txt +var templateCmakeMainRaw string +var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw) + +//go:embed cmake_module_cc.txt +var templateCmakeModuleCcRaw string +var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw) + +//go:embed cmake_module_aidl.txt +var templateCmakeModuleAidlRaw string +var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw) + +//go:embed cmake_ext_add_aidl_library.txt +var cmakeExtAddAidlLibrary string + +//go:embed cmake_ext_append_flags.txt +var cmakeExtAppendFlags string + +var defaultUnportableFlags []string = []string{ + "-Wno-class-memaccess", + "-Wno-exit-time-destructors", + "-Wno-inconsistent-missing-override", + "-Wreorder-init-list", + "-Wno-reorder-init-list", + "-Wno-restrict", + "-Wno-stringop-overread", + "-Wno-subobject-linkage", +} + +var ignoredSystemLibs []string = []string{ + "libc++", + "libc++_static", + "prebuilt_libclang_rt.builtins", + "prebuilt_libclang_rt.ubsan_minimal", +} + +// Mapping entry between Android's library name and the one used when building outside Android tree. +type LibraryMappingProperty struct { + // Android library name. + Android_name string + + // Library name used when building outside Android. + Mapped_name string + + // If the make file is already present in Android source tree, specify its location. + Package_pregenerated string + + // If the package is expected to be installed on the build host OS, specify its name. + Package_system string +} + +type CmakeSnapshotProperties struct { + // Modules to add to the snapshot package. Their dependencies are pulled in automatically. + Modules []string + + // Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android. + Prebuilts []string + + // Global cflags to add when building outside Android. + Cflags []string + + // Flags to skip when building outside Android. + Cflags_ignored []string + + // Mapping between library names used in Android tree and externally. + Library_mapping []LibraryMappingProperty + + // List of cflags that are not portable between compilers that could potentially be used to + // build a generated package. If left empty, it's initialized with a default list. + Unportable_flags []string + + // Whether to include source code as part of the snapshot package. + Include_sources bool +} + +var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]() + +type CmakeSnapshot struct { + android.ModuleBase + + Properties CmakeSnapshotProperties + + zipPath android.WritablePath +} + +type cmakeProcessedProperties struct { + LibraryMapping map[string]LibraryMappingProperty + PregeneratedPackages []string + SystemPackages []string +} + +type cmakeSnapshotDependencyTag struct { + blueprint.BaseDependencyTag + name string +} + +var ( + cmakeSnapshotModuleTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"} + cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"} +) + +func parseTemplate(templateContents string) *template.Template { + funcMap := template.FuncMap{ + "setList": func(name string, nameSuffix string, itemPrefix string, items []string) string { + var list strings.Builder + list.WriteString("set(" + name + nameSuffix) + templateListBuilder(&list, itemPrefix, items) + return list.String() + }, + "toStrings": func(files android.Paths) []string { + strings := make([]string, len(files)) + for idx, file := range files { + strings[idx] = file.String() + } + return strings + }, + "concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string { + return append(append(append(append(list1, list2...), list3...), list4...), list5...) + }, + "cflagsList": func(name string, nameSuffix string, flags []string, + unportableFlags []string, ignoredFlags []string) string { + if len(unportableFlags) == 0 { + unportableFlags = defaultUnportableFlags + } + + var filteredPortable []string + var filteredUnportable []string + for _, flag := range flags { + if slices.Contains(ignoredFlags, flag) { + continue + } else if slices.Contains(unportableFlags, flag) { + filteredUnportable = append(filteredUnportable, flag) + } else { + filteredPortable = append(filteredPortable, flag) + } + } + + var list strings.Builder + + list.WriteString("set(" + name + nameSuffix) + templateListBuilder(&list, "", filteredPortable) + + if len(filteredUnportable) > 0 { + list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix) + templateListBuilder(&list, "", filteredUnportable) + } + + return list.String() + }, + "getSources": func(m *Module) android.Paths { + return m.compiler.(CompiledInterface).Srcs() + }, + "getModuleType": getModuleType, + "getCompilerProperties": func(m *Module) BaseCompilerProperties { + return m.compiler.baseCompilerProps() + }, + "getLinkerProperties": func(m *Module) BaseLinkerProperties { + return m.linker.baseLinkerProps() + }, + "getExtraLibs": getExtraLibs, + "getIncludeDirs": getIncludeDirs, + "mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string { + var mappedLibs []string + for _, lib := range libs { + mappedLib, exists := mapping[lib] + if exists { + lib = mappedLib.Mapped_name + } else { + if !ctx.OtherModuleExists(lib) { + ctx.OtherModuleErrorf(m, "Dependency %s doesn't exist", lib) + } + lib = "android::" + lib + } + if lib == "" { + continue + } + mappedLibs = append(mappedLibs, lib) + } + sort.Strings(mappedLibs) + mappedLibs = slices.Compact(mappedLibs) + return mappedLibs + }, + "getAidlSources": func(m *Module) []string { + aidlInterface := m.compiler.baseCompilerProps().AidlInterface + aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator) + if aidlInterface.AidlRoot == "" { + aidlRoot = "" + } + var sources []string + for _, src := range aidlInterface.Sources { + if !strings.HasPrefix(src, aidlRoot) { + panic(fmt.Sprintf("Aidl source '%v' doesn't start with '%v'", src, aidlRoot)) + } + sources = append(sources, src[len(aidlRoot):]) + } + return sources + }, + } + + return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents)) +} + +func sliceWithPrefix(prefix string, slice []string) []string { + output := make([]string, len(slice)) + for i, elem := range slice { + output[i] = prefix + elem + } + return output +} + +func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) { + if len(items) > 0 { + builder.WriteString("\n") + for _, item := range items { + builder.WriteString(" " + itemPrefix + item + "\n") + } + } + builder.WriteString(")") +} + +func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string { + buffer.Reset() + if err := templ.Execute(buffer, data); err != nil { + panic(err) + } + output := strings.TrimSpace(buffer.String()) + buffer.Reset() + return output +} + +func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) { + variations := []blueprint.Variation{ + {"os", "linux_glibc"}, + {"arch", "x86_64"}, + } + ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...) + ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, m.Properties.Prebuilts...) +} + +func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var templateBuffer bytes.Buffer + var pprop cmakeProcessedProperties + m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") + + // Process Library_mapping for more efficient lookups + pprop.LibraryMapping = map[string]LibraryMappingProperty{} + for _, elem := range m.Properties.Library_mapping { + pprop.LibraryMapping[elem.Android_name] = elem + + if elem.Package_pregenerated != "" { + pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated) + } + sort.Strings(pprop.PregeneratedPackages) + pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages) + + if elem.Package_system != "" { + pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system) + } + sort.Strings(pprop.SystemPackages) + pprop.SystemPackages = slices.Compact(pprop.SystemPackages) + } + + // Generating CMakeLists.txt rules for all modules in dependency tree + moduleDirs := map[string][]string{} + sourceFiles := map[string]android.Path{} + visitedModules := map[string]bool{} + var pregeneratedModules []*Module + ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool { + moduleName := ctx.OtherModuleName(dep_a) + if visited := visitedModules[moduleName]; visited { + return false // visit only once + } + visitedModules[moduleName] = true + dep, ok := dep_a.(*Module) + if !ok { + return false // not a cc module + } + if mapping, ok := pprop.LibraryMapping[moduleName]; ok { + if mapping.Package_pregenerated != "" { + pregeneratedModules = append(pregeneratedModules, dep) + } + return false // mapped to system or pregenerated (we'll handle these later) + } + if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag { + return false // we'll handle cmakeSnapshotPrebuiltTag later + } + if slices.Contains(ignoredSystemLibs, moduleName) { + return false // system libs built in-tree for Android + } + if dep.compiler == nil { + return false // unsupported module type (e.g. prebuilt) + } + isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != "" + + if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) { + ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported", + "CMake snapshots not supported, despite being a dependency for %s", + ctx.OtherModuleName(parent)) + return false + } + + if veryVerbose { + fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName) + } + + // Generate CMakeLists.txt fragment for this module + templateToUse := templateCmakeModuleCc + if isAidlModule { + templateToUse = templateCmakeModuleAidl + } + moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct { + Ctx *android.ModuleContext + M *Module + Snapshot *CmakeSnapshot + Pprop *cmakeProcessedProperties + }{ + &ctx, + dep, + m, + &pprop, + }) + moduleDir := ctx.OtherModuleDir(dep) + moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment) + + if m.Properties.Include_sources { + files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider) + for _, file := range files { + sourceFiles[file.String()] = file + } + } + + // if it's AIDL module, no need to dive into their dependencies + return !isAidlModule + }) + + // Enumerate sources for pregenerated modules + if m.Properties.Include_sources { + for _, dep := range pregeneratedModules { + if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) { + ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported", + "Pregenerated CMake snapshots not supported, despite being requested for %s", + ctx.ModuleName()) + continue + } + + files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider) + for _, file := range files { + sourceFiles[file.String()] = file + } + } + } + + // Merging CMakeLists.txt contents for every module directory + var makefilesList android.Paths + for moduleDir, fragments := range moduleDirs { + moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt") + makefilesList = append(makefilesList, moduleCmakePath) + sort.Strings(fragments) + android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n")) + } + + // Generating top-level CMakeLists.txt + mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt") + makefilesList = append(makefilesList, mainCmakePath) + mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct { + Ctx *android.ModuleContext + M *CmakeSnapshot + ModuleDirs map[string][]string + Pprop *cmakeProcessedProperties + }{ + &ctx, + m, + moduleDirs, + &pprop, + }) + android.WriteFileRule(ctx, mainCmakePath, mainContents) + + // Generating CMake extensions + extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake") + makefilesList = append(makefilesList, extPath) + android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags) + extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake") + makefilesList = append(makefilesList, extPath) + android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary) + + // Generating the final zip file + zipRule := android.NewRuleBuilder(pctx, ctx) + zipCmd := zipRule.Command(). + BuiltTool("soong_zip"). + FlagWithOutput("-o ", m.zipPath) + + // Packaging all sources into the zip file + if m.Properties.Include_sources { + var sourcesList android.Paths + for _, file := range sourceFiles { + sourcesList = append(sourcesList, file) + } + + sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp") + zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList) + } + + // Packaging all make files into the zip file + makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp") + zipCmd. + FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()). + FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList) + + // Packaging all prebuilts into the zip file + if len(m.Properties.Prebuilts) > 0 { + var prebuiltsList android.Paths + + ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) { + for _, file := range dep.FilesToInstall() { + prebuiltsList = append(prebuiltsList, file) + } + }) + + prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp") + zipCmd. + FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()). + FlagWithArg("-P ", "prebuilts"). + FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList) + } + + // Finish generating the final zip file + zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName()) +} + +func (m *CmakeSnapshot) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + return android.Paths{m.zipPath}, nil + default: + return nil, fmt.Errorf("unsupported module reference tag %q", tag) + } +} + +func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "DATA", + OutputFile: android.OptionalPathForPath(m.zipPath), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) + }, + }, + }} +} + +func getModuleType(m *Module) string { + switch m.linker.(type) { + case *binaryDecorator: + return "executable" + case *libraryDecorator: + return "library" + case *testBinary: + return "executable" + } + panic(fmt.Sprintf("Unexpected module type: %T", m.compiler)) +} + +func getExtraLibs(m *Module) []string { + switch decorator := m.linker.(type) { + case *testBinary: + if decorator.testDecorator.gtest() { + return []string{"libgtest"} + } + } + return nil +} + +func getIncludeDirs(ctx android.ModuleContext, m *Module) []string { + moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator) + switch decorator := m.compiler.(type) { + case *libraryDecorator: + return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs) + } + return nil +} + +func cmakeSnapshotLoadHook(ctx android.LoadHookContext) { + props := struct { + Target struct { + Darwin struct { + Enabled *bool + } + Windows struct { + Enabled *bool + } + } + }{} + props.Target.Darwin.Enabled = proptools.BoolPtr(false) + props.Target.Windows.Enabled = proptools.BoolPtr(false) + ctx.AppendProperties(&props) +} + +// cmake_snapshot allows defining source packages for release outside of Android build tree. +// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions +// for selected source modules, their dependencies and optionally also the source code itself. +func CmakeSnapshotFactory() android.Module { + module := &CmakeSnapshot{} + module.AddProperties(&module.Properties) + android.AddLoadHook(module, cmakeSnapshotLoadHook) + android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) + return module +} + +func init() { + android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory) +} diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go new file mode 100644 index 000000000..8fca6c18f --- /dev/null +++ b/cc/cmake_snapshot_test.go @@ -0,0 +1,115 @@ +// Copyright 2024 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 ( + "runtime" + "strings" + "testing" + + "android/soong/android" +) + +func wasGenerated(t *testing.T, m *android.TestingModule, fileName string, ruleType string) { + t.Helper() + ruleName := "<nil>" + if rule := m.MaybeOutput(fileName).Rule; rule != nil { + ruleName = rule.String() + } + if !strings.HasSuffix(ruleName, ruleType) { + t.Errorf("Main Cmake file wasn't generated properly, expected rule %v, found %v", ruleType, ruleName) + } +} + +func TestEmptyCmakeSnapshot(t *testing.T) { + t.Parallel() + result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, ` + cc_cmake_snapshot { + name: "foo", + modules: [], + prebuilts: ["libc++"], + include_sources: true, + }`) + + if runtime.GOOS != "linux" { + t.Skip("CMake snapshots are only supported on Linux") + } + + snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64") + + wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy") + wasGenerated(t, &snapshotModule, "foo.zip", "") +} + +func TestCmakeSnapshotWithBinary(t *testing.T) { + t.Parallel() + xtra := android.FixtureAddTextFile("some/module/Android.bp", ` + cc_binary { + name: "foo_binary", + host_supported: true, + cmake_snapshot_supported: true, + } + `) + result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, ` + cc_cmake_snapshot { + name: "foo", + modules: [ + "foo_binary", + ], + include_sources: true, + }`) + + if runtime.GOOS != "linux" { + t.Skip("CMake snapshots are only supported on Linux") + } + + snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64") + + wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy") +} + +func TestCmakeSnapshotAsTestData(t *testing.T) { + t.Parallel() + result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, ` + cc_test { + name: "foo_test", + gtest: false, + srcs: [ + "foo_test.c", + ], + data: [ + ":foo", + ], + target: { + android: {enabled: false}, + }, + } + + cc_cmake_snapshot { + name: "foo", + modules: [], + prebuilts: ["libc++"], + include_sources: true, + }`) + + if runtime.GOOS != "linux" { + t.Skip("CMake snapshots are only supported on Linux") + } + + snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64") + + wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy") + wasGenerated(t, &snapshotModule, "foo.zip", "") +} diff --git a/cc/compiler.go b/cc/compiler.go index a1b329e2c..34d98c02d 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -120,6 +120,10 @@ type BaseCompilerProperties struct { // ban targeting bpf in cc rules instead use bpf_rules. (b/323415017) Bpf_target *bool + // Add "-Xclang -verify" to the cflags and appends "touch $out" to + // the clang command line. + Clang_verify bool + Yacc *YaccProperties Lex *LexProperties @@ -147,6 +151,9 @@ type BaseCompilerProperties struct { // list of aidl_interface sources Sources []string `blueprint:"mutated"` + // root directory of AIDL sources + AidlRoot string `blueprint:"mutated"` + // AIDL backend language (e.g. "cpp", "ndk") Lang string `blueprint:"mutated"` @@ -278,6 +285,10 @@ func (compiler *baseCompiler) compilerProps() []interface{} { return []interface{}{&compiler.Properties, &compiler.Proto} } +func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties { + return compiler.Properties +} + func includeBuildDirectory(prop *bool) bool { return proptools.BoolDefault(prop, true) } @@ -383,6 +394,11 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps flags.Yacc = compiler.Properties.Yacc flags.Lex = compiler.Properties.Lex + flags.ClangVerify = compiler.Properties.Clang_verify + if compiler.Properties.Clang_verify { + flags.Local.CFlags = append(flags.Local.CFlags, "-Xclang", "-verify") + } + // Include dir cflags localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs) if len(localIncludeDirs) > 0 { @@ -785,6 +801,9 @@ type RustBindgenClangProperties struct { // be added to the include path using -I Local_include_dirs []string `android:"arch_variant,variant_prepend"` + // list of Rust static libraries. + Static_rlibs []string `android:"arch_variant,variant_prepend"` + // list of static libraries that provide headers for this binding. Static_libs []string `android:"arch_variant,variant_prepend"` diff --git a/cc/config/global.go b/cc/config/global.go index 16b5e0938..290a27dfb 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -136,6 +136,11 @@ var ( // displaying logs in web browsers. "-fmessage-length=0", + // Disable C++17 "relaxed template template argument matching" as a workaround for + // our out-dated libcxx. + // http://b/341084395 + "-fno-relaxed-template-template-args", + // Using simple template names reduces the size of debug builds. "-gsimple-template-names", diff --git a/cc/fuzz.go b/cc/fuzz.go index b3e663962..164ec997b 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -597,7 +597,7 @@ func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilde ctx.WalkDeps(func(child, parent android.Module) bool { // If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive - // shared dependencies (even for rust_ffi_static) + // shared dependencies (even for rust_ffi_rlib or rust_ffi_static) if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() { if recursed[ctx.OtherModuleName(child)] { return false diff --git a/cc/library.go b/cc/library.go index 12ecc131a..b9018a7cf 100644 --- a/cc/library.go +++ b/cc/library.go @@ -274,11 +274,12 @@ func LibraryHostSharedFactory() android.Module { type flagExporter struct { Properties FlagExporterProperties - dirs android.Paths // Include directories to be included with -I - systemDirs android.Paths // System include directories to be included with -isystem - flags []string // Exported raw flags. - deps android.Paths - headers android.Paths + dirs android.Paths // Include directories to be included with -I + systemDirs android.Paths // System include directories to be included with -isystem + flags []string // Exported raw flags. + deps android.Paths + headers android.Paths + rustRlibDeps []RustRlibDep } // exportedIncludes returns the effective include paths for this module and @@ -339,6 +340,10 @@ func (f *flagExporter) reexportDeps(deps ...android.Path) { f.deps = append(f.deps, deps...) } +func (f *flagExporter) reexportRustStaticDeps(deps ...RustRlibDep) { + f.rustRlibDeps = append(f.rustRlibDeps, deps...) +} + // addExportedGeneratedHeaders does nothing but collects generated header files. // This can be differ to exportedDeps which may contain phony files to minimize ninja. func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) { @@ -356,6 +361,8 @@ func (f *flagExporter) setProvider(ctx android.ModuleContext) { // Used sparingly, for extra files that need to be explicitly exported to dependers, // or for phony files to minimize ninja. Deps: f.deps, + // Used for exporting rlib deps of static libraries to dependents. + RustRlibDeps: f.rustRlibDeps, // For exported generated headers, such as exported aidl headers, proto headers, or // sysprop headers. GeneratedHeaders: f.headers, @@ -1132,9 +1139,14 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext, linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...) linkerDeps = append(linkerDeps, deps.SharedLibsDeps...) linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...) + + if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil { + deps.StaticLibs = append(deps.StaticLibs, generatedLib) + } + transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, - deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, - linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles) + deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, + deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles) objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...) objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...) @@ -1350,20 +1362,25 @@ func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext, func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext, sourceDump, referenceDump android.Path, - baseName string, isLlndk bool, sourceVersion, prevVersion string) { + baseName, nameExt string, isLlndk bool, sourceVersion, prevDumpDir string) { - errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "." + errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the difference between your source code and the ABI dumps in " + prevDumpDir - library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, prevVersion, + library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt, isLlndk, true /* allowExtensions */, sourceVersion, errorMessage) } func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, sourceDump, referenceDump android.Path, - baseName, nameExt string, isLlndk bool) { + baseName, nameExt string, isLlndk bool, lsdumpTagName string) { libName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) - errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName + + targetRelease := ctx.Config().Getenv("TARGET_RELEASE") + if targetRelease != "" { + errorMessage += " --release " + targetRelease + } library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt, isLlndk, false /* allowExtensions */, "current", errorMessage) @@ -1371,13 +1388,19 @@ func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext, sourceDump, referenceDump android.Path, - baseName, nameExt string, refDumpDir string) { + baseName, nameExt string, refDumpDir string, lsdumpTagName string) { libName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) - errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir + errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName + " --ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir + + targetRelease := ctx.Config().Getenv("TARGET_RELEASE") + if targetRelease != "" { + errorMessage += " --release " + targetRelease + } + // Most opt-in libraries do not have dumps for all default architectures. if ctx.Config().HasDeviceProduct() { - errorMessage += " -products " + ctx.Config().DeviceProduct() + errorMessage += " --product " + ctx.Config().DeviceProduct() } library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt, @@ -1402,6 +1425,7 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD var llndkDump, apexVariantDump android.Path tags := classifySourceAbiDump(ctx) + optInTags := []lsdumpTag{} for _, tag := range tags { if tag == llndkLsdumpTag && currVendorVersion != "" { if llndkDump == nil { @@ -1423,6 +1447,9 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD } addLsdumpPath(string(tag) + ":" + apexVariantDump.String()) } else { + if tag.dirName() == "" { + optInTags = append(optInTags, tag) + } addLsdumpPath(string(tag) + ":" + implDump.String()) } } @@ -1467,7 +1494,7 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName) if prevDumpFile.Valid() { library.crossVersionAbiDiff(ctx, sourceDump, prevDumpFile.Path(), - fileName, isLlndk, currVersion, nameExt+prevVersion) + fileName, nameExt+prevVersion, isLlndk, currVersion, prevDumpDir) } // Check against the current version. sourceDump = implDump @@ -1487,9 +1514,20 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName) if currDumpFile.Valid() { library.sameVersionAbiDiff(ctx, sourceDump, currDumpFile.Path(), - fileName, nameExt, isLlndk) + fileName, nameExt, isLlndk, string(tag)) } } + + // Assert that a module is tagged with at most one of platformLsdumpTag, productLsdumpTag, or vendorLsdumpTag. + if len(headerAbiChecker.Ref_dump_dirs) > 0 && len(optInTags) != 1 { + ctx.ModuleErrorf("Expect exactly one opt-in lsdump tag when ref_dump_dirs are specified: %s", optInTags) + return + } + // Ensure that a module tagged with only platformLsdumpTag has ref_dump_dirs. + // Android.bp in vendor projects should be cleaned up before this is enforced for vendorLsdumpTag and productLsdumpTag. + if len(headerAbiChecker.Ref_dump_dirs) == 0 && len(tags) == 1 && tags[0] == platformLsdumpTag { + ctx.ModuleErrorf("header_abi_checker is explicitly enabled, but no ref_dump_dirs are specified.") + } // Check against the opt-in reference dumps. for i, optInDumpDir := range headerAbiChecker.Ref_dump_dirs { optInDumpDirPath := android.PathForModuleSrc(ctx, optInDumpDir) @@ -1501,7 +1539,7 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD } library.optInAbiDiff(ctx, implDump, optInDumpFile.Path(), - fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String()) + fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String(), string(optInTags[0])) } } } @@ -1593,6 +1631,10 @@ func (library *libraryDecorator) link(ctx ModuleContext, library.reexportDeps(deps.ReexportedDeps...) library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...) + if library.static() && len(deps.ReexportedRustRlibDeps) > 0 { + library.reexportRustStaticDeps(deps.ReexportedRustRlibDeps...) + } + // Optionally export aidl headers. if Bool(library.Properties.Aidl.Export_aidl_headers) { if library.baseCompiler.hasAidl(deps) { @@ -2120,14 +2162,12 @@ func LinkageMutator(mctx android.BottomUpMutatorContext) { // Header only } - } else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() { - + } else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) { // Non-cc.Modules may need an empty variant for their mutators. variations := []string{} if library.NonCcVariants() { variations = append(variations, "") } - isLLNDK := false if m, ok := mctx.Module().(*Module); ok { isLLNDK = m.IsLlndk() diff --git a/cc/linkable.go b/cc/linkable.go index 10cc38f64..5579aaea5 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -73,6 +73,12 @@ type LinkableInterface interface { // RustLibraryInterface returns true if this is a Rust library module RustLibraryInterface() bool + // CrateName returns the crateName for a Rust library, panics if not a Rust library. + CrateName() string + + // DepFlags returns a slice of Rustc string flags, panics if not a Rust library + ExportedCrateLinkDirs() []string + // BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module. BaseModuleName() string @@ -380,6 +386,7 @@ type FlagExporterInfo struct { SystemIncludeDirs android.Paths // System include directories to be included with -isystem Flags []string // Exported raw flags. Deps android.Paths + RustRlibDeps []RustRlibDep GeneratedHeaders android.Paths } diff --git a/cc/linker.go b/cc/linker.go index 56a68b2ce..f325c125d 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -39,6 +39,9 @@ type BaseLinkerProperties struct { // the dependency's .a file will be linked into this module using -Wl,--whole-archive. Whole_static_libs []string `android:"arch_variant,variant_prepend"` + // list of Rust libs that should be statically linked into this module. + Static_rlibs []string `android:"arch_variant"` + // list of modules that should be statically linked into this module. Static_libs []string `android:"arch_variant,variant_prepend"` @@ -116,10 +119,14 @@ type BaseLinkerProperties struct { // product variant of the C/C++ module. Static_libs []string - // list of ehader libs that only should be used to build vendor or product + // list of header libs that only should be used to build vendor or product // variant of the C/C++ module. Header_libs []string + // list of Rust libs that should be statically linked to build vendor or product + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build vendor or // product variant of the C/C++ module. Exclude_shared_libs []string @@ -148,6 +155,10 @@ type BaseLinkerProperties struct { // variant of the C/C++ module. Static_libs []string + // list of Rust libs that should be statically linked to build the recovery + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build // the recovery variant of the C/C++ module. Exclude_shared_libs []string @@ -165,10 +176,14 @@ type BaseLinkerProperties struct { Exclude_runtime_libs []string } Ramdisk struct { - // list of static libs that only should be used to build the recovery + // list of static libs that only should be used to build the ramdisk // variant of the C/C++ module. Static_libs []string + // list of Rust libs that should be statically linked to build the ramdisk + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build // the ramdisk variant of the C/C++ module. Exclude_shared_libs []string @@ -183,9 +198,13 @@ type BaseLinkerProperties struct { } Vendor_ramdisk struct { // list of shared libs that should not be used to build - // the recovery variant of the C/C++ module. + // the vendor ramdisk variant of the C/C++ module. Exclude_shared_libs []string + // list of Rust libs that should be statically linked to build the vendor ramdisk + // variant. + Static_rlibs []string + // list of static libs that should not be used to build // the vendor ramdisk variant of the C/C++ module. Exclude_static_libs []string @@ -201,6 +220,10 @@ type BaseLinkerProperties struct { // variants. Shared_libs []string + // list of Rust libs that should be statically linked to build the vendor ramdisk + // variant. + Static_rlibs []string + // list of ehader libs that only should be used to build platform variant of // the C/C++ module. Header_libs []string @@ -287,10 +310,15 @@ func (linker *baseLinker) linkerProps() []interface{} { return []interface{}{&linker.Properties, &linker.dynamicProperties} } +func (linker *baseLinker) baseLinkerProps() BaseLinkerProperties { + return linker.Properties +} + func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...) deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...) deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Static_rlibs...) deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...) deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...) @@ -334,6 +362,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor.Static_rlibs...) } if ctx.inProduct() { @@ -347,6 +376,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Product.Static_rlibs...) } if ctx.inRecovery() { @@ -360,6 +390,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Recovery.Static_rlibs...) } if ctx.inRamdisk() { @@ -370,6 +401,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Ramdisk.Static_rlibs...) } if ctx.inVendorRamdisk() { @@ -379,6 +411,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor_ramdisk.Static_rlibs...) } if !ctx.useSdk() { diff --git a/cc/sabi.go b/cc/sabi.go index edd9cfe80..64eab4160 100644 --- a/cc/sabi.go +++ b/cc/sabi.go @@ -43,8 +43,6 @@ func (tag *lsdumpTag) dirName() string { return "platform" case llndkLsdumpTag: return "vndk" - case platformLsdumpTag: - return "platform" default: return "" } @@ -134,10 +132,10 @@ func classifySourceAbiDump(ctx android.BaseModuleContext) []lsdumpTag { if m.isImplementationForLLNDKPublic() { result = append(result, llndkLsdumpTag) } - // APEX and opt-in platform dumps are placed in the same directory. if m.library.hasStubsVariants() { result = append(result, apexLsdumpTag) - } else if headerAbiChecker.enabled() { + } + if headerAbiChecker.enabled() { result = append(result, platformLsdumpTag) } } else if headerAbiChecker.enabled() { diff --git a/cc/test.go b/cc/test.go index 3a1a3af44..a96af31bb 100644 --- a/cc/test.go +++ b/cc/test.go @@ -359,6 +359,12 @@ func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps { func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags = test.binaryDecorator.linkerFlags(ctx, flags) flags = test.testDecorator.linkerFlags(ctx, flags) + + // Add a default rpath to allow tests to dlopen libraries specified in data_libs. + // Host modules already get an rpath specified in linker.go. + if !ctx.Host() { + flags.Global.LdFlags = append(flags.Global.LdFlags, `-Wl,-rpath,\$$ORIGIN`) + } return flags } diff --git a/cc/testing.go b/cc/testing.go index 4b4e866d4..989be0272 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -35,6 +35,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool) ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory) + ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory) ctx.RegisterModuleType("cc_object", ObjectFactory) ctx.RegisterModuleType("cc_genrule", GenRuleFactory) ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory) @@ -299,6 +300,7 @@ func commonDefaultModules() string { system_shared_libs: [], stl: "none", vendor_available: true, + vendor_ramdisk_available: true, product_available: true, recovery_available: true, host_supported: true, diff --git a/cc/util.go b/cc/util.go index 3ede8ff6d..8ffacae7d 100644 --- a/cc/util.go +++ b/cc/util.go @@ -68,6 +68,7 @@ func flagsToBuilderFlags(in Flags) builderFlags { needTidyFiles: in.NeedTidyFiles, sAbiDump: in.SAbiDump, emitXrefs: in.EmitXrefs, + clangVerify: in.ClangVerify, systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "), diff --git a/cmd/release_config/build_flag_declarations/Android.bp b/cmd/release_config/build_flag_declarations/Android.bp new file mode 100644 index 000000000..e4f999fd0 --- /dev/null +++ b/cmd/release_config/build_flag_declarations/Android.bp @@ -0,0 +1,32 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +blueprint_go_binary { + name: "build-flag-declarations", + deps: [ + "golang-protobuf-encoding-prototext", + "golang-protobuf-reflect-protoreflect", + "golang-protobuf-runtime-protoimpl", + "soong-cmd-release_config-proto", + "soong-cmd-release_config-lib", + ], + srcs: [ + "main.go", + ], +} + +bootstrap_go_package { + name: "soong-cmd-release_config-build_flag_declarations", + pkgPath: "android/soong/cmd/release_config/build_flag_declarations", + deps: [ + "golang-protobuf-encoding-prototext", + "golang-protobuf-reflect-protoreflect", + "golang-protobuf-runtime-protoimpl", + "soong-cmd-release_config-proto", + "soong-cmd-release_config-lib", + ], + srcs: [ + "main.go", + ], +} diff --git a/cmd/release_config/build_flag_declarations/main.go b/cmd/release_config/build_flag_declarations/main.go new file mode 100644 index 000000000..8315f5917 --- /dev/null +++ b/cmd/release_config/build_flag_declarations/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "flag" + "fmt" + "os" + + rc_lib "android/soong/cmd/release_config/release_config_lib" +) + +type Flags struct { + // The path to the top of the workspace. Default: ".". + top string + + // Output file. + output string + + // Format for output file + format string + + // List of flag_declaration files to add. + decls rc_lib.StringList + + // List of flag_artifacts files to merge. + intermediates rc_lib.StringList + + // Disable warning messages + quiet bool + + // Panic on errors. + debug bool +} + +func main() { + var flags Flags + topDir, err := rc_lib.GetTopDir() + + // Handle the common arguments + flag.StringVar(&flags.top, "top", topDir, "path to top of workspace") + flag.Var(&flags.decls, "decl", "path to a flag_declaration file. May be repeated") + flag.Var(&flags.intermediates, "intermediate", "path to a flag_artifacts file (output from a prior run). May be repeated") + flag.StringVar(&flags.format, "format", "pb", "output file format") + flag.StringVar(&flags.output, "output", "build_flags.pb", "output file") + flag.BoolVar(&flags.debug, "debug", false, "turn on debugging output for errors") + flag.BoolVar(&flags.quiet, "quiet", false, "disable warning messages") + flag.Parse() + + errorExit := func(err error) { + if flags.debug { + panic(err) + } + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } + + if flags.quiet { + rc_lib.DisableWarnings() + } + + if err = os.Chdir(flags.top); err != nil { + errorExit(err) + } + + flagArtifacts := rc_lib.FlagArtifactsFactory("") + for _, intermediate := range flags.intermediates { + fas := rc_lib.FlagArtifactsFactory(intermediate) + for _, fa := range *fas { + (*flagArtifacts)[*fa.FlagDeclaration.Name] = fa + } + } + for _, decl := range flags.decls { + fa := rc_lib.FlagArtifactFactory(decl) + (*flagArtifacts)[*fa.FlagDeclaration.Name] = fa + } + + message := flagArtifacts.GenerateFlagArtifacts() + err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message) + if err != nil { + errorExit(err) + } +} diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go index 4d763c8d7..cd39ffd11 100644 --- a/cmd/release_config/crunch_flags/main.go +++ b/cmd/release_config/crunch_flags/main.go @@ -137,7 +137,9 @@ func ProcessBuildFlags(dir string, namespaceMap map[string]string) error { workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT) switch { case declName == "RELEASE_ACONFIG_VALUE_SETS": - rootAconfigModule = declValue[1 : len(declValue)-1] + if strings.HasPrefix(declValue, "\"") { + rootAconfigModule = declValue[1 : len(declValue)-1] + } continue case strings.HasPrefix(declValue, "\""): // String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT. diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go index 54328068a..061783828 100644 --- a/cmd/release_config/release_config/main.go +++ b/cmd/release_config/release_config/main.go @@ -51,7 +51,7 @@ func main() { flag.BoolVar(&textproto, "textproto", true, "write artifacts as text protobuf") flag.BoolVar(&json, "json", true, "write artifacts as json") flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf") - flag.BoolVar(&allMake, "all_make", true, "write makefiles for all release configs") + flag.BoolVar(&allMake, "all_make", false, "write makefiles for all release configs") flag.BoolVar(&useBuildVar, "use_get_build_var", false, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS") flag.BoolVar(&guard, "guard", true, "whether to guard with RELEASE_BUILD_FLAGS_IN_PROTOBUF") @@ -77,7 +77,7 @@ func main() { panic(err) } - makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, targetRelease)) + makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, targetRelease)) useProto, ok := config.FlagArtifacts["RELEASE_BUILD_FLAGS_IN_PROTOBUF"] if guard && (!ok || rc_lib.MarshalValue(useProto.Value) == "") { // We were told to guard operation and either we have no build flag, or it is False. @@ -92,10 +92,10 @@ func main() { } if allMake { // Write one makefile per release config, using the canonical release name. - for k, _ := range configs.ReleaseConfigs { - if k != targetRelease { - makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, k)) - err = configs.WriteMakefile(makefilePath, k) + for _, c := range configs.GetSortedReleaseConfigs() { + if c.Name != targetRelease { + makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name)) + err = configs.WriteMakefile(makefilePath, c.Name) if err != nil { panic(err) } diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go index cba1b5cd8..cdd5af99b 100644 --- a/cmd/release_config/release_config_lib/flag_artifact.go +++ b/cmd/release_config/release_config_lib/flag_artifact.go @@ -15,7 +15,9 @@ package release_config_lib import ( + "cmp" "fmt" + "slices" rc_proto "android/soong/cmd/release_config/release_config_proto" @@ -45,6 +47,63 @@ type FlagArtifact struct { // Key is flag name. type FlagArtifacts map[string]*FlagArtifact +func FlagArtifactFactory(declPath string) *FlagArtifact { + fd := &rc_proto.FlagDeclaration{} + fa := &FlagArtifact{ + FlagDeclaration: fd, + DeclarationIndex: -1, + Traces: []*rc_proto.Tracepoint{}, + } + if declPath != "" { + LoadMessage(declPath, fd) + fa.Value = fd.GetValue() + fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(declPath), Value: fa.Value}) + } + return fa +} + +func FlagArtifactsFactory(artifactsPath string) *FlagArtifacts { + ret := make(FlagArtifacts) + if artifactsPath != "" { + fas := &rc_proto.FlagArtifacts{} + LoadMessage(artifactsPath, fas) + for _, fa_pb := range fas.FlagArtifacts { + fa := &FlagArtifact{} + fa.FlagDeclaration = fa_pb.GetFlagDeclaration() + if val := fa_pb.GetValue(); val != nil { + fa.Value = val + } + if traces := fa_pb.GetTraces(); traces != nil { + fa.Traces = traces + } + ret[*fa.FlagDeclaration.Name] = fa + } + } + return &ret +} + +func (fa *FlagArtifact) GenerateFlagArtifact() *rc_proto.FlagArtifact { + ret := &rc_proto.FlagArtifact{FlagDeclaration: fa.FlagDeclaration} + if fa.Value != nil { + ret.Value = fa.Value + } + if len(fa.Traces) > 0 { + ret.Traces = fa.Traces + } + return ret +} + +func (fas *FlagArtifacts) GenerateFlagArtifacts() *rc_proto.FlagArtifacts { + ret := &rc_proto.FlagArtifacts{FlagArtifacts: []*rc_proto.FlagArtifact{}} + for _, fa := range *fas { + ret.FlagArtifacts = append(ret.FlagArtifacts, fa.GenerateFlagArtifact()) + } + slices.SortFunc(ret.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int { + return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name) + }) + return ret +} + // Create a clone of the flag artifact. // // Returns: diff --git a/cmd/release_config/release_config_lib/flag_value.go b/cmd/release_config/release_config_lib/flag_value.go index 59021e260..76363cec4 100644 --- a/cmd/release_config/release_config_lib/flag_value.go +++ b/cmd/release_config/release_config_lib/flag_value.go @@ -74,3 +74,22 @@ func MarshalValue(value *rc_proto.Value) string { return "" } } + +// Returns a string representation of the type of the value for make +func ValueType(value *rc_proto.Value) string { + if value == nil || value.Val == nil { + return "unspecified" + } + switch value.Val.(type) { + case *rc_proto.Value_UnspecifiedValue: + return "unspecified" + case *rc_proto.Value_StringValue: + return "string" + case *rc_proto.Value_BoolValue: + return "bool" + case *rc_proto.Value_Obsolete: + return "obsolete" + default: + panic("Unhandled type") + } +} diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go index 820482239..547f0dcd4 100644 --- a/cmd/release_config/release_config_lib/release_config.go +++ b/cmd/release_config/release_config_lib/release_config.go @@ -69,6 +69,9 @@ type ReleaseConfig struct { // Unmarshalled flag artifacts FlagArtifacts FlagArtifacts + // The files used by this release config + FilesUsedMap map[string]bool + // Generated release config ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact @@ -80,10 +83,17 @@ type ReleaseConfig struct { } func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) { - return &ReleaseConfig{Name: name, DeclarationIndex: index} + return &ReleaseConfig{ + Name: name, + DeclarationIndex: index, + FilesUsedMap: make(map[string]bool), + } } func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { + for f := range iConfig.FilesUsedMap { + config.FilesUsedMap[f] = true + } for _, fa := range iConfig.FlagArtifacts { name := *fa.FlagDeclaration.Name myFa, ok := config.FlagArtifacts[name] @@ -91,7 +101,8 @@ func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { return fmt.Errorf("Could not inherit flag %s from %s", name, iConfig.Name) } if name == "RELEASE_ACONFIG_VALUE_SETS" { - if len(fa.Traces) > 0 { + // If there is a value assigned, add the trace. + if len(fa.Value.GetStringValue()) > 0 { myFa.Traces = append(myFa.Traces, fa.Traces...) myFa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{ myFa.Value.GetStringValue() + " " + fa.Value.GetStringValue()}} @@ -105,6 +116,17 @@ func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { return nil } +func (config *ReleaseConfig) GetSortedFileList() []string { + ret := []string{} + for k := range config.FilesUsedMap { + ret = append(ret, k) + } + slices.SortFunc(ret, func(a, b string) int { + return cmp.Compare(a, b) + }) + return ret +} + func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error { if config.ReleaseConfigArtifact != nil { return nil @@ -144,6 +166,15 @@ func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) erro return err } } + + // If we inherited nothing, then we need to mark the global files as used for this + // config. If we inherited, then we already marked them as part of inheritance. + if len(config.InheritNames) == 0 { + for f := range configs.FilesUsedMap { + config.FilesUsedMap[f] = true + } + } + contributionsToApply = append(contributionsToApply, config.Contributions...) workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go index 8a4e2d58b..c62a78e76 100644 --- a/cmd/release_config/release_config_lib/release_configs.go +++ b/cmd/release_config/release_config_lib/release_configs.go @@ -67,6 +67,9 @@ type ReleaseConfigs struct { // Map of directory to *ReleaseConfigMap releaseConfigMapsMap map[string]*ReleaseConfigMap + // The files used by all release configs + FilesUsedMap map[string]bool + // The list of config directories used. configDirs []string @@ -102,6 +105,7 @@ func ReleaseConfigsFactory() (c *ReleaseConfigs) { releaseConfigMapsMap: make(map[string]*ReleaseConfigMap), configDirs: []string{}, configDirIndexes: make(ReleaseConfigDirMap), + FilesUsedMap: make(map[string]bool), } workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) releaseAconfigValueSets := FlagArtifact{ @@ -120,6 +124,16 @@ func ReleaseConfigsFactory() (c *ReleaseConfigs) { return &configs } +func (configs *ReleaseConfigs) GetSortedReleaseConfigs() (ret []*ReleaseConfig) { + for _, config := range configs.ReleaseConfigs { + ret = append(ret, config) + } + slices.SortFunc(ret, func(a, b *ReleaseConfig) int { + return cmp.Compare(a.Name, b.Name) + }) + return ret +} + func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) { m = &ReleaseConfigMap{ path: protoPath, @@ -170,6 +184,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex return fmt.Errorf("Release config map %s has invalid container %s", path, container) } } + configs.FilesUsedMap[path] = true dir := filepath.Dir(path) // Record any aliases, checking for duplicates. for _, alias := range m.proto.Aliases { @@ -216,6 +231,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name) } // Set the initial value in the flag artifact. + configs.FilesUsedMap[path] = true configs.FlagArtifacts[name].UpdateValue( FlagValue{path: path, proto: rc_proto.FlagValue{ Name: proto.String(name), Value: flagDeclaration.Value}}) @@ -239,6 +255,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex) } config := configs.ReleaseConfigs[name] + config.FilesUsedMap[path] = true config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...) // Only walk flag_values/{RELEASE} for defined releases. @@ -250,6 +267,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex if *flagValue.proto.Name == "RELEASE_ACONFIG_VALUE_SETS" { return fmt.Errorf("%s: %s is a reserved build flag", path, *flagValue.proto.Name) } + config.FilesUsedMap[path] = true releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue) return nil }) @@ -283,14 +301,21 @@ func (configs *ReleaseConfigs) GetReleaseConfig(name string) (*ReleaseConfig, er return nil, fmt.Errorf("Missing config %s. Trace=%v", name, trace) } -// Write the makefile for this targetRelease. -func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) error { - makeVars := make(map[string]string) +func (configs *ReleaseConfigs) GetAllReleaseNames() []string { var allReleaseNames []string for _, v := range configs.ReleaseConfigs { allReleaseNames = append(allReleaseNames, v.Name) allReleaseNames = append(allReleaseNames, v.OtherNames...) } + slices.SortFunc(allReleaseNames, func(a, b string) int { + return cmp.Compare(a, b) + }) + return allReleaseNames +} + +// Write the makefile for this targetRelease. +func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) error { + makeVars := make(map[string]string) config, err := configs.GetReleaseConfig(targetRelease) if err != nil { return err @@ -323,6 +348,7 @@ func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) erro } value := MarshalValue(flag.Value) makeVars[name] = value + addVar(name, "TYPE", ValueType(flag.Value)) addVar(name, "PARTITIONS", strings.Join(decl.Containers, " ")) addVar(name, "DEFAULT", MarshalValue(decl.Value)) addVar(name, "VALUE", value) @@ -331,7 +357,7 @@ func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) erro addVar(name, "NAMESPACE", *decl.Namespace) } pNames := []string{} - for k, _ := range partitions { + for k := range partitions { pNames = append(pNames, k) } slices.SortFunc(pNames, func(a, b string) int { @@ -353,7 +379,8 @@ func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) erro data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease) } // The variable _all_release_configs will get deleted during processing, so do not mark it read-only. - data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(allReleaseNames, " ")) + data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(configs.GetAllReleaseNames(), " ")) + data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " ")) data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " ")) for _, pName := range pNames { data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " ")) @@ -385,8 +412,9 @@ func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) erro configs.ReleaseConfigs[name].OtherNames = aliases } - for _, config := range configs.ReleaseConfigs { - err := config.GenerateReleaseConfig(configs) + sortedReleaseConfigs := configs.GetSortedReleaseConfigs() + for _, c := range sortedReleaseConfigs { + err := c.GenerateReleaseConfig(configs) if err != nil { return err } @@ -396,17 +424,16 @@ func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) erro if err != nil { return err } + orc := []*rc_proto.ReleaseConfigArtifact{} + for _, c := range sortedReleaseConfigs { + if c.Name != releaseConfig.Name { + orc = append(orc, c.ReleaseConfigArtifact) + } + } + configs.Artifact = rc_proto.ReleaseConfigsArtifact{ - ReleaseConfig: releaseConfig.ReleaseConfigArtifact, - OtherReleaseConfigs: func() []*rc_proto.ReleaseConfigArtifact { - orc := []*rc_proto.ReleaseConfigArtifact{} - for name, config := range configs.ReleaseConfigs { - if name != releaseConfig.Name { - orc = append(orc, config.ReleaseConfigArtifact) - } - } - return orc - }(), + ReleaseConfig: releaseConfig.ReleaseConfigArtifact, + OtherReleaseConfigs: orc, ReleaseConfigMapsMap: func() map[string]*rc_proto.ReleaseConfigMap { ret := make(map[string]*rc_proto.ReleaseConfigMap) for k, v := range configs.releaseConfigMapsMap { @@ -436,7 +463,8 @@ func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease strin configs := ReleaseConfigsFactory() mapsRead := make(map[string]bool) - for idx, releaseConfigMapPath := range releaseConfigMapPaths { + var idx int + for _, releaseConfigMapPath := range releaseConfigMapPaths { // Maintain an ordered list of release config directories. configDir := filepath.Dir(releaseConfigMapPath) if mapsRead[configDir] { @@ -451,6 +479,7 @@ func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease strin if err != nil { return nil, err } + idx += 1 } // Now that we have all of the release config maps, can meld them and generate the artifacts. diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go index c0ea7897b..0af99a63e 100644 --- a/cmd/release_config/release_config_lib/util.go +++ b/cmd/release_config/release_config_lib/util.go @@ -58,13 +58,36 @@ func (l *StringList) String() string { // // error: any error encountered. func WriteMessage(path string, message proto.Message) (err error) { + format := filepath.Ext(path) + if len(format) > 1 { + // Strip any leading dot. + format = format[1:] + } + return WriteFormattedMessage(path, format, message) +} + +// Write a marshalled message to a file. +// +// Marshal the message using the given format. +// +// Args: +// +// path string: the path of the file to write to. Directories are not created. +// Supported extensions are: ".json", ".pb", and ".textproto". +// format string: one of "json", "pb", or "textproto". +// message proto.Message: the message to write. +// +// Returns: +// +// error: any error encountered. +func WriteFormattedMessage(path, format string, message proto.Message) (err error) { var data []byte - switch filepath.Ext(path) { - case ".json": + switch format { + case "json": data, err = json.MarshalIndent(message, "", " ") - case ".pb": + case "pb", "binaryproto", "protobuf": data, err = proto.Marshal(message) - case ".textproto": + case "textproto": data, err = prototext.MarshalOptions{Multiline: true}.Marshal(message) default: return fmt.Errorf("Unknown message format for %s", path) @@ -95,7 +118,7 @@ func LoadMessage(path string, message proto.Message) error { switch filepath.Ext(path) { case ".json": return json.Unmarshal(data, message) - case ".pb": + case ".pb", ".protobuf", ".binaryproto": return proto.Unmarshal(data, message) case ".textproto": return prototext.Unmarshal(data, message) @@ -126,9 +149,12 @@ func DisableWarnings() { disableWarnings = true } +// warnf will log to stdout if warnings are enabled. In make code, +// stdout is redirected to a file, so the warnings will not be shown +// in the terminal. func warnf(format string, args ...any) (n int, err error) { if !disableWarnings { - return fmt.Fprintf(os.Stderr, format, args...) + return fmt.Printf(format, args...) } return 0, nil } @@ -193,6 +219,7 @@ func GetDefaultMapPaths(queryMaps bool) (defaultMapPaths StringList, err error) var stdout strings.Builder getBuildVar.Stdin = strings.NewReader("") getBuildVar.Stdout = &stdout + getBuildVar.Stderr = os.Stderr err = getBuildVar.Run() if err != nil { return diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go index 483cffac1..73cc1dc95 100644 --- a/cmd/release_config/release_config_proto/build_flags_out.pb.go +++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go @@ -1,3 +1,7 @@ +// +// Copyright (C) 2024 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 // @@ -11,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: build_flags_out.proto diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto index 6f34d6f67..8c3be5fa4 100644 --- a/cmd/release_config/release_config_proto/build_flags_out.proto +++ b/cmd/release_config/release_config_proto/build_flags_out.proto @@ -1,3 +1,7 @@ +// +// Copyright (C) 2024 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 // diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go index dded97566..a53823684 100644 --- a/cmd/release_config/release_config_proto/build_flags_src.pb.go +++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go @@ -1,3 +1,7 @@ +// +// Copyright (C) 2024 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 // @@ -11,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: build_flags_src.proto @@ -34,7 +38,7 @@ const ( type Workflow int32 const ( - Workflow_UNSPECIFIED_workflow Workflow = 0 + Workflow_Workflow_Unspecified Workflow = 0 // Boolean value flags that progress from false to true. Workflow_LAUNCH Workflow = 1 // String value flags that get updated with new version strings to control @@ -48,13 +52,13 @@ const ( // Enum value maps for Workflow. var ( Workflow_name = map[int32]string{ - 0: "UNSPECIFIED_workflow", + 0: "Workflow_Unspecified", 1: "LAUNCH", 2: "PREBUILT", 3: "MANUAL", } Workflow_value = map[string]int32{ - "UNSPECIFIED_workflow": 0, + "Workflow_Unspecified": 0, "LAUNCH": 1, "PREBUILT": 2, "MANUAL": 3, @@ -295,7 +299,7 @@ func (x *FlagDeclaration) GetWorkflow() Workflow { if x != nil && x.Workflow != nil { return *x.Workflow } - return Workflow_UNSPECIFIED_workflow + return Workflow_Workflow_Unspecified } func (x *FlagDeclaration) GetContainers() []string { @@ -643,8 +647,8 @@ var file_build_flags_src_proto_rawDesc = []byte{ 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x4a, 0x0a, 0x08, - 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto index 0ef1a5ffa..cb0d9d9bb 100644 --- a/cmd/release_config/release_config_proto/build_flags_src.proto +++ b/cmd/release_config/release_config_proto/build_flags_src.proto @@ -1,3 +1,7 @@ +// +// Copyright (C) 2024 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 // @@ -39,7 +43,7 @@ option go_package = "android/soong/release_config/release_config_proto"; // com.android.1mypackage are invalid enum workflow { - UNSPECIFIED_workflow = 0; + Workflow_Unspecified = 0; // Boolean value flags that progress from false to true. LAUNCH = 1; diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index d64010e66..4490dd2af 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -108,7 +108,7 @@ func needToWriteNinjaHint(ctx *android.Context) bool { case "always": return true case "depend": - if _, err := os.Stat(filepath.Join(ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) { + if _, err := os.Stat(filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) { return true } } diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index 025ba27aa..e168edc63 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -59,6 +59,7 @@ func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory) ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory) ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory) + ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory) ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory) ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory) ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory) @@ -158,9 +159,6 @@ type PrebuiltEtc struct { additionalDependencies *android.Paths makeClass string - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths } type Defaults struct { @@ -421,7 +419,6 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { for _, ip := range installs { ip.addInstallRules(ctx) } - android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles) } type installProperties struct { @@ -486,7 +483,6 @@ func (p *PrebuiltEtc) AndroidMkEntries() []android.AndroidMkEntries { if p.additionalDependencies != nil { entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...) } - android.SetAconfigFileMkEntries(p.AndroidModuleBase(), entries, p.mergedAconfigFiles) }, }, }} @@ -655,6 +651,15 @@ func PrebuiltFontFactory() android.Module { return module } +// prebuilt_overlay is for a prebuilt artifact in <partition>/overlay directory. +func PrebuiltOverlayFactory() android.Module { + module := &PrebuiltEtc{} + InitPrebuiltEtcModule(module, "overlay") + // This module is device-only + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + // prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system // image. // If soc_specific property is set to true, the firmware file is installed to the diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go index 3ee234069..c44574aab 100644 --- a/etc/prebuilt_etc_test.go +++ b/etc/prebuilt_etc_test.go @@ -342,6 +342,19 @@ func TestPrebuiltFontInstallDirPath(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) } +func TestPrebuiltOverlayInstallDirPath(t *testing.T) { + result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` + prebuilt_overlay { + name: "foo.conf", + src: "foo.conf", + } + `) + + p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) + expected := "out/soong/target/product/test_device/system/overlay" + android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) +} + func TestPrebuiltFirmwareDirPath(t *testing.T) { targetPath := "out/soong/target/product/test_device" tests := []struct { diff --git a/filesystem/Android.bp b/filesystem/Android.bp index 854a3661a..a08f7cf17 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -15,6 +15,7 @@ bootstrap_go_package { "soong-phony", // for testing ], srcs: [ + "aconfig_files.go", "avb_add_hash_footer.go", "avb_gen_vbmeta_image.go", "bootimg.go", diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go new file mode 100644 index 000000000..8daee850e --- /dev/null +++ b/filesystem/aconfig_files.go @@ -0,0 +1,82 @@ +// Copyright (C) 2024 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 filesystem + +import ( + "android/soong/android" + "path/filepath" + "strings" + + "github.com/google/blueprint/proptools" +) + +func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, dir android.OutputPath) { + if !proptools.Bool(f.properties.Gen_aconfig_flags_pb) { + return + } + + aconfigFlagsBuilderPath := android.PathForModuleOut(ctx, "aconfig_flags_builder.sh") + aconfigToolPath := ctx.Config().HostToolPath(ctx, "aconfig") + cmd := builder.Command().Tool(aconfigFlagsBuilderPath).Implicit(aconfigToolPath) + + var caches []string + for _, ps := range specs { + cmd.Implicits(ps.GetAconfigPaths()) + caches = append(caches, ps.GetAconfigPaths().Strings()...) + } + caches = android.SortedUniqueStrings(caches) + + var sbCaches strings.Builder + for _, cache := range caches { + sbCaches.WriteString(" --cache ") + sbCaches.WriteString(cache) + sbCaches.WriteString(" \\\n") + } + sbCaches.WriteRune('\n') + + var sb strings.Builder + sb.WriteString("set -e\n") + + installAconfigFlagsPath := dir.Join(ctx, "etc", "aconfig_flags.pb") + sb.WriteString(aconfigToolPath.String()) + sb.WriteString(" dump-cache --dedup --format protobuf --out ") + sb.WriteString(installAconfigFlagsPath.String()) + sb.WriteString(" \\\n") + sb.WriteString(sbCaches.String()) + cmd.ImplicitOutput(installAconfigFlagsPath) + + installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig") + sb.WriteString("mkdir -p ") + sb.WriteString(installAconfigStorageDir.String()) + sb.WriteRune('\n') + + generatePartitionAconfigStorageFile := func(fileType, fileName string) { + sb.WriteString(aconfigToolPath.String()) + sb.WriteString(" create-storage --container ") + sb.WriteString(f.PartitionType()) + sb.WriteString(" --file ") + sb.WriteString(fileType) + sb.WriteString(" --out ") + sb.WriteString(filepath.Join(installAconfigStorageDir.String(), fileName)) + sb.WriteString(" \\\n") + sb.WriteString(sbCaches.String()) + cmd.ImplicitOutput(installAconfigStorageDir.Join(ctx, fileName)) + } + generatePartitionAconfigStorageFile("package_map", "package.map") + generatePartitionAconfigStorageFile("flag_map", "flag.map") + generatePartitionAconfigStorageFile("flag_val", "flag.val") + + android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String()) +} diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index b342ae930..d2572c269 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -106,7 +106,7 @@ type filesystemProperties struct { Base_dir *string // Directories to be created under root. e.g. /dev, /proc, etc. - Dirs []string + Dirs proptools.Configurable[[]string] // Symbolic links to be created under root with "ln -sf <target> <name>". Symlinks []symlinkDefinition @@ -131,6 +131,9 @@ type filesystemProperties struct { // Default is false Build_logtags *bool + // Install aconfig_flags.pb file for the modules installed in this partition. + Gen_aconfig_flags_pb *bool + Fsverity fsverityProperties } @@ -149,6 +152,7 @@ func filesystemFactory() android.Module { func initFilesystemModule(module *filesystem) { module.AddProperties(&module.properties) android.InitPackageModule(module) + module.PackagingBase.DepsCollectFirstTargetOnly = true android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) } @@ -239,7 +243,7 @@ func validatePartitionType(ctx android.ModuleContext, p partition) { // already in `rootDir`. func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) { // create dirs and symlinks - for _, dir := range f.properties.Dirs { + for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) { // OutputPath.Join verifies dir builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String()) } @@ -300,6 +304,7 @@ func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) androi f.addMakeBuiltFiles(ctx, builder, rootDir) f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) f.buildEventLogtagsFile(ctx, builder, rebasedDir) + f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir) // run host_init_verifier // Ideally we should have a concept of pluggable linters that verify the generated image. @@ -441,6 +446,7 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) f.buildNonDepsFiles(ctx, builder, rootDir) f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) f.buildEventLogtagsFile(ctx, builder, rebasedDir) + f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir) output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath cmd := builder.Command(). diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index acd481352..2dc8c21e0 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -465,3 +465,98 @@ func TestPreventDuplicatedEntries(t *testing.T) { } `) } + +func TestTrackPhonyAsRequiredDep(t *testing.T) { + result := fixture.RunTestWithBp(t, ` + android_filesystem { + name: "fs", + deps: ["foo"], + } + + cc_binary { + name: "foo", + required: ["phony"], + } + + phony { + name: "phony", + required: ["libbar"], + } + + cc_library { + name: "libbar", + } + `) + + fs := result.ModuleForTests("fs", "android_common").Module().(*filesystem) + expected := []string{ + "bin/foo", + "lib64/libbar.so", + } + for _, e := range expected { + android.AssertStringListContains(t, "missing entry", fs.entries, e) + } +} + +func TestFilterOutUnsupportedArches(t *testing.T) { + result := fixture.RunTestWithBp(t, ` + android_filesystem { + name: "fs_64_only", + deps: ["foo"], + } + + android_filesystem { + name: "fs_64_32", + compile_multilib: "both", + deps: ["foo"], + } + + cc_binary { + name: "foo", + required: ["phony"], + } + + phony { + name: "phony", + required: [ + "libbar", + "app", + ], + } + + cc_library { + name: "libbar", + } + + android_app { + name: "app", + srcs: ["a.java"], + platform_apis: true, + } + `) + testcases := []struct { + fsName string + expected []string + unexpected []string + }{ + { + fsName: "fs_64_only", + expected: []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so"}, + unexpected: []string{"lib/libbar.so"}, + }, + { + fsName: "fs_64_32", + expected: []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so", "lib/libbar.so"}, + unexpected: []string{}, + }, + } + for _, c := range testcases { + fs := result.ModuleForTests(c.fsName, "android_common").Module().(*filesystem) + for _, e := range c.expected { + android.AssertStringListContains(t, "missing entry", fs.entries, e) + } + for _, e := range c.unexpected { + android.AssertStringListDoesNotContain(t, "unexpected entry", fs.entries, e) + } + } +} diff --git a/genrule/genrule.go b/genrule/genrule.go index 67b96ca92..dd980cbb0 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -180,9 +180,6 @@ type Module struct { subName string subDir string - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths } type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask @@ -588,24 +585,6 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) g.outputDeps = android.Paths{phonyFile} } - android.CollectDependencyAconfigFiles(ctx, &g.mergedAconfigFiles) -} - -func (g *Module) AndroidMkEntries() []android.AndroidMkEntries { - ret := android.AndroidMkEntries{ - OutputFile: android.OptionalPathForPath(g.outputFiles[0]), - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - android.SetAconfigFileMkEntries(g.AndroidModuleBase(), entries, g.mergedAconfigFiles) - }, - }, - } - - return []android.AndroidMkEntries{ret} -} - -func (g *Module) AndroidModuleBase() *android.ModuleBase { - return &g.ModuleBase } // Collect information for opening IDE project files in java/jdeps.go. diff --git a/java/aar.go b/java/aar.go index 47c64bfe8..07392f6e5 100644 --- a/java/aar.go +++ b/java/aar.go @@ -1014,9 +1014,6 @@ type AARImport struct { usesLibrary classLoaderContexts dexpreopt.ClassLoaderContextMap - - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths } var _ android.OutputFileProducer = (*AARImport)(nil) @@ -1386,7 +1383,6 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{ JniPackages: a.jniPackages, }) - android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles) } func (a *AARImport) HeaderJars() android.Paths { diff --git a/java/androidmk.go b/java/androidmk.go index 9cd0bafea..a1bc90494 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -17,7 +17,6 @@ package java import ( "fmt" "io" - "strings" "android/soong/android" @@ -124,7 +123,6 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { if library.dexpreopter.configPath != nil { entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath) } - android.SetAconfigFileMkEntries(&library.ModuleBase, entries, library.mergedAconfigFiles) }, }, }) @@ -298,7 +296,6 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { if len(binary.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled) } - android.SetAconfigFileMkEntries(&binary.ModuleBase, entries, binary.mergedAconfigFiles) }, }, ExtraFooters: []android.AndroidMkExtraFootersFunc{ @@ -416,22 +413,11 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String()) entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String()) } else { + var names []string for _, jniLib := range app.jniLibs { - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name) - var partitionTag string - - // Mimic the creation of partition_tag in build/make, - // which defaults to an empty string when the partition is system. - // Otherwise, capitalize with a leading _ - if jniLib.partition == "system" { - partitionTag = "" - } else { - split := strings.Split(jniLib.partition, "/") - partitionTag = "_" + strings.ToUpper(split[len(split)-1]) - } - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(), - jniLib.name+":"+partitionTag) + names = append(names, jniLib.name) } + entries.AddStrings("LOCAL_REQUIRED_MODULES", names...) } if len(app.jniCoverageOutputs) > 0 { @@ -450,10 +436,6 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports) - if app.Name() != "framework-res" { - android.SetAconfigFileMkEntries(&app.ModuleBase, entries, app.mergedAconfigFiles) - } - entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", app.logtagsSrcs.Strings()...) }, }, @@ -531,7 +513,6 @@ func (a *AndroidLibrary) AndroidMkEntries() []android.AndroidMkEntries { entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile) entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true) - android.SetAconfigFileMkEntries(&a.ModuleBase, entries, a.mergedAconfigFiles) }) return entriesList diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 2978a40aa..243a2791e 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -20,8 +20,6 @@ import ( "android/soong/android" "android/soong/cc" - - "github.com/google/blueprint/proptools" ) func TestRequired(t *testing.T) { @@ -256,148 +254,50 @@ func TestGetOverriddenPackages(t *testing.T) { } } -func TestJniPartition(t *testing.T) { - bp := ` - cc_library { - name: "libjni_system", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - } - - cc_library { - name: "libjni_system_ext", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - system_ext_specific: true, - } - - cc_library { - name: "libjni_odm", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - device_specific: true, - } - - cc_library { - name: "libjni_product", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - product_specific: true, - } - - cc_library { - name: "libjni_vendor", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - soc_specific: true, - } - - android_app { - name: "test_app_system_jni_system", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - } - +func TestJniAsRequiredDeps(t *testing.T) { + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcDefaultModules, + android.PrepareForTestWithAndroidMk, + ).RunTestWithBp(t, ` android_app { - name: "test_app_system_jni_system_ext", - privileged: true, + name: "app", + jni_libs: ["libjni"], platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system_ext"], } android_app { - name: "test_app_system_ext_jni_system", - privileged: true, + name: "app_embedded", + jni_libs: ["libjni"], platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - system_ext_specific: true - } - - android_app { - name: "test_app_system_ext_jni_system_ext", - sdk_version: "core_platform", - jni_libs: ["libjni_system_ext"], - system_ext_specific: true + use_embedded_native_libs: true, } - android_app { - name: "test_app_product_jni_product", - sdk_version: "core_platform", - jni_libs: ["libjni_product"], - product_specific: true - } - - android_app { - name: "test_app_vendor_jni_odm", - sdk_version: "core_platform", - jni_libs: ["libjni_odm"], - soc_specific: true + cc_library { + name: "libjni", + system_shared_libs: [], + stl: "none", } + `) - android_app { - name: "test_app_odm_jni_vendor", - sdk_version: "core_platform", - jni_libs: ["libjni_vendor"], - device_specific: true - } - android_app { - name: "test_app_system_jni_multiple", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system", "libjni_system_ext"], - } - android_app { - name: "test_app_vendor_jni_multiple", - sdk_version: "core_platform", - jni_libs: ["libjni_odm", "libjni_vendor"], - soc_specific: true - } - ` - arch := "arm64" - ctx := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - cc.PrepareForTestWithCcDefaultModules, - android.PrepareForTestWithAndroidMk, - android.FixtureModifyConfig(func(config android.Config) { - config.TestProductVariables.DeviceArch = proptools.StringPtr(arch) - }), - ). - RunTestWithBp(t, bp) - testCases := []struct { - name string - partitionNames []string - partitionTags []string + testcases := []struct { + name string + expected []string }{ - {"test_app_system_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}}, - {"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}}, - {"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}}, - {"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}}, - {"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}}, + { + name: "app", + expected: []string{"libjni"}, + }, + { + name: "app_embedded", + expected: nil, + }, } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - mod := ctx.ModuleForTests(test.name, "android_common").Module() - entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0] - for i := range test.partitionNames { - actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i] - expected := test.partitionNames[i] + ":" + test.partitionTags[i] - android.AssertStringEquals(t, "Expected and actual differ", expected, actual) - } - }) + for _, tc := range testcases { + mod := ctx.ModuleForTests(tc.name, "android_common").Module() + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0] + required := entries.EntryMap["LOCAL_REQUIRED_MODULES"] + android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required) } } diff --git a/java/app.go b/java/app.go index 50d1a2f43..d2f2d0be1 100644 --- a/java/app.go +++ b/java/app.go @@ -274,16 +274,37 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { variation := append(jniTarget.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - // If the app builds against an Android SDK use the SDK variant of JNI dependencies - // unless jni_uses_platform_apis is set. - // Don't require the SDK variant for apps that are shipped on vendor, etc., as they already - // have stable APIs through the VNDK. - if (usesSDK && !a.RequiresStableAPIs(ctx) && - !Bool(a.appProperties.Jni_uses_platform_apis)) || - Bool(a.appProperties.Jni_uses_sdk_apis) { + // Test whether to use the SDK variant or the non-SDK variant of JNI dependencies. + // Many factors are considered here. + // 1. Basically, the selection follows whether the app has sdk_version set or not. + jniUsesSdkVariant := usesSDK + // 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it + if Bool(a.appProperties.Jni_uses_sdk_apis) { + jniUsesSdkVariant = true + } + if Bool(a.appProperties.Jni_uses_platform_apis) { + jniUsesSdkVariant = false + } + // 3. Then the use of SDK variant is again prohibited for the following cases: + // 3.1. the app is shipped on unbundled partitions like vendor. Since the entire + // partition (not only the app) is considered unbudled, there's no need to use the + // SDK variant. + // 3.2. the app doesn't support embedding the JNI libs + if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) { + jniUsesSdkVariant = false + } + if jniUsesSdkVariant { variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) } - ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...) + + // Use the installable dep tag when the JNIs are not embedded + var tag dependencyTag + if a.shouldEmbedJnis(ctx) { + tag = jniLibTag + } else { + tag = jniInstallTag + } + ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) } for _, aconfig_declaration := range a.aaptProperties.Flags_packages { ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) @@ -334,27 +355,17 @@ func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleCon func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.checkAppSdkVersions(ctx) + a.checkEmbedJnis(ctx) a.generateAndroidBuildActions(ctx) a.generateJavaUsedByApex(ctx) } -func (a *AndroidApp) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { - defaultMinSdkVersion := a.Module.MinSdkVersion(ctx) - if proptools.Bool(a.appProperties.Updatable) { - overrideApiLevel := android.MinSdkVersionFromValue(ctx, ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()) - if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(defaultMinSdkVersion) > 0 { - return overrideApiLevel - } - } - return defaultMinSdkVersion -} - func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { if a.Updatable() { if !a.SdkVersion(ctx).Stable() { ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx)) } - if String(a.deviceProperties.Min_sdk_version) == "" { + if String(a.overridableProperties.Min_sdk_version) == "" { ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.") } @@ -378,6 +389,17 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { a.checkSdkVersions(ctx) } +// Ensures that use_embedded_native_libs are set for apk-in-apex +func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + apkInApex := !apexInfo.IsForPlatform() + hasJnis := len(a.appProperties.Jni_libs) > 0 + + if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) { + ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true") + } +} + // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it. // This check is enforced for "updatable" APKs (including APK-in-APEX). func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) { @@ -433,9 +455,9 @@ func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool { } func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool { - apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) || - !apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs + Bool(a.appProperties.Updatable) || + a.appProperties.AlwaysPackageNativeLibs } func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string { @@ -521,7 +543,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { } // Use non final ids if we are doing optimized shrinking and are using R8. - nonFinalIds := Bool(a.dexProperties.Optimize.Optimized_shrink_resources) && a.dexer.effectiveOptimizeEnabled() + nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled() a.aapt.buildActions(ctx, aaptBuildActionOptions{ sdkContext: android.SdkContext(a), @@ -552,7 +574,7 @@ func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) { staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles) a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, staticLibProguardFlagFiles...) - if !Bool(a.dexProperties.Optimize.Optimized_shrink_resources) { + if !(a.dexProperties.optimizedResourceShrinkingEnabled(ctx)) { // When using the optimized shrinking the R8 enqueuer will traverse the xml files that become // live for code references and (transitively) mark these as live. // In this case we explicitly don't wan't the aapt2 generated keep files (which would keep the now @@ -591,7 +613,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, a var packageResources = a.exportPackage if ctx.ModuleName() != "framework-res" { - if a.dexProperties.resourceShrinkingEnabled() { + if a.dexProperties.resourceShrinkingEnabled(ctx) { protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk") aapt2Convert(ctx, protoFile, packageResources, "proto") a.dexer.resourcesInput = android.OptionalPathForPath(protoFile) @@ -614,7 +636,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, a } a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars) - if a.dexProperties.resourceShrinkingEnabled() { + if a.dexProperties.resourceShrinkingEnabled(ctx) { binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk") aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary") packageResources = binaryResources @@ -829,7 +851,9 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { dexJarFile, packageResources := a.dexBuildActions(ctx) - jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis)) + // No need to check the SDK version of the JNI deps unless we embed them + checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis) + jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion) jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx) if ctx.Failed() { @@ -911,6 +935,22 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { installed := ctx.InstallFile(a.installDir, extra.Base(), extra) extraInstalledPaths = append(extraInstalledPaths, installed) } + // If we don't embed jni libs, make sure that those are installed along with the + // app, and also place symlinks to the installed paths under the lib/<arch> + // directory of the app installation directory. ex: + // /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so + if !a.embeddedJniLibs { + for _, jniLib := range jniLibs { + archStr := jniLib.target.Arch.ArchType.String() + symlinkDir := a.installDir.Join(ctx, "lib", archStr) + for _, installedLib := range jniLib.installPaths { + // install the symlink itself + symlinkName := installedLib.Base() + symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib) + ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget) + } + } + } ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...) } @@ -998,6 +1038,7 @@ func collectJniDeps(ctx android.ModuleContext, coverageFile: dep.CoverageOutputFile(), unstrippedFile: dep.UnstrippedOutputFile(), partition: dep.Partition(), + installPaths: dep.FilesToInstall(), }) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{otherName}) @@ -1243,6 +1284,11 @@ func AndroidAppFactory() android.Module { Manifest: proptools.StringPtr(":" + rroManifestName), Resource_dirs: a.aaptProperties.Resource_dirs, } + if !Bool(a.aaptProperties.Aapt_include_all_resources) { + for _, aaptConfig := range ctx.Config().ProductAAPTConfig() { + rroProperties.Aaptflags = append(rroProperties.Aaptflags, "-c", aaptConfig) + } + } ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties) }) diff --git a/java/app_import.go b/java/app_import.go index bb07c423a..dc8470da7 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -87,9 +87,6 @@ type AndroidAppImport struct { hideApexVariantFromMake bool provenanceMetaDataFile android.OutputPath - - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths } type AndroidAppImportProperties struct { @@ -416,7 +413,6 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk) a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath) } - android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles) providePrebuiltInfo(ctx, prebuiltInfoProps{ diff --git a/java/app_test.go b/java/app_test.go index a7c48a1ed..804949435 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -4322,52 +4322,6 @@ func TestPrivappAllowlistAndroidMk(t *testing.T) { ) } -func TestApexGlobalMinSdkVersionOverride(t *testing.T) { - result := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.ApexGlobalMinSdkVersionOverride = proptools.StringPtr("Tiramisu") - }), - ).RunTestWithBp(t, ` - android_app { - name: "com.android.bar", - srcs: ["a.java"], - sdk_version: "current", - } - android_app { - name: "com.android.foo", - srcs: ["a.java"], - sdk_version: "current", - min_sdk_version: "S", - updatable: true, - } - override_android_app { - name: "com.android.go.foo", - base: "com.android.foo", - } - `) - foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") - fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer") - bar := result.ModuleForTests("com.android.bar", "android_common").Rule("manifestFixer") - - android.AssertStringDoesContain(t, - "expected manifest fixer to set com.android.bar minSdkVersion to S", - bar.BuildParams.Args["args"], - "--minSdkVersion S", - ) - android.AssertStringDoesContain(t, - "com.android.foo: expected manifest fixer to set minSdkVersion to T", - foo.BuildParams.Args["args"], - "--minSdkVersion T", - ) - android.AssertStringDoesContain(t, - "com.android.go.foo: expected manifest fixer to set minSdkVersion to T", - fooOverride.BuildParams.Args["args"], - "--minSdkVersion T", - ) - -} - func TestAppFlagsPackages(t *testing.T) { ctx := testApp(t, ` android_app { @@ -4492,3 +4446,36 @@ func TestAppStem(t *testing.T) { t.Errorf("Module output does not contain expected apk %s", "foo-new.apk") } } + +func TestAppMinSdkVersionOverride(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + updatable: true, + } + override_android_app { + name: "com.android.go.foo", + base: "com.android.foo", + min_sdk_version: "33", + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer") + + android.AssertStringDoesContain(t, + "com.android.foo: expected manifest fixer to set minSdkVersion to T", + foo.BuildParams.Args["args"], + "--minSdkVersion 31", + ) + android.AssertStringDoesContain(t, + "com.android.go.foo: expected manifest fixer to set minSdkVersion to T", + fooOverride.BuildParams.Args["args"], + "--minSdkVersion 33", + ) + +} diff --git a/java/base.go b/java/base.go index 06c18ca43..e97d28de2 100644 --- a/java/base.go +++ b/java/base.go @@ -229,10 +229,6 @@ type DeviceProperties struct { // If the SDK kind is empty, it will be set to public. Sdk_version *string - // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. - // Defaults to sdk_version if not set. See sdk_version for possible values. - Min_sdk_version *string - // if not blank, set the maximum version of the sdk that the compiled artifacts will run against. // Defaults to empty string "". See sdk_version for possible values. Max_sdk_version *string @@ -312,6 +308,10 @@ type OverridableProperties struct { // Otherwise, both the overridden and the overriding modules will have the same output name, which // can cause the duplicate output error. Stem *string + + // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. + // Defaults to sdk_version if not set. See sdk_version for possible values. + Min_sdk_version *string } // Functionality common to Module and Import @@ -537,9 +537,6 @@ type Module struct { // or the module should override Stem(). stem string - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths - // Values that will be set in the JarJarProvider data for jarjar repackaging, // and merged with our dependencies' rules. jarjarRenameRules map[string]string @@ -741,8 +738,8 @@ func (j *Module) SystemModules() string { } func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { - if j.deviceProperties.Min_sdk_version != nil { - return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version) + if j.overridableProperties.Min_sdk_version != nil { + return android.ApiLevelFrom(ctx, *j.overridableProperties.Min_sdk_version) } return j.SdkVersion(ctx).ApiLevel } @@ -1734,8 +1731,6 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath ctx.CheckbuildFile(outputFile) - android.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles) - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ HeaderJars: android.PathsIfNonNil(j.headerJarFile), RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile), diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 82a34ca9d..4d3d794d8 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -590,13 +590,36 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) // So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS. // TODO(b/202896428): Add better way to handle this. _, unknown = android.RemoveFromList("android.car-module", unknown) - if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 { - ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + if isApexVariant(ctx) && len(unknown) > 0 { + if android.IsModulePrebuilt(ctx.Module()) { + // prebuilt bcpf. the validation of this will be done at the top-level apex + providerClasspathFragmentValidationInfoProvider(ctx, unknown) + } else if !disableSourceApexVariant(ctx) { + // source bcpf, and prebuilt apex are not selected. + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + } } } return jars } +var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]() + +type ClasspathFragmentValidationInfo struct { + ClasspathFragmentModuleName string + UnknownJars []string +} + +// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS +// The validation will be done in the ctx of the top-level _selected_ apex +func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) { + info := ClasspathFragmentValidationInfo{ + ClasspathFragmentModuleName: ctx.ModuleName(), + UnknownJars: unknown, + } + android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info) +} + // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput { diff --git a/java/dex.go b/java/dex.go index 6caaa7f48..8cfffaf1f 100644 --- a/java/dex.go +++ b/java/dex.go @@ -111,8 +111,12 @@ func (d *dexer) effectiveOptimizeEnabled() bool { return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault) } -func (d *DexProperties) resourceShrinkingEnabled() bool { - return BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources)) +func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool { + return !ctx.Config().Eng() && BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources)) +} + +func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool { + return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources) } var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 1acac1b40..4d6dbffe1 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -262,6 +262,20 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName s if !isApexSystemServerJar { return true } + ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + allApexInfos := []android.ApexInfo{} + if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok { + allApexInfos = allApexInfosProvider.ApexInfos + } + if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) { + // Apex system server jars are dexpreopted and installed on to the system image. + // Since we can have BigAndroid and Go variants of system server jar providing apexes, + // and these two variants can have different min_sdk_versions, hide one of the apex variants + // from make to prevent collisions. + // + // Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries. + ctx.Module().MakeUninstallable() + } } else { // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. if isApexSystemServerJar { @@ -502,7 +516,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJa // Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars // The javalib from the deapexed prebuilt will be copied to this location. // TODO (b/331665856): Implement a principled solution for this. - copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) + copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake() dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule( ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex) if err != nil { diff --git a/java/droidstubs.go b/java/droidstubs.go index 730be14b0..ca81343f0 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -106,9 +106,6 @@ type Droidstubs struct { everythingArtifacts stubsArtifacts exportableArtifacts stubsArtifacts - // Single aconfig "cache file" merged from this module and all dependencies. - mergedAconfigFiles map[string]android.Paths - exportableApiFile android.WritablePath exportableRemovedApiFile android.WritablePath } @@ -1342,7 +1339,6 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("nullabilityWarningsCheck", "nullability warnings check") } - android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles) } func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index 330013ee4..62297978c 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -198,13 +198,22 @@ func TestHiddenAPISingletonSdks(t *testing.T) { hiddenApiFixtureFactory, tc.preparer, prepareForTestWithDefaultPlatformBootclasspath, + // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi + // file creation. + FixtureConfigureBootJars("platform:foo"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild) variables.BuildFlags = map[string]string{ "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", } }), - ).RunTest(t) + ).RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + `) hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") diff --git a/java/java.go b/java/java.go index 0df96a3a5..e3f482450 100644 --- a/java/java.go +++ b/java/java.go @@ -366,14 +366,14 @@ type dependencyTag struct { toolchain bool static bool + + installable bool } -// installDependencyTag is a dependency tag that is annotated to cause the installed files of the -// dependency to be installed when the parent module is installed. -type installDependencyTag struct { - blueprint.BaseDependencyTag - android.InstallAlwaysNeededDependencyTag - name string +var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil) + +func (d dependencyTag) InstallDepNeeded() bool { + return d.installable } func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { @@ -405,7 +405,7 @@ func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDepe } func IsJniDepTag(depTag blueprint.DependencyTag) bool { - return depTag == jniLibTag + return depTag == jniLibTag || depTag == jniInstallTag } var ( @@ -434,8 +434,8 @@ var ( javaApiContributionTag = dependencyTag{name: "java-api-contribution"} depApiSrcsTag = dependencyTag{name: "dep-api-srcs"} aconfigDeclarationTag = dependencyTag{name: "aconfig-declaration"} - jniInstallTag = installDependencyTag{name: "jni install"} - binaryInstallTag = installDependencyTag{name: "binary install"} + jniInstallTag = dependencyTag{name: "jni install", runtimeLinked: true, installable: true} + binaryInstallTag = dependencyTag{name: "binary install", runtimeLinked: true, installable: true} usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false) usesLibOptTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true) usesLibCompat28OptTag = makeUsesLibraryDependencyTag(28, true) @@ -491,6 +491,7 @@ type jniLib struct { coverageFile android.OptionalPath unstrippedFile android.Path partition string + installPaths android.InstallPaths } func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) { @@ -908,7 +909,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Check min_sdk_version of the transitive dependencies if this module is created from // java_sdk_library. - if j.deviceProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil { + if j.overridableProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil { j.CheckDepsMinSdkVersion(ctx) } @@ -1096,7 +1097,7 @@ func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberCo // If the min_sdk_version was set then add the canonical representation of the API level to the // snapshot. - if j.deviceProperties.Min_sdk_version != nil { + if j.overridableProperties.Min_sdk_version != nil { canonical, err := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String()) if err != nil { ctx.ModuleErrorf("%s", err) diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index b3c9ce50f..8d4cf6823 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -294,6 +294,15 @@ func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule { + createEmptyHiddenApiFiles := func() { + paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV} + for _, path := range paths { + ctx.Build(pctx, android.BuildParams{ + Rule: android.Touch, + Output: path, + }) + } + } // Save the paths to the monolithic files for retrieval via OutputFiles(). b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags @@ -306,13 +315,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // optimization that can be used to reduce the incremental build time but as its name suggests it // can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath. if ctx.Config().DisableHiddenApiChecks() { - paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV} - for _, path := range paths { - ctx.Build(pctx, android.BuildParams{ - Rule: android.Touch, - Output: path, - }) - } + createEmptyHiddenApiFiles() return bootDexJarByModule } @@ -325,6 +328,13 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // the fragments will have already provided the flags that are needed. classesJars := monolithicInfo.ClassesJars + if len(classesJars) == 0 { + // This product does not include any monolithic jars. Monolithic hiddenapi flag generation is not required. + // However, generate an empty file so that the dist tags in f/b/boot/Android.bp can be resolved, and `m dist` works. + createEmptyHiddenApiFiles() + return bootDexJarByModule + } + // Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() diff --git a/java/sdk_library.go b/java/sdk_library.go index 677b32a5e..8c91288c3 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1504,6 +1504,12 @@ func IsXmlPermissionsFileDepTag(depTag blueprint.DependencyTag) bool { var implLibraryTag = sdkLibraryComponentTag{name: "impl-library"} +var _ android.InstallNeededDependencyTag = sdkLibraryComponentTag{} + +func (t sdkLibraryComponentTag) InstallDepNeeded() bool { + return t.name == "xml-permissions-file" || t.name == "impl-library" +} + // Add the dependencies on the child modules in the component deps mutator. func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { for _, apiScope := range module.getGeneratedApiScopes(ctx) { @@ -1650,6 +1656,14 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost + // Properties required for Library.AndroidMkEntries + module.logtagsSrcs = module.implLibraryModule.logtagsSrcs + module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled + module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile + module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary + module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip + module.linter.reports = module.implLibraryModule.linter.reports + if !module.Host() { module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile } @@ -1814,7 +1828,6 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) props := struct { Name *string Visibility []string - Instrument bool Libs []string Static_libs []string Apex_available []string @@ -1822,8 +1835,6 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Visibility: visibility, - // Set the instrument property to ensure it is instrumented when instrumentation is required. - Instrument: true, Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...), @@ -1842,6 +1853,7 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) &module.dexProperties, &module.dexpreoptProperties, &module.linter.properties, + &module.overridableProperties, &props, module.sdkComponentPropertiesForChildLibrary(), } diff --git a/python/binary.go b/python/binary.go index c84eeeedb..b935aba45 100644 --- a/python/binary.go +++ b/python/binary.go @@ -71,9 +71,6 @@ type PythonBinaryModule struct { installedDest android.Path androidMkSharedLibs []string - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths } var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil) @@ -106,7 +103,6 @@ func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleConte p.buildBinary(ctx) p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""), p.installSource.Base(), p.installSource) - android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles) } func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) { @@ -170,7 +166,6 @@ func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_MODULE_STEM", stem) entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...) entries.SetBool("LOCAL_CHECK_ELF_FILES", false) - android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles) }) return []android.AndroidMkEntries{entries} diff --git a/python/python.go b/python/python.go index e14fdf333..1ee533fa8 100644 --- a/python/python.go +++ b/python/python.go @@ -506,8 +506,8 @@ func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, p } for _, d := range expandedData { - if d.Ext() == pyExt || d.Ext() == protoExt { - ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String()) + if d.Ext() == pyExt { + ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String()) continue } runfilesPath := filepath.Join(pkgPath, d.Rel()) @@ -523,19 +523,19 @@ func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath s relativeRootMap := make(map[string]android.Paths) var protoSrcs android.Paths addPathMapping := func(path pathMapping) { - // handle proto sources separately - if path.src.Ext() == protoExt { - protoSrcs = append(protoSrcs, path.src) - } else { - relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel()) - relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src) - } + relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel()) + relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src) } // "srcs" or "data" properties may contain filegroups so it might happen that // the root directory for each source path is different. for _, path := range p.srcsPathMappings { - addPathMapping(path) + // handle proto sources separately + if path.src.Ext() == protoExt { + protoSrcs = append(protoSrcs, path.src) + } else { + addPathMapping(path) + } } for _, path := range p.dataPathMappings { addPathMapping(path) diff --git a/python/python_test.go b/python/python_test.go index c0b7295f9..6a6bd1d91 100644 --- a/python/python_test.go +++ b/python/python_test.go @@ -50,7 +50,7 @@ var ( " Second file: in module %s at path %q." noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!" badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!" - badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!" + badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!" bpFile = "Android.bp" data = []struct { diff --git a/python/test.go b/python/test.go index 2b939e7e4..85decf931 100644 --- a/python/test.go +++ b/python/test.go @@ -18,6 +18,7 @@ import ( "fmt" "android/soong/testing" + "github.com/google/blueprint/proptools" "android/soong/android" @@ -151,7 +152,6 @@ func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext // just use buildBinary() so that the binary is not installed into the location // it would be for regular binaries. p.PythonLibraryModule.GenerateAndroidBuildActions(ctx) - android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles) p.buildBinary(ctx) var configs []tradefed.Option @@ -227,7 +227,6 @@ func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries { } entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true)) - android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles) p.testProperties.Test_options.SetAndroidMkEntries(entries) }) diff --git a/rust/androidmk.go b/rust/androidmk.go index 021dd6067..8de6b6004 100644 --- a/rust/androidmk.go +++ b/rust/androidmk.go @@ -69,7 +69,6 @@ func (mod *Module) AndroidMkEntries() []android.AndroidMkEntries { } else if mod.InProduct() { entries.SetBool("LOCAL_IN_PRODUCT", true) } - android.SetAconfigFileMkEntries(mod.AndroidModuleBase(), entries, mod.mergedAconfigFiles) }, }, } diff --git a/rust/builder.go b/rust/builder.go index 4f45e33c1..1ce92f4ec 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -21,6 +21,7 @@ import ( "github.com/google/blueprint" "android/soong/android" + "android/soong/cc" "android/soong/rust/config" ) @@ -118,42 +119,129 @@ type buildOutput struct { func init() { pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") + cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib +} + +type transformProperties struct { + crateName string + targetTriple string + is64Bit bool + bootstrap bool + inRecovery bool + inRamdisk bool + inVendorRamdisk bool + cargoOutDir android.OptionalPath + synthetic bool + crateType string +} + +// Populates a standard transformProperties struct for Rust modules +func getTransformProperties(ctx ModuleContext, crateType string) transformProperties { + module := ctx.RustModule() + return transformProperties{ + crateName: module.CrateName(), + is64Bit: ctx.toolchain().Is64Bit(), + targetTriple: ctx.toolchain().RustTriple(), + bootstrap: module.Bootstrap(), + inRecovery: module.InRecovery(), + inRamdisk: module.InRamdisk(), + inVendorRamdisk: module.InVendorRamdisk(), + cargoOutDir: module.compiler.cargoOutDir(), + + // crateType indicates what type of crate to build + crateType: crateType, + + // synthetic indicates whether this is an actual Rust module or not + synthetic: false, + } } func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin")) } func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib")) +} + +func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep, + outputFile android.WritablePath) android.Path { + + var rustPathDeps PathDeps + var rustFlags Flags + + for _, rlibDep := range deps { + rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName}) + rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...) + } + + ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module) + toolchain := config.FindToolchain(ctx.Os(), ctx.Arch()) + t := transformProperties{ + // Crate name can be a predefined value as this is a staticlib and + // it does not need to be unique. The crate name is used for name + // mangling, but it is mixed with the metadata for that purpose, which we + // already set to the module name. + crateName: "generated_rust_staticlib", + is64Bit: toolchain.Is64Bit(), + targetTriple: toolchain.RustTriple(), + bootstrap: ccModule.Bootstrap(), + inRecovery: ccModule.InRecovery(), + inRamdisk: ccModule.InRamdisk(), + inVendorRamdisk: ccModule.InVendorRamdisk(), + + // crateType indicates what type of crate to build + crateType: "staticlib", + + // synthetic indicates whether this is an actual Rust module or not + synthetic: true, + } + + rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags) + rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags) + rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin") + + rustFlags.EmitXrefs = false + + return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile } func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib")) } func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } + + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib")) } func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } + + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib")) } func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro")) } func rustLibsToPaths(libs RustLibraries) android.Paths { @@ -185,18 +273,18 @@ func makeLibFlags(deps PathDeps) []string { return libFlags } -func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { +func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string { var envVars []string // libstd requires a specific environment variable to be set. This is // not officially documented and may be removed in the future. See // https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866. - if ctx.RustModule().CrateName() == "std" { - envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType]) + if crateName == "std" { + envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType]) } - if len(deps.SrcDeps) > 0 { - moduleGenDir := ctx.RustModule().compiler.cargoOutDir() + if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() { + moduleGenDir := cargoOutDir // We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this) // assumes that paths are relative to the source file. var outDirPrefix string @@ -215,13 +303,15 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx)) - if ctx.RustModule().compiler.cargoEnvCompat() { - if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok { + if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() { + // We only emulate cargo environment variables for 3p code, which is only ever built + // by defining a Rust module, so we only need to set these for true Rust modules. + if bin, ok := rustMod.compiler.(*binaryDecorator); ok { envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx)) } - envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName()) - envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName()) - pkgVersion := ctx.RustModule().compiler.cargoPkgVersion() + envVars = append(envVars, "CARGO_CRATE_NAME="+crateName) + envVars = append(envVars, "CARGO_PKG_NAME="+crateName) + pkgVersion := rustMod.compiler.cargoPkgVersion() if pkgVersion != "" { envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion) @@ -245,8 +335,8 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { return envVars } -func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, crateType string) buildOutput { +func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, + outputFile android.WritablePath, t transformProperties) buildOutput { var inputs android.Paths var implicits android.Paths @@ -256,23 +346,21 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl var earlyLinkFlags string output.outputFile = outputFile - crateName := ctx.RustModule().CrateName() - targetTriple := ctx.toolchain().RustTriple() - envVars := rustEnvVars(ctx, deps) + envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir) inputs = append(inputs, main) // Collect rustc flags rustcFlags = append(rustcFlags, flags.GlobalRustFlags...) rustcFlags = append(rustcFlags, flags.RustFlags...) - rustcFlags = append(rustcFlags, "--crate-type="+crateType) - if crateName != "" { - rustcFlags = append(rustcFlags, "--crate-name="+crateName) + rustcFlags = append(rustcFlags, "--crate-type="+t.crateType) + if t.crateName != "" { + rustcFlags = append(rustcFlags, "--crate-name="+t.crateName) } - if targetTriple != "" { - rustcFlags = append(rustcFlags, "--target="+targetTriple) - linkFlags = append(linkFlags, "-target "+targetTriple) + if t.targetTriple != "" { + rustcFlags = append(rustcFlags, "--target="+t.targetTriple) + linkFlags = append(linkFlags, "-target "+t.targetTriple) } // Suppress an implicit sysroot @@ -302,9 +390,9 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl linkFlags = append(linkFlags, flags.LinkFlags...) // Check if this module needs to use the bootstrap linker - if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() { + if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk { dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker" - if ctx.toolchain().Is64Bit() { + if t.is64Bit { dynamicLinker += "64" } linkFlags = append(linkFlags, dynamicLinker) @@ -326,49 +414,56 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl orderOnly = append(orderOnly, deps.SharedLibs...) - if len(deps.SrcDeps) > 0 { - moduleGenDir := ctx.RustModule().compiler.cargoOutDir() - var outputs android.WritablePaths - - for _, genSrc := range deps.SrcDeps { - if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { - ctx.PropertyErrorf("srcs", - "multiple source providers generate the same filename output: "+genSrc.Base()) + if !t.synthetic { + // Only worry about OUT_DIR for actual Rust modules. + // Libraries built from cc use generated source, and do not utilize OUT_DIR. + if len(deps.SrcDeps) > 0 { + var outputs android.WritablePaths + + for _, genSrc := range deps.SrcDeps { + if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { + ctx.PropertyErrorf("srcs", + "multiple source providers generate the same filename output: "+genSrc.Base()) + } + outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) } - outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) - } - ctx.Build(pctx, android.BuildParams{ - Rule: cp, - Description: "cp " + moduleGenDir.Path().Rel(), - Outputs: outputs, - Inputs: deps.SrcDeps, - Args: map[string]string{ - "outDir": moduleGenDir.String(), - }, - }) - implicits = append(implicits, outputs.Paths()...) + ctx.Build(pctx, android.BuildParams{ + Rule: cp, + Description: "cp " + t.cargoOutDir.Path().Rel(), + Outputs: outputs, + Inputs: deps.SrcDeps, + Args: map[string]string{ + "outDir": t.cargoOutDir.String(), + }, + }) + implicits = append(implicits, outputs.Paths()...) + } } - if flags.Clippy { - clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") - ctx.Build(pctx, android.BuildParams{ - Rule: clippyDriver, - Description: "clippy " + main.Rel(), - Output: clippyFile, - ImplicitOutputs: nil, - Inputs: inputs, - Implicits: implicits, - OrderOnly: orderOnly, - Args: map[string]string{ - "rustcFlags": strings.Join(rustcFlags, " "), - "libFlags": strings.Join(libFlags, " "), - "clippyFlags": strings.Join(flags.ClippyFlags, " "), - "envVars": strings.Join(envVars, " "), - }, - }) - // Declare the clippy build as an implicit dependency of the original crate. - implicits = append(implicits, clippyFile) + if !t.synthetic { + // Only worry about clippy for actual Rust modules. + // Libraries built from cc use generated source, and don't need to run clippy. + if flags.Clippy { + clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") + ctx.Build(pctx, android.BuildParams{ + Rule: clippyDriver, + Description: "clippy " + main.Rel(), + Output: clippyFile, + ImplicitOutputs: nil, + Inputs: inputs, + Implicits: implicits, + OrderOnly: orderOnly, + Args: map[string]string{ + "rustcFlags": strings.Join(rustcFlags, " "), + "libFlags": strings.Join(libFlags, " "), + "clippyFlags": strings.Join(flags.ClippyFlags, " "), + "envVars": strings.Join(envVars, " "), + }, + }) + // Declare the clippy build as an implicit dependency of the original crate. + implicits = append(implicits, clippyFile) + } } ctx.Build(pctx, android.BuildParams{ @@ -389,25 +484,28 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl }, }) - if flags.EmitXrefs { - kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") - ctx.Build(pctx, android.BuildParams{ - Rule: kytheExtract, - Description: "Xref Rust extractor " + main.Rel(), - Output: kytheFile, - Inputs: inputs, - Implicits: implicits, - OrderOnly: orderOnly, - Args: map[string]string{ - "rustcFlags": strings.Join(rustcFlags, " "), - "linkFlags": strings.Join(linkFlags, " "), - "libFlags": strings.Join(libFlags, " "), - "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), - "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), - "envVars": strings.Join(envVars, " "), - }, - }) - output.kytheFile = kytheFile + if !t.synthetic { + // Only emit xrefs for true Rust modules. + if flags.EmitXrefs { + kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") + ctx.Build(pctx, android.BuildParams{ + Rule: kytheExtract, + Description: "Xref Rust extractor " + main.Rel(), + Output: kytheFile, + Inputs: inputs, + Implicits: implicits, + OrderOnly: orderOnly, + Args: map[string]string{ + "rustcFlags": strings.Join(rustcFlags, " "), + "linkFlags": strings.Join(linkFlags, " "), + "libFlags": strings.Join(libFlags, " "), + "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), + "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), + "envVars": strings.Join(envVars, " "), + }, + }) + output.kytheFile = kytheFile + } } return output } @@ -457,7 +555,7 @@ func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, Args: map[string]string{ "rustdocFlags": strings.Join(rustdocFlags, " "), "outDir": docDir.String(), - "envVars": strings.Join(rustEnvVars(ctx, deps), " "), + "envVars": strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "), }, }) diff --git a/rust/builder_test.go b/rust/builder_test.go index 639f6d4d4..c093ac4df 100644 --- a/rust/builder_test.go +++ b/rust/builder_test.go @@ -46,6 +46,9 @@ func TestSourceProviderCollision(t *testing.T) { } func TestCompilationOutputFiles(t *testing.T) { + + // Note: Rustdoc output is produced for the PrimaryModule, so if the variant + // order changes, then it may be produced for a different variant. ctx := testRust(t, ` rust_library { name: "libfizz_buzz", @@ -126,6 +129,16 @@ func TestCompilationOutputFiles(t *testing.T) { }, }, { + testName: "rust_ffi rlib", + moduleName: "librust_ffi", + variant: "android_arm64_armv8-a_rlib_rlib-std", + expectedFiles: []string{ + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib", + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib.clippy", + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/meta_lic", + }, + }, + { testName: "rust_ffi shared", moduleName: "librust_ffi", variant: "android_arm64_armv8-a_shared", diff --git a/rust/compiler.go b/rust/compiler.go index 03fdf2b7a..efc3deef3 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -47,6 +47,7 @@ type compiler interface { edition() string features() []string rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath + Thinlto() bool // Output directory in which source-generated code from dependencies is // copied. This is equivalent to Cargo's OUT_DIR variable. @@ -231,6 +232,15 @@ type BaseCompilerProperties struct { // If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value. Cargo_pkg_version *string + + // Control whether LTO is used for the final (Rust) linkage. This does not impact + // cross-language LTO. + Lto struct { + // Whether thin LTO should be enabled. By default this is true. + // LTO provides such a large code size benefit for Rust, this should always + // be enabled for production builds unless there's a clear need to disable it. + Thin *bool `android:"arch_variant"` + } `android:"arch_variant"` } type baseCompiler struct { @@ -273,6 +283,11 @@ func (compiler *baseCompiler) Disabled() bool { return false } +// Thin LTO is enabled by default. +func (compiler *baseCompiler) Thinlto() bool { + return BoolDefault(compiler.Properties.Lto.Thin, true) +} + func (compiler *baseCompiler) SetDisabled() { panic("baseCompiler does not implement SetDisabled()") } @@ -322,9 +337,9 @@ func (compiler *baseCompiler) compilerProps() []interface{} { return []interface{}{&compiler.Properties} } -func (compiler *baseCompiler) cfgsToFlags() []string { +func cfgsToFlags(cfgs []string) []string { flags := []string{} - for _, cfg := range compiler.Properties.Cfgs { + for _, cfg := range cfgs { flags = append(flags, "--cfg '"+cfg+"'") } @@ -351,23 +366,61 @@ func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags return flags } -func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { - if ctx.RustModule().InVendorOrProduct() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk") - if ctx.RustModule().InVendor() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor") - } else if ctx.RustModule().InProduct() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product") +func CommonDefaultCfgFlags(flags Flags, vendor bool, product bool) Flags { + var cfgs []string + if vendor || product { + cfgs = append(cfgs, "android_vndk") + if vendor { + cfgs = append(cfgs, "android_vendor") + } else if product { + cfgs = append(cfgs, "android_product") } } - flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...) + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(cfgs)...) + return flags +} + +func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { + flags = CommonDefaultCfgFlags(flags, ctx.RustModule().InVendor(), ctx.RustModule().InProduct()) + + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(compiler.Properties.Cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(compiler.Properties.Cfgs)...) + + return flags +} + +func CommonDefaultFlags(ctx android.ModuleContext, toolchain config.Toolchain, flags Flags) Flags { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...) + flags.GlobalRustFlags = append(flags.GlobalRustFlags, toolchain.ToolchainRustFlags()) + flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, toolchain.ToolchainLinkFlags()) + flags.EmitXrefs = ctx.Config().EmitXrefRules() + + if ctx.Host() && !ctx.Windows() { + flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...) + } + + if ctx.Os() == android.Linux { + // Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match + // the default behavior of device builds. + flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...) + } else if ctx.Os() == android.Darwin { + // Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default + // behavior of device builds. + flags.LinkFlags = append(flags.LinkFlags, + "-lc", + "-ldl", + "-lpthread", + "-lm", + ) + } return flags } func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags { + flags = CommonDefaultFlags(ctx, ctx.toolchain(), flags) lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints) if err != nil { ctx.PropertyErrorf("lints", err.Error()) @@ -396,29 +449,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flag flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition()) flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition()) flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...) - flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...) - flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags()) - flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags()) - flags.EmitXrefs = ctx.Config().EmitXrefRules() - if ctx.Host() && !ctx.Windows() { - flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...) - } - - if ctx.Os() == android.Linux { - // Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match - // the default behavior of device builds. - flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...) - } else if ctx.Os() == android.Darwin { - // Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default - // behavior of device builds. - flags.LinkFlags = append(flags.LinkFlags, - "-lc", - "-ldl", - "-lpthread", - "-lm", - ) - } return flags } @@ -568,11 +599,11 @@ func (compiler *baseCompiler) installTestData(ctx ModuleContext, data []android. compiler.installDeps = append(compiler.installDeps, installedData...) } -func (compiler *baseCompiler) getStem(ctx ModuleContext) string { +func (compiler *baseCompiler) getStem(ctx android.ModuleContext) string { return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix) } -func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string { +func (compiler *baseCompiler) getStemWithoutSuffix(ctx android.BaseModuleContext) string { stem := ctx.ModuleName() if String(compiler.Properties.Stem) != "" { stem = String(compiler.Properties.Stem) diff --git a/rust/compiler_test.go b/rust/compiler_test.go index 89f4d1abf..4caa12b3e 100644 --- a/rust/compiler_test.go +++ b/rust/compiler_test.go @@ -63,6 +63,35 @@ func TestCfgsToFlags(t *testing.T) { } } +func TestLtoFlag(t *testing.T) { + ctx := testRust(t, ` + rust_library_host { + name: "libfoo", + srcs: ["foo.rs"], + crate_name: "foo", + lto: { + thin: false, + } + } + + rust_library_host { + name: "libfoo_lto", + srcs: ["foo.rs"], + crate_name: "foo", + } + `) + + libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") + libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc") + + if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") { + t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"]) + } + if !strings.Contains(libfooLto.Args["rustcFlags"], "-C lto=thin") { + t.Fatalf("libfoo expected to enable lto by default -- rustcFlags: %#v", libfooLto.Args["rustcFlags"]) + } +} + // Test that we reject multiple source files. func TestEnforceSingleSourceFile(t *testing.T) { diff --git a/rust/coverage.go b/rust/coverage.go index 91a78060d..e0e919c69 100644 --- a/rust/coverage.go +++ b/rust/coverage.go @@ -47,7 +47,7 @@ func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency. if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() { - ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins) + ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins) } } diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go index ee28c6d3a..0d4622a7e 100644 --- a/rust/fuzz_test.go +++ b/rust/fuzz_test.go @@ -120,13 +120,17 @@ func TestCCFuzzDepBundling(t *testing.T) { } cc_fuzz { name: "fuzz_static_libtest", + static_rlibs: ["libtest_fuzzing"], + } + cc_fuzz { + name: "fuzz_staticffi_libtest", static_libs: ["libtest_fuzzing"], } - `) fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) + fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String()) @@ -134,4 +138,7 @@ func TestCCFuzzDepBundling(t *testing.T) { if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String()) } + if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { + t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String()) + } } diff --git a/rust/image_test.go b/rust/image_test.go index ba94906c6..71e271c89 100644 --- a/rust/image_test.go +++ b/rust/image_test.go @@ -22,33 +22,45 @@ import ( "android/soong/cc" ) -// Test that cc modules can link against vendor_available rust_ffi_static libraries. +// Test that cc modules can link against vendor_available rust_ffi_rlib/rust_ffi_static libraries. func TestVendorLinkage(t *testing.T) { ctx := testRust(t, ` cc_binary { - name: "fizz_vendor", - static_libs: ["libfoo_vendor"], + name: "fizz_vendor_available", + static_libs: ["libfoo_vendor_static"], + static_rlibs: ["libfoo_vendor"], + vendor_available: true, + } + cc_binary { + name: "fizz_soc_specific", + static_rlibs: ["libfoo_vendor"], soc_specific: true, } - rust_ffi_static { + rust_ffi_rlib { name: "libfoo_vendor", crate_name: "foo", srcs: ["foo.rs"], vendor_available: true, } + rust_ffi_static { + name: "libfoo_vendor_static", + crate_name: "foo", + srcs: ["foo.rs"], + vendor_available: true, + } `) - vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor_arm64_armv8-a").Module().(*cc.Module) + vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module) - if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) { - t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs) + if !android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) { + t.Errorf("vendorBinary should have a dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs) } } // Test that variants which use the vndk emit the appropriate cfg flag. func TestImageCfgFlag(t *testing.T) { ctx := testRust(t, ` - rust_ffi_static { + rust_ffi_shared { name: "libfoo", crate_name: "foo", srcs: ["foo.rs"], @@ -57,7 +69,7 @@ func TestImageCfgFlag(t *testing.T) { } `) - vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_static").Rule("rustc") + vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc") if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"]) @@ -69,7 +81,7 @@ func TestImageCfgFlag(t *testing.T) { t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"]) } - product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_static").Rule("rustc") + product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc") if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"]) } @@ -80,7 +92,7 @@ func TestImageCfgFlag(t *testing.T) { t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"]) } - system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc") + system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc") if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"]) } @@ -93,27 +105,34 @@ func TestImageCfgFlag(t *testing.T) { } -// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries. +// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries. func TestVendorRamdiskLinkage(t *testing.T) { ctx := testRust(t, ` - cc_library_static { + cc_library_shared { name: "libcc_vendor_ramdisk", - static_libs: ["libfoo_vendor_ramdisk"], + static_rlibs: ["libfoo_vendor_ramdisk"], + static_libs: ["libfoo_static_vendor_ramdisk"], system_shared_libs: [], vendor_ramdisk_available: true, } - rust_ffi_static { + rust_ffi_rlib { name: "libfoo_vendor_ramdisk", crate_name: "foo", srcs: ["foo.rs"], vendor_ramdisk_available: true, } + rust_ffi_static { + name: "libfoo_static_vendor_ramdisk", + crate_name: "foo", + srcs: ["foo.rs"], + vendor_ramdisk_available: true, + } `) - vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module) + vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module) - if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) { - t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk") + if !android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) { + t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_static_vendor_ramdisk") } } diff --git a/rust/library.go b/rust/library.go index f58a54fcc..1eb0c5ea2 100644 --- a/rust/library.go +++ b/rust/library.go @@ -37,10 +37,15 @@ func init() { android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory) android.RegisterModuleType("rust_ffi", RustFFIFactory) android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory) - android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory) + android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory) android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory) android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory) - android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory) + android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory) + + // TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib + // Alias rust_ffi_static to the combined rust_ffi_rlib factory + android.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory) + android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory) } type VariantLibraryProperties struct { @@ -104,6 +109,8 @@ type libraryDecorator struct { includeDirs android.Paths sourceProvider SourceProvider + isFFI bool + // table-of-contents file for cdylib crates to optimize out relinking when possible tocFile android.OptionalPath } @@ -143,6 +150,8 @@ type libraryInterface interface { BuildOnlyShared() toc() android.OptionalPath + + isFFILibrary() bool } func (library *libraryDecorator) nativeCoverage() bool { @@ -250,7 +259,7 @@ func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) aut } func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage { - if library.static() || library.MutatedProperties.VariantIsStaticStd { + if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) { return RlibLinkage } else if library.baseCompiler.preferRlib() { return RlibLinkage @@ -270,8 +279,8 @@ func RustLibraryFactory() android.Module { return module.Init() } -// rust_ffi produces all FFI variants (rust_ffi_shared and -// rust_ffi_static). +// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and +// rust_ffi_rlib). func RustFFIFactory() android.Module { module, library := NewRustLibrary(android.HostAndDeviceSupported) library.BuildOnlyFFI() @@ -300,14 +309,6 @@ func RustFFISharedFactory() android.Module { return module.Init() } -// rust_ffi_static produces a static library (Rust crate type -// "staticlib"). -func RustFFIStaticFactory() android.Module { - module, library := NewRustLibrary(android.HostAndDeviceSupported) - library.BuildOnlyStatic() - return module.Init() -} - // rust_library_host produces all Rust variants for the host // (rust_library_dylib_host and rust_library_rlib_host). func RustLibraryHostFactory() android.Module { @@ -317,7 +318,7 @@ func RustLibraryHostFactory() android.Module { } // rust_ffi_host produces all FFI variants for the host -// (rust_ffi_static_host and rust_ffi_shared_host). +// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host). func RustFFIHostFactory() android.Module { module, library := NewRustLibrary(android.HostSupported) library.BuildOnlyFFI() @@ -340,14 +341,6 @@ func RustLibraryRlibHostFactory() android.Module { return module.Init() } -// rust_ffi_static_host produces a static library for the host (Rust -// crate type "staticlib"). -func RustFFIStaticHostFactory() android.Module { - module, library := NewRustLibrary(android.HostSupported) - library.BuildOnlyStatic() - return module.Init() -} - // rust_ffi_shared_host produces an shared library for the host (Rust // crate type "cdylib"). func RustFFISharedHostFactory() android.Module { @@ -356,11 +349,51 @@ func RustFFISharedHostFactory() android.Module { return module.Init() } +// rust_ffi_rlib_host produces an rlib for the host (Rust crate +// type "rlib"). +func RustFFIRlibHostFactory() android.Module { + module, library := NewRustLibrary(android.HostSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_rlib produces an rlib (Rust crate type "rlib"). +func RustFFIRlibFactory() android.Module { + module, library := NewRustLibrary(android.HostAndDeviceSupported) + library.BuildOnlyRlib() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_static produces a staticlib and an rlib variant +func RustFFIStaticRlibFactory() android.Module { + module, library := NewRustLibrary(android.HostAndDeviceSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_static_host produces a staticlib and an rlib variant for the host +func RustFFIStaticRlibHostFactory() android.Module { + module, library := NewRustLibrary(android.HostSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + func (library *libraryDecorator) BuildOnlyFFI() { library.MutatedProperties.BuildDylib = false - library.MutatedProperties.BuildRlib = false + // we build rlibs for later static ffi linkage. + library.MutatedProperties.BuildRlib = true library.MutatedProperties.BuildShared = true library.MutatedProperties.BuildStatic = true + + library.isFFI = true } func (library *libraryDecorator) BuildOnlyRust() { @@ -384,11 +417,21 @@ func (library *libraryDecorator) BuildOnlyRlib() { library.MutatedProperties.BuildStatic = false } +func (library *libraryDecorator) BuildOnlyRlibStatic() { + library.MutatedProperties.BuildDylib = false + library.MutatedProperties.BuildRlib = true + library.MutatedProperties.BuildShared = false + library.MutatedProperties.BuildStatic = true + library.isFFI = true +} + func (library *libraryDecorator) BuildOnlyStatic() { library.MutatedProperties.BuildRlib = false library.MutatedProperties.BuildDylib = false library.MutatedProperties.BuildShared = false library.MutatedProperties.BuildStatic = true + + library.isFFI = true } func (library *libraryDecorator) BuildOnlyShared() { @@ -396,6 +439,12 @@ func (library *libraryDecorator) BuildOnlyShared() { library.MutatedProperties.BuildDylib = false library.MutatedProperties.BuildStatic = false library.MutatedProperties.BuildShared = true + + library.isFFI = true +} + +func (library *libraryDecorator) isFFILibrary() bool { + return library.isFFI } func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) { @@ -446,8 +495,15 @@ func (library *libraryDecorator) sharedLibFilename(ctx ModuleContext) string { return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix() } +// Library cfg flags common to all variants +func CommonLibraryCfgFlags(ctx android.ModuleContext, flags Flags) Flags { + return flags +} + func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseCompiler.cfgFlags(ctx, flags) + flags = CommonLibraryCfgFlags(ctx, flags) + if library.dylib() { // We need to add a dependency on std in order to link crates as dylibs. // The hack to add this dependency is guarded by the following cfg so @@ -455,8 +511,15 @@ func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib") } - flags.RustFlags = append(flags.RustFlags, library.baseCompiler.cfgsToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, library.baseCompiler.cfgsToFlags()...) + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...) + + return flags +} + +// Common flags applied to all libraries irrespective of properties or variant should be included here +func CommonLibraryCompilerFlags(ctx android.ModuleContext, flags Flags) Flags { + flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) return flags } @@ -464,11 +527,13 @@ func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseCompiler.compilerFlags(ctx, flags) - flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) - if library.shared() || library.static() { + flags = CommonLibraryCompilerFlags(ctx, flags) + + if library.isFFI { library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...) library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...) } + if library.shared() { if ctx.Darwin() { flags.LinkFlags = append( @@ -494,6 +559,9 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...) } + // Ensure link dirs are not duplicated + deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs) + // Calculate output filename if library.rlib() { fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix() @@ -549,9 +617,10 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa library.flagExporter.exportLinkObjects(deps.linkObjects...) } - if library.static() || library.shared() { + // Since we have FFI rlibs, we need to collect their includes as well + if library.static() || library.shared() || library.rlib() { android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{ - IncludeDirs: library.includeDirs, + IncludeDirs: android.FirstUniquePaths(library.includeDirs), }) } @@ -666,6 +735,11 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { return } + // Don't produce rlib/dylib/source variants for shared or static variants + if library.shared() || library.static() { + return + } + var variants []string // The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib) // depend on this variant. It must be the first variant to be declared. @@ -705,6 +779,9 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { // The source variant does not produce any library. // Disable the compilation steps. v.(*Module).compiler.SetDisabled() + case "": + // if there's an empty variant, alias it so it is the default variant + mctx.AliasVariation("") } } @@ -729,20 +806,29 @@ func LibstdMutator(mctx android.BottomUpMutatorContext) { case libraryInterface: // Only create a variant if a library is actually being built. if library.rlib() && !library.sysroot() { - variants := []string{"rlib-std", "dylib-std"} - modules := mctx.CreateLocalVariations(variants...) - - rlib := modules[0].(*Module) - dylib := modules[1].(*Module) - rlib.compiler.(libraryInterface).setRlibStd() - dylib.compiler.(libraryInterface).setDylibStd() - if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { - // TODO(b/165791368) - // Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib - // variants are properly supported. - dylib.Disable() + // If this is a rust_ffi variant it only needs rlib-std + if library.isFFILibrary() { + variants := []string{"rlib-std"} + modules := mctx.CreateLocalVariations(variants...) + rlib := modules[0].(*Module) + rlib.compiler.(libraryInterface).setRlibStd() + rlib.Properties.RustSubName += RlibStdlibSuffix + } else { + variants := []string{"rlib-std", "dylib-std"} + modules := mctx.CreateLocalVariations(variants...) + + rlib := modules[0].(*Module) + dylib := modules[1].(*Module) + rlib.compiler.(libraryInterface).setRlibStd() + dylib.compiler.(libraryInterface).setDylibStd() + if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { + // TODO(b/165791368) + // Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib + // variants are properly supported. + dylib.Disable() + } + rlib.Properties.RustSubName += RlibStdlibSuffix } - rlib.Properties.RustSubName += RlibStdlibSuffix } } } diff --git a/rust/library_test.go b/rust/library_test.go index 7275b66c1..1133c28ed 100644 --- a/rust/library_test.go +++ b/rust/library_test.go @@ -37,9 +37,10 @@ func TestLibraryVariants(t *testing.T) { }`) // Test all variants are being built. + libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc") libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") - libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc") + libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc") rlibCrateType := "rlib" @@ -62,6 +63,11 @@ func TestLibraryVariants(t *testing.T) { t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"]) } + // Test crate type for FFI rlibs is correct + if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"]) + } + // Test crate type for C shared libraries is correct. if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) { t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"]) @@ -182,15 +188,20 @@ func TestSharedLibraryToc(t *testing.T) { func TestStaticLibraryLinkage(t *testing.T) { ctx := testRust(t, ` - rust_ffi_static { + rust_ffi { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", }`) - libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static") + libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std") + libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static") if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) { + t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v", + libfoo.Module().(*Module).Properties.AndroidMkDylibs) + } + if !android.InList("libstd", libfooStatic.Module().(*Module).Properties.AndroidMkRlibs) { t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v", libfoo.Module().(*Module).Properties.AndroidMkDylibs) } @@ -198,6 +209,12 @@ func TestStaticLibraryLinkage(t *testing.T) { func TestNativeDependencyOfRlib(t *testing.T) { ctx := testRust(t, ` + rust_ffi_rlib { + name: "libffi_rlib", + crate_name: "ffi_rlib", + rlibs: ["librust_rlib"], + srcs: ["foo.rs"], + } rust_ffi_static { name: "libffi_static", crate_name: "ffi_static", @@ -224,10 +241,12 @@ func TestNativeDependencyOfRlib(t *testing.T) { rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std") rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std") ffiStatic := ctx.ModuleForTests("libffi_static", "android_arm64_armv8-a_static") + ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std") modules := []android.TestingModule{ rustRlibRlibStd, rustRlibDylibStd, + ffiRlib, ffiStatic, } @@ -290,27 +309,28 @@ func TestAutoDeps(t *testing.T) { libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std") libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib") + libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std") libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static") libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared") - for _, static := range []android.TestingModule{libfooRlib, libfooStatic} { + for _, static := range []android.TestingModule{libfooRlib, libfooStatic, libfooFFIRlib} { if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("libbar not present as rlib dependency in static lib") + t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name()) } if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) { - t.Errorf("libbar present as dynamic dependency in static lib") + t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name()) } } for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} { if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) { - t.Errorf("libbar not present as dynamic dependency in dynamic lib") + t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name()) } if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("libbar present as rlib dependency in dynamic lib") + t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name()) } if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("librlib_only should be selected by rustlibs as an rlib.") + t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name()) } } } @@ -375,6 +395,7 @@ func TestLibstdLinkage(t *testing.T) { libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module) libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module) + libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module) // prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here. libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module) @@ -398,6 +419,12 @@ func TestLibstdLinkage(t *testing.T) { if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) { t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant") } + if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) { + t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib") + } + if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) { + t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant") + } if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) { t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib") } diff --git a/rust/proc_macro.go b/rust/proc_macro.go index b491449f1..1ff6637a6 100644 --- a/rust/proc_macro.go +++ b/rust/proc_macro.go @@ -76,6 +76,7 @@ func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, dep srcPath := crateRootPath(ctx, procMacro) ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile) procMacro.baseCompiler.unstrippedOutputFile = outputFile + return ret } diff --git a/rust/rust.go b/rust/rust.go index de049f77f..93853e534 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -158,6 +158,8 @@ type Module struct { sourceProvider SourceProvider subAndroidMkOnce map[SubAndroidMkProvider]bool + exportedLinkDirs []string + // Output file to be installed, may be stripped or unstripped. outputFile android.OptionalPath @@ -172,9 +174,6 @@ type Module struct { apexSdkVersion android.ApiLevel transitiveAndroidMkSharedLibs *android.DepSet[string] - - // Aconfig files for all transitive deps. Also exposed via TransitiveDeclarationsInfo - mergedAconfigFiles map[string]android.Paths } func (mod *Module) Header() bool { @@ -234,8 +233,8 @@ func (mod *Module) SelectedStl() string { func (mod *Module) NonCcVariants() bool { if mod.compiler != nil { - if _, ok := mod.compiler.(libraryInterface); ok { - return false + if library, ok := mod.compiler.(libraryInterface); ok { + return library.buildRlib() || library.buildDylib() } } panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName())) @@ -465,6 +464,11 @@ type PathDeps struct { linkDirs []string linkObjects []string + // exportedLinkDirs are exported linkDirs for direct rlib dependencies to + // cc_library_static dependants of rlibs. + // Track them separately from linkDirs so superfluous -L flags don't get emitted. + exportedLinkDirs []string + // Used by bindgen modules which call clang depClangFlags []string depIncludePaths android.Paths @@ -477,6 +481,9 @@ type PathDeps struct { // Paths to generated source files SrcDeps android.Paths srcProviderFiles android.Paths + + // Used by Generated Libraries + depExportedRlibs []cc.RustRlibDep } type RustLibraries []RustLibrary @@ -543,6 +550,10 @@ func (mod *Module) VndkVersion() string { return mod.Properties.VndkVersion } +func (mod *Module) ExportedCrateLinkDirs() []string { + return mod.exportedLinkDirs +} + func (mod *Module) PreventInstall() bool { return mod.Properties.PreventInstall } @@ -657,15 +668,6 @@ func (mod *Module) UnstrippedOutputFile() android.Path { return nil } -func (mod *Module) IncludeDirs() android.Paths { - if mod.compiler != nil { - if library, ok := mod.compiler.(*libraryDecorator); ok { - return library.includeDirs - } - } - panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName())) -} - func (mod *Module) SetStatic() { if mod.compiler != nil { if library, ok := mod.compiler.(libraryInterface); ok { @@ -914,6 +916,10 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } deps := mod.depsToPaths(ctx) + // Export linkDirs for CC rust generatedlibs + mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.exportedLinkDirs...) + mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.linkDirs...) + flags := Flags{ Toolchain: toolchain, } @@ -991,6 +997,9 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if ctx.Failed() { return } + // Export your own directory as a linkDir + mod.exportedLinkDirs = append(mod.exportedLinkDirs, linkPathFromFilePath(mod.OutputFile().Path())) + } ctx.Phony("rust", ctx.RustModule().OutputFile().Path()) @@ -998,8 +1007,6 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if mod.testModule { android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) } - - android.CollectDependencyAconfigFiles(ctx, &mod.mergedAconfigFiles) } func (mod *Module) deps(ctx DepsContext) Deps { @@ -1223,7 +1230,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { return } - if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() { + if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() { //Handle Rust Modules makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName) @@ -1249,9 +1256,16 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName) mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName)) + // rust_ffi rlibs may export include dirs, so collect those here. + exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider) + depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...) + depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path())) + case procMacroDepTag: directProcMacroDeps = append(directProcMacroDeps, rustDep) mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName) + // proc_macro link dirs need to be exported, so collect those here. + depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path())) case sourceDepTag: if _, ok := mod.sourceProvider.(*protobufDecorator); ok { @@ -1281,12 +1295,12 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep) } + exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider) //Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS if depTag != procMacroDepTag { - exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider) - depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...) depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...) depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...) + depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...) } if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag { @@ -1296,6 +1310,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { lib.exportLinkDirs(linkDir) } } + if depTag == sourceDepTag { if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() { if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok { @@ -1560,6 +1575,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { } rlibDepVariations := commonDepVariations + rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() { rlibDepVariations = append(rlibDepVariations, @@ -1575,6 +1591,8 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { // dylibs dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation}) + dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) + for _, lib := range deps.Dylibs { actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib) } @@ -1594,7 +1612,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { // otherwise select the rlib variant. autoDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}) - + autoDepVariations = append(autoDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) { actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib) @@ -1609,7 +1627,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { for _, lib := range deps.Rustlibs { srcProviderVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "source"}) + srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""}) + // Only add rustlib dependencies if they're source providers themselves. + // This is used to track which crate names need to be added to the source generated + // in the rust_protobuf mod.rs. if actx.OtherModuleDependencyVariantExists(srcProviderVariations, lib) { actx.AddVariationDependencies(srcProviderVariations, sourceDepTag, lib) } @@ -1621,7 +1643,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { if deps.Stdlibs != nil { if mod.compiler.stdLinkage(ctx) == RlibLinkage { for _, lib := range deps.Stdlibs { - actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...), + actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...), rlibDepTag, lib) } } else { diff --git a/rust/rust_test.go b/rust/rust_test.go index 6d083f69f..8b96df8b3 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -150,15 +150,11 @@ func TestDepsTracking(t *testing.T) { host_supported: true, name: "cc_stubs_dep", } - rust_ffi_host_static { + cc_library_host_static { name: "libstatic", - srcs: ["foo.rs"], - crate_name: "static", } - rust_ffi_host_static { + cc_library_host_static { name: "libwholestatic", - srcs: ["foo.rs"], - crate_name: "wholestatic", } rust_ffi_host_shared { name: "libshared", @@ -435,6 +431,105 @@ func TestRustAliases(t *testing.T) { } } +func TestRustRlibs(t *testing.T) { + ctx := testRust(t, ` + rust_ffi_rlib { + name: "libbar", + crate_name: "bar", + srcs: ["src/lib.rs"], + export_include_dirs: ["bar_includes"] + } + + rust_ffi_rlib { + name: "libfoo", + crate_name: "foo", + srcs: ["src/lib.rs"], + export_include_dirs: ["foo_includes"] + } + + cc_library_shared { + name: "libcc_shared", + srcs:["foo.c"], + static_rlibs: ["libbar"], + } + + cc_library_static { + name: "libcc_static", + srcs:["foo.c"], + static_rlibs: ["libfoo"], + } + + cc_binary { + name: "ccBin", + srcs:["foo.c"], + static_rlibs: ["libbar"], + static_libs: ["libcc_static"], + } + `) + + libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc") + libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc") + libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld") + libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc") + ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc") + ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld") + ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc") + + if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"]) + } + + // Make sure there's a rustc command, and it's producing a staticlib + if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", + "staticlib", libcc_shared_rustc.Args["rustcFlags"]) + } + + // Make sure the static lib is included in the ld command + if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") { + t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v", + "libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"]) + } + + // Make sure the static lib includes are in the cc command + if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ibar_includes", libcc_shared_cc.Args["cFlags"]) + } + + // Make sure there's a rustc command, and it's producing a staticlib + if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"]) + } + + // Make sure the static lib is included in the cc command + if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") { + t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v", + "ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"]) + } + + // Make sure the static lib includes are in the ld command + if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ibar_includes", ccbin_cc.Args) + } + + // Make sure that direct dependencies and indirect dependencies are + // propagating correctly to the generated rlib. + if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") { + t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) + } + if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") { + t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) + } + + // Test indirect includes propagation + if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ifoo_includes", ccbin_cc.Args) + } +} + func assertString(t *testing.T, got, expected string) { t.Helper() if got != expected { diff --git a/rust/testing.go b/rust/testing.go index 5837dccde..f31c59188 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -49,16 +49,28 @@ var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers( func GatherRequiredDepsForTest() string { bp := ` rust_prebuilt_library { - name: "libstd", - crate_name: "std", - rlib: { - srcs: ["libstd.rlib"], - }, - dylib: { - srcs: ["libstd.so"], - }, - host_supported: true, - sysroot: true, + name: "libstd", + crate_name: "std", + rlib: { + srcs: ["libstd/libstd.rlib"], + }, + dylib: { + srcs: ["libstd/libstd.so"], + }, + host_supported: true, + sysroot: true, + } + rust_prebuilt_library { + name: "libcore.sysroot", + crate_name: "core", + rlib: { + srcs: ["libcore/libcore.rlib"], + }, + dylib: { + srcs: ["libcore/libcore.so"], + }, + host_supported: true, + sysroot: true, } ////////////////////////////// // Device module requirements @@ -176,10 +188,12 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory) ctx.RegisterModuleType("rust_ffi", RustFFIFactory) ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory) - ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory) + ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory) + ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory) ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory) ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory) - ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory) + ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory) + ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory) ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory) ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory) ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory) diff --git a/scripts/Android.bp b/scripts/Android.bp index 790fbe0c1..80cd93579 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -296,3 +296,9 @@ python_binary_host { main: "buildinfo.py", srcs: ["buildinfo.py"], } + +python_binary_host { + name: "extra_install_zips_file_list", + main: "extra_install_zips_file_list.py", + srcs: ["extra_install_zips_file_list.py"], +} diff --git a/scripts/extra_install_zips_file_list.py b/scripts/extra_install_zips_file_list.py new file mode 100755 index 000000000..148d6ccd9 --- /dev/null +++ b/scripts/extra_install_zips_file_list.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys +import zipfile +from typing import List + +def list_files_in_zip(zipfile_path: str) -> List[str]: + with zipfile.ZipFile(zipfile_path, 'r') as zf: + return zf.namelist() + +def main(): + parser = argparse.ArgumentParser( + description='Lists paths to all files inside an EXTRA_INSTALL_ZIPS zip file relative to a partition staging directory. ' + 'This script is just a helper because its difficult to implement this logic in make code.' + ) + parser.add_argument('staging_dir', + help='Path to the partition staging directory') + parser.add_argument('extra_install_zips', nargs='*', + help='The value of EXTRA_INSTALL_ZIPS from make. ' + 'It should be a list of primary_file:extraction_dir:zip_file trios. ' + 'The primary file will be ignored by this script, you should ensure that ' + 'the list of trios given to this script is already filtered by relevant primary files.') + args = parser.parse_args() + + staging_dir = args.staging_dir.removesuffix('/') + '/' + + for zip_trio in args.extra_install_zips: + _, d, z = zip_trio.split(':') + d = d.removesuffix('/') + '/' + + if d.startswith(staging_dir): + d = os.path.relpath(d, staging_dir) + if d == '.': + d = '' + for f in list_files_in_zip(z): + print(os.path.join(d, f)) + + +if __name__ == "__main__": + main() diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index 6e2512262..7048a15b7 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -281,6 +281,19 @@ func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, co "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", } }), + // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi + // file creation. + java.FixtureConfigureBootJars("platform:foo"), + android.FixtureModifyMockFS(func(fs android.MockFS) { + fs["platform/Android.bp"] = []byte(` + java_library { + name: "foo", + srcs: ["Test.java"], + compile_dex: true, + } + `) + fs["platform/Test.java"] = nil + }), android.FixtureWithRootAndroidBp(sdk+` apex { diff --git a/ui/build/soong.go b/ui/build/soong.go index 79584c66f..9955b1fdc 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -15,7 +15,6 @@ package build import ( - "android/soong/ui/tracer" "fmt" "io/fs" "os" @@ -26,6 +25,8 @@ import ( "sync/atomic" "time" + "android/soong/ui/tracer" + "android/soong/bazel" "android/soong/ui/metrics" "android/soong/ui/metrics/metrics_proto" @@ -270,7 +271,13 @@ func bootstrapEpochCleanup(ctx Context, config Config) { } else if !exists { // The tree is out of date for the current epoch, delete files used by bootstrap // and force the primary builder to rerun. - os.Remove(config.SoongNinjaFile()) + soongNinjaFile := config.SoongNinjaFile() + os.Remove(soongNinjaFile) + for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) { + if ok, _ := fileExists(file); ok { + os.Remove(file) + } + } for _, globFile := range bootstrapGlobFileList(config) { os.Remove(globFile) } @@ -680,7 +687,13 @@ func runSoong(ctx Context, config Config) { loadSoongBuildMetrics(ctx, config, beforeSoongTimestamp) - distGzipFile(ctx, config, config.SoongNinjaFile(), "soong") + soongNinjaFile := config.SoongNinjaFile() + distGzipFile(ctx, config, soongNinjaFile, "soong") + for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) { + if ok, _ := fileExists(file); ok { + distGzipFile(ctx, config, file, "soong") + } + } distFile(ctx, config, config.SoongVarsFile(), "soong") if !config.SkipKati() { |