license metadata reverse trace

Introduce the below command-line tool:

rtrace outputs a list of targets and conditions causing one or more
projects or target nodes to require sharing to resolve a restricted
condition.

Bug: 68860345
Bug: 151177513
Bug: 151953481
Bug: 213388645
Bug: 210912771

Test: m all
Test: m systemlicense
Test: m rtrace; out/soong/host/linux-x85/rtrace -rtrace=...

where ... is a project or license metadata file followed by the path to
the .meta_lic file for the system image. In my case if

$ export PRODUCT=$(realpath $ANDROID_PRODUCT_OUT --relative-to=$PWD)

... can be expressed as:

system/core ${PRODUCT}/gen/META/lic_intermediates/${PRODUCT}/system.img.meta_lic
or
${PRODUCT}/gen/META/lic_intermediates/${PRODUCT}/system.img.meta_lic ${PRODUCT}/gen/META/lic_intermediates/${PRODUCT}/system.img.meta_lic

Change-Id: I40a0586699d9b8a8dd2bd4ba26756c9649ebf964
diff --git a/tools/compliance/policy_resolve.go b/tools/compliance/policy_resolve.go
index 336894a..d357aec 100644
--- a/tools/compliance/policy_resolve.go
+++ b/tools/compliance/policy_resolve.go
@@ -18,6 +18,16 @@
 	"sync"
 )
 
+var (
+	// AllResolutions is a TraceConditions function that resolves all
+	// unfiltered license conditions.
+	AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })
+)
+
+// TraceConditions is a function that returns the conditions to trace for each
+// target node `tn`.
+type TraceConditions func(tn *TargetNode) LicenseConditionSet
+
 // ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
 // propagating conditions up the graph as necessary according to the properties
 // of each edge and according to each license condition in question.
@@ -29,6 +39,14 @@
 // not resolve the library and its transitive closure, but the later top-down
 // walk will.
 func ResolveBottomUpConditions(lg *LicenseGraph) {
+	TraceBottomUpConditions(lg, AllResolutions)
+}
+
+// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph
+// propagating trace conditions from `conditionsFn` up the graph as necessary
+// according to the properties of each edge and according to each license
+// condition in question.
+func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
 
 	// short-cut if already walked and cached
 	lg.mu.Lock()
@@ -70,7 +88,7 @@
 				// needs to walk again in non-aggregate context
 				delete(cmap, target)
 			} else {
-				target.resolution |= target.licenseConditions
+				target.resolution |= conditionsFn(target)
 				amap[target] = struct{}{}
 			}
 			if treatAsAggregate {
@@ -123,6 +141,13 @@
 // dependency except restricted. For restricted, the policy is to share the
 // source of any libraries linked to restricted code and to provide notice.
 func ResolveTopDownConditions(lg *LicenseGraph) {
+	TraceTopDownConditions(lg, AllResolutions)
+}
+
+// TraceTopDownCondtions performs a top-down walk of the LicenseGraph
+// propagating trace conditions returned by `conditionsFn` from target to
+// dependency.
+func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
 
 	// short-cut if already walked and cached
 	lg.mu.Lock()
@@ -139,7 +164,7 @@
 	lg.mu.Unlock()
 
 	// start with the conditions propagated up the graph
-	ResolveBottomUpConditions(lg)
+	TraceBottomUpConditions(lg, conditionsFn)
 
 	// amap contains the set of targets already walked. (guarded by mu)
 	amap := make(map[*TargetNode]struct{})
@@ -156,7 +181,7 @@
 	walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
 		defer wg.Done()
 		mu.Lock()
-		fnode.resolution |= fnode.licenseConditions
+		fnode.resolution |= conditionsFn(fnode)
 		fnode.resolution |= cs
 		amap[fnode] = struct{}{}
 		if treatAsAggregate {
@@ -168,7 +193,7 @@
 		for _, edge := range fnode.edges {
 			func(edge *TargetEdge) {
 				// dcs holds the dpendency conditions inherited from the target
-				dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate)
+				dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
 				dnode := edge.dependency
 				mu.Lock()
 				defer mu.Unlock()