1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage pathtools 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "encoding/json" 19*1fa6dee9SAndroid Build Coastguard Worker "errors" 20*1fa6dee9SAndroid Build Coastguard Worker "fmt" 21*1fa6dee9SAndroid Build Coastguard Worker "io/ioutil" 22*1fa6dee9SAndroid Build Coastguard Worker "os" 23*1fa6dee9SAndroid Build Coastguard Worker "path/filepath" 24*1fa6dee9SAndroid Build Coastguard Worker "slices" 25*1fa6dee9SAndroid Build Coastguard Worker "strings" 26*1fa6dee9SAndroid Build Coastguard Worker) 27*1fa6dee9SAndroid Build Coastguard Worker 28*1fa6dee9SAndroid Build Coastguard Workervar GlobMultipleRecursiveErr = errors.New("pattern contains multiple '**'") 29*1fa6dee9SAndroid Build Coastguard Workervar GlobLastRecursiveErr = errors.New("pattern has '**' as last path element") 30*1fa6dee9SAndroid Build Coastguard Workervar GlobInvalidRecursiveErr = errors.New("pattern contains other characters between '**' and path separator") 31*1fa6dee9SAndroid Build Coastguard Worker 32*1fa6dee9SAndroid Build Coastguard Worker// GlobResult is a container holding the results of a call to Glob. 33*1fa6dee9SAndroid Build Coastguard Workertype GlobResult struct { 34*1fa6dee9SAndroid Build Coastguard Worker // Pattern is the pattern that was passed to Glob. 35*1fa6dee9SAndroid Build Coastguard Worker Pattern string 36*1fa6dee9SAndroid Build Coastguard Worker // Excludes is the list of excludes that were passed to Glob. 37*1fa6dee9SAndroid Build Coastguard Worker Excludes []string 38*1fa6dee9SAndroid Build Coastguard Worker 39*1fa6dee9SAndroid Build Coastguard Worker // Matches is the list of files or directories that matched the pattern but not the excludes. 40*1fa6dee9SAndroid Build Coastguard Worker Matches []string 41*1fa6dee9SAndroid Build Coastguard Worker 42*1fa6dee9SAndroid Build Coastguard Worker // Deps is the list of files or directories that must be depended on to regenerate the glob. 43*1fa6dee9SAndroid Build Coastguard Worker Deps []string 44*1fa6dee9SAndroid Build Coastguard Worker} 45*1fa6dee9SAndroid Build Coastguard Worker 46*1fa6dee9SAndroid Build Coastguard Worker// FileList returns the list of files matched by a glob for writing to an output file. 47*1fa6dee9SAndroid Build Coastguard Workerfunc (result GlobResult) FileList() []byte { 48*1fa6dee9SAndroid Build Coastguard Worker return []byte(strings.Join(result.Matches, "\n") + "\n") 49*1fa6dee9SAndroid Build Coastguard Worker} 50*1fa6dee9SAndroid Build Coastguard Worker 51*1fa6dee9SAndroid Build Coastguard Workerfunc (result GlobResult) Clone() GlobResult { 52*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{ 53*1fa6dee9SAndroid Build Coastguard Worker Pattern: result.Pattern, 54*1fa6dee9SAndroid Build Coastguard Worker Excludes: slices.Clone(result.Excludes), 55*1fa6dee9SAndroid Build Coastguard Worker Matches: slices.Clone(result.Matches), 56*1fa6dee9SAndroid Build Coastguard Worker Deps: slices.Clone(result.Deps), 57*1fa6dee9SAndroid Build Coastguard Worker } 58*1fa6dee9SAndroid Build Coastguard Worker} 59*1fa6dee9SAndroid Build Coastguard Worker 60*1fa6dee9SAndroid Build Coastguard Worker// MultipleGlobResults is a list of GlobResult structs. 61*1fa6dee9SAndroid Build Coastguard Workertype MultipleGlobResults []GlobResult 62*1fa6dee9SAndroid Build Coastguard Worker 63*1fa6dee9SAndroid Build Coastguard Worker// FileList returns the list of files matched by a list of multiple globs for writing to an output file. 64*1fa6dee9SAndroid Build Coastguard Workerfunc (results MultipleGlobResults) FileList() []byte { 65*1fa6dee9SAndroid Build Coastguard Worker multipleMatches := make([][]string, len(results)) 66*1fa6dee9SAndroid Build Coastguard Worker for i, result := range results { 67*1fa6dee9SAndroid Build Coastguard Worker multipleMatches[i] = result.Matches 68*1fa6dee9SAndroid Build Coastguard Worker } 69*1fa6dee9SAndroid Build Coastguard Worker buf, err := json.Marshal(multipleMatches) 70*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 71*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("failed to marshal glob results to json: %w", err)) 72*1fa6dee9SAndroid Build Coastguard Worker } 73*1fa6dee9SAndroid Build Coastguard Worker return buf 74*1fa6dee9SAndroid Build Coastguard Worker} 75*1fa6dee9SAndroid Build Coastguard Worker 76*1fa6dee9SAndroid Build Coastguard Worker// Deps returns the deps from all of the GlobResults. 77*1fa6dee9SAndroid Build Coastguard Workerfunc (results MultipleGlobResults) Deps() []string { 78*1fa6dee9SAndroid Build Coastguard Worker var deps []string 79*1fa6dee9SAndroid Build Coastguard Worker for _, result := range results { 80*1fa6dee9SAndroid Build Coastguard Worker deps = append(deps, result.Deps...) 81*1fa6dee9SAndroid Build Coastguard Worker } 82*1fa6dee9SAndroid Build Coastguard Worker return deps 83*1fa6dee9SAndroid Build Coastguard Worker} 84*1fa6dee9SAndroid Build Coastguard Worker 85*1fa6dee9SAndroid Build Coastguard Worker// Glob returns the list of files and directories that match the given pattern 86*1fa6dee9SAndroid Build Coastguard Worker// but do not match the given exclude patterns, along with the list of 87*1fa6dee9SAndroid Build Coastguard Worker// directories and other dependencies that were searched to construct the file 88*1fa6dee9SAndroid Build Coastguard Worker// list. The supported glob and exclude patterns are equivalent to 89*1fa6dee9SAndroid Build Coastguard Worker// filepath.Glob, with an extension that recursive glob (** matching zero or 90*1fa6dee9SAndroid Build Coastguard Worker// more complete path entries) is supported. Any directories in the matches 91*1fa6dee9SAndroid Build Coastguard Worker// list will have a '/' suffix. 92*1fa6dee9SAndroid Build Coastguard Worker// 93*1fa6dee9SAndroid Build Coastguard Worker// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps 94*1fa6dee9SAndroid Build Coastguard Worker// should be used instead, as they will automatically set up dependencies 95*1fa6dee9SAndroid Build Coastguard Worker// to rerun the primary builder when the list of matching files changes. 96*1fa6dee9SAndroid Build Coastguard Workerfunc Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) { 97*1fa6dee9SAndroid Build Coastguard Worker return startGlob(OsFs, pattern, excludes, follow) 98*1fa6dee9SAndroid Build Coastguard Worker} 99*1fa6dee9SAndroid Build Coastguard Worker 100*1fa6dee9SAndroid Build Coastguard Workerfunc startGlob(fs FileSystem, pattern string, excludes []string, 101*1fa6dee9SAndroid Build Coastguard Worker follow ShouldFollowSymlinks) (GlobResult, error) { 102*1fa6dee9SAndroid Build Coastguard Worker 103*1fa6dee9SAndroid Build Coastguard Worker if filepath.Base(pattern) == "**" { 104*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{}, GlobLastRecursiveErr 105*1fa6dee9SAndroid Build Coastguard Worker } 106*1fa6dee9SAndroid Build Coastguard Worker 107*1fa6dee9SAndroid Build Coastguard Worker matches, deps, err := glob(fs, pattern, false, follow) 108*1fa6dee9SAndroid Build Coastguard Worker 109*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 110*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{}, err 111*1fa6dee9SAndroid Build Coastguard Worker } 112*1fa6dee9SAndroid Build Coastguard Worker 113*1fa6dee9SAndroid Build Coastguard Worker matches, err = filterExcludes(matches, excludes) 114*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 115*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{}, err 116*1fa6dee9SAndroid Build Coastguard Worker } 117*1fa6dee9SAndroid Build Coastguard Worker 118*1fa6dee9SAndroid Build Coastguard Worker // If the pattern has wildcards, we added dependencies on the 119*1fa6dee9SAndroid Build Coastguard Worker // containing directories to know about changes. 120*1fa6dee9SAndroid Build Coastguard Worker // 121*1fa6dee9SAndroid Build Coastguard Worker // If the pattern didn't have wildcards, and didn't find matches, the 122*1fa6dee9SAndroid Build Coastguard Worker // most specific found directories were added. 123*1fa6dee9SAndroid Build Coastguard Worker // 124*1fa6dee9SAndroid Build Coastguard Worker // But if it didn't have wildcards, and did find a match, no 125*1fa6dee9SAndroid Build Coastguard Worker // dependencies were added, so add the match itself to detect when it 126*1fa6dee9SAndroid Build Coastguard Worker // is removed. 127*1fa6dee9SAndroid Build Coastguard Worker if !isWild(pattern) { 128*1fa6dee9SAndroid Build Coastguard Worker deps = append(deps, matches...) 129*1fa6dee9SAndroid Build Coastguard Worker } 130*1fa6dee9SAndroid Build Coastguard Worker 131*1fa6dee9SAndroid Build Coastguard Worker for i, match := range matches { 132*1fa6dee9SAndroid Build Coastguard Worker var info os.FileInfo 133*1fa6dee9SAndroid Build Coastguard Worker if follow == DontFollowSymlinks { 134*1fa6dee9SAndroid Build Coastguard Worker info, err = fs.Lstat(match) 135*1fa6dee9SAndroid Build Coastguard Worker } else { 136*1fa6dee9SAndroid Build Coastguard Worker info, err = fs.Stat(match) 137*1fa6dee9SAndroid Build Coastguard Worker if err != nil && os.IsNotExist(err) { 138*1fa6dee9SAndroid Build Coastguard Worker // ErrNotExist from Stat may be due to a dangling symlink, retry with lstat. 139*1fa6dee9SAndroid Build Coastguard Worker info, err = fs.Lstat(match) 140*1fa6dee9SAndroid Build Coastguard Worker } 141*1fa6dee9SAndroid Build Coastguard Worker } 142*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 143*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{}, err 144*1fa6dee9SAndroid Build Coastguard Worker } 145*1fa6dee9SAndroid Build Coastguard Worker 146*1fa6dee9SAndroid Build Coastguard Worker if info.IsDir() { 147*1fa6dee9SAndroid Build Coastguard Worker matches[i] = match + "/" 148*1fa6dee9SAndroid Build Coastguard Worker } 149*1fa6dee9SAndroid Build Coastguard Worker } 150*1fa6dee9SAndroid Build Coastguard Worker 151*1fa6dee9SAndroid Build Coastguard Worker return GlobResult{ 152*1fa6dee9SAndroid Build Coastguard Worker Pattern: pattern, 153*1fa6dee9SAndroid Build Coastguard Worker Excludes: excludes, 154*1fa6dee9SAndroid Build Coastguard Worker Matches: matches, 155*1fa6dee9SAndroid Build Coastguard Worker Deps: deps, 156*1fa6dee9SAndroid Build Coastguard Worker }, nil 157*1fa6dee9SAndroid Build Coastguard Worker} 158*1fa6dee9SAndroid Build Coastguard Worker 159*1fa6dee9SAndroid Build Coastguard Worker// glob is a recursive helper function to handle globbing each level of the pattern individually, 160*1fa6dee9SAndroid Build Coastguard Worker// allowing searched directories to be tracked. Also handles the recursive glob pattern, **. 161*1fa6dee9SAndroid Build Coastguard Workerfunc glob(fs FileSystem, pattern string, hasRecursive bool, 162*1fa6dee9SAndroid Build Coastguard Worker follow ShouldFollowSymlinks) (matches, dirs []string, err error) { 163*1fa6dee9SAndroid Build Coastguard Worker 164*1fa6dee9SAndroid Build Coastguard Worker if !isWild(pattern) { 165*1fa6dee9SAndroid Build Coastguard Worker // If there are no wilds in the pattern, check whether the file exists or not. 166*1fa6dee9SAndroid Build Coastguard Worker // Uses filepath.Glob instead of manually statting to get consistent results. 167*1fa6dee9SAndroid Build Coastguard Worker pattern = filepath.Clean(pattern) 168*1fa6dee9SAndroid Build Coastguard Worker matches, err = fs.glob(pattern) 169*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 170*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, err 171*1fa6dee9SAndroid Build Coastguard Worker } 172*1fa6dee9SAndroid Build Coastguard Worker 173*1fa6dee9SAndroid Build Coastguard Worker if len(matches) == 0 { 174*1fa6dee9SAndroid Build Coastguard Worker // Some part of the non-wild pattern didn't exist. Add the last existing directory 175*1fa6dee9SAndroid Build Coastguard Worker // as a dependency. 176*1fa6dee9SAndroid Build Coastguard Worker var matchDirs []string 177*1fa6dee9SAndroid Build Coastguard Worker for len(matchDirs) == 0 { 178*1fa6dee9SAndroid Build Coastguard Worker pattern = filepath.Dir(pattern) 179*1fa6dee9SAndroid Build Coastguard Worker matchDirs, err = fs.glob(pattern) 180*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 181*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, err 182*1fa6dee9SAndroid Build Coastguard Worker } 183*1fa6dee9SAndroid Build Coastguard Worker } 184*1fa6dee9SAndroid Build Coastguard Worker dirs = append(dirs, matchDirs...) 185*1fa6dee9SAndroid Build Coastguard Worker } 186*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, err 187*1fa6dee9SAndroid Build Coastguard Worker } 188*1fa6dee9SAndroid Build Coastguard Worker 189*1fa6dee9SAndroid Build Coastguard Worker dir, file := quickSplit(pattern) 190*1fa6dee9SAndroid Build Coastguard Worker 191*1fa6dee9SAndroid Build Coastguard Worker if file == "**" { 192*1fa6dee9SAndroid Build Coastguard Worker if hasRecursive { 193*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, GlobMultipleRecursiveErr 194*1fa6dee9SAndroid Build Coastguard Worker } 195*1fa6dee9SAndroid Build Coastguard Worker hasRecursive = true 196*1fa6dee9SAndroid Build Coastguard Worker } else if strings.Contains(file, "**") { 197*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, GlobInvalidRecursiveErr 198*1fa6dee9SAndroid Build Coastguard Worker } 199*1fa6dee9SAndroid Build Coastguard Worker 200*1fa6dee9SAndroid Build Coastguard Worker dirMatches, dirs, err := glob(fs, dir, hasRecursive, follow) 201*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 202*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, err 203*1fa6dee9SAndroid Build Coastguard Worker } 204*1fa6dee9SAndroid Build Coastguard Worker 205*1fa6dee9SAndroid Build Coastguard Worker for _, m := range dirMatches { 206*1fa6dee9SAndroid Build Coastguard Worker isDir, err := fs.IsDir(m) 207*1fa6dee9SAndroid Build Coastguard Worker if os.IsNotExist(err) { 208*1fa6dee9SAndroid Build Coastguard Worker if isSymlink, _ := fs.IsSymlink(m); isSymlink { 209*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("dangling symlink: %s", m) 210*1fa6dee9SAndroid Build Coastguard Worker } 211*1fa6dee9SAndroid Build Coastguard Worker } 212*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 213*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("unexpected error after glob: %s", err) 214*1fa6dee9SAndroid Build Coastguard Worker } 215*1fa6dee9SAndroid Build Coastguard Worker 216*1fa6dee9SAndroid Build Coastguard Worker if isDir { 217*1fa6dee9SAndroid Build Coastguard Worker if file == "**" { 218*1fa6dee9SAndroid Build Coastguard Worker recurseDirs, err := fs.ListDirsRecursive(m, follow) 219*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 220*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, err 221*1fa6dee9SAndroid Build Coastguard Worker } 222*1fa6dee9SAndroid Build Coastguard Worker matches = append(matches, recurseDirs...) 223*1fa6dee9SAndroid Build Coastguard Worker } else { 224*1fa6dee9SAndroid Build Coastguard Worker dirs = append(dirs, m) 225*1fa6dee9SAndroid Build Coastguard Worker newMatches, err := fs.glob(filepath.Join(MatchEscape(m), file)) 226*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 227*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, err 228*1fa6dee9SAndroid Build Coastguard Worker } 229*1fa6dee9SAndroid Build Coastguard Worker if file[0] != '.' { 230*1fa6dee9SAndroid Build Coastguard Worker newMatches = filterDotFiles(newMatches) 231*1fa6dee9SAndroid Build Coastguard Worker } 232*1fa6dee9SAndroid Build Coastguard Worker matches = append(matches, newMatches...) 233*1fa6dee9SAndroid Build Coastguard Worker } 234*1fa6dee9SAndroid Build Coastguard Worker } 235*1fa6dee9SAndroid Build Coastguard Worker } 236*1fa6dee9SAndroid Build Coastguard Worker 237*1fa6dee9SAndroid Build Coastguard Worker return matches, dirs, nil 238*1fa6dee9SAndroid Build Coastguard Worker} 239*1fa6dee9SAndroid Build Coastguard Worker 240*1fa6dee9SAndroid Build Coastguard Worker// Faster version of dir, file := filepath.Dir(path), filepath.File(path) with no allocations 241*1fa6dee9SAndroid Build Coastguard Worker// Similar to filepath.Split, but returns "." if dir is empty and trims trailing slash if dir is 242*1fa6dee9SAndroid Build Coastguard Worker// not "/". Returns ".", "" if path is "." 243*1fa6dee9SAndroid Build Coastguard Workerfunc quickSplit(path string) (dir, file string) { 244*1fa6dee9SAndroid Build Coastguard Worker if path == "." { 245*1fa6dee9SAndroid Build Coastguard Worker return ".", "" 246*1fa6dee9SAndroid Build Coastguard Worker } 247*1fa6dee9SAndroid Build Coastguard Worker dir, file = filepath.Split(path) 248*1fa6dee9SAndroid Build Coastguard Worker switch dir { 249*1fa6dee9SAndroid Build Coastguard Worker case "": 250*1fa6dee9SAndroid Build Coastguard Worker dir = "." 251*1fa6dee9SAndroid Build Coastguard Worker case "/": 252*1fa6dee9SAndroid Build Coastguard Worker // Nothing 253*1fa6dee9SAndroid Build Coastguard Worker default: 254*1fa6dee9SAndroid Build Coastguard Worker dir = dir[:len(dir)-1] 255*1fa6dee9SAndroid Build Coastguard Worker } 256*1fa6dee9SAndroid Build Coastguard Worker return dir, file 257*1fa6dee9SAndroid Build Coastguard Worker} 258*1fa6dee9SAndroid Build Coastguard Worker 259*1fa6dee9SAndroid Build Coastguard Workerfunc isWild(pattern string) bool { 260*1fa6dee9SAndroid Build Coastguard Worker return strings.ContainsAny(pattern, "*?[") 261*1fa6dee9SAndroid Build Coastguard Worker} 262*1fa6dee9SAndroid Build Coastguard Worker 263*1fa6dee9SAndroid Build Coastguard Worker// Filters the strings in matches based on the glob patterns in excludes. Hierarchical (a/*) and 264*1fa6dee9SAndroid Build Coastguard Worker// recursive (**) glob patterns are supported. 265*1fa6dee9SAndroid Build Coastguard Workerfunc filterExcludes(matches []string, excludes []string) ([]string, error) { 266*1fa6dee9SAndroid Build Coastguard Worker if len(excludes) == 0 { 267*1fa6dee9SAndroid Build Coastguard Worker return matches, nil 268*1fa6dee9SAndroid Build Coastguard Worker } 269*1fa6dee9SAndroid Build Coastguard Worker 270*1fa6dee9SAndroid Build Coastguard Worker var ret []string 271*1fa6dee9SAndroid Build Coastguard WorkermatchLoop: 272*1fa6dee9SAndroid Build Coastguard Worker for _, m := range matches { 273*1fa6dee9SAndroid Build Coastguard Worker for _, e := range excludes { 274*1fa6dee9SAndroid Build Coastguard Worker exclude, err := Match(e, m) 275*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 276*1fa6dee9SAndroid Build Coastguard Worker return nil, err 277*1fa6dee9SAndroid Build Coastguard Worker } 278*1fa6dee9SAndroid Build Coastguard Worker if exclude { 279*1fa6dee9SAndroid Build Coastguard Worker continue matchLoop 280*1fa6dee9SAndroid Build Coastguard Worker } 281*1fa6dee9SAndroid Build Coastguard Worker } 282*1fa6dee9SAndroid Build Coastguard Worker ret = append(ret, m) 283*1fa6dee9SAndroid Build Coastguard Worker } 284*1fa6dee9SAndroid Build Coastguard Worker 285*1fa6dee9SAndroid Build Coastguard Worker return ret, nil 286*1fa6dee9SAndroid Build Coastguard Worker} 287*1fa6dee9SAndroid Build Coastguard Worker 288*1fa6dee9SAndroid Build Coastguard Worker// filterDotFiles filters out files that start with '.' 289*1fa6dee9SAndroid Build Coastguard Workerfunc filterDotFiles(matches []string) []string { 290*1fa6dee9SAndroid Build Coastguard Worker ret := make([]string, 0, len(matches)) 291*1fa6dee9SAndroid Build Coastguard Worker 292*1fa6dee9SAndroid Build Coastguard Worker for _, match := range matches { 293*1fa6dee9SAndroid Build Coastguard Worker _, name := filepath.Split(match) 294*1fa6dee9SAndroid Build Coastguard Worker if name[0] == '.' { 295*1fa6dee9SAndroid Build Coastguard Worker continue 296*1fa6dee9SAndroid Build Coastguard Worker } 297*1fa6dee9SAndroid Build Coastguard Worker ret = append(ret, match) 298*1fa6dee9SAndroid Build Coastguard Worker } 299*1fa6dee9SAndroid Build Coastguard Worker 300*1fa6dee9SAndroid Build Coastguard Worker return ret 301*1fa6dee9SAndroid Build Coastguard Worker} 302*1fa6dee9SAndroid Build Coastguard Worker 303*1fa6dee9SAndroid Build Coastguard Worker// Match returns true if name matches pattern using the same rules as filepath.Match, but supporting 304*1fa6dee9SAndroid Build Coastguard Worker// recursive globs (**). 305*1fa6dee9SAndroid Build Coastguard Workerfunc Match(pattern, name string) (bool, error) { 306*1fa6dee9SAndroid Build Coastguard Worker if filepath.Base(pattern) == "**" { 307*1fa6dee9SAndroid Build Coastguard Worker return false, GlobLastRecursiveErr 308*1fa6dee9SAndroid Build Coastguard Worker } 309*1fa6dee9SAndroid Build Coastguard Worker 310*1fa6dee9SAndroid Build Coastguard Worker patternDir := pattern[len(pattern)-1] == '/' 311*1fa6dee9SAndroid Build Coastguard Worker nameDir := name[len(name)-1] == '/' 312*1fa6dee9SAndroid Build Coastguard Worker 313*1fa6dee9SAndroid Build Coastguard Worker if patternDir != nameDir { 314*1fa6dee9SAndroid Build Coastguard Worker return false, nil 315*1fa6dee9SAndroid Build Coastguard Worker } 316*1fa6dee9SAndroid Build Coastguard Worker 317*1fa6dee9SAndroid Build Coastguard Worker if nameDir { 318*1fa6dee9SAndroid Build Coastguard Worker name = name[:len(name)-1] 319*1fa6dee9SAndroid Build Coastguard Worker pattern = pattern[:len(pattern)-1] 320*1fa6dee9SAndroid Build Coastguard Worker } 321*1fa6dee9SAndroid Build Coastguard Worker 322*1fa6dee9SAndroid Build Coastguard Worker for { 323*1fa6dee9SAndroid Build Coastguard Worker var patternFile, nameFile string 324*1fa6dee9SAndroid Build Coastguard Worker pattern, patternFile = filepath.Dir(pattern), filepath.Base(pattern) 325*1fa6dee9SAndroid Build Coastguard Worker 326*1fa6dee9SAndroid Build Coastguard Worker if patternFile == "**" { 327*1fa6dee9SAndroid Build Coastguard Worker if strings.Contains(pattern, "**") { 328*1fa6dee9SAndroid Build Coastguard Worker return false, GlobMultipleRecursiveErr 329*1fa6dee9SAndroid Build Coastguard Worker } 330*1fa6dee9SAndroid Build Coastguard Worker // Test if the any prefix of name matches the part of the pattern before ** 331*1fa6dee9SAndroid Build Coastguard Worker for { 332*1fa6dee9SAndroid Build Coastguard Worker if name == "." || name == "/" { 333*1fa6dee9SAndroid Build Coastguard Worker return name == pattern, nil 334*1fa6dee9SAndroid Build Coastguard Worker } 335*1fa6dee9SAndroid Build Coastguard Worker if match, err := filepath.Match(pattern, name); err != nil { 336*1fa6dee9SAndroid Build Coastguard Worker return false, err 337*1fa6dee9SAndroid Build Coastguard Worker } else if match { 338*1fa6dee9SAndroid Build Coastguard Worker return true, nil 339*1fa6dee9SAndroid Build Coastguard Worker } 340*1fa6dee9SAndroid Build Coastguard Worker name = filepath.Dir(name) 341*1fa6dee9SAndroid Build Coastguard Worker } 342*1fa6dee9SAndroid Build Coastguard Worker } else if strings.Contains(patternFile, "**") { 343*1fa6dee9SAndroid Build Coastguard Worker return false, GlobInvalidRecursiveErr 344*1fa6dee9SAndroid Build Coastguard Worker } 345*1fa6dee9SAndroid Build Coastguard Worker 346*1fa6dee9SAndroid Build Coastguard Worker name, nameFile = filepath.Dir(name), filepath.Base(name) 347*1fa6dee9SAndroid Build Coastguard Worker 348*1fa6dee9SAndroid Build Coastguard Worker if nameFile == "." && patternFile == "." { 349*1fa6dee9SAndroid Build Coastguard Worker return true, nil 350*1fa6dee9SAndroid Build Coastguard Worker } else if nameFile == "/" && patternFile == "/" { 351*1fa6dee9SAndroid Build Coastguard Worker return true, nil 352*1fa6dee9SAndroid Build Coastguard Worker } else if nameFile == "." || patternFile == "." || nameFile == "/" || patternFile == "/" { 353*1fa6dee9SAndroid Build Coastguard Worker return false, nil 354*1fa6dee9SAndroid Build Coastguard Worker } 355*1fa6dee9SAndroid Build Coastguard Worker 356*1fa6dee9SAndroid Build Coastguard Worker match, err := filepath.Match(patternFile, nameFile) 357*1fa6dee9SAndroid Build Coastguard Worker if err != nil || !match { 358*1fa6dee9SAndroid Build Coastguard Worker return match, err 359*1fa6dee9SAndroid Build Coastguard Worker } 360*1fa6dee9SAndroid Build Coastguard Worker } 361*1fa6dee9SAndroid Build Coastguard Worker} 362*1fa6dee9SAndroid Build Coastguard Worker 363*1fa6dee9SAndroid Build Coastguard Worker// IsGlob returns true if the pattern contains any glob characters (*, ?, or [). 364*1fa6dee9SAndroid Build Coastguard Workerfunc IsGlob(pattern string) bool { 365*1fa6dee9SAndroid Build Coastguard Worker return strings.IndexAny(pattern, "*?[") >= 0 366*1fa6dee9SAndroid Build Coastguard Worker} 367*1fa6dee9SAndroid Build Coastguard Worker 368*1fa6dee9SAndroid Build Coastguard Worker// HasGlob returns true if any string in the list contains any glob characters (*, ?, or [). 369*1fa6dee9SAndroid Build Coastguard Workerfunc HasGlob(in []string) bool { 370*1fa6dee9SAndroid Build Coastguard Worker for _, s := range in { 371*1fa6dee9SAndroid Build Coastguard Worker if IsGlob(s) { 372*1fa6dee9SAndroid Build Coastguard Worker return true 373*1fa6dee9SAndroid Build Coastguard Worker } 374*1fa6dee9SAndroid Build Coastguard Worker } 375*1fa6dee9SAndroid Build Coastguard Worker 376*1fa6dee9SAndroid Build Coastguard Worker return false 377*1fa6dee9SAndroid Build Coastguard Worker} 378*1fa6dee9SAndroid Build Coastguard Worker 379*1fa6dee9SAndroid Build Coastguard Worker// WriteFileIfChanged wraps ioutil.WriteFile, but only writes the file if 380*1fa6dee9SAndroid Build Coastguard Worker// the files does not already exist with identical contents. This can be used 381*1fa6dee9SAndroid Build Coastguard Worker// along with ninja restat rules to skip rebuilding downstream rules if no 382*1fa6dee9SAndroid Build Coastguard Worker// changes were made by a rule. 383*1fa6dee9SAndroid Build Coastguard Workerfunc WriteFileIfChanged(filename string, data []byte, perm os.FileMode) error { 384*1fa6dee9SAndroid Build Coastguard Worker var isChanged bool 385*1fa6dee9SAndroid Build Coastguard Worker 386*1fa6dee9SAndroid Build Coastguard Worker dir := filepath.Dir(filename) 387*1fa6dee9SAndroid Build Coastguard Worker err := os.MkdirAll(dir, 0777) 388*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 389*1fa6dee9SAndroid Build Coastguard Worker return err 390*1fa6dee9SAndroid Build Coastguard Worker } 391*1fa6dee9SAndroid Build Coastguard Worker 392*1fa6dee9SAndroid Build Coastguard Worker info, err := os.Stat(filename) 393*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 394*1fa6dee9SAndroid Build Coastguard Worker if os.IsNotExist(err) { 395*1fa6dee9SAndroid Build Coastguard Worker // The file does not exist yet. 396*1fa6dee9SAndroid Build Coastguard Worker isChanged = true 397*1fa6dee9SAndroid Build Coastguard Worker } else { 398*1fa6dee9SAndroid Build Coastguard Worker return err 399*1fa6dee9SAndroid Build Coastguard Worker } 400*1fa6dee9SAndroid Build Coastguard Worker } else { 401*1fa6dee9SAndroid Build Coastguard Worker if info.Size() != int64(len(data)) { 402*1fa6dee9SAndroid Build Coastguard Worker isChanged = true 403*1fa6dee9SAndroid Build Coastguard Worker } else { 404*1fa6dee9SAndroid Build Coastguard Worker oldData, err := ioutil.ReadFile(filename) 405*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 406*1fa6dee9SAndroid Build Coastguard Worker return err 407*1fa6dee9SAndroid Build Coastguard Worker } 408*1fa6dee9SAndroid Build Coastguard Worker 409*1fa6dee9SAndroid Build Coastguard Worker if len(oldData) != len(data) { 410*1fa6dee9SAndroid Build Coastguard Worker isChanged = true 411*1fa6dee9SAndroid Build Coastguard Worker } else { 412*1fa6dee9SAndroid Build Coastguard Worker for i := range data { 413*1fa6dee9SAndroid Build Coastguard Worker if oldData[i] != data[i] { 414*1fa6dee9SAndroid Build Coastguard Worker isChanged = true 415*1fa6dee9SAndroid Build Coastguard Worker break 416*1fa6dee9SAndroid Build Coastguard Worker } 417*1fa6dee9SAndroid Build Coastguard Worker } 418*1fa6dee9SAndroid Build Coastguard Worker } 419*1fa6dee9SAndroid Build Coastguard Worker } 420*1fa6dee9SAndroid Build Coastguard Worker } 421*1fa6dee9SAndroid Build Coastguard Worker 422*1fa6dee9SAndroid Build Coastguard Worker if isChanged { 423*1fa6dee9SAndroid Build Coastguard Worker err = ioutil.WriteFile(filename, data, perm) 424*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 425*1fa6dee9SAndroid Build Coastguard Worker return err 426*1fa6dee9SAndroid Build Coastguard Worker } 427*1fa6dee9SAndroid Build Coastguard Worker } 428*1fa6dee9SAndroid Build Coastguard Worker 429*1fa6dee9SAndroid Build Coastguard Worker return nil 430*1fa6dee9SAndroid Build Coastguard Worker} 431*1fa6dee9SAndroid Build Coastguard Worker 432*1fa6dee9SAndroid Build Coastguard Workervar matchEscaper = strings.NewReplacer( 433*1fa6dee9SAndroid Build Coastguard Worker `*`, `\*`, 434*1fa6dee9SAndroid Build Coastguard Worker `?`, `\?`, 435*1fa6dee9SAndroid Build Coastguard Worker `[`, `\[`, 436*1fa6dee9SAndroid Build Coastguard Worker `]`, `\]`, 437*1fa6dee9SAndroid Build Coastguard Worker) 438*1fa6dee9SAndroid Build Coastguard Worker 439*1fa6dee9SAndroid Build Coastguard Worker// MatchEscape returns its inputs with characters that would be interpreted by 440*1fa6dee9SAndroid Build Coastguard Workerfunc MatchEscape(s string) string { 441*1fa6dee9SAndroid Build Coastguard Worker return matchEscaper.Replace(s) 442*1fa6dee9SAndroid Build Coastguard Worker} 443