From ab9a31c9bc61b0dac74f5573532532983a3a3f44 Mon Sep 17 00:00:00 2001 From: Kiyoung Kim Date: Fri, 23 Jul 2021 15:47:56 +0900 Subject: [PATCH] Create new module type 'snapshot_etc' It requires new module type to generate 'prebuilt_etc' modules in the snapshot, because of name conflict with existing modules. This change defines new module type 'snapshot_etc' as prebuilt of 'prebuilt_etc', so it can replace original module on certain conditions. Bug: 192430376 Test: Tested with snapshot_etc of ld.config.recovery.txt module Change-Id: Ia4d27ce2077a10591597d2614c7c29c23a245149 --- etc/Android.bp | 2 + etc/snapshot_etc.go | 186 +++++++++++++++++++++++++++++++++++++++ etc/snapshot_etc_test.go | 185 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 etc/snapshot_etc.go create mode 100644 etc/snapshot_etc_test.go diff --git a/etc/Android.bp b/etc/Android.bp index 06a2fa15d..c67023679 100644 --- a/etc/Android.bp +++ b/etc/Android.bp @@ -13,9 +13,11 @@ bootstrap_go_package { ], srcs: [ "prebuilt_etc.go", + "snapshot_etc.go", ], testSrcs: [ "prebuilt_etc_test.go", + "snapshot_etc_test.go", ], pluginFor: ["soong_build"], } diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go new file mode 100644 index 000000000..9a25d5a72 --- /dev/null +++ b/etc/snapshot_etc.go @@ -0,0 +1,186 @@ +// Copyright 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 etc + +// This file implements snapshot module of 'prebuilt_etc' type +// 'snapshot_etc' module defines android.PrebuiltInterface so it can be handled +// as prebuilt of 'prebuilt_etc' type. +// Properties of 'snapshot_etc' follows properties from snapshotJsonFlags type + +import ( + "android/soong/android" + "fmt" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +func RegisterSnapshotEtcModule(ctx android.RegistrationContext) { + ctx.RegisterModuleType("snapshot_etc", SnapshotEtcFactory) +} + +func init() { + RegisterSnapshotEtcModule(android.InitRegistrationContext) +} + +// snapshot_etc is a prebuilt module type to be installed under etc which is auto-generated by +// development/vendor_snapshot/update.py. This module will override prebuilt_etc module with same +// name when 'prefer' property is true. +func SnapshotEtcFactory() android.Module { + module := &SnapshotEtc{} + module.AddProperties(&module.properties) + + var srcsSupplier = func(_ android.BaseModuleContext, prebuilt android.Module) []string { + s, ok := prebuilt.(*SnapshotEtc) + if !ok || s.properties.Src == nil { + return []string{} + } + + return []string{*s.properties.Src} + } + + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "src") + return module +} + +type snapshotEtcProperties struct { + Src *string `android:"path,arch_variant"` // Source of snapshot_etc file + Filename *string `android:"arch_variant"` // Target file name when it differs from module name + Relative_install_path *string `android:"arch_variant"` // Relative install path when it should be installed subdirectory of etc +} + +type SnapshotEtc struct { + android.ModuleBase + prebuilt android.Prebuilt + properties snapshotEtcProperties + + outputFilePath android.OutputPath + installDirPath android.InstallPath +} + +func (s *SnapshotEtc) Prebuilt() *android.Prebuilt { + return &s.prebuilt +} + +func (s *SnapshotEtc) Name() string { + return s.prebuilt.Name(s.BaseModuleName()) +} + +func (s *SnapshotEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if s.properties.Src == nil { + ctx.PropertyErrorf("src", "missing prebuilt source file") + return + } + + sourceFilePath := s.prebuilt.SingleSourcePath(ctx) + + // Determine the output file basename. + // If Filename is set, use the name specified by the property. + // Otherwise use the module name. + filename := proptools.String(s.properties.Filename) + if filename == "" { + filename = ctx.ModuleName() + } + + s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath + + if strings.Contains(filename, "/") { + ctx.PropertyErrorf("filename", "filename cannot contain separator '/'") + return + } + + subDir := "" + if s.properties.Relative_install_path != nil { + subDir = *s.properties.Relative_install_path + } + + s.installDirPath = android.PathForModuleInstall(ctx, "etc", subDir) + + // This ensures that outputFilePath has the correct name for others to + // use, as the source file may have a different name. + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: sourceFilePath, + Output: s.outputFilePath, + Description: "Install snapshot etc module " + s.BaseModuleName(), + }) + + ctx.InstallFile(s.installDirPath, s.outputFilePath.Base(), sourceFilePath) +} + +func (p *SnapshotEtc) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(p.outputFilePath), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_TAGS", "optional") + entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base()) + }, + }, + }} +} + +type snapshotEtcDependencyTag struct { + blueprint.DependencyTag +} + +var tag = snapshotEtcDependencyTag{} + +func (s *SnapshotEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool { + return !s.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk() && + !s.ModuleBase.InstallInVendorRamdisk() && !s.ModuleBase.InstallInDebugRamdisk() +} + +func (p *SnapshotEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return p.ModuleBase.InstallInRamdisk() +} + +func (p *SnapshotEtc) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return p.ModuleBase.InstallInVendorRamdisk() +} + +func (p *SnapshotEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return p.ModuleBase.InstallInDebugRamdisk() +} + +func (p *SnapshotEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { + return p.ModuleBase.InstallInRecovery() +} + +func (p *SnapshotEtc) ExtraImageVariations(ctx android.BaseModuleContext) []string { + return nil +} + +func (p *SnapshotEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { +} + +func (p *SnapshotEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {} + +func (p *SnapshotEtc) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + return android.Paths{p.outputFilePath}, nil + default: + return nil, fmt.Errorf("unsupported module reference tag %q", tag) + } + +} + +var _ android.PrebuiltInterface = (*SnapshotEtc)(nil) +var _ android.ImageInterface = (*SnapshotEtc)(nil) +var _ android.OutputFileProducer = (*SnapshotEtc)(nil) diff --git a/etc/snapshot_etc_test.go b/etc/snapshot_etc_test.go new file mode 100644 index 000000000..b9d55047c --- /dev/null +++ b/etc/snapshot_etc_test.go @@ -0,0 +1,185 @@ +// Copyright 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 etc + +import ( + "android/soong/android" + "testing" + + "github.com/google/blueprint" +) + +var registerSourceModule = func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("source", newSourceModule) +} + +type sourceModuleProperties struct { + Deps []string `android:"path,arch_variant"` +} + +type sourceModule struct { + android.ModuleBase + android.OverridableModuleBase + + properties sourceModuleProperties + dependsOnSourceModule, dependsOnPrebuiltModule bool + deps android.Paths + src android.Path +} + +func newSourceModule() android.Module { + m := &sourceModule{} + m.AddProperties(&m.properties) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst) + android.InitOverridableModule(m, nil) + return m +} + +func (s *sourceModule) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) { + // s.properties.Deps are annotated with android:path, so they are + // automatically added to the dependency by pathDeps mutator +} + +func (s *sourceModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + s.deps = android.PathsForModuleSrc(ctx, s.properties.Deps) + s.src = android.PathForModuleSrc(ctx, "source_file") +} + +func (s *sourceModule) Srcs() android.Paths { + return android.Paths{s.src} +} + +var prepareForSnapshotEtcTest = android.GroupFixturePreparers( + android.PrepareForTestWithArchMutator, + android.PrepareForTestWithPrebuilts, + PrepareForTestWithPrebuiltEtc, + android.FixtureRegisterWithContext(RegisterSnapshotEtcModule), + android.FixtureRegisterWithContext(registerSourceModule), + android.FixtureMergeMockFs(android.MockFS{ + "foo.conf": nil, + "bar.conf": nil, + }), +) + +func TestSnapshotWithFilename(t *testing.T) { + var androidBp = ` + snapshot_etc { + name: "etc_module", + src: "foo.conf", + filename: "bar.conf", + } + ` + + result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp) + for _, variant := range result.ModuleVariantsForTests("etc_module") { + module := result.ModuleForTests("etc_module", variant) + s, ok := module.Module().(*SnapshotEtc) + if !ok { + t.Errorf("Expected snapshot_etc module type") + } + if s.outputFilePath.Base() != "bar.conf" { + t.Errorf("Output file path does not match with specified filename") + } + } +} + +func TestSnapshotEtcWithOrigin(t *testing.T) { + var androidBp = ` + prebuilt_etc { + name: "etc_module", + src: "foo.conf", + } + + snapshot_etc { + name: "etc_module", + src: "bar.conf", + } + + source { + name: "source", + deps: [":etc_module"], + } + ` + + result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp) + + for _, variant := range result.ModuleVariantsForTests("source") { + source := result.ModuleForTests("source", variant) + + result.VisitDirectDeps(source.Module(), func(m blueprint.Module) { + if _, ok := m.(*PrebuiltEtc); !ok { + t.Errorf("Original prebuilt_etc module expected.") + } + }) + } +} + +func TestSnapshotEtcWithOriginAndPrefer(t *testing.T) { + var androidBp = ` + prebuilt_etc { + name: "etc_module", + src: "foo.conf", + } + + snapshot_etc { + name: "etc_module", + src: "bar.conf", + prefer: true, + } + + source { + name: "source", + deps: [":etc_module"], + } + ` + + result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp) + + for _, variant := range result.ModuleVariantsForTests("source") { + source := result.ModuleForTests("source", variant) + + result.VisitDirectDeps(source.Module(), func(m blueprint.Module) { + if _, ok := m.(*SnapshotEtc); !ok { + t.Errorf("Preferred snapshot_etc module expected.") + } + }) + } +} + +func TestSnapshotEtcWithoutOrigin(t *testing.T) { + var androidBp = ` + snapshot_etc { + name: "etc_module", + src: "bar.conf", + } + + source { + name: "source", + deps: [":etc_module"], + } + ` + + result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp) + + for _, variant := range result.ModuleVariantsForTests("source") { + source := result.ModuleForTests("source", variant) + + result.VisitDirectDeps(source.Module(), func(m blueprint.Module) { + if _, ok := m.(*SnapshotEtc); !ok { + t.Errorf("Only source snapshot_etc module expected.") + } + }) + } +}