Merge changes I5d48eaba,I4ff3f988

* changes:
  compliance package documentation
  compliance package: listshare and checkshare
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
index d671f45..afb3080 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -18,6 +18,20 @@
 }
 
 blueprint_go_binary {
+    name: "checkshare",
+    srcs: ["cmd/checkshare.go"],
+    deps: ["compliance-module"],
+    testSrcs: ["cmd/checkshare_test.go"],
+}
+
+blueprint_go_binary {
+    name: "listshare",
+    srcs: ["cmd/listshare.go"],
+    deps: ["compliance-module"],
+    testSrcs: ["cmd/listshare_test.go"],
+}
+
+blueprint_go_binary {
     name: "dumpgraph",
     srcs: ["cmd/dumpgraph.go"],
     deps: ["compliance-module"],
@@ -37,6 +51,7 @@
         "actionset.go",
         "condition.go",
         "conditionset.go",
+        "doc.go",
         "graph.go",
         "policy/policy.go",
         "policy/resolve.go",
diff --git a/tools/compliance/cmd/checkshare.go b/tools/compliance/cmd/checkshare.go
new file mode 100644
index 0000000..efac8dc
--- /dev/null
+++ b/tools/compliance/cmd/checkshare.go
@@ -0,0 +1,114 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"compliance"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"sort"
+)
+
+func init() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
+
+Reports on stderr any targets where policy says that the source both
+must and must not be shared. The error report indicates the target, the
+license condition with origin that has a source privacy policy, and the
+license condition with origin that has a source sharing policy.
+
+Any given target may appear multiple times with different combinations
+of conflicting license conditions.
+
+If all the source code that policy says must be shared may be shared,
+outputs "PASS" to stdout and exits with status 0.
+
+If policy says any source must both be shared and not be shared,
+outputs "FAIL" to stdout and exits with status 1.
+`, filepath.Base(os.Args[0]))
+	}
+}
+
+var (
+	failConflicts = fmt.Errorf("conflicts")
+	failNoneRequested = fmt.Errorf("\nNo metadata files requested")
+	failNoLicenses = fmt.Errorf("No licenses")
+)
+
+
+// byError orders conflicts by error string
+type byError []compliance.SourceSharePrivacyConflict
+
+func (l byError) Len() int           { return len(l) }
+func (l byError) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
+func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() }
+
+func main() {
+	flag.Parse()
+
+	// Must specify at least one root target.
+	if flag.NArg() == 0 {
+		flag.Usage()
+		os.Exit(2)
+	}
+
+	err := checkShare(os.Stdout, os.Stderr, flag.Args()...)
+	if err != nil {
+		if err != failConflicts {
+			if err == failNoneRequested {
+				flag.Usage()
+			}
+			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+		}
+		os.Exit(1)
+	}
+	os.Exit(0)
+}
+
+// checkShare implements the checkshare utility.
+func checkShare(stdout, stderr io.Writer, files ...string) error {
+
+	if len(files) < 1 {
+		return failNoneRequested
+	}
+
+	// Read the license graph from the license metadata files (*.meta_lic).
+	licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+	if err != nil {
+		return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err)
+	}
+	if licenseGraph == nil {
+		return failNoLicenses
+	}
+
+	// Apply policy to find conflicts and report them to stderr lexicographically ordered.
+	conflicts := compliance.ConflictingSharedPrivateSource(licenseGraph)
+	sort.Sort(byError(conflicts))
+	for _, conflict := range conflicts {
+		fmt.Fprintln(stderr, conflict.Error())
+	}
+
+	// Indicate pass or fail on stdout.
+	if len(conflicts) > 0 {
+		fmt.Fprintln(stdout, "FAIL")
+		return failConflicts
+	}
+	fmt.Fprintln(stdout, "PASS")
+	return nil
+}
diff --git a/tools/compliance/cmd/checkshare_test.go b/tools/compliance/cmd/checkshare_test.go
new file mode 100644
index 0000000..8ea7748
--- /dev/null
+++ b/tools/compliance/cmd/checkshare_test.go
@@ -0,0 +1,299 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+type outcome struct {
+	target           string
+	privacyOrigin    string
+	privacyCondition string
+	shareOrigin      string
+	shareCondition   string
+}
+
+func (o *outcome) String() string {
+	return fmt.Sprintf("%s %s from %s and must share from %s %s",
+		o.target, o.privacyCondition, o.privacyOrigin, o.shareCondition, o.shareOrigin)
+}
+
+type outcomeList []*outcome
+
+func (ol outcomeList) String() string {
+	result := ""
+	for _, o := range ol {
+		result = result + o.String() + "\n"
+	}
+	return result
+}
+
+func Test(t *testing.T) {
+	tests := []struct {
+		condition        string
+		name             string
+		roots            []string
+		expectedStdout   string
+		expectedOutcomes outcomeList
+	}{
+		{
+			condition:      "firstparty",
+			name:           "apex",
+			roots:          []string{"highest.apex.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "firstparty",
+			name:           "container",
+			roots:          []string{"container.zip.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "firstparty",
+			name:           "application",
+			roots:          []string{"application.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "firstparty",
+			name:           "binary",
+			roots:          []string{"bin/bin2.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "firstparty",
+			name:           "library",
+			roots:          []string{"lib/libd.so.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "notice",
+			name:           "apex",
+			roots:          []string{"highest.apex.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "notice",
+			name:           "container",
+			roots:          []string{"container.zip.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "notice",
+			name:           "application",
+			roots:          []string{"application.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "notice",
+			name:           "binary",
+			roots:          []string{"bin/bin2.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "notice",
+			name:           "library",
+			roots:          []string{"lib/libd.so.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "reciprocal",
+			name:           "apex",
+			roots:          []string{"highest.apex.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "reciprocal",
+			name:           "container",
+			roots:          []string{"container.zip.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "reciprocal",
+			name:           "application",
+			roots:          []string{"application.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "reciprocal",
+			name:           "binary",
+			roots:          []string{"bin/bin2.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "reciprocal",
+			name:           "library",
+			roots:          []string{"lib/libd.so.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "restricted",
+			name:           "apex",
+			roots:          []string{"highest.apex.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "restricted",
+			name:           "container",
+			roots:          []string{"container.zip.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "restricted",
+			name:           "application",
+			roots:          []string{"application.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "restricted",
+			name:           "binary",
+			roots:          []string{"bin/bin2.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "restricted",
+			name:           "library",
+			roots:          []string{"lib/libd.so.meta_lic"},
+			expectedStdout: "PASS",
+		},
+		{
+			condition:      "proprietary",
+			name:           "apex",
+			roots:          []string{"highest.apex.meta_lic"},
+			expectedStdout: "FAIL",
+			expectedOutcomes: outcomeList{
+				&outcome{
+					target:           "testdata/proprietary/bin/bin2.meta_lic",
+					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
+					privacyCondition: "proprietary",
+					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
+					shareCondition:   "restricted",
+				},
+			},
+		},
+		{
+			condition:      "proprietary",
+			name:           "container",
+			roots:          []string{"container.zip.meta_lic"},
+			expectedStdout: "FAIL",
+			expectedOutcomes: outcomeList{
+				&outcome{
+					target:           "testdata/proprietary/bin/bin2.meta_lic",
+					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
+					privacyCondition: "proprietary",
+					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
+					shareCondition:   "restricted",
+				},
+			},
+		},
+		{
+			condition:      "proprietary",
+			name:           "application",
+			roots:          []string{"application.meta_lic"},
+			expectedStdout: "FAIL",
+			expectedOutcomes: outcomeList{
+				&outcome{
+					target:           "testdata/proprietary/lib/liba.so.meta_lic",
+					privacyOrigin:    "testdata/proprietary/lib/liba.so.meta_lic",
+					privacyCondition: "proprietary",
+					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
+					shareCondition:   "restricted",
+				},
+			},
+		},
+		{
+			condition:      "proprietary",
+			name:           "binary",
+			roots:          []string{"bin/bin2.meta_lic", "lib/libb.so.meta_lic"},
+			expectedStdout: "FAIL",
+			expectedOutcomes: outcomeList{
+				&outcome{
+					target:           "testdata/proprietary/bin/bin2.meta_lic",
+					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
+					privacyCondition: "proprietary",
+					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
+					shareCondition:   "restricted",
+				},
+			},
+		},
+		{
+			condition:      "proprietary",
+			name:           "library",
+			roots:          []string{"lib/libd.so.meta_lic"},
+			expectedStdout: "PASS",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+			stdout := &bytes.Buffer{}
+			stderr := &bytes.Buffer{}
+
+			rootFiles := make([]string, 0, len(tt.roots))
+			for _, r := range tt.roots {
+				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+			}
+			err := checkShare(stdout, stderr, rootFiles...)
+			if err != nil && err != failConflicts {
+				t.Fatalf("checkshare: error = %v, stderr = %v", err, stderr)
+				return
+			}
+			var actualStdout string
+			for _, s := range strings.Split(stdout.String(), "\n") {
+				ts := strings.TrimLeft(s, " \t")
+				if len(ts) < 1 {
+					continue
+				}
+				if 0 < len(actualStdout) {
+					t.Errorf("checkshare: unexpected multiple output lines %q, want %q", actualStdout+"\n"+ts, tt.expectedStdout)
+				}
+				actualStdout = ts
+			}
+			if actualStdout != tt.expectedStdout {
+				t.Errorf("checkshare: unexpected stdout %q, want %q", actualStdout, tt.expectedStdout)
+			}
+			errList := strings.Split(stderr.String(), "\n")
+			actualOutcomes := make(outcomeList, 0, len(errList))
+			for _, cstring := range errList {
+				ts := strings.TrimLeft(cstring, " \t")
+				if len(ts) < 1 {
+					continue
+				}
+				cFields := strings.Split(ts, " ")
+				actualOutcomes = append(actualOutcomes, &outcome{
+					target:           cFields[0],
+					privacyOrigin:    cFields[3],
+					privacyCondition: cFields[1],
+					shareOrigin:      cFields[9],
+					shareCondition:   cFields[8],
+				})
+			}
+			if len(actualOutcomes) != len(tt.expectedOutcomes) {
+				t.Errorf("checkshare: unexpected got %d outcomes %s, want %d outcomes %s",
+					len(actualOutcomes), actualOutcomes, len(tt.expectedOutcomes), tt.expectedOutcomes)
+				return
+			}
+			for i := range actualOutcomes {
+				if actualOutcomes[i].String() != tt.expectedOutcomes[i].String() {
+					t.Errorf("checkshare: unexpected outcome #%d, got %q, want %q",
+						i+1, actualOutcomes[i], tt.expectedOutcomes[i])
+				}
+			}
+		})
+	}
+}
diff --git a/tools/compliance/cmd/listshare.go b/tools/compliance/cmd/listshare.go
new file mode 100644
index 0000000..bba2308
--- /dev/null
+++ b/tools/compliance/cmd/listshare.go
@@ -0,0 +1,124 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"compliance"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"sort"
+)
+
+func init() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
+
+Outputs a csv file with 1 project per line in the first field followed
+by target:condition pairs describing why the project must be shared.
+
+Each target is the path to a generated license metadata file for a
+Soong module or Make target, and the license condition is either
+restricted (e.g. GPL) or reciprocal (e.g. MPL).
+`, filepath.Base(os.Args[0]))
+	}
+}
+
+var (
+	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+	failNoLicenses = fmt.Errorf("No licenses found")
+)
+
+func main() {
+	flag.Parse()
+
+	// Must specify at least one root target.
+	if flag.NArg() == 0 {
+		flag.Usage()
+		os.Exit(2)
+	}
+
+	err := listShare(os.Stdout, os.Stderr, flag.Args()...)
+	if err != nil {
+		if err == failNoneRequested {
+			flag.Usage()
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+		os.Exit(1)
+	}
+	os.Exit(0)
+}
+
+// listShare implements the listshare utility.
+func listShare(stdout, stderr io.Writer, files ...string) error {
+	// Must be at least one root file.
+	if len(files) < 1 {
+		return failNoneRequested
+	}
+
+	// Read the license graph from the license metadata files (*.meta_lic).
+	licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+	if err != nil {
+		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
+	}
+	if licenseGraph == nil {
+		return failNoLicenses
+	}
+
+	// shareSource contains all source-sharing resolutions.
+	shareSource := compliance.ResolveSourceSharing(licenseGraph)
+
+	// Group the resolutions by project.
+	presolution := make(map[string]*compliance.LicenseConditionSet)
+	for _, target := range shareSource.AttachesTo() {
+		rl := shareSource.Resolutions(target)
+		sort.Sort(rl)
+		for _, r := range rl {
+			for _, p := range r.ActsOn().Projects() {
+				if _, ok := presolution[p]; !ok {
+					presolution[p] = r.Resolves().Copy()
+					continue
+				}
+				presolution[p].AddSet(r.Resolves())
+			}
+		}
+	}
+
+	// Sort the projects for repeatability/stability.
+	projects := make([]string, 0, len(presolution))
+	for p := range presolution {
+		projects = append(projects, p)
+	}
+	sort.Strings(projects)
+
+	// Output the sorted projects and the source-sharing license conditions that each project resolves.
+	for _, p := range projects {
+		fmt.Fprintf(stdout, "%s", p)
+
+		// Sort the conditions for repeatability/stability.
+		conditions := presolution[p].AsList()
+		sort.Sort(conditions)
+
+		// Output the sorted origin:condition pairs.
+		for _, lc := range conditions {
+			fmt.Fprintf(stdout, ",%s:%s", lc.Origin().Name(), lc.Name())
+		}
+		fmt.Fprintf(stdout, "\n")
+	}
+
+	return nil
+}
diff --git a/tools/compliance/cmd/listshare_test.go b/tools/compliance/cmd/listshare_test.go
new file mode 100644
index 0000000..b4847e3
--- /dev/null
+++ b/tools/compliance/cmd/listshare_test.go
@@ -0,0 +1,405 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"strings"
+	"testing"
+)
+
+func Test(t *testing.T) {
+	type projectShare struct {
+		project    string
+		conditions []string
+	}
+	tests := []struct {
+		condition   string
+		name        string
+		roots       []string
+		expectedOut []projectShare
+	}{
+		{
+			condition:   "firstparty",
+			name:        "apex",
+			roots:       []string{"highest.apex.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "firstparty",
+			name:        "container",
+			roots:       []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "firstparty",
+			name:        "application",
+			roots:       []string{"application.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "firstparty",
+			name:        "binary",
+			roots:       []string{"bin/bin1.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "firstparty",
+			name:        "library",
+			roots:       []string{"lib/libd.so.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "notice",
+			name:        "apex",
+			roots:       []string{"highest.apex.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "notice",
+			name:        "container",
+			roots:       []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "notice",
+			name:        "application",
+			roots:       []string{"application.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "notice",
+			name:        "binary",
+			roots:       []string{"bin/bin1.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "notice",
+			name:        "library",
+			roots:       []string{"lib/libd.so.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition: "reciprocal",
+			name:      "apex",
+			roots:     []string{"highest.apex.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "device/library",
+					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition: "reciprocal",
+			name:      "container",
+			roots:     []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "device/library",
+					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition: "reciprocal",
+			name:      "application",
+			roots:     []string{"application.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "device/library",
+					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+				},
+			},
+		},
+		{
+			condition: "reciprocal",
+			name:      "binary",
+			roots:     []string{"bin/bin1.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project: "device/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:reciprocal",
+					},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition:   "reciprocal",
+			name:        "library",
+			roots:       []string{"lib/libd.so.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition: "restricted",
+			name:      "apex",
+			roots:     []string{"highest.apex.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "base/library",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "device/library",
+					conditions: []string{"lib/liba.so.meta_lic:restricted"},
+				},
+				{
+					project:    "dynamic/binary",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project: "highest/apex",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libb.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "static/binary",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition: "restricted",
+			name:      "container",
+			roots:     []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "base/library",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project: "container/zip",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libb.so.meta_lic:restricted",
+					},
+				},
+				{
+					project:    "device/library",
+					conditions: []string{"lib/liba.so.meta_lic:restricted"},
+				},
+				{
+					project:    "dynamic/binary",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project: "static/binary",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition: "restricted",
+			name:      "application",
+			roots:     []string{"application.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project: "device/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libb.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "distributable/application",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libb.so.meta_lic:restricted",
+					},
+				},
+			},
+		},
+		{
+			condition: "restricted",
+			name:      "binary",
+			roots:     []string{"bin/bin1.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project: "device/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "static/binary",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+					},
+				},
+				{
+					project: "static/library",
+					conditions: []string{
+						"lib/liba.so.meta_lic:restricted",
+						"lib/libc.a.meta_lic:reciprocal",
+					},
+				},
+			},
+		},
+		{
+			condition:   "restricted",
+			name:        "library",
+			roots:       []string{"lib/libd.so.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition: "proprietary",
+			name:      "apex",
+			roots:     []string{"highest.apex.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "base/library",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "dynamic/binary",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "highest/apex",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+			},
+		},
+		{
+			condition: "proprietary",
+			name:      "container",
+			roots:     []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "base/library",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "container/zip",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "dynamic/binary",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+			},
+		},
+		{
+			condition: "proprietary",
+			name:      "application",
+			roots:     []string{"application.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "device/library",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+				{
+					project:    "distributable/application",
+					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+				},
+			},
+		},
+		{
+			condition:   "proprietary",
+			name:        "binary",
+			roots:       []string{"bin/bin1.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+		{
+			condition:   "proprietary",
+			name:        "library",
+			roots:       []string{"lib/libd.so.meta_lic"},
+			expectedOut: []projectShare{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+			expectedOut := &bytes.Buffer{}
+			for _, p := range tt.expectedOut {
+				expectedOut.WriteString(p.project)
+				for _, lc := range p.conditions {
+					expectedOut.WriteString(",")
+					expectedOut.WriteString("testdata/")
+					expectedOut.WriteString(tt.condition)
+					expectedOut.WriteString("/")
+					expectedOut.WriteString(lc)
+				}
+				expectedOut.WriteString("\n")
+			}
+
+			stdout := &bytes.Buffer{}
+			stderr := &bytes.Buffer{}
+
+			rootFiles := make([]string, 0, len(tt.roots))
+			for _, r := range tt.roots {
+				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+			}
+			err := listShare(stdout, stderr, rootFiles...)
+			if err != nil {
+				t.Fatalf("listshare: error = %v, stderr = %v", err, stderr)
+				return
+			}
+			if stderr.Len() > 0 {
+				t.Errorf("listshare: gotStderr = %v, want none", stderr)
+			}
+			out := stdout.String()
+			expected := expectedOut.String()
+			if out != expected {
+				outList := strings.Split(out, "\n")
+				expectedList := strings.Split(expected, "\n")
+				startLine := 0
+				for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
+					startLine++
+				}
+				t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
+					out, expected, startLine+1, outList[startLine], expectedList[startLine])
+			}
+		})
+	}
+}
diff --git a/tools/compliance/doc.go b/tools/compliance/doc.go
new file mode 100644
index 0000000..a47c1cf
--- /dev/null
+++ b/tools/compliance/doc.go
@@ -0,0 +1,77 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/*
+
+Package compliance provides an approved means for reading, consuming, and
+analyzing license metadata graphs.
+
+Assuming the license metadata and dependencies are fully and accurately
+recorded in the build system, any discrepancy between the official policy for
+open source license compliance and this code is a bug in this code.
+
+A few principal types to understand are LicenseGraph, LicenseCondition, and
+ResolutionSet.
+
+LicenseGraph
+------------
+
+A LicenseGraph is an immutable graph of the targets and dependencies reachable
+from a specific set of root targets. In general, the root targets will be the
+artifacts in a release or distribution. While conceptually immutable, parts of
+the graph may be loaded or evaluated lazily.
+
+LicenseCondition
+----------------
+
+A LicenseCondition is an immutable tuple pairing a condition name with an
+originating target. e.g. Per current policy, a static library licensed under an
+MIT license would pair a "notice" condition with the static library target, and
+a dynamic license licensed under GPL would pair a "restricted" condition with
+the dynamic library target.
+
+ResolutionSet
+-------------
+
+A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`
+tuples describing how license conditions apply to targets.
+
+`AttachesTo` is the trigger for acting. Distribution of the target invokes
+the policy.
+
+`ActsOn` is the target to share, give notice for, hide etc.
+
+`Resolves` is the license condition that the action resolves.
+
+Remember: Each license condition pairs a condition name with an originating
+target so each resolution in a ResolutionSet has two targets it applies to and
+one target from which it originates, all of which may be the same target.
+
+For most condition types, `ActsOn` and `Resolves.Origin` will be the same
+target. For example, a notice condition policy means attribution or notice must
+be given for the target where the condition originates. Likewise, a proprietary
+condition policy means the privacy of the target where the condition originates
+must be respected. i.e. The thing acted on is the origin.
+
+Restricted conditions are different. The infectious nature of restricted often
+means sharing code that is not the target where the restricted condition
+originates. Linking an MIT library to a GPL library implies a policy to share
+the MIT library despite the MIT license having no source sharing requirement.
+
+In this case, one or more resolution tuples will have the MIT license module in
+`ActsOn` and the restricted condition originating at the GPL library module in
+`Resolves`. These tuples will `AttachTo` every target that depends on the GPL
+library because shipping any of those targets trigger the policy to share the
+code.
+*/
+package compliance