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