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)