Track transitive packaged license deps of containers

Containers generally package the transitive installable
dependencies of their direct dependencies, track them as license
deps.

Bug: 207445310
Test: m checkbuild
Change-Id: Ic8640152cee0e0cfec5e85a1649a8adfd29d517a
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 3bc53d6..4c71a8c 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -45,9 +45,18 @@
 		return
 	}
 
+	var outputFiles Paths
+	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
+		outputFiles, _ = outputFileProducer.OutputFiles("")
+		outputFiles = PathsIfNonNil(outputFiles...)
+	}
+
+	isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
+
 	var allDepMetadataFiles Paths
 	var allDepMetadataArgs []string
 	var allDepOutputFiles Paths
+	var allDepMetadataDepSets []*PathsDepSet
 
 	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
 		dep, _ := bpdep.(Module)
@@ -61,6 +70,9 @@
 		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
 			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
+			if isContainer || IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
+			}
 
 			depAnnotations := licenseAnnotationsFromTag(ctx.OtherModuleDependencyTag(dep))
 
@@ -105,9 +117,14 @@
 	args = append(args,
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
 
-	args = append(args,
-		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d "))
-	orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...)
+	if isContainer {
+		args = append(args,
+			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(newPathsDepSet(nil, allDepMetadataDepSets).ToList().Strings()), "-d "))
+	} else {
+		args = append(args,
+			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d "))
+		orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...)
+	}
 
 	args = append(args,
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepOutputFiles.Strings()), "-s "))
@@ -117,12 +134,6 @@
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m "))
 
 	// Built files
-	var outputFiles Paths
-	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
-		outputFiles, _ = outputFileProducer.OutputFiles("")
-		outputFiles = PathsIfNonNil(outputFiles...)
-	}
-
 	if len(outputFiles) > 0 {
 		args = append(args,
 			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
@@ -134,7 +145,6 @@
 	args = append(args,
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
 
-	isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
 	if isContainer {
 		args = append(args, "--is_container")
 	}
@@ -152,7 +162,8 @@
 	})
 
 	ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
-		LicenseMetadataPath: licenseMetadataFile,
+		LicenseMetadataPath:   licenseMetadataFile,
+		LicenseMetadataDepSet: newPathsDepSet(Paths{licenseMetadataFile}, allDepMetadataDepSets),
 	})
 }
 
@@ -179,7 +190,8 @@
 
 // LicenseMetadataInfo stores the license metadata path for a module.
 type LicenseMetadataInfo struct {
-	LicenseMetadataPath Path
+	LicenseMetadataPath   Path
+	LicenseMetadataDepSet *PathsDepSet
 }
 
 // licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
diff --git a/android/paths.go b/android/paths.go
index 70e427b..4c69de7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -2149,3 +2149,23 @@
 	}
 	return false
 }
+
+// PathsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
+// topological order.
+type PathsDepSet struct {
+	depSet
+}
+
+// newPathsDepSet returns an immutable PathsDepSet with the given direct and
+// transitive contents.
+func newPathsDepSet(direct Paths, transitive []*PathsDepSet) *PathsDepSet {
+	return &PathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the PathsDepSet flattened to a list in topological order.
+func (d *PathsDepSet) ToList() Paths {
+	if d == nil {
+		return nil
+	}
+	return d.depSet.ToList().(Paths)
+}