blob: f7b4cd2e392636e93e3c3f6df6e0c7e5a6b31d03 [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 (
Bob Badour986a8392022-06-03 10:42:27 -070018 "bytes"
Bob Badour6dd00352021-10-01 15:21:58 -070019 "flag"
20 "fmt"
21 "io"
Bob Badourc778e4c2022-03-22 13:05:19 -070022 "io/fs"
Bob Badour6dd00352021-10-01 15:21:58 -070023 "os"
24 "path/filepath"
25 "sort"
Bob Badour986a8392022-06-03 10:42:27 -070026 "strings"
Colin Cross38a61932022-01-27 15:26:49 -080027
Bob Badour986a8392022-06-03 10:42:27 -070028 "android/soong/response"
Colin Cross38a61932022-01-27 15:26:49 -080029 "android/soong/tools/compliance"
Bob Badour6dd00352021-10-01 15:21:58 -070030)
31
Bob Badour6dd00352021-10-01 15:21:58 -070032var (
Colin Cross35f79c32022-01-27 15:18:52 -080033 failConflicts = fmt.Errorf("conflicts")
Bob Badour6dd00352021-10-01 15:21:58 -070034 failNoneRequested = fmt.Errorf("\nNo metadata files requested")
Colin Cross35f79c32022-01-27 15:18:52 -080035 failNoLicenses = fmt.Errorf("No licenses")
Bob Badour6dd00352021-10-01 15:21:58 -070036)
37
Bob Badour6dd00352021-10-01 15:21:58 -070038// byError orders conflicts by error string
39type byError []compliance.SourceSharePrivacyConflict
40
41func (l byError) Len() int { return len(l) }
42func (l byError) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
43func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() }
44
45func main() {
Bob Badour986a8392022-06-03 10:42:27 -070046 var expandedArgs []string
47 for _, arg := range os.Args[1:] {
48 if strings.HasPrefix(arg, "@") {
49 f, err := os.Open(strings.TrimPrefix(arg, "@"))
50 if err != nil {
51 fmt.Fprintln(os.Stderr, err.Error())
52 os.Exit(1)
53 }
54
55 respArgs, err := response.ReadRspFile(f)
56 f.Close()
57 if err != nil {
58 fmt.Fprintln(os.Stderr, err.Error())
59 os.Exit(1)
60 }
61 expandedArgs = append(expandedArgs, respArgs...)
62 } else {
63 expandedArgs = append(expandedArgs, arg)
64 }
65 }
66
67 flags := flag.NewFlagSet("flags", flag.ExitOnError)
68
69 flags.Usage = func() {
70 fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...}
71
72Reports on stderr any targets where policy says that the source both
73must and must not be shared. The error report indicates the target, the
74license condition that has a source privacy policy, and the license
75condition that has a source sharing policy.
76
77Any given target may appear multiple times with different combinations
78of conflicting license conditions.
79
80If all the source code that policy says must be shared may be shared,
81outputs "PASS" to stdout and exits with status 0.
82
83If policy says any source must both be shared and not be shared,
84outputs "FAIL" to stdout and exits with status 1.
85`, filepath.Base(os.Args[0]))
86 flags.PrintDefaults()
87 }
88
89 outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
90
91 flags.Parse(expandedArgs)
Bob Badour6dd00352021-10-01 15:21:58 -070092
93 // Must specify at least one root target.
Bob Badour986a8392022-06-03 10:42:27 -070094 if flags.NArg() == 0 {
95 flags.Usage()
Bob Badour6dd00352021-10-01 15:21:58 -070096 os.Exit(2)
97 }
98
Bob Badour986a8392022-06-03 10:42:27 -070099 if len(*outputFile) == 0 {
100 flags.Usage()
101 fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
102 os.Exit(2)
103 } else {
104 dir, err := filepath.Abs(filepath.Dir(*outputFile))
105 if err != nil {
106 fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
107 os.Exit(1)
108 }
109 fi, err := os.Stat(dir)
110 if err != nil {
111 fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
112 os.Exit(1)
113 }
114 if !fi.IsDir() {
115 fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
116 os.Exit(1)
117 }
118 }
119
120 var ofile io.Writer
121 ofile = os.Stdout
122 var obuf *bytes.Buffer
123 if *outputFile != "-" {
124 obuf = &bytes.Buffer{}
125 ofile = obuf
126 }
127
128 err := checkShare(ofile, os.Stderr, compliance.FS, flags.Args()...)
Bob Badour6dd00352021-10-01 15:21:58 -0700129 if err != nil {
130 if err != failConflicts {
131 if err == failNoneRequested {
Bob Badour986a8392022-06-03 10:42:27 -0700132 flags.Usage()
Bob Badour6dd00352021-10-01 15:21:58 -0700133 }
134 fmt.Fprintf(os.Stderr, "%s\n", err.Error())
135 }
136 os.Exit(1)
137 }
Bob Badour986a8392022-06-03 10:42:27 -0700138 if *outputFile != "-" {
139 err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
140 if err != nil {
141 fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
142 os.Exit(1)
143 }
144 }
Bob Badour6dd00352021-10-01 15:21:58 -0700145 os.Exit(0)
146}
147
148// checkShare implements the checkshare utility.
Bob Badourc778e4c2022-03-22 13:05:19 -0700149func checkShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {
Bob Badour6dd00352021-10-01 15:21:58 -0700150
151 if len(files) < 1 {
152 return failNoneRequested
153 }
154
155 // Read the license graph from the license metadata files (*.meta_lic).
Bob Badourc778e4c2022-03-22 13:05:19 -0700156 licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
Bob Badour6dd00352021-10-01 15:21:58 -0700157 if err != nil {
Bob Badour986a8392022-06-03 10:42:27 -0700158 return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %w\n", files, os.Getenv("PWD"), err)
Bob Badour6dd00352021-10-01 15:21:58 -0700159 }
160 if licenseGraph == nil {
161 return failNoLicenses
162 }
163
164 // Apply policy to find conflicts and report them to stderr lexicographically ordered.
165 conflicts := compliance.ConflictingSharedPrivateSource(licenseGraph)
166 sort.Sort(byError(conflicts))
167 for _, conflict := range conflicts {
168 fmt.Fprintln(stderr, conflict.Error())
169 }
170
171 // Indicate pass or fail on stdout.
172 if len(conflicts) > 0 {
173 fmt.Fprintln(stdout, "FAIL")
174 return failConflicts
175 }
176 fmt.Fprintln(stdout, "PASS")
177 return nil
178}