Support multiple sources for prebuilt_etc

Keep the Src attribute for compatibility. The new attribute (Srcs) is
mutually exclusive with Src.

Keep SourceFilePath and OutputFile for compatibility with other modules.
These can be removed in a follow up change.

Bug: 328313691
Test: presubmit
Test: m blueprint_tests
Test: prebuilts/build-tools/build-prebuilts.sh (on build-tools branch)
Change-Id: I5d5b2657715a7180a829c7ed0f501872d561b662
This commit is contained in:
Thiébaud Weksteen
2024-03-18 14:06:00 +11:00
parent 64342427b8
commit 00e8b31ee6
4 changed files with 168 additions and 83 deletions

View File

@@ -1648,10 +1648,9 @@ func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFil
return af
}
func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile {
dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
fileToCopy := prebuilt.OutputFile()
return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
return newApexFile(ctx, outputFile, outputFile.Base(), dirInApex, etc, prebuilt)
}
func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
@@ -2120,7 +2119,10 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
}
case prebuiltTag:
if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
filesToCopy, _ := prebuilt.OutputFiles("")
for _, etcFile := range filesToCopy {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
}
addAconfigFiles(vctx, ctx, child)
} else {
ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
@@ -2263,7 +2265,10 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
} else if java.IsXmlPermissionsFileDepTag(depTag) {
if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
filesToCopy, _ := prebuilt.OutputFiles("")
for _, etcFile := range filesToCopy {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
}
}
} else if rust.IsDylibDepTag(depTag) {
if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {

View File

@@ -71,10 +71,15 @@ var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterP
type prebuiltEtcProperties struct {
// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
// Mutually exclusive with srcs.
Src *string `android:"path,arch_variant"`
// Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax.
// Mutually exclusive with src. When used, filename_from_src is set to true.
Srcs []string `android:"path,arch_variant"`
// Optional name for the installed file. If unspecified, name of the module is used as the file
// name.
// name. Only available when using a single source (src).
Filename *string `android:"arch_variant"`
// When set to true, and filename property is not set, the name for the installed file
@@ -127,9 +132,9 @@ type PrebuiltEtcModule interface {
// Returns the sub install directory relative to BaseDir().
SubDir() string
// Returns an android.OutputPath to the intermeidate file, which is the renamed prebuilt source
// Returns an android.OutputPath to the intermediate file, which is the renamed prebuilt source
// file.
OutputFile() android.OutputPath
OutputFiles(tag string) (android.Paths, error)
}
type PrebuiltEtc struct {
@@ -142,8 +147,8 @@ type PrebuiltEtc struct {
properties prebuiltEtcProperties
subdirProperties prebuiltSubdirProperties
sourceFilePath android.Path
outputFilePath android.OutputPath
sourceFilePaths android.Paths
outputFilePaths android.OutputPaths
// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
installDirBase string
installDirBase64 string
@@ -246,6 +251,9 @@ func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation
}
func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
if len(p.properties.Srcs) > 0 {
panic(fmt.Errorf("SourceFilePath not available on multi-source prebuilt %q", p.Name()))
}
return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
}
@@ -260,7 +268,10 @@ func (p *PrebuiltEtc) SetAdditionalDependencies(paths android.Paths) {
}
func (p *PrebuiltEtc) OutputFile() android.OutputPath {
return p.outputFilePath
if len(p.properties.Srcs) > 0 {
panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name()))
}
return p.outputFilePaths[0]
}
var _ android.OutputFileProducer = (*PrebuiltEtc)(nil)
@@ -268,7 +279,7 @@ var _ android.OutputFileProducer = (*PrebuiltEtc)(nil)
func (p *PrebuiltEtc) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
return android.Paths{p.outputFilePath}, nil
return p.outputFilePaths.Paths(), nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -301,50 +312,7 @@ func (p *PrebuiltEtc) ExcludeFromRecoverySnapshot() bool {
return false
}
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
filename := proptools.String(p.properties.Filename)
filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
if p.properties.Src != nil {
p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
// Determine the output file basename.
// If Filename is set, use the name specified by the property.
// If Filename_from_src is set, use the source file name.
// Otherwise use the module name.
if filename != "" {
if filenameFromSrc {
ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
return
}
} else if filenameFromSrc {
filename = p.sourceFilePath.Base()
} else {
filename = ctx.ModuleName()
}
} else if ctx.Config().AllowMissingDependencies() {
// If no srcs was set and AllowMissingDependencies is enabled then
// mark the module as missing dependencies and set a fake source path
// and file name.
ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
p.sourceFilePath = android.PathForModuleSrc(ctx)
if filename == "" {
filename = ctx.ModuleName()
}
} else {
ctx.PropertyErrorf("src", "missing prebuilt source file")
return
}
if strings.Contains(filename, "/") {
ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
return
}
// Check that `sub_dir` and `relative_install_path` are not set at the same time.
if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
func (p *PrebuiltEtc) installBaseDir(ctx android.ModuleContext) string {
// If soc install dir was specified and SOC specific is set, set the installDirPath to the
// specified socInstallDirBase.
installBaseDir := p.installDirBase
@@ -357,47 +325,138 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if p.installAvoidMultilibConflict && !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
installBaseDir = filepath.Join(installBaseDir, ctx.Arch().ArchType.String())
}
return installBaseDir
}
p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
var installs []installProperties
// Call InstallFile even when uninstallable to make the module included in the package
ip := installProperties{
installable: p.Installable(),
filename: filename,
sourceFilePath: p.sourceFilePath,
symlinks: p.properties.Symlinks,
if p.properties.Src != nil && len(p.properties.Srcs) > 0 {
ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
}
// Check that `sub_dir` and `relative_install_path` are not set at the same time.
if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
p.installDirPath = android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
filename := proptools.String(p.properties.Filename)
filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
if p.properties.Src != nil {
p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{proptools.String(p.properties.Src)})
// If the source was not found, set a fake source path to
// support AllowMissingDependencies executions.
if len(p.sourceFilePaths) == 0 {
p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)}
}
// Determine the output file basename.
// If Filename is set, use the name specified by the property.
// If Filename_from_src is set, use the source file name.
// Otherwise use the module name.
if filename != "" {
if filenameFromSrc {
ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
return
}
} else if filenameFromSrc {
filename = p.sourceFilePaths[0].Base()
} else {
filename = ctx.ModuleName()
}
if strings.Contains(filename, "/") {
ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
return
}
p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
ip := installProperties{
filename: filename,
sourceFilePath: p.sourceFilePaths[0],
outputFilePath: p.outputFilePaths[0],
installDirPath: p.installDirPath,
symlinks: p.properties.Symlinks,
}
installs = append(installs, ip)
} else if len(p.properties.Srcs) > 0 {
if filename != "" {
ctx.PropertyErrorf("filename", "filename cannot be set when using srcs")
}
if len(p.properties.Symlinks) > 0 {
ctx.PropertyErrorf("symlinks", "symlinks cannot be set when using srcs")
}
if p.properties.Filename_from_src != nil {
ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
}
p.sourceFilePaths = android.PathsForModuleSrc(ctx, p.properties.Srcs)
for _, src := range p.sourceFilePaths {
filename := src.Base()
output := android.PathForModuleOut(ctx, filename).OutputPath
ip := installProperties{
filename: filename,
sourceFilePath: src,
outputFilePath: output,
installDirPath: p.installDirPath,
}
p.outputFilePaths = append(p.outputFilePaths, output)
installs = append(installs, ip)
}
} else if ctx.Config().AllowMissingDependencies() {
// If no srcs was set and AllowMissingDependencies is enabled then
// mark the module as missing dependencies and set a fake source path
// and file name.
ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)}
if filename == "" {
filename = ctx.ModuleName()
}
p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
ip := installProperties{
filename: filename,
sourceFilePath: p.sourceFilePaths[0],
outputFilePath: p.outputFilePaths[0],
installDirPath: p.installDirPath,
}
installs = append(installs, ip)
} else {
ctx.PropertyErrorf("src", "missing prebuilt source file")
return
}
// Call InstallFile even when uninstallable to make the module included in the package.
if !p.Installable() {
p.SkipInstall()
}
for _, ip := range installs {
ip.addInstallRules(ctx)
}
p.addInstallRules(ctx, ip)
android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
}
type installProperties struct {
installable bool
filename string
sourceFilePath android.Path
outputFilePath android.OutputPath
installDirPath android.InstallPath
symlinks []string
}
// utility function to add install rules to the build graph.
// Reduces code duplication between Soong and Mixed build analysis
func (p *PrebuiltEtc) addInstallRules(ctx android.ModuleContext, ip installProperties) {
if !ip.installable {
p.SkipInstall()
}
func (ip *installProperties) addInstallRules(ctx android.ModuleContext) {
// Copy the file from src to a location in out/ with the correct `filename`
// This ensures that outputFilePath has the correct name for others to
// use, as the source file may have a different name.
p.outputFilePath = android.PathForModuleOut(ctx, ip.filename).OutputPath
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Output: p.outputFilePath,
Output: ip.outputFilePath,
Input: ip.sourceFilePath,
})
installPath := ctx.InstallFile(p.installDirPath, ip.filename, p.outputFilePath)
installPath := ctx.InstallFile(ip.installDirPath, ip.filename, ip.outputFilePath)
for _, sl := range ip.symlinks {
ctx.InstallSymlink(p.installDirPath, sl, installPath)
ctx.InstallSymlink(ip.installDirPath, sl, installPath)
}
}
@@ -421,15 +480,15 @@ func (p *PrebuiltEtc) AndroidMkEntries() []android.AndroidMkEntries {
class = "ETC"
}
return []android.AndroidMkEntries{android.AndroidMkEntries{
return []android.AndroidMkEntries{{
Class: class,
SubName: nameSuffix,
OutputFile: android.OptionalPathForPath(p.outputFilePath),
OutputFile: android.OptionalPathForPath(p.outputFilePaths[0]),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePaths[0].Base())
if len(p.properties.Symlinks) > 0 {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
}
@@ -700,7 +759,11 @@ func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.Singleto
targetArch := "arch-" + m.Target().Arch.ArchType.String()
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
outputs, _ := m.OutputFiles("")
for _, output := range outputs {
cp := copyFile(ctx, output, snapshotLibOut, s.Fake)
snapshotOutputs = append(snapshotOutputs, cp)
}
prop := snapshot.SnapshotJsonFlags{}
propOut := snapshotLibOut + ".json"

View File

@@ -96,7 +96,7 @@ func TestPrebuiltEtcOutputPath(t *testing.T) {
`)
p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePath.Base())
android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePaths[0].Base())
}
func TestPrebuiltEtcGlob(t *testing.T) {
@@ -113,10 +113,24 @@ func TestPrebuiltEtcGlob(t *testing.T) {
`)
p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePath.Base())
android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePaths[0].Base())
p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePath.Base())
android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePaths[0].Base())
}
func TestPrebuiltEtcMultipleSrcs(t *testing.T) {
result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo",
srcs: ["*.conf"],
}
`)
p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertStringEquals(t, "output file path", "bar.conf", p.outputFilePaths[0].Base())
android.AssertStringEquals(t, "output file path", "baz.conf", p.outputFilePaths[1].Base())
android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[2].Base())
}
func TestPrebuiltEtcAndroidMk(t *testing.T) {

View File

@@ -30,6 +30,7 @@ import (
"android/soong/android"
"android/soong/dexpreopt"
"android/soong/etc"
)
const (
@@ -3163,10 +3164,12 @@ func (module *sdkLibraryXml) SubDir() string {
}
// from android.PrebuiltEtcModule
func (module *sdkLibraryXml) OutputFile() android.OutputPath {
return module.outputFilePath
func (module *sdkLibraryXml) OutputFiles(tag string) (android.Paths, error) {
return android.OutputPaths{module.outputFilePath}.Paths(), nil
}
var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
// from android.ApexModule
func (module *sdkLibraryXml) AvailableFor(what string) bool {
return true