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 "strings" 26 27 "android/soong/response" 28 "android/soong/tools/compliance" 29) 30 31var ( 32 failNoneRequested = fmt.Errorf("\nNo license metadata files requested") 33 failNoLicenses = fmt.Errorf("No licenses found") 34) 35 36type context struct { 37 stdout io.Writer 38 stderr io.Writer 39 rootFS fs.FS 40} 41 42func main() { 43 var expandedArgs []string 44 for _, arg := range os.Args[1:] { 45 if strings.HasPrefix(arg, "@") { 46 f, err := os.Open(strings.TrimPrefix(arg, "@")) 47 if err != nil { 48 fmt.Fprintln(os.Stderr, err.Error()) 49 os.Exit(1) 50 } 51 52 respArgs, err := response.ReadRspFile(f) 53 f.Close() 54 if err != nil { 55 fmt.Fprintln(os.Stderr, err.Error()) 56 os.Exit(1) 57 } 58 expandedArgs = append(expandedArgs, respArgs...) 59 } else { 60 expandedArgs = append(expandedArgs, arg) 61 } 62 } 63 64 flags := flag.NewFlagSet("flags", flag.ExitOnError) 65 66 outputFile := flags.String("o", "-", "Where to write the library list. (default stdout)") 67 68 flags.Usage = func() { 69 fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} 70 71Outputs a list of libraries used in the shipped images. 72 73Options: 74`, filepath.Base(os.Args[0])) 75 flags.PrintDefaults() 76 } 77 78 err := flags.Parse(expandedArgs) 79 if err != nil { 80 flags.Usage() 81 fmt.Fprintf(os.Stderr, "%v\n", err) 82 } 83 84 // Must specify at least one root target. 85 if flags.NArg() == 0 { 86 flags.Usage() 87 os.Exit(2) 88 } 89 90 if len(*outputFile) == 0 { 91 flags.Usage() 92 fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") 93 os.Exit(2) 94 } else { 95 dir, err := filepath.Abs(filepath.Dir(*outputFile)) 96 if err != nil { 97 fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) 98 os.Exit(1) 99 } 100 fi, err := os.Stat(dir) 101 if err != nil { 102 fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) 103 os.Exit(1) 104 } 105 if !fi.IsDir() { 106 fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) 107 os.Exit(1) 108 } 109 } 110 111 var ofile io.Writer 112 ofile = os.Stdout 113 if *outputFile != "-" { 114 ofile = &bytes.Buffer{} 115 } 116 117 ctx := &context{ofile, os.Stderr, compliance.FS} 118 119 err = shippedLibs(ctx, flags.Args()...) 120 if err != nil { 121 if err == failNoneRequested { 122 flags.Usage() 123 } 124 fmt.Fprintf(os.Stderr, "%s\n", err.Error()) 125 os.Exit(1) 126 } 127 if *outputFile != "-" { 128 err := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666) 129 if err != nil { 130 fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err) 131 os.Exit(1) 132 } 133 } 134 os.Exit(0) 135} 136 137// shippedLibs implements the shippedlibs utility. 138func shippedLibs(ctx *context, files ...string) error { 139 // Must be at least one root file. 140 if len(files) < 1 { 141 return failNoneRequested 142 } 143 144 // Read the license graph from the license metadata files (*.meta_lic). 145 licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files) 146 if err != nil { 147 return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) 148 } 149 if licenseGraph == nil { 150 return failNoLicenses 151 } 152 153 // rs contains all notice resolutions. 154 rs := compliance.ResolveNotices(licenseGraph) 155 156 ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs) 157 if err != nil { 158 return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err) 159 } 160 161 for lib := range ni.Libraries() { 162 fmt.Fprintln(ctx.stdout, lib) 163 } 164 return nil 165} 166