diff --git a/android/packaging.go b/android/packaging.go index da745ff19..df2533f80 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -49,6 +49,11 @@ func (p *PackagingSpec) FileName() string { return "" } +// Path relative to the root of the package +func (p *PackagingSpec) RelPathInPackage() string { + return p.relPathInPackage +} + type PackageModule interface { Module packagingBase() *PackagingBase @@ -87,9 +92,17 @@ type packagingMultilibProperties struct { Lib64 depsProperty `android:"arch_variant"` } +type packagingArchProperties struct { + Arm64 depsProperty + Arm depsProperty + X86_64 depsProperty + X86 depsProperty +} + type PackagingProperties struct { Deps []string `android:"arch_variant"` Multilib packagingMultilibProperties `android:"arch_variant"` + Arch packagingArchProperties } func InitPackageModule(p PackageModule) { @@ -116,6 +129,7 @@ func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []s } else if arch == Common { ret = append(ret, p.properties.Multilib.Common.Deps...) } + for i, t := range ctx.MultiTargets() { if t.Arch.ArchType == arch { ret = append(ret, p.properties.Deps...) @@ -124,6 +138,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...) + case Arm: + ret = append(ret, p.properties.Arch.Arm.Deps...) + case X86_64: + ret = append(ret, p.properties.Arch.X86_64.Deps...) + case X86: + ret = append(ret, p.properties.Arch.X86.Deps...) + } + } + return FirstUniqueStrings(ret) } diff --git a/android/packaging_test.go b/android/packaging_test.go index 7269bfb24..dc259ddad 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -61,13 +61,20 @@ type packageTestModule struct { entries []string } -func packageTestModuleFactory() Module { +func packageMultiTargetTestModuleFactory() Module { module := &packageTestModule{} InitPackageModule(module) InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon) return module } +func packageTestModuleFactory() Module { + module := &packageTestModule{} + InitPackageModule(module) + InitAndroidArchModule(module, DeviceSupported, MultilibBoth) + return module +} + func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) { m.AddDeps(ctx, installDepTag{}) } @@ -77,14 +84,22 @@ func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { m.entries = m.CopyDepsToZip(ctx, zipFile) } -func runPackagingTest(t *testing.T, bp string, expected []string) { +func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) { t.Helper() config := TestArchConfig(buildDir, nil, bp, nil) ctx := NewTestArchContext(config) ctx.RegisterModuleType("component", componentTestModuleFactory) - ctx.RegisterModuleType("package_module", packageTestModuleFactory) + + var archVariant string + if multitarget { + archVariant = "android_common" + ctx.RegisterModuleType("package_module", packageMultiTargetTestModuleFactory) + } else { + archVariant = "android_arm64_armv8-a" + ctx.RegisterModuleType("package_module", packageTestModuleFactory) + } ctx.Register() _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) @@ -92,7 +107,7 @@ func runPackagingTest(t *testing.T, bp string, expected []string) { _, errs = ctx.PrepareBuildActions(config) FailIfErrored(t, errs) - p := ctx.ModuleForTests("package", "android_common").Module().(*packageTestModule) + p := ctx.ModuleForTests("package", archVariant).Module().(*packageTestModule) actual := p.entries actual = SortedUniqueStrings(actual) expected = SortedUniqueStrings(expected) @@ -101,8 +116,9 @@ func runPackagingTest(t *testing.T, bp string, expected []string) { } } -func TestPackagingBase(t *testing.T) { - runPackagingTest(t, +func TestPackagingBaseMultiTarget(t *testing.T) { + multiTarget := true + runPackagingTest(t, multiTarget, ` component { name: "foo", @@ -114,7 +130,7 @@ func TestPackagingBase(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, + runPackagingTest(t, multiTarget, ` component { name: "foo", @@ -131,7 +147,7 @@ func TestPackagingBase(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, + runPackagingTest(t, multiTarget, ` component { name: "foo", @@ -149,7 +165,7 @@ func TestPackagingBase(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"}) - runPackagingTest(t, + runPackagingTest(t, multiTarget, ` component { name: "foo", @@ -172,7 +188,7 @@ func TestPackagingBase(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo"}) - runPackagingTest(t, + runPackagingTest(t, multiTarget, ` component { name: "foo", @@ -193,4 +209,136 @@ func TestPackagingBase(t *testing.T) { compile_multilib: "both", } `, []string{"lib32/foo", "lib64/foo", "lib64/bar"}) + + runPackagingTest(t, multiTarget, + ` + 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{"lib32/foo", "lib64/foo", "lib64/bar"}) +} + +func TestPackagingBaseSingleTarget(t *testing.T) { + multiTarget := false + runPackagingTest(t, multiTarget, + ` + component { + name: "foo", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo"}) + + runPackagingTest(t, multiTarget, + ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, multiTarget, + ` + component { + name: "foo", + } + + component { + name: "bar", + compile_multilib: "32", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + lib32: { + deps: ["bar"], + }, + }, + } + `, []string{"lib64/foo"}) + + runPackagingTest(t, multiTarget, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + lib64: { + deps: ["bar"], + }, + }, + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, multiTarget, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + component { + name: "baz", + } + + package_module { + name: "package", + deps: ["foo"], + arch: { + arm64: { + deps: ["bar"], + }, + x86_64: { + deps: ["baz"], + }, + }, + } + `, []string{"lib64/foo", "lib64/bar"}) } diff --git a/filesystem/Android.bp b/filesystem/Android.bp index 926df6e38..999424109 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -7,6 +7,7 @@ bootstrap_go_package { "soong-android", ], srcs: [ + "bootimg.go", "filesystem.go", ], testSrcs: [ diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go new file mode 100644 index 000000000..4bc182396 --- /dev/null +++ b/filesystem/bootimg.go @@ -0,0 +1,222 @@ +// 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 ( + "fmt" + "strconv" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + + "android/soong/android" +) + +func init() { + android.RegisterModuleType("bootimg", bootimgFactory) +} + +type bootimg struct { + android.ModuleBase + + properties bootimgProperties + + output android.OutputPath + installDir android.InstallPath +} + +type bootimgProperties struct { + // Path to the linux kernel prebuilt file + Kernel_prebuilt *string `android:"arch_variant,path"` + + // Filesystem module that is used as ramdisk + Ramdisk_module *string + + // Path to the device tree blob (DTB) prebuilt file to add to this boot image + Dtb_prebuilt *string `android:"arch_variant,path"` + + // Header version number. Must be set to one of the version numbers that are currently + // supported. Refer to + // https://source.android.com/devices/bootloader/boot-image-header + Header_version *string + + // Determines if this image is for the vendor_boot partition. Default is false. Refer to + // https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions + Vendor_boot *bool + + // Optional kernel commandline + Cmdline *string + + // When set to true, sign the image with avbtool. Default is false. + Use_avb *bool + + // Name of the partition stored in vbmeta desc. Defaults to the name of this module. + Partition_name *string + + // Path to the private key that avbtool will use to sign this filesystem image. + // TODO(jiyong): allow apex_key to be specified here + Avb_private_key *string `android:"path"` + + // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. + Avb_algorithm *string +} + +// bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb. +func bootimgFactory() android.Module { + module := &bootimg{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + +type bootimgDep struct { + blueprint.BaseDependencyTag + kind string +} + +var bootimgRamdiskDep = bootimgDep{kind: "ramdisk"} + +func (b *bootimg) DepsMutator(ctx android.BottomUpMutatorContext) { + ramdisk := proptools.String(b.properties.Ramdisk_module) + if ramdisk != "" { + ctx.AddDependency(ctx.Module(), bootimgRamdiskDep, ramdisk) + } +} + +func (b *bootimg) installFileName() string { + return b.BaseModuleName() + ".img" +} + +func (b *bootimg) partitionName() string { + return proptools.StringDefault(b.properties.Partition_name, b.BaseModuleName()) +} + +func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var unsignedOutput android.OutputPath + if proptools.Bool(b.properties.Vendor_boot) { + unsignedOutput = b.buildVendorBootImage(ctx) + } else { + // TODO(jiyong): fix this + ctx.PropertyErrorf("vendor_boot", "only vendor_boot:true is supported") + } + + if proptools.Bool(b.properties.Use_avb) { + b.output = b.signImage(ctx, unsignedOutput) + } else { + b.output = unsignedOutput + } + + b.installDir = android.PathForModuleInstall(ctx, "etc") + ctx.InstallFile(b.installDir, b.installFileName(), b.output) +} + +func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.OutputPath { + output := android.PathForModuleOut(ctx, "unsigned.img").OutputPath + builder := android.NewRuleBuilder(pctx, ctx) + cmd := builder.Command().BuiltTool("mkbootimg") + + kernel := android.OptionalPathForModuleSrc(ctx, b.properties.Kernel_prebuilt) + if kernel.Valid() { + ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") + return output + } + + dtbName := proptools.String(b.properties.Dtb_prebuilt) + if dtbName == "" { + ctx.PropertyErrorf("dtb_prebuilt", "must be set") + return output + } + dtb := android.PathForModuleSrc(ctx, dtbName) + cmd.FlagWithInput("--dtb ", dtb) + + cmdline := proptools.String(b.properties.Cmdline) + if cmdline != "" { + cmd.FlagWithArg("--vendor_cmdline ", "\""+cmdline+"\"") + } + + headerVersion := proptools.String(b.properties.Header_version) + if headerVersion == "" { + ctx.PropertyErrorf("header_version", "must be set") + return output + } + verNum, err := strconv.Atoi(headerVersion) + if err != nil { + ctx.PropertyErrorf("header_version", "%q is not a number", headerVersion) + return output + } + if verNum < 3 { + ctx.PropertyErrorf("header_version", "must be 3 or higher for vendor_boot") + return output + } + cmd.FlagWithArg("--header_version ", headerVersion) + + ramdiskName := proptools.String(b.properties.Ramdisk_module) + if ramdiskName == "" { + ctx.PropertyErrorf("ramdisk_module", "must be set") + return output + } + ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep) + if filesystem, ok := ramdisk.(*filesystem); ok { + cmd.FlagWithInput("--vendor_ramdisk ", filesystem.OutputPath()) + } else { + ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name()) + return output + } + + cmd.FlagWithOutput("--vendor_boot ", output) + + builder.Build("build_vendor_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName())) + return output +} + +func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath { + signedImage := android.PathForModuleOut(ctx, "signed.img").OutputPath + key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)) + + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("cp").Input(unsignedImage).Output(signedImage) + builder.Command(). + BuiltTool("avbtool"). + Flag("add_hash_footer"). + FlagWithArg("--partition_name ", b.partitionName()). + FlagWithInput("--key ", key). + FlagWithOutput("--image ", signedImage) + + builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName())) + + return signedImage +} + +var _ android.AndroidMkEntriesProvider = (*bootimg)(nil) + +// Implements android.AndroidMkEntriesProvider +func (b *bootimg) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(b.output), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName()) + }, + }, + }} +} + +var _ Filesystem = (*bootimg)(nil) + +func (b *bootimg) OutputPath() android.Path { + return b.output +} diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 654617c2d..5092ad06e 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -47,6 +47,10 @@ type filesystemProperties struct { // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. Avb_algorithm *string + + // Type of the filesystem. Currently, ext4 and compressed_cpio are supported. Default is + // ext4. + Type *string } // android_filesystem packages a set of modules and their transitive dependencies into a filesystem @@ -71,6 +75,27 @@ func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) { f.AddDeps(ctx, dependencyTag) } +type fsType int + +const ( + ext4Type fsType = iota + compressedCpioType + unknown +) + +func (f *filesystem) fsType(ctx android.ModuleContext) fsType { + typeStr := proptools.StringDefault(f.properties.Type, "ext4") + switch typeStr { + case "ext4": + return ext4Type + case "compressed_cpio": + return compressedCpioType + default: + ctx.PropertyErrorf("type", "%q not supported", typeStr) + return unknown + } +} + func (f *filesystem) installFileName() string { return f.BaseModuleName() + ".img" } @@ -78,6 +103,20 @@ func (f *filesystem) installFileName() string { var pctx = android.NewPackageContext("android/soong/filesystem") func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { + switch f.fsType(ctx) { + case ext4Type: + f.output = f.buildImageUsingBuildImage(ctx) + case compressedCpioType: + f.output = f.buildCompressedCpioImage(ctx) + default: + return + } + + f.installDir = android.PathForModuleInstall(ctx, "etc") + ctx.InstallFile(f.installDir, f.installFileName(), f.output) +} + +func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath { zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath f.CopyDepsToZip(ctx, zipFile) @@ -89,19 +128,18 @@ func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { Input(zipFile) propFile, toolDeps := f.buildPropFile(ctx) - f.output = android.PathForModuleOut(ctx, f.installFileName()).OutputPath + output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath builder.Command().BuiltTool("build_image"). Text(rootDir.String()). // input directory Input(propFile). Implicits(toolDeps). - Output(f.output). + Output(output). Text(rootDir.String()) // directory where to find fs_config_files|dirs // rootDir is not deleted. Might be useful for quick inspection. builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName())) - f.installDir = android.PathForModuleInstall(ctx, "etc") - ctx.InstallFile(f.installDir, f.installFileName(), f.output) + return output } func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) { @@ -120,8 +158,17 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. deps = append(deps, path) } - // TODO(jiyong): support more filesystem types other than ext4 - addStr("fs_type", "ext4") + // Type string that build_image.py accepts. + fsTypeStr := func(t fsType) string { + switch t { + // TODO(jiyong): add more types like f2fs, erofs, etc. + case ext4Type: + return "ext4" + } + panic(fmt.Errorf("unsupported fs type %v", t)) + } + + addStr("fs_type", fsTypeStr(f.fsType(ctx))) addStr("mount_point", "system") addStr("use_dynamic_partition_size", "true") addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")) @@ -154,6 +201,39 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. return propFile, deps } +func (f *filesystem) buildCompressedCpioImage(ctx android.ModuleContext) android.OutputPath { + if proptools.Bool(f.properties.Use_avb) { + ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+ + "Consider adding this to bootimg module and signing the entire boot image.") + } + + zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath + f.CopyDepsToZip(ctx, zipFile) + + rootDir := android.PathForModuleOut(ctx, "root").OutputPath + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command(). + BuiltTool("zipsync"). + FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear. + Input(zipFile) + + output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath + builder.Command(). + BuiltTool("mkbootfs"). + Text(rootDir.String()). // input directory + Text("|"). + BuiltTool("lz4"). + Flag("--favor-decSpeed"). // for faster boot + Flag("-12"). // maximum compression level + Flag("-l"). // legacy format for kernel + Text(">").Output(output) + + // rootDir is not deleted. Might be useful for quick inspection. + builder.Build("build_compressed_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName())) + + return output +} + var _ android.AndroidMkEntriesProvider = (*filesystem)(nil) // Implements android.AndroidMkEntriesProvider diff --git a/kernel/Android.bp b/kernel/Android.bp new file mode 100644 index 000000000..f8a48d956 --- /dev/null +++ b/kernel/Android.bp @@ -0,0 +1,18 @@ +bootstrap_go_package { + name: "soong-kernel", + pkgPath: "android/soong/kernel", + deps: [ + "blueprint", + "soong", + "soong-android", + "soong-cc", + "soong-cc-config", + ], + srcs: [ + "prebuilt_kernel_modules.go", + ], + testSrcs: [ + "prebuilt_kernel_modules_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go new file mode 100644 index 000000000..94e04cbc2 --- /dev/null +++ b/kernel/prebuilt_kernel_modules.go @@ -0,0 +1,166 @@ +// 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 kernel + +import ( + "fmt" + "path/filepath" + "strings" + + "android/soong/android" + _ "android/soong/cc/config" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +func init() { + android.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory) + pctx.Import("android/soong/cc/config") +} + +type prebuiltKernelModules struct { + android.ModuleBase + + properties prebuiltKernelModulesProperties + + installDir android.InstallPath +} + +type prebuiltKernelModulesProperties struct { + // List or filegroup of prebuilt kernel module files. Should have .ko suffix. + Srcs []string `android:"path,arch_variant"` + + // Kernel version that these modules are for. Kernel modules are installed to + // /lib/modules/ directory in the corresponding partition. Default is "". + Kernel_version *string +} + +// prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory. +// In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias +// using depmod and installs them as well. +func prebuiltKernelModulesFactory() android.Module { + module := &prebuiltKernelModules{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + +func (pkm *prebuiltKernelModules) KernelVersion() string { + return proptools.StringDefault(pkm.properties.Kernel_version, "") +} + +func (pkm *prebuiltKernelModules) DepsMutator(ctx android.BottomUpMutatorContext) { + // do nothing +} + +func (pkm *prebuiltKernelModules) GenerateAndroidBuildActions(ctx android.ModuleContext) { + modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs) + + depmodOut := runDepmod(ctx, modules) + strippedModules := stripDebugSymbols(ctx, modules) + + installDir := android.PathForModuleInstall(ctx, "lib", "module") + if pkm.KernelVersion() != "" { + installDir = installDir.Join(ctx, pkm.KernelVersion()) + } + + for _, m := range strippedModules { + ctx.InstallFile(installDir, filepath.Base(m.String()), m) + } + ctx.InstallFile(installDir, "modules.load", depmodOut.modulesLoad) + ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep) + ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep) + ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias) +} + +var ( + pctx = android.NewPackageContext("android/soong/kernel") + + stripRule = pctx.AndroidStaticRule("strip", + blueprint.RuleParams{ + Command: "$stripCmd -o $out --strip-debug $in", + CommandDeps: []string{"$stripCmd"}, + }, "stripCmd") +) + +func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.OutputPaths { + dir := android.PathForModuleOut(ctx, "stripped").OutputPath + var outputs android.OutputPaths + + for _, m := range modules { + stripped := dir.Join(ctx, filepath.Base(m.String())) + ctx.Build(pctx, android.BuildParams{ + Rule: stripRule, + Input: m, + Output: stripped, + Args: map[string]string{ + "stripCmd": "${config.ClangBin}/llvm-strip", + }, + }) + outputs = append(outputs, stripped) + } + + return outputs +} + +type depmodOutputs struct { + modulesLoad android.OutputPath + modulesDep android.OutputPath + modulesSoftdep android.OutputPath + modulesAlias android.OutputPath +} + +func runDepmod(ctx android.ModuleContext, modules android.Paths) depmodOutputs { + baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath + fakeVer := "0.0" // depmod demands this anyway + modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer) + + builder := android.NewRuleBuilder(pctx, ctx) + + // Copy the module files to a temporary dir + builder.Command().Text("rm").Flag("-rf").Text(modulesDir.String()) + builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String()) + for _, m := range modules { + builder.Command().Text("cp").Input(m).Text(modulesDir.String()) + } + + // Enumerate modules to load + modulesLoad := modulesDir.Join(ctx, "modules.load") + var basenames []string + for _, m := range modules { + basenames = append(basenames, filepath.Base(m.String())) + } + builder.Command(). + Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\""). + Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\""). + Text(">").Output(modulesLoad) + + // Run depmod to build modules.dep/softdep/alias files + modulesDep := modulesDir.Join(ctx, "modules.dep") + modulesSoftdep := modulesDir.Join(ctx, "modules.softdep") + modulesAlias := modulesDir.Join(ctx, "modules.alias") + builder.Command(). + BuiltTool("depmod"). + FlagWithArg("-b ", baseDir.String()). + Text(fakeVer). + ImplicitOutput(modulesDep). + ImplicitOutput(modulesSoftdep). + ImplicitOutput(modulesAlias) + + builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName())) + + return depmodOutputs{modulesLoad, modulesDep, modulesSoftdep, modulesAlias} +} diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go new file mode 100644 index 000000000..b49e1679c --- /dev/null +++ b/kernel/prebuilt_kernel_modules_test.go @@ -0,0 +1,129 @@ +// Copyright 2021 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 kernel + +import ( + "io/ioutil" + "os" + "reflect" + "strings" + "testing" + + "android/soong/android" + "android/soong/cc" +) + +func testKernelModules(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) { + bp = bp + ` + cc_binary_host { + name: "depmod", + srcs: ["depmod.cpp"], + stl: "none", + static_executable: true, + system_shared_libs: [], + } + ` + bp = bp + cc.GatherRequiredDepsForTest(android.Android) + + fs["depmod.cpp"] = nil + cc.GatherRequiredFilesForTest(fs) + + config := android.TestArchConfig(buildDir, nil, bp, fs) + + ctx := android.NewTestArchContext(config) + ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory) + ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + cc.RegisterRequiredBuildComponentsForTest(ctx) + + ctx.Register() + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + android.FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + android.FailIfErrored(t, errs) + + return ctx, config +} + +func ensureListContains(t *testing.T, result []string, expected string) { + t.Helper() + if !android.InList(expected, result) { + t.Errorf("%q is not found in %v", expected, result) + } +} + +func ensureContains(t *testing.T, result string, expected string) { + t.Helper() + if !strings.Contains(result, expected) { + t.Errorf("%q is not found in %q", expected, result) + } +} + +func TestKernelModulesFilelist(t *testing.T) { + ctx, _ := testKernelModules(t, ` + prebuilt_kernel_modules { + name: "foo", + srcs: ["*.ko"], + kernel_version: "5.10", + } + `, + map[string][]byte{ + "mod1.ko": nil, + "mod2.ko": nil, + }) + + expected := []string{ + "lib/module/5.10/mod1.ko", + "lib/module/5.10/mod2.ko", + "lib/module/5.10/modules.load", + "lib/module/5.10/modules.dep", + "lib/module/5.10/modules.softdep", + "lib/module/5.10/modules.alias", + } + + var actual []string + for _, ps := range ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().PackagingSpecs() { + actual = append(actual, ps.RelPathInPackage()) + } + actual = android.SortedUniqueStrings(actual) + expected = android.SortedUniqueStrings(expected) + if !reflect.DeepEqual(actual, expected) { + t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected) + } +} + +var buildDir string + +func setUp() { + var err error + buildDir, err = ioutil.TempDir("", "soong_kernel_test") + if err != nil { + panic(err) + } +} + +func tearDown() { + os.RemoveAll(buildDir) +} + +func TestMain(m *testing.M) { + run := func() int { + setUp() + defer tearDown() + + return m.Run() + } + + os.Exit(run()) +}