Automatically reorder C/C++ link dependencies in Soong
This uses knowledge of transitive dependencies to reorder
linker command line arguments such that if module A depends
on module B, then module A is automatically listed before
module B in the linker command line.
This should mostly remove the need for Android.bp files to
list all of their static dependencies in link order
Bug: 66260943
Test: reorder the entries of static_libs in an Android.bp and see that linking still succeeds
Change-Id: I20f851ab9f2f30031254e4f30023b6140d15d6c3
diff --git a/cc/cc.go b/cc/cc.go
index 2fafaa2..7163696 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -326,6 +326,11 @@
// Flags used to compile this module
flags Flags
+
+ // When calling a linker, if module A depends on module B, then A must precede B in its command
+ // line invocation. staticDepsInLinkOrder stores the proper ordering of all of the transitive
+ // deps of this module
+ staticDepsInLinkOrder android.Paths
}
func (c *Module) Init() android.Module {
@@ -540,6 +545,51 @@
return name
}
+// orderDeps reorders dependencies into a list such that if module A depends on B, then
+// A will precede B in the resultant list.
+// This is convenient for passing into a linker.
+func orderDeps(directDeps []android.Path, transitiveDeps map[android.Path][]android.Path) (orderedAllDeps []android.Path, orderedDeclaredDeps []android.Path) {
+ // If A depends on B, then
+ // Every list containing A will also contain B later in the list
+ // So, after concatenating all lists, the final instance of B will have come from the same
+ // original list as the final instance of A
+ // So, the final instance of B will be later in the concatenation than the final A
+ // So, keeping only the final instance of A and of B ensures that A is earlier in the output
+ // list than B
+ for _, dep := range directDeps {
+ orderedAllDeps = append(orderedAllDeps, dep)
+ orderedAllDeps = append(orderedAllDeps, transitiveDeps[dep]...)
+ }
+
+ orderedAllDeps = lastUniquePaths(orderedAllDeps)
+
+ // We don't want to add any new dependencies into directDeps (to allow the caller to
+ // intentionally exclude or replace any unwanted transitive dependencies), so we limit the
+ // resultant list to only what the caller has chosen to include in directDeps
+ _, orderedDeclaredDeps = android.FilterPathList(orderedAllDeps, directDeps)
+
+ return orderedAllDeps, orderedDeclaredDeps
+}
+
+func orderStaticModuleDeps(module *Module, deps []*Module) (results []android.Path) {
+ // make map of transitive dependencies
+ transitiveStaticDepNames := make(map[android.Path][]android.Path, len(deps))
+ for _, dep := range deps {
+ transitiveStaticDepNames[dep.outputFile.Path()] = dep.staticDepsInLinkOrder
+ }
+ // get the output file for each declared dependency
+ depFiles := []android.Path{}
+ for _, dep := range deps {
+ depFiles = append(depFiles, dep.outputFile.Path())
+ }
+
+ // reorder the dependencies based on transitive dependencies
+ module.staticDepsInLinkOrder, results = orderDeps(depFiles, transitiveStaticDepNames)
+
+ return results
+
+}
+
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
ctx := &moduleContext{
@@ -985,6 +1035,8 @@
func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
+ directStaticDeps := []*Module{}
+
ctx.VisitDirectDeps(func(dep blueprint.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
@@ -1108,7 +1160,8 @@
depPtr = &depPaths.LateSharedLibsDeps
depFile = ccDep.linker.(libraryInterface).toc()
case staticDepTag, staticExportDepTag:
- ptr = &depPaths.StaticLibs
+ ptr = nil
+ directStaticDeps = append(directStaticDeps, ccDep)
case lateStaticDepTag:
ptr = &depPaths.LateStaticLibs
case wholeStaticDepTag:
@@ -1192,6 +1245,9 @@
}
})
+ // use the ordered dependencies as this module's dependencies
+ depPaths.StaticLibs = append(depPaths.StaticLibs, orderStaticModuleDeps(c, directStaticDeps)...)
+
// Dedup exported flags from dependencies
depPaths.Flags = firstUniqueElements(depPaths.Flags)
depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
@@ -1417,6 +1473,23 @@
return list[totalSkip:]
}
+// lastUniquePaths is the same as lastUniqueElements but uses Path structs
+func lastUniquePaths(list []android.Path) []android.Path {
+ totalSkip := 0
+ for i := len(list) - 1; i >= totalSkip; i-- {
+ skip := 0
+ for j := i - 1; j >= totalSkip; j-- {
+ if list[i] == list[j] {
+ skip++
+ } else {
+ list[j+skip] = list[j]
+ }
+ }
+ totalSkip += skip
+ }
+ return list[totalSkip:]
+}
+
func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
if ctx.AConfig().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt {
return strconv.Itoa(config.NdkMaxPrebuiltVersionInt)