1// 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 "bytes" 19 "flag" 20 "fmt" 21 "io" 22 "io/fs" 23 "os" 24 "path/filepath" 25 "sort" 26 "strings" 27 28 "android/soong/response" 29 "android/soong/tools/compliance" 30) 31 32var ( 33 failConflicts = fmt.Errorf("conflicts") 34 failNoneRequested = fmt.Errorf("\nNo metadata files requested") 35 failNoLicenses = fmt.Errorf("No licenses") 36) 37 38// 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() { 46 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) 92 93 // Must specify at least one root target. 94 if flags.NArg() == 0 { 95 flags.Usage() 96 os.Exit(2) 97 } 98 99 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()...) 129 if err != nil { 130 if err != failConflicts { 131 if err == failNoneRequested { 132 flags.Usage() 133 } 134 fmt.Fprintf(os.Stderr, "%s\n", err.Error()) 135 } 136 os.Exit(1) 137 } 138 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 } 145 os.Exit(0) 146} 147 148// checkShare implements the checkshare utility. 149func checkShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error { 150 151 if len(files) < 1 { 152 return failNoneRequested 153 } 154 155 // Read the license graph from the license metadata files (*.meta_lic). 156 licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) 157 if err != nil { 158 return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %w\n", files, os.Getenv("PWD"), err) 159 } 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} 179