xref: /aosp_15_r20/build/blueprint/pathtools/glob.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
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