blob: 450290c6d60a5b485308160efd39f06f8f5d0159 [file] [log] [blame]
Bob Badoure6fdd142021-12-09 22:10:43 -08001// 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"
Bob Badour682e1ba2022-02-02 15:15:56 -080019 "compress/gzip"
Bob Badoure6fdd142021-12-09 22:10:43 -080020 "flag"
21 "fmt"
22 "io"
23 "io/fs"
24 "os"
25 "path/filepath"
Bob Badourab5cfbd2022-10-17 17:40:04 -070026 "sort"
Bob Badoure6fdd142021-12-09 22:10:43 -080027 "strings"
Colin Cross38a61932022-01-27 15:26:49 -080028
Bob Badour986a8392022-06-03 10:42:27 -070029 "android/soong/response"
Colin Cross38a61932022-01-27 15:26:49 -080030 "android/soong/tools/compliance"
Colin Crossbb45f8c2022-01-28 15:18:19 -080031
32 "github.com/google/blueprint/deptools"
Bob Badoure6fdd142021-12-09 22:10:43 -080033)
34
35var (
Bob Badoure6fdd142021-12-09 22:10:43 -080036 failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
37 failNoLicenses = fmt.Errorf("No licenses found")
38)
39
40type context struct {
41 stdout io.Writer
42 stderr io.Writer
43 rootFS fs.FS
Bob Badour49dd4f72022-02-04 14:49:01 -080044 product string
Bob Badour682e1ba2022-02-02 15:15:56 -080045 stripPrefix []string
46 title string
Colin Crossbb45f8c2022-01-28 15:18:19 -080047 deps *[]string
Bob Badoure6fdd142021-12-09 22:10:43 -080048}
49
Bob Badour682e1ba2022-02-02 15:15:56 -080050func (ctx context) strip(installPath string) string {
51 for _, prefix := range ctx.stripPrefix {
52 if strings.HasPrefix(installPath, prefix) {
53 p := strings.TrimPrefix(installPath, prefix)
54 if 0 == len(p) {
Bob Badour49dd4f72022-02-04 14:49:01 -080055 p = ctx.product
Bob Badour682e1ba2022-02-02 15:15:56 -080056 }
57 if 0 == len(p) {
58 continue
59 }
60 return p
61 }
62 }
63 return installPath
64}
65
Bob Badour682e1ba2022-02-02 15:15:56 -080066// newMultiString creates a flag that allows multiple values in an array.
Bob Badour986a8392022-06-03 10:42:27 -070067func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
Bob Badour682e1ba2022-02-02 15:15:56 -080068 var f multiString
Bob Badour986a8392022-06-03 10:42:27 -070069 flags.Var(&f, name, usage)
Bob Badour682e1ba2022-02-02 15:15:56 -080070 return &f
71}
72
73// multiString implements the flag `Value` interface for multiple strings.
74type multiString []string
75
76func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
77func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
78
Bob Badoure6fdd142021-12-09 22:10:43 -080079func main() {
Bob Badour986a8392022-06-03 10:42:27 -070080 var expandedArgs []string
81 for _, arg := range os.Args[1:] {
82 if strings.HasPrefix(arg, "@") {
83 f, err := os.Open(strings.TrimPrefix(arg, "@"))
84 if err != nil {
85 fmt.Fprintln(os.Stderr, err.Error())
86 os.Exit(1)
87 }
88
89 respArgs, err := response.ReadRspFile(f)
90 f.Close()
91 if err != nil {
92 fmt.Fprintln(os.Stderr, err.Error())
93 os.Exit(1)
94 }
95 expandedArgs = append(expandedArgs, respArgs...)
96 } else {
97 expandedArgs = append(expandedArgs, arg)
98 }
99 }
100
101 flags := flag.NewFlagSet("flags", flag.ExitOnError)
102
103 flags.Usage = func() {
104 fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
105
106Outputs a text NOTICE file.
107
108Options:
109`, filepath.Base(os.Args[0]))
110 flags.PrintDefaults()
111 }
112
113 outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
114 depsFile := flags.String("d", "", "Where to write the deps file")
115 product := flags.String("product", "", "The name of the product for which the notice is generated.")
116 stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
117 title := flags.String("title", "", "The title of the notice file.")
118
119 flags.Parse(expandedArgs)
Bob Badoure6fdd142021-12-09 22:10:43 -0800120
121 // Must specify at least one root target.
Bob Badour986a8392022-06-03 10:42:27 -0700122 if flags.NArg() == 0 {
123 flags.Usage()
Bob Badoure6fdd142021-12-09 22:10:43 -0800124 os.Exit(2)
125 }
126
127 if len(*outputFile) == 0 {
Bob Badour986a8392022-06-03 10:42:27 -0700128 flags.Usage()
Bob Badoure6fdd142021-12-09 22:10:43 -0800129 fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
130 os.Exit(2)
131 } else {
132 dir, err := filepath.Abs(filepath.Dir(*outputFile))
133 if err != nil {
Colin Cross179ec3e2022-01-27 15:47:09 -0800134 fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
Bob Badoure6fdd142021-12-09 22:10:43 -0800135 os.Exit(1)
136 }
137 fi, err := os.Stat(dir)
138 if err != nil {
Colin Cross179ec3e2022-01-27 15:47:09 -0800139 fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
Bob Badoure6fdd142021-12-09 22:10:43 -0800140 os.Exit(1)
141 }
142 if !fi.IsDir() {
143 fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
144 os.Exit(1)
145 }
146 }
147
148 var ofile io.Writer
Bob Badour682e1ba2022-02-02 15:15:56 -0800149 var closer io.Closer
Bob Badoure6fdd142021-12-09 22:10:43 -0800150 ofile = os.Stdout
Bob Badour682e1ba2022-02-02 15:15:56 -0800151 var obuf *bytes.Buffer
Bob Badoure6fdd142021-12-09 22:10:43 -0800152 if *outputFile != "-" {
Bob Badour682e1ba2022-02-02 15:15:56 -0800153 obuf = &bytes.Buffer{}
154 ofile = obuf
155 }
156 if strings.HasSuffix(*outputFile, ".gz") {
157 ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)
158 closer = ofile.(io.Closer)
Bob Badoure6fdd142021-12-09 22:10:43 -0800159 }
160
Colin Crossbb45f8c2022-01-28 15:18:19 -0800161 var deps []string
162
Bob Badourc778e4c2022-03-22 13:05:19 -0700163 ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
Bob Badoure6fdd142021-12-09 22:10:43 -0800164
Bob Badour986a8392022-06-03 10:42:27 -0700165 err := textNotice(ctx, flags.Args()...)
Bob Badoure6fdd142021-12-09 22:10:43 -0800166 if err != nil {
167 if err == failNoneRequested {
Bob Badour986a8392022-06-03 10:42:27 -0700168 flags.Usage()
Bob Badoure6fdd142021-12-09 22:10:43 -0800169 }
170 fmt.Fprintf(os.Stderr, "%s\n", err.Error())
171 os.Exit(1)
172 }
Bob Badour682e1ba2022-02-02 15:15:56 -0800173 if closer != nil {
174 closer.Close()
175 }
176
Bob Badoure6fdd142021-12-09 22:10:43 -0800177 if *outputFile != "-" {
Bob Badour682e1ba2022-02-02 15:15:56 -0800178 err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
Bob Badoure6fdd142021-12-09 22:10:43 -0800179 if err != nil {
Colin Cross179ec3e2022-01-27 15:47:09 -0800180 fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
Bob Badoure6fdd142021-12-09 22:10:43 -0800181 os.Exit(1)
182 }
183 }
Colin Crossbb45f8c2022-01-28 15:18:19 -0800184 if *depsFile != "" {
185 err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
186 if err != nil {
187 fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
188 os.Exit(1)
189 }
190 }
Bob Badoure6fdd142021-12-09 22:10:43 -0800191 os.Exit(0)
192}
193
194// textNotice implements the textNotice utility.
195func textNotice(ctx *context, files ...string) error {
196 // Must be at least one root file.
197 if len(files) < 1 {
198 return failNoneRequested
199 }
200
201 // Read the license graph from the license metadata files (*.meta_lic).
202 licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
203 if err != nil {
204 return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
205 }
206 if licenseGraph == nil {
207 return failNoLicenses
208 }
209
210 // rs contains all notice resolutions.
211 rs := compliance.ResolveNotices(licenseGraph)
212
213 ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
214 if err != nil {
215 return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
216 }
217
Bob Badoure9b38c12022-02-09 15:56:59 -0800218 if len(ctx.title) > 0 {
Bob Badour49dd4f72022-02-04 14:49:01 -0800219 fmt.Fprintf(ctx.stdout, "%s\n\n", ctx.title)
220 }
Bob Badoure6fdd142021-12-09 22:10:43 -0800221 for h := range ni.Hashes() {
222 fmt.Fprintln(ctx.stdout, "==============================================================================")
223 for _, libName := range ni.HashLibs(h) {
224 fmt.Fprintf(ctx.stdout, "%s used by:\n", libName)
225 for _, installPath := range ni.HashLibInstalls(h, libName) {
Bob Badour682e1ba2022-02-02 15:15:56 -0800226 fmt.Fprintf(ctx.stdout, " %s\n", ctx.strip(installPath))
Bob Badoure6fdd142021-12-09 22:10:43 -0800227 }
228 fmt.Fprintln(ctx.stdout)
229 }
230 ctx.stdout.Write(ni.HashText(h))
231 fmt.Fprintln(ctx.stdout)
232 }
Colin Crossbb45f8c2022-01-28 15:18:19 -0800233
Bob Badourab5cfbd2022-10-17 17:40:04 -0700234 *ctx.deps = ni.InputFiles()
235 sort.Strings(*ctx.deps)
Colin Crossbb45f8c2022-01-28 15:18:19 -0800236
Bob Badoure6fdd142021-12-09 22:10:43 -0800237 return nil
238}