Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a8759c2 authored by Jihoon Kang's avatar Jihoon Kang
Browse files

Support generating prebuilt_* modules from 1:n mapping of PRODUCT_COPY_FILES

The current implementation of prebuilt_* modules generation from
PRODUCT_COPY_FILES relies on the assumption that its entries are 1:1
mapping. However, there are entries where the mapping is 1:n (i.e. a
source file is installed multiple times with different names).

Some of the cases are copying the same file multiple times in the same
install directory with different file names. This cannot be supported
with a single prebuilt_* module, and a multiple prebuilt_* modules need
to be generated for a single source file to support this.

Implementation details:
- Import PRODUCT_COPY_FILES as a string list instead of map and process
  it
- Reformat the generated prebuilt_* module name to end with an interger
  instead of file extension
- Group source files to ensure that a source file cannot be duplicated
  in each generated prebuilt_* modules.

Test: m soong_generated_vendor_filesystem_test && observe etc/ueventd.rc is not shown
Bug: 375053752
Change-Id: Iac40ebfa4433a6589a5bc1ad31ce0cb7140bcfc4
parent b3147242
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -611,7 +611,7 @@ type PartitionVariables struct {
	BoardInfoFiles      []string `json:",omitempty"`
	BootLoaderBoardName string   `json:",omitempty"`

	ProductCopyFiles map[string]string `json:",omitempty"`
	ProductCopyFiles []string `json:",omitempty"`

	BuildingSystemDlkmImage bool     `json:",omitempty"`
	SystemKernelModules     []string `json:",omitempty"`
+11 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ import (
	"android/soong/android"
	"fmt"
	"slices"
	"strings"
	"sync"

	"github.com/google/blueprint/proptools"
@@ -298,12 +299,21 @@ func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {

var HighPriorityDeps = []string{}

func isHighPriorityDep(depName string) bool {
	for _, highPriorityDeps := range HighPriorityDeps {
		if strings.HasPrefix(depName, highPriorityDeps) {
			return true
		}
	}
	return false
}

func generateDepStruct(deps map[string]*depCandidateProps) *packagingPropsStruct {
	depsStruct := packagingPropsStruct{}
	for depName, depProps := range deps {
		bitness := getBitness(depProps.Arch)
		fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
		if android.InList(depName, HighPriorityDeps) {
		if isHighPriorityDep(depName) {
			depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
		} else if android.InList("32", bitness) && android.InList("64", bitness) {
			// If both 32 and 64 bit variants are enabled for this module
+102 −57
Original line number Diff line number Diff line
@@ -85,15 +85,25 @@ func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToIns
	}
}

func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string]string {
// 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)
	filtered := make(map[string][]string)

	for src, dest := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
	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]
		if _, ok := seen[dest]; !ok {
			if optionalPath := android.ExistentPathForSource(ctx, src); optionalPath.Valid() {
				seen[dest] = true
				filtered[src] = dest
				filtered[src] = append(filtered[src], dest)
			}
		}
	}
@@ -121,13 +131,15 @@ func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSr

	groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
	for _, src := range android.SortedKeys(productCopyFileMap) {
		dest := productCopyFileMap[src]
		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
}
@@ -193,12 +205,9 @@ var (
	}
)

func createPrebuiltEtcModule(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) string {
	moduleProps := &prebuiltModuleProperties{}
	propsList := []interface{}{moduleProps}

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>-<install file extension>
	// <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{"", "."}) {
@@ -207,23 +216,48 @@ func createPrebuiltEtcModule(ctx android.LoadHookContext, partition, srcDir, des
	if !android.InList(destDir, []string{"", "."}) {
		moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
	}
	if len(destFiles) > 0 {
		if ext := filepath.Ext(destFiles[0].srcBaseFile); ext != "" {
			moduleName += fmt.Sprintf("-%s", strings.TrimPrefix(ext, "."))
	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(moduleName, partition string) prebuiltModuleProperties {
	moduleProps := prebuiltModuleProperties{}
	moduleProps.Name = proptools.StringPtr(moduleName)

	allCopyFileNamesUnchanged := true
	var srcBaseFiles, installBaseFiles []string
	for _, tuple := range destFiles {
		if tuple.srcBaseFile != tuple.installBaseFile {
			allCopyFileNamesUnchanged = false
	// 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)
	}
		srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
		installBaseFiles = append(installBaseFiles, tuple.installBaseFile)

	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) {
@@ -233,51 +267,62 @@ func createPrebuiltEtcModule(ctx android.LoadHookContext, partition, srcDir, des
			etcInstallPathKey = etcInstallPath
		}
	}
	destDir, _ = filepath.Rel(etcInstallPathKey, destDir)
	relDestDirFromInstallDirBase, _ := filepath.Rel(etcInstallPathKey, destDir)

	// 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)
	for fileIndex := range maxLen {
		srcTuple := []srcBaseFileInstallBaseFileTuple{}
		for _, groupedDestFile := range groupedDestFiles {
			if len(groupedDestFile) > fileIndex {
				srcTuple = append(srcTuple, groupedDestFile[fileIndex])
			}
		}

		moduleName := generatedPrebuiltEtcModuleName(partition, srcDir, destDir, fileIndex)
		moduleProps := prebuiltEtcModuleProps(moduleName, partition)
		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)
		}

		// Set appropriate srcs, dsts, and releative_install_path based on
		// the source and install file names
		if allCopyFileNamesUnchanged {
		moduleProps.Srcs = srcBaseFiles
			modulePropsPtr.Srcs = srcBaseFiles

			// Specify relative_install_path if it is not installed in the root directory of the
			// partition
		if !android.InList(destDir, []string{"", "."}) {
			if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
				propsList = append(propsList, &prebuiltSubdirProperties{
				Relative_install_path: proptools.StringPtr(destDir),
					Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
				})
			}
		} else {
		moduleProps.Srcs = srcBaseFiles
			modulePropsPtr.Srcs = srcBaseFiles
			dsts := []string{}
			for _, installBaseFile := range installBaseFiles {
			dsts = append(dsts, filepath.Join(destDir, installBaseFile))
				dsts = append(dsts, filepath.Join(relDestDirFromInstallDirBase, installBaseFile))
			}
		moduleProps.Dsts = dsts
			modulePropsPtr.Dsts = dsts
		}

	moduleProps.No_full_install = proptools.BoolPtr(true)
	moduleProps.NamespaceExportedToMake = true
	moduleProps.Visibility = []string{"//visibility:public"}

		ctx.CreateModuleInDirectory(etcInstallPathToFactoryList[etcInstallPathKey], srcDir, propsList...)
		moduleNames = append(moduleNames, moduleName)
	}

	return 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, createPrebuiltEtcModule(ctx, partition, srcDir, destDir, destDirFilesMap[destDir]))
		ret = append(ret, createPrebuiltEtcModulesInDirectory(ctx, partition, srcDir, destDir, destDirFilesMap[destDir])...)
	}
	return ret
}