xref: /aosp_15_r20/build/make/tools/compliance/noticeindex.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker// Copyright 2021 Google LLC
2*9e94795aSAndroid Build Coastguard Worker//
3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*9e94795aSAndroid Build Coastguard Worker//
7*9e94795aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*9e94795aSAndroid Build Coastguard Worker//
9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*9e94795aSAndroid Build Coastguard Worker// limitations under the License.
14*9e94795aSAndroid Build Coastguard Worker
15*9e94795aSAndroid Build Coastguard Workerpackage compliance
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport (
18*9e94795aSAndroid Build Coastguard Worker	"crypto/md5"
19*9e94795aSAndroid Build Coastguard Worker	"fmt"
20*9e94795aSAndroid Build Coastguard Worker	"io"
21*9e94795aSAndroid Build Coastguard Worker	"io/fs"
22*9e94795aSAndroid Build Coastguard Worker	"net/url"
23*9e94795aSAndroid Build Coastguard Worker	"path/filepath"
24*9e94795aSAndroid Build Coastguard Worker	"regexp"
25*9e94795aSAndroid Build Coastguard Worker	"sort"
26*9e94795aSAndroid Build Coastguard Worker	"strings"
27*9e94795aSAndroid Build Coastguard Worker
28*9e94795aSAndroid Build Coastguard Worker	"android/soong/tools/compliance/projectmetadata"
29*9e94795aSAndroid Build Coastguard Worker)
30*9e94795aSAndroid Build Coastguard Worker
31*9e94795aSAndroid Build Coastguard Workervar (
32*9e94795aSAndroid Build Coastguard Worker	licensesPathRegexp = regexp.MustCompile(`licen[cs]es?/`)
33*9e94795aSAndroid Build Coastguard Worker)
34*9e94795aSAndroid Build Coastguard Worker
35*9e94795aSAndroid Build Coastguard Worker// NoticeIndex transforms license metadata into license text hashes, library
36*9e94795aSAndroid Build Coastguard Worker// names, and install paths indexing them for fast lookup/iteration.
37*9e94795aSAndroid Build Coastguard Workertype NoticeIndex struct {
38*9e94795aSAndroid Build Coastguard Worker	// lg identifies the license graph to which the index applies.
39*9e94795aSAndroid Build Coastguard Worker	lg *LicenseGraph
40*9e94795aSAndroid Build Coastguard Worker	// pmix indexes project metadata
41*9e94795aSAndroid Build Coastguard Worker	pmix *projectmetadata.Index
42*9e94795aSAndroid Build Coastguard Worker	// rs identifies the set of resolutions upon which the index is based.
43*9e94795aSAndroid Build Coastguard Worker	rs ResolutionSet
44*9e94795aSAndroid Build Coastguard Worker	// shipped identifies the set of target nodes shipped directly or as derivative works.
45*9e94795aSAndroid Build Coastguard Worker	shipped TargetNodeSet
46*9e94795aSAndroid Build Coastguard Worker	// rootFS locates the root of the file system from which to read the files.
47*9e94795aSAndroid Build Coastguard Worker	rootFS fs.FS
48*9e94795aSAndroid Build Coastguard Worker	// hash maps license text filenames to content hashes
49*9e94795aSAndroid Build Coastguard Worker	hash map[string]hash
50*9e94795aSAndroid Build Coastguard Worker	// text maps content hashes to content
51*9e94795aSAndroid Build Coastguard Worker	text map[hash][]byte
52*9e94795aSAndroid Build Coastguard Worker	// hashLibInstall maps hashes to libraries to install paths.
53*9e94795aSAndroid Build Coastguard Worker	hashLibInstall map[hash]map[string]map[string]struct{}
54*9e94795aSAndroid Build Coastguard Worker	// installHashLib maps install paths to libraries to hashes.
55*9e94795aSAndroid Build Coastguard Worker	installHashLib map[string]map[hash]map[string]struct{}
56*9e94795aSAndroid Build Coastguard Worker	// libHash maps libraries to hashes.
57*9e94795aSAndroid Build Coastguard Worker	libHash map[string]map[hash]struct{}
58*9e94795aSAndroid Build Coastguard Worker	// targetHash maps target nodes to hashes.
59*9e94795aSAndroid Build Coastguard Worker	targetHashes map[*TargetNode]map[hash]struct{}
60*9e94795aSAndroid Build Coastguard Worker	// projectName maps project directory names to project name text.
61*9e94795aSAndroid Build Coastguard Worker	projectName map[string]string
62*9e94795aSAndroid Build Coastguard Worker	// files lists all the files accessed during indexing
63*9e94795aSAndroid Build Coastguard Worker	files []string
64*9e94795aSAndroid Build Coastguard Worker}
65*9e94795aSAndroid Build Coastguard Worker
66*9e94795aSAndroid Build Coastguard Worker// IndexLicenseTexts creates a hashed index of license texts for `lg` and `rs`
67*9e94795aSAndroid Build Coastguard Worker// using the files rooted at `rootFS`.
68*9e94795aSAndroid Build Coastguard Workerfunc IndexLicenseTexts(rootFS fs.FS, lg *LicenseGraph, rs ResolutionSet) (*NoticeIndex, error) {
69*9e94795aSAndroid Build Coastguard Worker	if rs == nil {
70*9e94795aSAndroid Build Coastguard Worker		rs = ResolveNotices(lg)
71*9e94795aSAndroid Build Coastguard Worker	}
72*9e94795aSAndroid Build Coastguard Worker	ni := &NoticeIndex{
73*9e94795aSAndroid Build Coastguard Worker		lg:             lg,
74*9e94795aSAndroid Build Coastguard Worker		pmix:           projectmetadata.NewIndex(rootFS),
75*9e94795aSAndroid Build Coastguard Worker		rs:             rs,
76*9e94795aSAndroid Build Coastguard Worker		shipped:        ShippedNodes(lg),
77*9e94795aSAndroid Build Coastguard Worker		rootFS:         rootFS,
78*9e94795aSAndroid Build Coastguard Worker		hash:           make(map[string]hash),
79*9e94795aSAndroid Build Coastguard Worker		text:           make(map[hash][]byte),
80*9e94795aSAndroid Build Coastguard Worker		hashLibInstall: make(map[hash]map[string]map[string]struct{}),
81*9e94795aSAndroid Build Coastguard Worker		installHashLib: make(map[string]map[hash]map[string]struct{}),
82*9e94795aSAndroid Build Coastguard Worker		libHash:        make(map[string]map[hash]struct{}),
83*9e94795aSAndroid Build Coastguard Worker		targetHashes:   make(map[*TargetNode]map[hash]struct{}),
84*9e94795aSAndroid Build Coastguard Worker		projectName:    make(map[string]string),
85*9e94795aSAndroid Build Coastguard Worker	}
86*9e94795aSAndroid Build Coastguard Worker
87*9e94795aSAndroid Build Coastguard Worker	// index adds all license texts for `tn` to the index.
88*9e94795aSAndroid Build Coastguard Worker	index := func(tn *TargetNode) (map[hash]struct{}, error) {
89*9e94795aSAndroid Build Coastguard Worker		if hashes, ok := ni.targetHashes[tn]; ok {
90*9e94795aSAndroid Build Coastguard Worker			return hashes, nil
91*9e94795aSAndroid Build Coastguard Worker		}
92*9e94795aSAndroid Build Coastguard Worker		hashes := make(map[hash]struct{})
93*9e94795aSAndroid Build Coastguard Worker		for _, text := range tn.LicenseTexts() {
94*9e94795aSAndroid Build Coastguard Worker			fname := strings.SplitN(text, ":", 2)[0]
95*9e94795aSAndroid Build Coastguard Worker			if _, ok := ni.hash[fname]; !ok {
96*9e94795aSAndroid Build Coastguard Worker				err := ni.addText(fname)
97*9e94795aSAndroid Build Coastguard Worker				if err != nil {
98*9e94795aSAndroid Build Coastguard Worker					return nil, err
99*9e94795aSAndroid Build Coastguard Worker				}
100*9e94795aSAndroid Build Coastguard Worker			}
101*9e94795aSAndroid Build Coastguard Worker			hash := ni.hash[fname]
102*9e94795aSAndroid Build Coastguard Worker			if _, ok := hashes[hash]; !ok {
103*9e94795aSAndroid Build Coastguard Worker				hashes[hash] = struct{}{}
104*9e94795aSAndroid Build Coastguard Worker			}
105*9e94795aSAndroid Build Coastguard Worker		}
106*9e94795aSAndroid Build Coastguard Worker		ni.targetHashes[tn] = hashes
107*9e94795aSAndroid Build Coastguard Worker		return hashes, nil
108*9e94795aSAndroid Build Coastguard Worker	}
109*9e94795aSAndroid Build Coastguard Worker
110*9e94795aSAndroid Build Coastguard Worker	link := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) error {
111*9e94795aSAndroid Build Coastguard Worker		for h := range hashes {
112*9e94795aSAndroid Build Coastguard Worker			libName, err := ni.getLibName(tn, h)
113*9e94795aSAndroid Build Coastguard Worker			if err != nil {
114*9e94795aSAndroid Build Coastguard Worker				return err
115*9e94795aSAndroid Build Coastguard Worker			}
116*9e94795aSAndroid Build Coastguard Worker			if _, ok := ni.libHash[libName]; !ok {
117*9e94795aSAndroid Build Coastguard Worker				ni.libHash[libName] = make(map[hash]struct{})
118*9e94795aSAndroid Build Coastguard Worker			}
119*9e94795aSAndroid Build Coastguard Worker			if _, ok := ni.hashLibInstall[h]; !ok {
120*9e94795aSAndroid Build Coastguard Worker				ni.hashLibInstall[h] = make(map[string]map[string]struct{})
121*9e94795aSAndroid Build Coastguard Worker			}
122*9e94795aSAndroid Build Coastguard Worker			if _, ok := ni.libHash[libName][h]; !ok {
123*9e94795aSAndroid Build Coastguard Worker				ni.libHash[libName][h] = struct{}{}
124*9e94795aSAndroid Build Coastguard Worker			}
125*9e94795aSAndroid Build Coastguard Worker			for _, installPath := range installPaths {
126*9e94795aSAndroid Build Coastguard Worker				if _, ok := ni.installHashLib[installPath]; !ok {
127*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath] = make(map[hash]map[string]struct{})
128*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath][h] = make(map[string]struct{})
129*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath][h][libName] = struct{}{}
130*9e94795aSAndroid Build Coastguard Worker				} else if _, ok = ni.installHashLib[installPath][h]; !ok {
131*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath][h] = make(map[string]struct{})
132*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath][h][libName] = struct{}{}
133*9e94795aSAndroid Build Coastguard Worker				} else if _, ok = ni.installHashLib[installPath][h][libName]; !ok {
134*9e94795aSAndroid Build Coastguard Worker					ni.installHashLib[installPath][h][libName] = struct{}{}
135*9e94795aSAndroid Build Coastguard Worker				}
136*9e94795aSAndroid Build Coastguard Worker				if _, ok := ni.hashLibInstall[h]; !ok {
137*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h] = make(map[string]map[string]struct{})
138*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h][libName] = make(map[string]struct{})
139*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h][libName][installPath] = struct{}{}
140*9e94795aSAndroid Build Coastguard Worker				} else if _, ok = ni.hashLibInstall[h][libName]; !ok {
141*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h][libName] = make(map[string]struct{})
142*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h][libName][installPath] = struct{}{}
143*9e94795aSAndroid Build Coastguard Worker				} else if _, ok = ni.hashLibInstall[h][libName][installPath]; !ok {
144*9e94795aSAndroid Build Coastguard Worker					ni.hashLibInstall[h][libName][installPath] = struct{}{}
145*9e94795aSAndroid Build Coastguard Worker				}
146*9e94795aSAndroid Build Coastguard Worker			}
147*9e94795aSAndroid Build Coastguard Worker		}
148*9e94795aSAndroid Build Coastguard Worker		return nil
149*9e94795aSAndroid Build Coastguard Worker	}
150*9e94795aSAndroid Build Coastguard Worker
151*9e94795aSAndroid Build Coastguard Worker	cacheMetadata := func(tn *TargetNode) {
152*9e94795aSAndroid Build Coastguard Worker		ni.pmix.MetadataForProjects(tn.Projects()...)
153*9e94795aSAndroid Build Coastguard Worker	}
154*9e94795aSAndroid Build Coastguard Worker
155*9e94795aSAndroid Build Coastguard Worker	// returns error from walk below.
156*9e94795aSAndroid Build Coastguard Worker	var err error
157*9e94795aSAndroid Build Coastguard Worker
158*9e94795aSAndroid Build Coastguard Worker	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
159*9e94795aSAndroid Build Coastguard Worker		if err != nil {
160*9e94795aSAndroid Build Coastguard Worker			return false
161*9e94795aSAndroid Build Coastguard Worker		}
162*9e94795aSAndroid Build Coastguard Worker		if !ni.shipped.Contains(tn) {
163*9e94795aSAndroid Build Coastguard Worker			return false
164*9e94795aSAndroid Build Coastguard Worker		}
165*9e94795aSAndroid Build Coastguard Worker		go cacheMetadata(tn)
166*9e94795aSAndroid Build Coastguard Worker		installPaths := getInstallPaths(tn, path)
167*9e94795aSAndroid Build Coastguard Worker		var hashes map[hash]struct{}
168*9e94795aSAndroid Build Coastguard Worker		hashes, err = index(tn)
169*9e94795aSAndroid Build Coastguard Worker		if err != nil {
170*9e94795aSAndroid Build Coastguard Worker			return false
171*9e94795aSAndroid Build Coastguard Worker		}
172*9e94795aSAndroid Build Coastguard Worker		err = link(tn, hashes, installPaths)
173*9e94795aSAndroid Build Coastguard Worker		if err != nil {
174*9e94795aSAndroid Build Coastguard Worker			return false
175*9e94795aSAndroid Build Coastguard Worker		}
176*9e94795aSAndroid Build Coastguard Worker		if tn.IsContainer() {
177*9e94795aSAndroid Build Coastguard Worker			return true
178*9e94795aSAndroid Build Coastguard Worker		}
179*9e94795aSAndroid Build Coastguard Worker
180*9e94795aSAndroid Build Coastguard Worker		for _, r := range rs.Resolutions(tn) {
181*9e94795aSAndroid Build Coastguard Worker			hashes, err = index(r.actsOn)
182*9e94795aSAndroid Build Coastguard Worker			if err != nil {
183*9e94795aSAndroid Build Coastguard Worker				return false
184*9e94795aSAndroid Build Coastguard Worker			}
185*9e94795aSAndroid Build Coastguard Worker			err = link(r.actsOn, hashes, installPaths)
186*9e94795aSAndroid Build Coastguard Worker			if err != nil {
187*9e94795aSAndroid Build Coastguard Worker				return false
188*9e94795aSAndroid Build Coastguard Worker			}
189*9e94795aSAndroid Build Coastguard Worker		}
190*9e94795aSAndroid Build Coastguard Worker		return false
191*9e94795aSAndroid Build Coastguard Worker	})
192*9e94795aSAndroid Build Coastguard Worker
193*9e94795aSAndroid Build Coastguard Worker	if err != nil {
194*9e94795aSAndroid Build Coastguard Worker		return nil, err
195*9e94795aSAndroid Build Coastguard Worker	}
196*9e94795aSAndroid Build Coastguard Worker
197*9e94795aSAndroid Build Coastguard Worker	return ni, nil
198*9e94795aSAndroid Build Coastguard Worker}
199*9e94795aSAndroid Build Coastguard Worker
200*9e94795aSAndroid Build Coastguard Worker// Hashes returns an ordered channel of the hashed license texts.
201*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) Hashes() chan hash {
202*9e94795aSAndroid Build Coastguard Worker	c := make(chan hash)
203*9e94795aSAndroid Build Coastguard Worker	go func() {
204*9e94795aSAndroid Build Coastguard Worker		libs := make([]string, 0, len(ni.libHash))
205*9e94795aSAndroid Build Coastguard Worker		for libName := range ni.libHash {
206*9e94795aSAndroid Build Coastguard Worker			libs = append(libs, libName)
207*9e94795aSAndroid Build Coastguard Worker		}
208*9e94795aSAndroid Build Coastguard Worker		sort.Strings(libs)
209*9e94795aSAndroid Build Coastguard Worker		hashes := make(map[hash]struct{})
210*9e94795aSAndroid Build Coastguard Worker		for _, libName := range libs {
211*9e94795aSAndroid Build Coastguard Worker			hl := make([]hash, 0, len(ni.libHash[libName]))
212*9e94795aSAndroid Build Coastguard Worker			for h := range ni.libHash[libName] {
213*9e94795aSAndroid Build Coastguard Worker				if _, ok := hashes[h]; ok {
214*9e94795aSAndroid Build Coastguard Worker					continue
215*9e94795aSAndroid Build Coastguard Worker				}
216*9e94795aSAndroid Build Coastguard Worker				hashes[h] = struct{}{}
217*9e94795aSAndroid Build Coastguard Worker				hl = append(hl, h)
218*9e94795aSAndroid Build Coastguard Worker			}
219*9e94795aSAndroid Build Coastguard Worker			if len(hl) > 0 {
220*9e94795aSAndroid Build Coastguard Worker				sort.Sort(hashList{ni, libName, "", &hl})
221*9e94795aSAndroid Build Coastguard Worker				for _, h := range hl {
222*9e94795aSAndroid Build Coastguard Worker					c <- h
223*9e94795aSAndroid Build Coastguard Worker				}
224*9e94795aSAndroid Build Coastguard Worker			}
225*9e94795aSAndroid Build Coastguard Worker		}
226*9e94795aSAndroid Build Coastguard Worker		close(c)
227*9e94795aSAndroid Build Coastguard Worker	}()
228*9e94795aSAndroid Build Coastguard Worker	return c
229*9e94795aSAndroid Build Coastguard Worker
230*9e94795aSAndroid Build Coastguard Worker}
231*9e94795aSAndroid Build Coastguard Worker
232*9e94795aSAndroid Build Coastguard Worker// InputFiles returns the complete list of files read during indexing.
233*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) InputFiles() []string {
234*9e94795aSAndroid Build Coastguard Worker	projectMeta := ni.pmix.AllMetadataFiles()
235*9e94795aSAndroid Build Coastguard Worker	files := make([]string, 0, len(ni.files) + len(ni.lg.targets) + len(projectMeta))
236*9e94795aSAndroid Build Coastguard Worker	files = append(files, ni.files...)
237*9e94795aSAndroid Build Coastguard Worker	for f := range ni.lg.targets {
238*9e94795aSAndroid Build Coastguard Worker		files = append(files, f)
239*9e94795aSAndroid Build Coastguard Worker	}
240*9e94795aSAndroid Build Coastguard Worker	files = append(files, projectMeta...)
241*9e94795aSAndroid Build Coastguard Worker	return files
242*9e94795aSAndroid Build Coastguard Worker}
243*9e94795aSAndroid Build Coastguard Worker
244*9e94795aSAndroid Build Coastguard Worker// HashLibs returns the ordered array of library names using the license text
245*9e94795aSAndroid Build Coastguard Worker// hashed as `h`.
246*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) HashLibs(h hash) []string {
247*9e94795aSAndroid Build Coastguard Worker	libs := make([]string, 0, len(ni.hashLibInstall[h]))
248*9e94795aSAndroid Build Coastguard Worker	for libName := range ni.hashLibInstall[h] {
249*9e94795aSAndroid Build Coastguard Worker		libs = append(libs, libName)
250*9e94795aSAndroid Build Coastguard Worker	}
251*9e94795aSAndroid Build Coastguard Worker	sort.Strings(libs)
252*9e94795aSAndroid Build Coastguard Worker	return libs
253*9e94795aSAndroid Build Coastguard Worker}
254*9e94795aSAndroid Build Coastguard Worker
255*9e94795aSAndroid Build Coastguard Worker// HashLibInstalls returns the ordered array of install paths referencing
256*9e94795aSAndroid Build Coastguard Worker// library `libName` using the license text hashed as `h`.
257*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) HashLibInstalls(h hash, libName string) []string {
258*9e94795aSAndroid Build Coastguard Worker	installs := make([]string, 0, len(ni.hashLibInstall[h][libName]))
259*9e94795aSAndroid Build Coastguard Worker	for installPath := range ni.hashLibInstall[h][libName] {
260*9e94795aSAndroid Build Coastguard Worker		installs = append(installs, installPath)
261*9e94795aSAndroid Build Coastguard Worker	}
262*9e94795aSAndroid Build Coastguard Worker	sort.Strings(installs)
263*9e94795aSAndroid Build Coastguard Worker	return installs
264*9e94795aSAndroid Build Coastguard Worker}
265*9e94795aSAndroid Build Coastguard Worker
266*9e94795aSAndroid Build Coastguard Worker// InstallPaths returns the ordered channel of indexed install paths.
267*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) InstallPaths() chan string {
268*9e94795aSAndroid Build Coastguard Worker	c := make(chan string)
269*9e94795aSAndroid Build Coastguard Worker	go func() {
270*9e94795aSAndroid Build Coastguard Worker		paths := make([]string, 0, len(ni.installHashLib))
271*9e94795aSAndroid Build Coastguard Worker		for path := range ni.installHashLib {
272*9e94795aSAndroid Build Coastguard Worker			paths = append(paths, path)
273*9e94795aSAndroid Build Coastguard Worker		}
274*9e94795aSAndroid Build Coastguard Worker		sort.Strings(paths)
275*9e94795aSAndroid Build Coastguard Worker		for _, installPath := range paths {
276*9e94795aSAndroid Build Coastguard Worker			c <- installPath
277*9e94795aSAndroid Build Coastguard Worker		}
278*9e94795aSAndroid Build Coastguard Worker		close(c)
279*9e94795aSAndroid Build Coastguard Worker	}()
280*9e94795aSAndroid Build Coastguard Worker	return c
281*9e94795aSAndroid Build Coastguard Worker}
282*9e94795aSAndroid Build Coastguard Worker
283*9e94795aSAndroid Build Coastguard Worker// InstallHashes returns the ordered array of hashes attached to `installPath`.
284*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) InstallHashes(installPath string) []hash {
285*9e94795aSAndroid Build Coastguard Worker	result := make([]hash, 0, len(ni.installHashLib[installPath]))
286*9e94795aSAndroid Build Coastguard Worker	for h := range ni.installHashLib[installPath] {
287*9e94795aSAndroid Build Coastguard Worker		result = append(result, h)
288*9e94795aSAndroid Build Coastguard Worker	}
289*9e94795aSAndroid Build Coastguard Worker	if len(result) > 0 {
290*9e94795aSAndroid Build Coastguard Worker		sort.Sort(hashList{ni, "", installPath, &result})
291*9e94795aSAndroid Build Coastguard Worker	}
292*9e94795aSAndroid Build Coastguard Worker	return result
293*9e94795aSAndroid Build Coastguard Worker}
294*9e94795aSAndroid Build Coastguard Worker
295*9e94795aSAndroid Build Coastguard Worker// InstallHashLibs returns the ordered array of library names attached to
296*9e94795aSAndroid Build Coastguard Worker// `installPath` as hash `h`.
297*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) InstallHashLibs(installPath string, h hash) []string {
298*9e94795aSAndroid Build Coastguard Worker	result := make([]string, 0, len(ni.installHashLib[installPath][h]))
299*9e94795aSAndroid Build Coastguard Worker	for libName := range ni.installHashLib[installPath][h] {
300*9e94795aSAndroid Build Coastguard Worker		result = append(result, libName)
301*9e94795aSAndroid Build Coastguard Worker	}
302*9e94795aSAndroid Build Coastguard Worker	sort.Strings(result)
303*9e94795aSAndroid Build Coastguard Worker	return result
304*9e94795aSAndroid Build Coastguard Worker}
305*9e94795aSAndroid Build Coastguard Worker
306*9e94795aSAndroid Build Coastguard Worker// Libraries returns the ordered channel of indexed library names.
307*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) Libraries() chan string {
308*9e94795aSAndroid Build Coastguard Worker	c := make(chan string)
309*9e94795aSAndroid Build Coastguard Worker	go func() {
310*9e94795aSAndroid Build Coastguard Worker		libs := make([]string, 0, len(ni.libHash))
311*9e94795aSAndroid Build Coastguard Worker		for lib := range ni.libHash {
312*9e94795aSAndroid Build Coastguard Worker			libs = append(libs, lib)
313*9e94795aSAndroid Build Coastguard Worker		}
314*9e94795aSAndroid Build Coastguard Worker		sort.Strings(libs)
315*9e94795aSAndroid Build Coastguard Worker		for _, lib := range libs {
316*9e94795aSAndroid Build Coastguard Worker			c <- lib
317*9e94795aSAndroid Build Coastguard Worker		}
318*9e94795aSAndroid Build Coastguard Worker		close(c)
319*9e94795aSAndroid Build Coastguard Worker	}()
320*9e94795aSAndroid Build Coastguard Worker	return c
321*9e94795aSAndroid Build Coastguard Worker}
322*9e94795aSAndroid Build Coastguard Worker
323*9e94795aSAndroid Build Coastguard Worker// HashText returns the file content of the license text hashed as `h`.
324*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) HashText(h hash) []byte {
325*9e94795aSAndroid Build Coastguard Worker	return ni.text[h]
326*9e94795aSAndroid Build Coastguard Worker}
327*9e94795aSAndroid Build Coastguard Worker
328*9e94795aSAndroid Build Coastguard Worker// getLibName returns the name of the library associated with `noticeFor`.
329*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) (string, error) {
330*9e94795aSAndroid Build Coastguard Worker	for _, text := range noticeFor.LicenseTexts() {
331*9e94795aSAndroid Build Coastguard Worker		if !strings.Contains(text, ":") {
332*9e94795aSAndroid Build Coastguard Worker			if ni.hash[text].key != h.key {
333*9e94795aSAndroid Build Coastguard Worker				continue
334*9e94795aSAndroid Build Coastguard Worker			}
335*9e94795aSAndroid Build Coastguard Worker			ln, err := ni.checkMetadataForLicenseText(noticeFor, text)
336*9e94795aSAndroid Build Coastguard Worker			if err != nil {
337*9e94795aSAndroid Build Coastguard Worker				return "", err
338*9e94795aSAndroid Build Coastguard Worker			}
339*9e94795aSAndroid Build Coastguard Worker			if len(ln) > 0 {
340*9e94795aSAndroid Build Coastguard Worker				return ln, nil
341*9e94795aSAndroid Build Coastguard Worker			}
342*9e94795aSAndroid Build Coastguard Worker			continue
343*9e94795aSAndroid Build Coastguard Worker		}
344*9e94795aSAndroid Build Coastguard Worker
345*9e94795aSAndroid Build Coastguard Worker		fields := strings.SplitN(text, ":", 2)
346*9e94795aSAndroid Build Coastguard Worker		fname, pname := fields[0], fields[1]
347*9e94795aSAndroid Build Coastguard Worker		if ni.hash[fname].key != h.key {
348*9e94795aSAndroid Build Coastguard Worker			continue
349*9e94795aSAndroid Build Coastguard Worker		}
350*9e94795aSAndroid Build Coastguard Worker
351*9e94795aSAndroid Build Coastguard Worker		ln, err := url.QueryUnescape(pname)
352*9e94795aSAndroid Build Coastguard Worker		if err != nil {
353*9e94795aSAndroid Build Coastguard Worker			continue
354*9e94795aSAndroid Build Coastguard Worker		}
355*9e94795aSAndroid Build Coastguard Worker		return ln, nil
356*9e94795aSAndroid Build Coastguard Worker	}
357*9e94795aSAndroid Build Coastguard Worker	// use name from METADATA if available
358*9e94795aSAndroid Build Coastguard Worker	ln, err := ni.checkMetadata(noticeFor)
359*9e94795aSAndroid Build Coastguard Worker	if err != nil {
360*9e94795aSAndroid Build Coastguard Worker		return "", err
361*9e94795aSAndroid Build Coastguard Worker	}
362*9e94795aSAndroid Build Coastguard Worker	if len(ln) > 0 {
363*9e94795aSAndroid Build Coastguard Worker		return ln, nil
364*9e94795aSAndroid Build Coastguard Worker	}
365*9e94795aSAndroid Build Coastguard Worker	// use package_name: from license{} module if available
366*9e94795aSAndroid Build Coastguard Worker	pn := noticeFor.PackageName()
367*9e94795aSAndroid Build Coastguard Worker	if len(pn) > 0 {
368*9e94795aSAndroid Build Coastguard Worker		return pn, nil
369*9e94795aSAndroid Build Coastguard Worker	}
370*9e94795aSAndroid Build Coastguard Worker	for _, p := range noticeFor.Projects() {
371*9e94795aSAndroid Build Coastguard Worker		if strings.HasPrefix(p, "prebuilts/") {
372*9e94795aSAndroid Build Coastguard Worker			for _, licenseText := range noticeFor.LicenseTexts() {
373*9e94795aSAndroid Build Coastguard Worker				if !strings.HasPrefix(licenseText, "prebuilts/") {
374*9e94795aSAndroid Build Coastguard Worker					continue
375*9e94795aSAndroid Build Coastguard Worker				}
376*9e94795aSAndroid Build Coastguard Worker				if !strings.Contains(licenseText, ":") {
377*9e94795aSAndroid Build Coastguard Worker					if ni.hash[licenseText].key != h.key {
378*9e94795aSAndroid Build Coastguard Worker						continue
379*9e94795aSAndroid Build Coastguard Worker					}
380*9e94795aSAndroid Build Coastguard Worker				} else {
381*9e94795aSAndroid Build Coastguard Worker					fields := strings.SplitN(licenseText, ":", 2)
382*9e94795aSAndroid Build Coastguard Worker					fname := fields[0]
383*9e94795aSAndroid Build Coastguard Worker					if ni.hash[fname].key != h.key {
384*9e94795aSAndroid Build Coastguard Worker						continue
385*9e94795aSAndroid Build Coastguard Worker					}
386*9e94795aSAndroid Build Coastguard Worker				}
387*9e94795aSAndroid Build Coastguard Worker				for _, safePrebuiltPrefix := range safePrebuiltPrefixes {
388*9e94795aSAndroid Build Coastguard Worker					match := safePrebuiltPrefix.re.FindString(licenseText)
389*9e94795aSAndroid Build Coastguard Worker					if len(match) == 0 {
390*9e94795aSAndroid Build Coastguard Worker						continue
391*9e94795aSAndroid Build Coastguard Worker					}
392*9e94795aSAndroid Build Coastguard Worker					if safePrebuiltPrefix.strip {
393*9e94795aSAndroid Build Coastguard Worker						// strip entire prefix
394*9e94795aSAndroid Build Coastguard Worker						match = licenseText[len(match):]
395*9e94795aSAndroid Build Coastguard Worker					} else {
396*9e94795aSAndroid Build Coastguard Worker						// strip from prebuilts/ until safe prefix
397*9e94795aSAndroid Build Coastguard Worker						match = licenseText[len(match)-len(safePrebuiltPrefix.prefix):]
398*9e94795aSAndroid Build Coastguard Worker					}
399*9e94795aSAndroid Build Coastguard Worker					// remove LICENSE or NOTICE or other filename
400*9e94795aSAndroid Build Coastguard Worker					li := strings.LastIndex(match, "/")
401*9e94795aSAndroid Build Coastguard Worker					if li > 0 {
402*9e94795aSAndroid Build Coastguard Worker						match = match[:li]
403*9e94795aSAndroid Build Coastguard Worker					}
404*9e94795aSAndroid Build Coastguard Worker					// remove *licenses/ path segment and subdirectory if in path
405*9e94795aSAndroid Build Coastguard Worker					if offsets := licensesPathRegexp.FindAllStringIndex(match, -1); offsets != nil && offsets[len(offsets)-1][0] > 0 {
406*9e94795aSAndroid Build Coastguard Worker						match = match[:offsets[len(offsets)-1][0]]
407*9e94795aSAndroid Build Coastguard Worker						li = strings.LastIndex(match, "/")
408*9e94795aSAndroid Build Coastguard Worker						if li > 0 {
409*9e94795aSAndroid Build Coastguard Worker							match = match[:li]
410*9e94795aSAndroid Build Coastguard Worker						}
411*9e94795aSAndroid Build Coastguard Worker					}
412*9e94795aSAndroid Build Coastguard Worker					return match, nil
413*9e94795aSAndroid Build Coastguard Worker				}
414*9e94795aSAndroid Build Coastguard Worker				break
415*9e94795aSAndroid Build Coastguard Worker			}
416*9e94795aSAndroid Build Coastguard Worker		}
417*9e94795aSAndroid Build Coastguard Worker		for _, safePathPrefix := range safePathPrefixes {
418*9e94795aSAndroid Build Coastguard Worker			if strings.HasPrefix(p, safePathPrefix.prefix) {
419*9e94795aSAndroid Build Coastguard Worker				if safePathPrefix.strip {
420*9e94795aSAndroid Build Coastguard Worker					return p[len(safePathPrefix.prefix):], nil
421*9e94795aSAndroid Build Coastguard Worker				} else {
422*9e94795aSAndroid Build Coastguard Worker					return p, nil
423*9e94795aSAndroid Build Coastguard Worker				}
424*9e94795aSAndroid Build Coastguard Worker			}
425*9e94795aSAndroid Build Coastguard Worker		}
426*9e94795aSAndroid Build Coastguard Worker	}
427*9e94795aSAndroid Build Coastguard Worker	// strip off [./]meta_lic from license metadata path and extract base name
428*9e94795aSAndroid Build Coastguard Worker	n := noticeFor.name[:len(noticeFor.name)-9]
429*9e94795aSAndroid Build Coastguard Worker	li := strings.LastIndex(n, "/")
430*9e94795aSAndroid Build Coastguard Worker	if li > 0 {
431*9e94795aSAndroid Build Coastguard Worker		n = n[li+1:]
432*9e94795aSAndroid Build Coastguard Worker	}
433*9e94795aSAndroid Build Coastguard Worker	fi := strings.Index(n, "@")
434*9e94795aSAndroid Build Coastguard Worker	if fi > 0 {
435*9e94795aSAndroid Build Coastguard Worker		n = n[:fi]
436*9e94795aSAndroid Build Coastguard Worker	}
437*9e94795aSAndroid Build Coastguard Worker	return n, nil
438*9e94795aSAndroid Build Coastguard Worker}
439*9e94795aSAndroid Build Coastguard Worker
440*9e94795aSAndroid Build Coastguard Worker// checkMetadata tries to look up a library name from a METADATA file associated with `noticeFor`.
441*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) checkMetadata(noticeFor *TargetNode) (string, error) {
442*9e94795aSAndroid Build Coastguard Worker	pms, err := ni.pmix.MetadataForProjects(noticeFor.Projects()...)
443*9e94795aSAndroid Build Coastguard Worker	if err != nil {
444*9e94795aSAndroid Build Coastguard Worker		return "", err
445*9e94795aSAndroid Build Coastguard Worker	}
446*9e94795aSAndroid Build Coastguard Worker	for _, pm := range pms {
447*9e94795aSAndroid Build Coastguard Worker		name := pm.VersionedName()
448*9e94795aSAndroid Build Coastguard Worker		if name != "" {
449*9e94795aSAndroid Build Coastguard Worker			return name, nil
450*9e94795aSAndroid Build Coastguard Worker		}
451*9e94795aSAndroid Build Coastguard Worker	}
452*9e94795aSAndroid Build Coastguard Worker	return "", nil
453*9e94795aSAndroid Build Coastguard Worker}
454*9e94795aSAndroid Build Coastguard Worker
455*9e94795aSAndroid Build Coastguard Worker// checkMetadataForLicenseText
456*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) (string, error) {
457*9e94795aSAndroid Build Coastguard Worker	p := ""
458*9e94795aSAndroid Build Coastguard Worker	for _, proj := range noticeFor.Projects() {
459*9e94795aSAndroid Build Coastguard Worker		if strings.HasPrefix(licenseText, proj) {
460*9e94795aSAndroid Build Coastguard Worker			p = proj
461*9e94795aSAndroid Build Coastguard Worker		}
462*9e94795aSAndroid Build Coastguard Worker	}
463*9e94795aSAndroid Build Coastguard Worker	if len(p) == 0 {
464*9e94795aSAndroid Build Coastguard Worker		p = filepath.Dir(licenseText)
465*9e94795aSAndroid Build Coastguard Worker		for {
466*9e94795aSAndroid Build Coastguard Worker			fi, err := fs.Stat(ni.rootFS, filepath.Join(p, ".git"))
467*9e94795aSAndroid Build Coastguard Worker			if err == nil && fi.IsDir() {
468*9e94795aSAndroid Build Coastguard Worker				break
469*9e94795aSAndroid Build Coastguard Worker			}
470*9e94795aSAndroid Build Coastguard Worker			if strings.Contains(p, "/") && p != "/" {
471*9e94795aSAndroid Build Coastguard Worker				p = filepath.Dir(p)
472*9e94795aSAndroid Build Coastguard Worker				continue
473*9e94795aSAndroid Build Coastguard Worker			}
474*9e94795aSAndroid Build Coastguard Worker			return "", nil
475*9e94795aSAndroid Build Coastguard Worker		}
476*9e94795aSAndroid Build Coastguard Worker	}
477*9e94795aSAndroid Build Coastguard Worker	pms, err := ni.pmix.MetadataForProjects(p)
478*9e94795aSAndroid Build Coastguard Worker	if err != nil {
479*9e94795aSAndroid Build Coastguard Worker		return "", err
480*9e94795aSAndroid Build Coastguard Worker	}
481*9e94795aSAndroid Build Coastguard Worker	if pms == nil {
482*9e94795aSAndroid Build Coastguard Worker		return "", nil
483*9e94795aSAndroid Build Coastguard Worker	}
484*9e94795aSAndroid Build Coastguard Worker	return pms[0].VersionedName(), nil
485*9e94795aSAndroid Build Coastguard Worker}
486*9e94795aSAndroid Build Coastguard Worker
487*9e94795aSAndroid Build Coastguard Worker// addText reads and indexes the content of a license text file.
488*9e94795aSAndroid Build Coastguard Workerfunc (ni *NoticeIndex) addText(file string) error {
489*9e94795aSAndroid Build Coastguard Worker	f, err := ni.rootFS.Open(filepath.Clean(file))
490*9e94795aSAndroid Build Coastguard Worker	if err != nil {
491*9e94795aSAndroid Build Coastguard Worker		return fmt.Errorf("error opening license text file %q: %w", file, err)
492*9e94795aSAndroid Build Coastguard Worker	}
493*9e94795aSAndroid Build Coastguard Worker
494*9e94795aSAndroid Build Coastguard Worker	// read the file
495*9e94795aSAndroid Build Coastguard Worker	text, err := io.ReadAll(f)
496*9e94795aSAndroid Build Coastguard Worker	if err != nil {
497*9e94795aSAndroid Build Coastguard Worker		return fmt.Errorf("error reading license text file %q: %w", file, err)
498*9e94795aSAndroid Build Coastguard Worker	}
499*9e94795aSAndroid Build Coastguard Worker
500*9e94795aSAndroid Build Coastguard Worker	hash := hash{fmt.Sprintf("%x", md5.Sum(text))}
501*9e94795aSAndroid Build Coastguard Worker	ni.hash[file] = hash
502*9e94795aSAndroid Build Coastguard Worker	if _, alreadyPresent := ni.text[hash]; !alreadyPresent {
503*9e94795aSAndroid Build Coastguard Worker		ni.text[hash] = text
504*9e94795aSAndroid Build Coastguard Worker	}
505*9e94795aSAndroid Build Coastguard Worker
506*9e94795aSAndroid Build Coastguard Worker	ni.files = append(ni.files, file)
507*9e94795aSAndroid Build Coastguard Worker
508*9e94795aSAndroid Build Coastguard Worker	return nil
509*9e94795aSAndroid Build Coastguard Worker}
510*9e94795aSAndroid Build Coastguard Worker
511*9e94795aSAndroid Build Coastguard Worker// getInstallPaths returns the names of the used dependencies mapped to their
512*9e94795aSAndroid Build Coastguard Worker// installed locations.
513*9e94795aSAndroid Build Coastguard Workerfunc getInstallPaths(attachesTo *TargetNode, path TargetEdgePath) []string {
514*9e94795aSAndroid Build Coastguard Worker	if len(path) == 0 {
515*9e94795aSAndroid Build Coastguard Worker		installs := attachesTo.Installed()
516*9e94795aSAndroid Build Coastguard Worker		if 0 == len(installs) {
517*9e94795aSAndroid Build Coastguard Worker			installs = attachesTo.Built()
518*9e94795aSAndroid Build Coastguard Worker		}
519*9e94795aSAndroid Build Coastguard Worker		return installs
520*9e94795aSAndroid Build Coastguard Worker	}
521*9e94795aSAndroid Build Coastguard Worker
522*9e94795aSAndroid Build Coastguard Worker	var getInstalls func(path TargetEdgePath) []string
523*9e94795aSAndroid Build Coastguard Worker
524*9e94795aSAndroid Build Coastguard Worker	getInstalls = func(path TargetEdgePath) []string {
525*9e94795aSAndroid Build Coastguard Worker		// deps contains the output targets from the dependencies in the path
526*9e94795aSAndroid Build Coastguard Worker		var deps []string
527*9e94795aSAndroid Build Coastguard Worker		if len(path) > 1 {
528*9e94795aSAndroid Build Coastguard Worker			// recursively get the targets from the sub-path skipping 1 path segment
529*9e94795aSAndroid Build Coastguard Worker			deps = getInstalls(path[1:])
530*9e94795aSAndroid Build Coastguard Worker		} else {
531*9e94795aSAndroid Build Coastguard Worker			// stop recursion at 1 path segment
532*9e94795aSAndroid Build Coastguard Worker			deps = path[0].Dependency().TargetFiles()
533*9e94795aSAndroid Build Coastguard Worker		}
534*9e94795aSAndroid Build Coastguard Worker		size := 0
535*9e94795aSAndroid Build Coastguard Worker		prefixes := path[0].Target().TargetFiles()
536*9e94795aSAndroid Build Coastguard Worker		installMap := path[0].Target().InstallMap()
537*9e94795aSAndroid Build Coastguard Worker		sources := path[0].Target().Sources()
538*9e94795aSAndroid Build Coastguard Worker		for _, dep := range deps {
539*9e94795aSAndroid Build Coastguard Worker			found := false
540*9e94795aSAndroid Build Coastguard Worker			for _, source := range sources {
541*9e94795aSAndroid Build Coastguard Worker				if strings.HasPrefix(dep, source) {
542*9e94795aSAndroid Build Coastguard Worker					found = true
543*9e94795aSAndroid Build Coastguard Worker					break
544*9e94795aSAndroid Build Coastguard Worker				}
545*9e94795aSAndroid Build Coastguard Worker			}
546*9e94795aSAndroid Build Coastguard Worker			if !found {
547*9e94795aSAndroid Build Coastguard Worker				continue
548*9e94795aSAndroid Build Coastguard Worker			}
549*9e94795aSAndroid Build Coastguard Worker			for _, im := range installMap {
550*9e94795aSAndroid Build Coastguard Worker				if strings.HasPrefix(dep, im.FromPath) {
551*9e94795aSAndroid Build Coastguard Worker					size += len(prefixes)
552*9e94795aSAndroid Build Coastguard Worker					break
553*9e94795aSAndroid Build Coastguard Worker				}
554*9e94795aSAndroid Build Coastguard Worker			}
555*9e94795aSAndroid Build Coastguard Worker		}
556*9e94795aSAndroid Build Coastguard Worker
557*9e94795aSAndroid Build Coastguard Worker		installs := make([]string, 0, size)
558*9e94795aSAndroid Build Coastguard Worker		for _, dep := range deps {
559*9e94795aSAndroid Build Coastguard Worker			found := false
560*9e94795aSAndroid Build Coastguard Worker			for _, source := range sources {
561*9e94795aSAndroid Build Coastguard Worker				if strings.HasPrefix(dep, source) {
562*9e94795aSAndroid Build Coastguard Worker					found = true
563*9e94795aSAndroid Build Coastguard Worker					break
564*9e94795aSAndroid Build Coastguard Worker				}
565*9e94795aSAndroid Build Coastguard Worker			}
566*9e94795aSAndroid Build Coastguard Worker			if !found {
567*9e94795aSAndroid Build Coastguard Worker				continue
568*9e94795aSAndroid Build Coastguard Worker			}
569*9e94795aSAndroid Build Coastguard Worker			for _, im := range installMap {
570*9e94795aSAndroid Build Coastguard Worker				if strings.HasPrefix(dep, im.FromPath) {
571*9e94795aSAndroid Build Coastguard Worker					for _, prefix := range prefixes {
572*9e94795aSAndroid Build Coastguard Worker						installs = append(installs, prefix+im.ContainerPath+dep[len(im.FromPath):])
573*9e94795aSAndroid Build Coastguard Worker					}
574*9e94795aSAndroid Build Coastguard Worker					break
575*9e94795aSAndroid Build Coastguard Worker				}
576*9e94795aSAndroid Build Coastguard Worker			}
577*9e94795aSAndroid Build Coastguard Worker		}
578*9e94795aSAndroid Build Coastguard Worker		return installs
579*9e94795aSAndroid Build Coastguard Worker	}
580*9e94795aSAndroid Build Coastguard Worker	allInstalls := getInstalls(path)
581*9e94795aSAndroid Build Coastguard Worker	installs := path[0].Target().Installed()
582*9e94795aSAndroid Build Coastguard Worker	if len(installs) == 0 {
583*9e94795aSAndroid Build Coastguard Worker		return allInstalls
584*9e94795aSAndroid Build Coastguard Worker	}
585*9e94795aSAndroid Build Coastguard Worker	result := make([]string, 0, len(allInstalls))
586*9e94795aSAndroid Build Coastguard Worker	for _, install := range allInstalls {
587*9e94795aSAndroid Build Coastguard Worker		for _, prefix := range installs {
588*9e94795aSAndroid Build Coastguard Worker			if strings.HasPrefix(install, prefix) {
589*9e94795aSAndroid Build Coastguard Worker				result = append(result, install)
590*9e94795aSAndroid Build Coastguard Worker			}
591*9e94795aSAndroid Build Coastguard Worker		}
592*9e94795aSAndroid Build Coastguard Worker	}
593*9e94795aSAndroid Build Coastguard Worker	return result
594*9e94795aSAndroid Build Coastguard Worker}
595*9e94795aSAndroid Build Coastguard Worker
596*9e94795aSAndroid Build Coastguard Worker// hash is an opaque string derived from md5sum.
597*9e94795aSAndroid Build Coastguard Workertype hash struct {
598*9e94795aSAndroid Build Coastguard Worker	key string
599*9e94795aSAndroid Build Coastguard Worker}
600*9e94795aSAndroid Build Coastguard Worker
601*9e94795aSAndroid Build Coastguard Worker// String returns the hexadecimal representation of the hash.
602*9e94795aSAndroid Build Coastguard Workerfunc (h hash) String() string {
603*9e94795aSAndroid Build Coastguard Worker	return h.key
604*9e94795aSAndroid Build Coastguard Worker}
605*9e94795aSAndroid Build Coastguard Worker
606*9e94795aSAndroid Build Coastguard Worker// hashList orders an array of hashes
607*9e94795aSAndroid Build Coastguard Workertype hashList struct {
608*9e94795aSAndroid Build Coastguard Worker	ni          *NoticeIndex
609*9e94795aSAndroid Build Coastguard Worker	libName     string
610*9e94795aSAndroid Build Coastguard Worker	installPath string
611*9e94795aSAndroid Build Coastguard Worker	hashes      *[]hash
612*9e94795aSAndroid Build Coastguard Worker}
613*9e94795aSAndroid Build Coastguard Worker
614*9e94795aSAndroid Build Coastguard Worker// Len returns the count of elements in the slice.
615*9e94795aSAndroid Build Coastguard Workerfunc (l hashList) Len() int { return len(*l.hashes) }
616*9e94795aSAndroid Build Coastguard Worker
617*9e94795aSAndroid Build Coastguard Worker// Swap rearranges 2 elements of the slice so that each occupies the other's
618*9e94795aSAndroid Build Coastguard Worker// former position.
619*9e94795aSAndroid Build Coastguard Workerfunc (l hashList) Swap(i, j int) { (*l.hashes)[i], (*l.hashes)[j] = (*l.hashes)[j], (*l.hashes)[i] }
620*9e94795aSAndroid Build Coastguard Worker
621*9e94795aSAndroid Build Coastguard Worker// Less returns true when the `i`th element is lexicographically less than
622*9e94795aSAndroid Build Coastguard Worker// the `j`th element.
623*9e94795aSAndroid Build Coastguard Workerfunc (l hashList) Less(i, j int) bool {
624*9e94795aSAndroid Build Coastguard Worker	var insti, instj int
625*9e94795aSAndroid Build Coastguard Worker	if len(l.libName) > 0 {
626*9e94795aSAndroid Build Coastguard Worker		insti = len(l.ni.hashLibInstall[(*l.hashes)[i]][l.libName])
627*9e94795aSAndroid Build Coastguard Worker		instj = len(l.ni.hashLibInstall[(*l.hashes)[j]][l.libName])
628*9e94795aSAndroid Build Coastguard Worker	} else {
629*9e94795aSAndroid Build Coastguard Worker		libsi := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[i])
630*9e94795aSAndroid Build Coastguard Worker		libsj := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[j])
631*9e94795aSAndroid Build Coastguard Worker		libsis := strings.Join(libsi, " ")
632*9e94795aSAndroid Build Coastguard Worker		libsjs := strings.Join(libsj, " ")
633*9e94795aSAndroid Build Coastguard Worker		if libsis != libsjs {
634*9e94795aSAndroid Build Coastguard Worker			return libsis < libsjs
635*9e94795aSAndroid Build Coastguard Worker		}
636*9e94795aSAndroid Build Coastguard Worker	}
637*9e94795aSAndroid Build Coastguard Worker	if insti == instj {
638*9e94795aSAndroid Build Coastguard Worker		leni := len(l.ni.text[(*l.hashes)[i]])
639*9e94795aSAndroid Build Coastguard Worker		lenj := len(l.ni.text[(*l.hashes)[j]])
640*9e94795aSAndroid Build Coastguard Worker		if leni == lenj {
641*9e94795aSAndroid Build Coastguard Worker			// all else equal, just order by hash value
642*9e94795aSAndroid Build Coastguard Worker			return (*l.hashes)[i].key < (*l.hashes)[j].key
643*9e94795aSAndroid Build Coastguard Worker		}
644*9e94795aSAndroid Build Coastguard Worker		// put shortest texts first within same # of installs
645*9e94795aSAndroid Build Coastguard Worker		return leni < lenj
646*9e94795aSAndroid Build Coastguard Worker	}
647*9e94795aSAndroid Build Coastguard Worker	// reverse order of # installs so that most popular appears first
648*9e94795aSAndroid Build Coastguard Worker	return instj < insti
649*9e94795aSAndroid Build Coastguard Worker}
650