diff --git a/cc/stub_library.go b/cc/stub_library.go index 76d236ce6..81c8be783 100644 --- a/cc/stub_library.go +++ b/cc/stub_library.go @@ -30,7 +30,7 @@ type stubLibraries struct { } // Check if the module defines stub, or itself is stub -func isStubTarget(m *Module) bool { +func IsStubTarget(m *Module) bool { if m.IsStubs() || m.HasStubsVariants() { return true } @@ -61,7 +61,7 @@ func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) { // Visit all generated soong modules and store stub library file names. ctx.VisitAllModules(func(module android.Module) { if m, ok := module.(*Module); ok { - if isStubTarget(m) { + if IsStubTarget(m) { if name := getInstalledFileName(m); name != "" { s.stubLibraryMap[name] = true } diff --git a/filesystem/Android.bp b/filesystem/Android.bp index 3cdaa6418..38684d3b2 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -9,11 +9,13 @@ bootstrap_go_package { "blueprint", "soong", "soong-android", + "soong-linkerconfig", ], srcs: [ "bootimg.go", "filesystem.go", "logical_partition.go", + "system_image.go", "vbmeta.go", "testing.go", ], diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index cf9871701..b2caa51d2 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -31,6 +31,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_filesystem", filesystemFactory) + ctx.RegisterModuleType("android_system_image", systemImageFactory) } type filesystem struct { @@ -39,6 +40,9 @@ type filesystem struct { properties filesystemProperties + // Function that builds extra files under the root directory and returns the files + buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths + output android.OutputPath installDir android.InstallPath } @@ -87,10 +91,14 @@ type filesystemProperties struct { // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory. func filesystemFactory() android.Module { module := &filesystem{} + initFilesystemModule(module) + return module +} + +func initFilesystemModule(module *filesystem) { module.AddProperties(&module.properties) android.InitPackageModule(module) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module } var dependencyTag = struct { @@ -148,7 +156,7 @@ func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.InstallFile(f.installDir, f.installFileName(), f.output) } -// root zip will contain stuffs like dirs or symlinks. +// root zip will contain extra files/dirs that are not from the `deps` property. func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath { rootDir := android.PathForModuleGen(ctx, "root").OutputPath builder := android.NewRuleBuilder(pctx, ctx) @@ -182,15 +190,34 @@ func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String()) } - zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath + // create extra files if there's any + rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath + var extraFiles android.OutputPaths + if f.buildExtraFiles != nil { + extraFiles = f.buildExtraFiles(ctx, rootForExtraFiles) + for _, f := range extraFiles { + rel, _ := filepath.Rel(rootForExtraFiles.String(), f.String()) + if strings.HasPrefix(rel, "..") { + panic(fmt.Errorf("%q is not under %q\n", f, rootForExtraFiles)) + } + } + } - builder.Command(). - BuiltTool("soong_zip"). - FlagWithOutput("-o ", zipOut). + // Zip them all + zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath + zipCommand := builder.Command().BuiltTool("soong_zip") + zipCommand.FlagWithOutput("-o ", zipOut). FlagWithArg("-C ", rootDir.String()). Flag("-L 0"). // no compression because this will be unzipped soon FlagWithArg("-D ", rootDir.String()). Flag("-d") // include empty directories + if len(extraFiles) > 0 { + zipCommand.FlagWithArg("-C ", rootForExtraFiles.String()) + for _, f := range extraFiles { + zipCommand.FlagWithInput("-f ", f) + } + } + builder.Command().Text("rm -rf").Text(rootDir.String()) builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName())) diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 880b17741..e78fdffde 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -19,6 +19,7 @@ import ( "testing" "android/soong/android" + "android/soong/cc" ) func TestMain(m *testing.M) { @@ -27,6 +28,7 @@ func TestMain(m *testing.M) { var fixture = android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, + cc.PrepareForIntegrationTestWithCc, PrepareForTestWithFilesystemBuildComponents, ) @@ -40,3 +42,35 @@ func TestFileSystemDeps(t *testing.T) { // produces "myfilesystem.img" result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img") } + +func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) { + result := fixture.RunTestWithBp(t, ` + android_system_image { + name: "myfilesystem", + deps: [ + "libfoo", + "libbar", + ], + linker_config_src: "linker.config.json", + } + + cc_library { + name: "libfoo", + stubs: { + symbol_file: "libfoo.map.txt", + }, + } + + cc_library { + name: "libbar", + } + `) + + module := result.ModuleForTests("myfilesystem", "android_common") + output := module.Output("system/etc/linker.config.pb") + + android.AssertStringDoesContain(t, "linker.config.pb should have libfoo", + output.RuleParams.Command, "libfoo.so") + android.AssertStringDoesNotContain(t, "linker.config.pb should not have libbar", + output.RuleParams.Command, "libbar.so") +} diff --git a/filesystem/system_image.go b/filesystem/system_image.go new file mode 100644 index 000000000..a7c914395 --- /dev/null +++ b/filesystem/system_image.go @@ -0,0 +1,68 @@ +// Copyright (C) 2021 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" + "android/soong/linkerconfig" +) + +type systemImage struct { + filesystem + + properties systemImageProperties +} + +type systemImageProperties struct { + // Path to the input linker config json file. + Linker_config_src *string +} + +// android_system_image is a specialization of android_filesystem for the 'system' partition. +// Currently, the only difference is the inclusion of linker.config.pb file which specifies +// the provided and the required libraries to and from APEXes. +func systemImageFactory() android.Module { + module := &systemImage{} + module.AddProperties(&module.properties) + module.filesystem.buildExtraFiles = module.buildExtraFiles + initFilesystemModule(&module.filesystem) + return module +} + +func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths { + lc := s.buildLinkerConfigFile(ctx, root) + // Add more files if needed + return []android.OutputPath{lc} +} + +func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root android.OutputPath) android.OutputPath { + input := android.PathForModuleSrc(ctx, android.String(s.properties.Linker_config_src)) + output := root.Join(ctx, "system", "etc", "linker.config.pb") + var otherModules []android.Module + ctx.WalkDeps(func(child, parent android.Module) bool { + // Don't track direct dependencies that aren't not packaged + if parent == s { + if pi, ok := ctx.OtherModuleDependencyTag(child).(android.PackagingItem); !ok || !pi.IsPackagingItem() { + return false + } + } + otherModules = append(otherModules, child) + return true + }) + builder := android.NewRuleBuilder(pctx, ctx) + linkerconfig.BuildLinkerConfig(ctx, builder, input, otherModules, output) + builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String()) + return output +} diff --git a/linkerconfig/Android.bp b/linkerconfig/Android.bp index 9161f0eb5..76a632517 100644 --- a/linkerconfig/Android.bp +++ b/linkerconfig/Android.bp @@ -9,6 +9,7 @@ bootstrap_go_package { "blueprint", "soong", "soong-android", + "soong-cc", "soong-etc", ], srcs: [ diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go index 241cac691..8d0ad7cf2 100644 --- a/linkerconfig/linkerconfig.go +++ b/linkerconfig/linkerconfig.go @@ -15,11 +15,15 @@ package linkerconfig import ( - "android/soong/android" - "android/soong/etc" "fmt" + "sort" + "strings" "github.com/google/blueprint/proptools" + + "android/soong/android" + "android/soong/cc" + "android/soong/etc" ) var ( @@ -81,24 +85,59 @@ func (l *linkerConfig) OutputFiles(tag string) (android.Paths, error) { } func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { - inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src)) - l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath - l.installDirPath = android.PathForModuleInstall(ctx, "etc") - linkerConfigRule := android.NewRuleBuilder(pctx, ctx) - linkerConfigRule.Command(). - BuiltTool("conv_linker_config"). - Flag("proto"). - FlagWithInput("-s ", inputFile). - FlagWithOutput("-o ", l.outputFilePath) - linkerConfigRule.Build("conv_linker_config", - "Generate linker config protobuf "+l.outputFilePath.String()) + input := android.PathForModuleSrc(ctx, android.String(l.properties.Src)) + output := android.PathForModuleOut(ctx, "linker.config.pb").OutputPath + builder := android.NewRuleBuilder(pctx, ctx) + BuildLinkerConfig(ctx, builder, input, nil, output) + builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String()) + + l.outputFilePath = output + l.installDirPath = android.PathForModuleInstall(ctx, "etc") if !proptools.BoolDefault(l.properties.Installable, true) { l.SkipInstall() } ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath) } +func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder, + input android.Path, otherModules []android.Module, output android.OutputPath) { + + // First, convert the input json to protobuf format + interimOutput := android.PathForModuleOut(ctx, "temp.pb") + builder.Command(). + BuiltTool("conv_linker_config"). + Flag("proto"). + FlagWithInput("-s ", input). + FlagWithOutput("-o ", interimOutput) + + // Secondly, if there's provideLibs gathered from otherModules, append them + var provideLibs []string + for _, m := range otherModules { + if c, ok := m.(*cc.Module); ok && cc.IsStubTarget(c) { + for _, ps := range c.PackagingSpecs() { + provideLibs = append(provideLibs, ps.FileName()) + } + } + } + provideLibs = android.FirstUniqueStrings(provideLibs) + sort.Strings(provideLibs) + if len(provideLibs) > 0 { + builder.Command(). + BuiltTool("conv_linker_config"). + Flag("append"). + FlagWithInput("-s ", interimOutput). + FlagWithOutput("-o ", output). + FlagWithArg("--key ", "provideLibs"). + FlagWithArg("--value ", proptools.ShellEscapeIncludingSpaces(strings.Join(provideLibs, " "))) + } else { + // If nothing to add, just cp to the final output + builder.Command().Text("cp").Input(interimOutput).Output(output) + } + builder.Temporary(interimOutput) + builder.DeleteTemporaryFiles() +} + // linker_config generates protobuf file from json file. This protobuf file will be used from // linkerconfig while generating ld.config.txt. Format of this file can be found from // https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md