blob: ae9382ef6d15e1e5026e047c389a0a7b39b496a7 [file] [log] [blame]
Bob Badour6dd00352021-10-01 15:21:58 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "compliance"
19 "flag"
20 "fmt"
21 "io"
22 "os"
23 "path/filepath"
24 "sort"
Bob Badour103eb0f2022-01-10 13:50:57 -080025 "strings"
Bob Badour6dd00352021-10-01 15:21:58 -070026)
27
28func init() {
29 flag.Usage = func() {
30 fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
31
32Outputs a csv file with 1 project per line in the first field followed
33by target:condition pairs describing why the project must be shared.
34
35Each target is the path to a generated license metadata file for a
36Soong module or Make target, and the license condition is either
37restricted (e.g. GPL) or reciprocal (e.g. MPL).
38`, filepath.Base(os.Args[0]))
39 }
40}
41
42var (
43 failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
Colin Cross35f79c32022-01-27 15:18:52 -080044 failNoLicenses = fmt.Errorf("No licenses found")
Bob Badour6dd00352021-10-01 15:21:58 -070045)
46
47func main() {
48 flag.Parse()
49
50 // Must specify at least one root target.
51 if flag.NArg() == 0 {
52 flag.Usage()
53 os.Exit(2)
54 }
55
56 err := listShare(os.Stdout, os.Stderr, flag.Args()...)
57 if err != nil {
58 if err == failNoneRequested {
59 flag.Usage()
60 }
61 fmt.Fprintf(os.Stderr, "%s\n", err.Error())
62 os.Exit(1)
63 }
64 os.Exit(0)
65}
66
67// listShare implements the listshare utility.
68func listShare(stdout, stderr io.Writer, files ...string) error {
69 // Must be at least one root file.
70 if len(files) < 1 {
71 return failNoneRequested
72 }
73
74 // Read the license graph from the license metadata files (*.meta_lic).
75 licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
76 if err != nil {
77 return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
78 }
79 if licenseGraph == nil {
80 return failNoLicenses
81 }
82
83 // shareSource contains all source-sharing resolutions.
84 shareSource := compliance.ResolveSourceSharing(licenseGraph)
85
86 // Group the resolutions by project.
Bob Badour103eb0f2022-01-10 13:50:57 -080087 presolution := make(map[string]compliance.LicenseConditionSet)
Bob Badour6dd00352021-10-01 15:21:58 -070088 for _, target := range shareSource.AttachesTo() {
89 rl := shareSource.Resolutions(target)
90 sort.Sort(rl)
91 for _, r := range rl {
92 for _, p := range r.ActsOn().Projects() {
93 if _, ok := presolution[p]; !ok {
Bob Badour103eb0f2022-01-10 13:50:57 -080094 presolution[p] = r.Resolves()
Bob Badour6dd00352021-10-01 15:21:58 -070095 continue
96 }
Bob Badour103eb0f2022-01-10 13:50:57 -080097 presolution[p] = presolution[p].Union(r.Resolves())
Bob Badour6dd00352021-10-01 15:21:58 -070098 }
99 }
100 }
101
102 // Sort the projects for repeatability/stability.
103 projects := make([]string, 0, len(presolution))
104 for p := range presolution {
105 projects = append(projects, p)
106 }
107 sort.Strings(projects)
108
109 // Output the sorted projects and the source-sharing license conditions that each project resolves.
110 for _, p := range projects {
Bob Badour103eb0f2022-01-10 13:50:57 -0800111 if presolution[p].IsEmpty() {
112 fmt.Fprintf(stdout, "%s\n", p)
113 } else {
114 fmt.Fprintf(stdout, "%s,%s\n", p, strings.Join(presolution[p].Names(), ","))
Bob Badour6dd00352021-10-01 15:21:58 -0700115 }
Bob Badour6dd00352021-10-01 15:21:58 -0700116 }
117
118 return nil
119}