diff --git a/android/packaging.go b/android/packaging.go index 7a14479c2..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 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()) +}