If any of the other modules relative dir matches (eg lib/) it
would hit a neverallow as installing in the root directory
is allowlisted.
Test: m --no-skip-soong-tests
Test: m on tegra device with
PRODUCT_COPY_FILES += \
tegra21x_xusb_firmware:recovery/root/tegra21x_xusb_firmware \
Change-Id: I157826703974544614f0049b8fa157358e7b17a7
435 lines
17 KiB
Go
435 lines
17 KiB
Go
// Copyright (C) 2024 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 fsgen
|
|
|
|
import (
|
|
"android/soong/android"
|
|
"android/soong/etc"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/google/blueprint/proptools"
|
|
)
|
|
|
|
type srcBaseFileInstallBaseFileTuple struct {
|
|
srcBaseFile string
|
|
installBaseFile string
|
|
}
|
|
|
|
// prebuilt src files grouped by the install partitions.
|
|
// Each groups are a mapping of the relative install path to the name of the files
|
|
type prebuiltSrcGroupByInstallPartition struct {
|
|
system map[string][]srcBaseFileInstallBaseFileTuple
|
|
system_ext map[string][]srcBaseFileInstallBaseFileTuple
|
|
product map[string][]srcBaseFileInstallBaseFileTuple
|
|
vendor map[string][]srcBaseFileInstallBaseFileTuple
|
|
recovery map[string][]srcBaseFileInstallBaseFileTuple
|
|
}
|
|
|
|
func newPrebuiltSrcGroupByInstallPartition() *prebuiltSrcGroupByInstallPartition {
|
|
return &prebuiltSrcGroupByInstallPartition{
|
|
system: map[string][]srcBaseFileInstallBaseFileTuple{},
|
|
system_ext: map[string][]srcBaseFileInstallBaseFileTuple{},
|
|
product: map[string][]srcBaseFileInstallBaseFileTuple{},
|
|
vendor: map[string][]srcBaseFileInstallBaseFileTuple{},
|
|
recovery: map[string][]srcBaseFileInstallBaseFileTuple{},
|
|
}
|
|
}
|
|
|
|
func isSubdirectory(parent, child string) bool {
|
|
rel, err := filepath.Rel(parent, child)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return !strings.HasPrefix(rel, "..")
|
|
}
|
|
|
|
func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToInstallPath, destPath, srcPath string, srcGroup *prebuiltSrcGroupByInstallPartition) {
|
|
for _, part := range partitionToInstallPathList {
|
|
partition := part.name
|
|
installPath := part.installPath
|
|
|
|
if isSubdirectory(installPath, destPath) {
|
|
relativeInstallPath, _ := filepath.Rel(installPath, destPath)
|
|
relativeInstallDir := filepath.Dir(relativeInstallPath)
|
|
var srcMap map[string][]srcBaseFileInstallBaseFileTuple
|
|
switch partition {
|
|
case "system":
|
|
srcMap = srcGroup.system
|
|
case "system_ext":
|
|
srcMap = srcGroup.system_ext
|
|
case "product":
|
|
srcMap = srcGroup.product
|
|
case "vendor":
|
|
srcMap = srcGroup.vendor
|
|
case "recovery":
|
|
srcMap = srcGroup.recovery
|
|
}
|
|
if srcMap != nil {
|
|
srcMap[relativeInstallDir] = append(srcMap[relativeInstallDir], srcBaseFileInstallBaseFileTuple{
|
|
srcBaseFile: filepath.Base(srcPath),
|
|
installBaseFile: filepath.Base(destPath),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a map of source files to the list of destination files from PRODUCT_COPY_FILES entries.
|
|
// Note that the value of the map is a list of string, given that a single source file can be
|
|
// copied to multiple files.
|
|
// This function also checks the existence of the source files, and validates that there is no
|
|
// multiple source files copying to the same dest file.
|
|
func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string][]string {
|
|
seen := make(map[string]bool)
|
|
filtered := make(map[string][]string)
|
|
|
|
for _, copyFilePair := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
|
|
srcDestList := strings.Split(copyFilePair, ":")
|
|
if len(srcDestList) < 2 {
|
|
ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair)
|
|
}
|
|
src, dest := srcDestList[0], srcDestList[1]
|
|
|
|
// Some downstream branches use absolute path as entries in PRODUCT_COPY_FILES.
|
|
// Convert them to relative path from top and check if they do not escape the tree root.
|
|
relSrc := android.ToRelativeSourcePath(ctx, src)
|
|
|
|
if _, ok := seen[dest]; !ok {
|
|
if optionalPath := android.ExistentPathForSource(ctx, relSrc); optionalPath.Valid() {
|
|
seen[dest] = true
|
|
filtered[relSrc] = append(filtered[relSrc], dest)
|
|
}
|
|
}
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
type partitionToInstallPath struct {
|
|
name string
|
|
installPath string
|
|
}
|
|
|
|
func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSrcGroupByInstallPartition {
|
|
// Filter out duplicate dest entries and non existing src entries
|
|
productCopyFileMap := uniqueExistingProductCopyFileMap(ctx)
|
|
|
|
// System is intentionally added at the last to consider the scenarios where
|
|
// non-system partitions are installed as part of the system partition
|
|
partitionToInstallPathList := []partitionToInstallPath{
|
|
{name: "recovery", installPath: "recovery/root"},
|
|
{name: "vendor", installPath: ctx.DeviceConfig().VendorPath()},
|
|
{name: "product", installPath: ctx.DeviceConfig().ProductPath()},
|
|
{name: "system_ext", installPath: ctx.DeviceConfig().SystemExtPath()},
|
|
{name: "system", installPath: "system"},
|
|
}
|
|
|
|
groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
|
|
for _, src := range android.SortedKeys(productCopyFileMap) {
|
|
destFiles := productCopyFileMap[src]
|
|
srcFileDir := filepath.Dir(src)
|
|
if _, ok := groupedSources[srcFileDir]; !ok {
|
|
groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition()
|
|
}
|
|
for _, dest := range destFiles {
|
|
appendIfCorrectInstallPartition(partitionToInstallPathList, dest, filepath.Base(src), groupedSources[srcFileDir])
|
|
}
|
|
}
|
|
|
|
return groupedSources
|
|
}
|
|
|
|
type prebuiltModuleProperties struct {
|
|
Name *string
|
|
|
|
Soc_specific *bool
|
|
Product_specific *bool
|
|
System_ext_specific *bool
|
|
Recovery *bool
|
|
Ramdisk *bool
|
|
|
|
Srcs []string
|
|
|
|
No_full_install *bool
|
|
|
|
NamespaceExportedToMake bool
|
|
|
|
Visibility []string
|
|
}
|
|
|
|
// Split relative_install_path to a separate struct, because it is not supported for every
|
|
// modules listed in [etcInstallPathToFactoryMap]
|
|
type prebuiltSubdirProperties struct {
|
|
// If the base file name of the src and dst all match, dsts property does not need to be
|
|
// set, and only relative_install_path can be set.
|
|
Relative_install_path *string
|
|
}
|
|
|
|
// Split install_in_root to a separate struct as it is part of rootProperties instead of
|
|
// properties
|
|
type prebuiltInstallInRootProperties struct {
|
|
Install_in_root *bool
|
|
}
|
|
|
|
var (
|
|
etcInstallPathToFactoryList = map[string]android.ModuleFactory{
|
|
"": etc.PrebuiltRootFactory,
|
|
"avb": etc.PrebuiltAvbFactory,
|
|
"bin": etc.PrebuiltBinaryFactory,
|
|
"bt_firmware": etc.PrebuiltBtFirmwareFactory,
|
|
"cacerts": etc.PrebuiltEtcCaCertsFactory,
|
|
"dsp": etc.PrebuiltDSPFactory,
|
|
"etc": etc.PrebuiltEtcFactory,
|
|
"etc/dsp": etc.PrebuiltDSPFactory,
|
|
"etc/firmware": etc.PrebuiltFirmwareFactory,
|
|
"firmware": etc.PrebuiltFirmwareFactory,
|
|
"gpu": etc.PrebuiltGPUFactory,
|
|
"first_stage_ramdisk": etc.PrebuiltFirstStageRamdiskFactory,
|
|
"fonts": etc.PrebuiltFontFactory,
|
|
"framework": etc.PrebuiltFrameworkFactory,
|
|
"lib": etc.PrebuiltRenderScriptBitcodeFactory,
|
|
"lib64": etc.PrebuiltRenderScriptBitcodeFactory,
|
|
"lib/rfsa": etc.PrebuiltRFSAFactory,
|
|
"media": etc.PrebuiltMediaFactory,
|
|
"odm": etc.PrebuiltOdmFactory,
|
|
"optee": etc.PrebuiltOpteeFactory,
|
|
"overlay": etc.PrebuiltOverlayFactory,
|
|
"priv-app": etc.PrebuiltPrivAppFactory,
|
|
"radio": etc.PrebuiltRadioFactory,
|
|
"sbin": etc.PrebuiltSbinFactory,
|
|
"system": etc.PrebuiltSystemFactory,
|
|
"res": etc.PrebuiltResFactory,
|
|
"rfs": etc.PrebuiltRfsFactory,
|
|
"tts": etc.PrebuiltVoicepackFactory,
|
|
"tvconfig": etc.PrebuiltTvConfigFactory,
|
|
"tvservice": etc.PrebuiltTvServiceFactory,
|
|
"usr/share": etc.PrebuiltUserShareFactory,
|
|
"usr/hyphen-data": etc.PrebuiltUserHyphenDataFactory,
|
|
"usr/keylayout": etc.PrebuiltUserKeyLayoutFactory,
|
|
"usr/keychars": etc.PrebuiltUserKeyCharsFactory,
|
|
"usr/srec": etc.PrebuiltUserSrecFactory,
|
|
"usr/idc": etc.PrebuiltUserIdcFactory,
|
|
"vendor": etc.PrebuiltVendorFactory,
|
|
"vendor_dlkm": etc.PrebuiltVendorDlkmFactory,
|
|
"wallpaper": etc.PrebuiltWallpaperFactory,
|
|
"wlc_upt": etc.PrebuiltWlcUptFactory,
|
|
}
|
|
)
|
|
|
|
func generatedPrebuiltEtcModuleName(partition, srcDir, destDir string, count int) string {
|
|
// generated module name follows the pattern:
|
|
// <install partition>-<src file path>-<relative install path from partition root>-<number>
|
|
// Note that all path separators are replaced with "_" in the name
|
|
moduleName := partition
|
|
if !android.InList(srcDir, []string{"", "."}) {
|
|
moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(srcDir, string(filepath.Separator), "_"))
|
|
}
|
|
if !android.InList(destDir, []string{"", "."}) {
|
|
moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
|
|
}
|
|
moduleName += fmt.Sprintf("-%d", count)
|
|
|
|
return moduleName
|
|
}
|
|
|
|
func groupDestFilesBySrc(destFiles []srcBaseFileInstallBaseFileTuple) (ret map[string][]srcBaseFileInstallBaseFileTuple, maxLen int) {
|
|
ret = map[string][]srcBaseFileInstallBaseFileTuple{}
|
|
maxLen = 0
|
|
for _, tuple := range destFiles {
|
|
if _, ok := ret[tuple.srcBaseFile]; !ok {
|
|
ret[tuple.srcBaseFile] = []srcBaseFileInstallBaseFileTuple{}
|
|
}
|
|
ret[tuple.srcBaseFile] = append(ret[tuple.srcBaseFile], tuple)
|
|
maxLen = max(maxLen, len(ret[tuple.srcBaseFile]))
|
|
}
|
|
return ret, maxLen
|
|
}
|
|
|
|
func prebuiltEtcModuleProps(ctx android.LoadHookContext, moduleName, partition, destDir string) prebuiltModuleProperties {
|
|
moduleProps := prebuiltModuleProperties{}
|
|
moduleProps.Name = proptools.StringPtr(moduleName)
|
|
|
|
// Set partition specific properties
|
|
switch partition {
|
|
case "system_ext":
|
|
moduleProps.System_ext_specific = proptools.BoolPtr(true)
|
|
case "product":
|
|
moduleProps.Product_specific = proptools.BoolPtr(true)
|
|
case "vendor":
|
|
moduleProps.Soc_specific = proptools.BoolPtr(true)
|
|
case "recovery":
|
|
// To match the logic in modulePartition() in android/paths.go
|
|
if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && strings.HasPrefix(destDir, "first_stage_ramdisk") {
|
|
moduleProps.Ramdisk = proptools.BoolPtr(true)
|
|
} else {
|
|
moduleProps.Recovery = proptools.BoolPtr(true)
|
|
}
|
|
}
|
|
|
|
moduleProps.No_full_install = proptools.BoolPtr(true)
|
|
moduleProps.NamespaceExportedToMake = true
|
|
moduleProps.Visibility = []string{"//visibility:public"}
|
|
|
|
return moduleProps
|
|
}
|
|
|
|
func createPrebuiltEtcModulesInDirectory(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) (moduleNames []string) {
|
|
groupedDestFiles, maxLen := groupDestFilesBySrc(destFiles)
|
|
|
|
// Find out the most appropriate module type to generate
|
|
var etcInstallPathKey string
|
|
for _, etcInstallPath := range android.SortedKeys(etcInstallPathToFactoryList) {
|
|
// Do not break when found but iterate until the end to find a module with more
|
|
// specific install path
|
|
if strings.HasPrefix(destDir, etcInstallPath) {
|
|
etcInstallPathKey = etcInstallPath
|
|
}
|
|
}
|
|
moduleFactory := etcInstallPathToFactoryList[etcInstallPathKey]
|
|
relDestDirFromInstallDirBase, _ := filepath.Rel(etcInstallPathKey, destDir)
|
|
|
|
for fileIndex := range maxLen {
|
|
srcTuple := []srcBaseFileInstallBaseFileTuple{}
|
|
for _, srcFile := range android.SortedKeys(groupedDestFiles) {
|
|
groupedDestFile := groupedDestFiles[srcFile]
|
|
if len(groupedDestFile) > fileIndex {
|
|
srcTuple = append(srcTuple, groupedDestFile[fileIndex])
|
|
}
|
|
}
|
|
|
|
moduleName := generatedPrebuiltEtcModuleName(partition, srcDir, destDir, fileIndex)
|
|
moduleProps := prebuiltEtcModuleProps(ctx, moduleName, partition, destDir)
|
|
modulePropsPtr := &moduleProps
|
|
propsList := []interface{}{modulePropsPtr}
|
|
|
|
allCopyFileNamesUnchanged := true
|
|
var srcBaseFiles, installBaseFiles []string
|
|
for _, tuple := range srcTuple {
|
|
if tuple.srcBaseFile != tuple.installBaseFile {
|
|
allCopyFileNamesUnchanged = false
|
|
}
|
|
srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
|
|
installBaseFiles = append(installBaseFiles, tuple.installBaseFile)
|
|
}
|
|
|
|
// Recovery partition-installed modules are installed to `recovery/root/system` by
|
|
// default (See modulePartition() in android/paths.go). If the destination file
|
|
// directory is not `recovery/root/system/...`, it should set install_in_root to true
|
|
// to prevent being installed in `recovery/root/system`.
|
|
if partition == "recovery" && !strings.HasPrefix(destDir, "system") {
|
|
propsList = append(propsList, &prebuiltInstallInRootProperties{
|
|
Install_in_root: proptools.BoolPtr(true),
|
|
})
|
|
// Discard any previously picked module and force it to prebuilt_{root,any} as
|
|
// they are the only modules allowed to specify the `install_in_root` property.
|
|
etcInstallPathKey = ""
|
|
relDestDirFromInstallDirBase = destDir
|
|
}
|
|
|
|
// Set appropriate srcs, dsts, and releative_install_path based on
|
|
// the source and install file names
|
|
modulePropsPtr.Srcs = srcBaseFiles
|
|
|
|
// prebuilt_root should only be used in very limited cases in prebuilt_etc moddule gen, where:
|
|
// - all source file names are identical to the installed file names, and
|
|
// - all source files are installed in root, not the subdirectories of root
|
|
// prebuilt_root currently does not have a good way to specify the names of the multiple
|
|
// installed files, and prebuilt_root does not allow installing files at a subdirectory
|
|
// of the root.
|
|
// Use prebuilt_any instead of prebuilt_root if either of the conditions are not met as
|
|
// a fallback behavior.
|
|
if etcInstallPathKey == "" {
|
|
if !(allCopyFileNamesUnchanged && android.InList(relDestDirFromInstallDirBase, []string{"", "."})) {
|
|
moduleFactory = etc.PrebuiltAnyFactory
|
|
}
|
|
}
|
|
|
|
if allCopyFileNamesUnchanged {
|
|
// Specify relative_install_path if it is not installed in the root directory of the
|
|
// partition
|
|
if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
|
|
propsList = append(propsList, &prebuiltSubdirProperties{
|
|
Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
|
|
})
|
|
}
|
|
} else {
|
|
// If dsts property has to be set and the selected module type is prebuilt_root,
|
|
// use prebuilt_any instead.
|
|
dsts := proptools.NewConfigurable[[]string](nil, nil)
|
|
for _, installBaseFile := range installBaseFiles {
|
|
dsts.AppendSimpleValue([]string{filepath.Join(relDestDirFromInstallDirBase, installBaseFile)})
|
|
}
|
|
propsList = append(propsList, &etc.PrebuiltDstsProperties{
|
|
Dsts: dsts,
|
|
})
|
|
}
|
|
|
|
ctx.CreateModuleInDirectory(moduleFactory, srcDir, propsList...)
|
|
moduleNames = append(moduleNames, moduleName)
|
|
}
|
|
|
|
return moduleNames
|
|
}
|
|
|
|
func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) {
|
|
for _, destDir := range android.SortedKeys(destDirFilesMap) {
|
|
ret = append(ret, createPrebuiltEtcModulesInDirectory(ctx, partition, srcDir, destDir, destDirFilesMap[destDir])...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// Creates prebuilt_* modules based on the install paths and returns the list of generated
|
|
// module names
|
|
func createPrebuiltEtcModules(ctx android.LoadHookContext) (ret []string) {
|
|
groupedSources := processProductCopyFiles(ctx)
|
|
for _, srcDir := range android.SortedKeys(groupedSources) {
|
|
groupedSource := groupedSources[srcDir]
|
|
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system", srcDir, groupedSource.system)...)
|
|
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system_ext", srcDir, groupedSource.system_ext)...)
|
|
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "product", srcDir, groupedSource.product)...)
|
|
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "vendor", srcDir, groupedSource.vendor)...)
|
|
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "recovery", srcDir, groupedSource.recovery)...)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func createAvbpubkeyModule(ctx android.LoadHookContext) bool {
|
|
avbKeyPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbKeyPath
|
|
if avbKeyPath == "" {
|
|
return false
|
|
}
|
|
ctx.CreateModuleInDirectory(
|
|
etc.AvbpubkeyModuleFactory,
|
|
".",
|
|
&struct {
|
|
Name *string
|
|
Product_specific *bool
|
|
Private_key *string
|
|
No_full_install *bool
|
|
Visibility []string
|
|
}{
|
|
Name: proptools.StringPtr("system_other_avbpubkey"),
|
|
Product_specific: proptools.BoolPtr(true),
|
|
Private_key: proptools.StringPtr(avbKeyPath),
|
|
No_full_install: proptools.BoolPtr(true),
|
|
Visibility: []string{"//visibility:public"},
|
|
},
|
|
)
|
|
return true
|
|
}
|