xref: /aosp_15_r20/build/blueprint/glob.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2015 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 blueprint
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
19*1fa6dee9SAndroid Build Coastguard Worker	"slices"
20*1fa6dee9SAndroid Build Coastguard Worker	"sort"
21*1fa6dee9SAndroid Build Coastguard Worker	"strings"
22*1fa6dee9SAndroid Build Coastguard Worker
23*1fa6dee9SAndroid Build Coastguard Worker	"github.com/google/blueprint/pathtools"
24*1fa6dee9SAndroid Build Coastguard Worker)
25*1fa6dee9SAndroid Build Coastguard Worker
26*1fa6dee9SAndroid Build Coastguard Workerfunc verifyGlob(key globKey, pattern string, excludes []string, g pathtools.GlobResult) {
27*1fa6dee9SAndroid Build Coastguard Worker	if pattern != g.Pattern {
28*1fa6dee9SAndroid Build Coastguard Worker		panic(fmt.Errorf("Mismatched patterns %q and %q for glob key %q", pattern, g.Pattern, key))
29*1fa6dee9SAndroid Build Coastguard Worker	}
30*1fa6dee9SAndroid Build Coastguard Worker	if len(excludes) != len(g.Excludes) {
31*1fa6dee9SAndroid Build Coastguard Worker		panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key))
32*1fa6dee9SAndroid Build Coastguard Worker	}
33*1fa6dee9SAndroid Build Coastguard Worker
34*1fa6dee9SAndroid Build Coastguard Worker	for i := range excludes {
35*1fa6dee9SAndroid Build Coastguard Worker		if g.Excludes[i] != excludes[i] {
36*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key))
37*1fa6dee9SAndroid Build Coastguard Worker		}
38*1fa6dee9SAndroid Build Coastguard Worker	}
39*1fa6dee9SAndroid Build Coastguard Worker}
40*1fa6dee9SAndroid Build Coastguard Worker
41*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Context) glob(pattern string, excludes []string) ([]string, error) {
42*1fa6dee9SAndroid Build Coastguard Worker	// Sort excludes so that two globs with the same excludes in a different order reuse the same
43*1fa6dee9SAndroid Build Coastguard Worker	// key.  Make a copy first to avoid modifying the caller's version.
44*1fa6dee9SAndroid Build Coastguard Worker	excludes = slices.Clone(excludes)
45*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(excludes)
46*1fa6dee9SAndroid Build Coastguard Worker
47*1fa6dee9SAndroid Build Coastguard Worker	key := globToKey(pattern, excludes)
48*1fa6dee9SAndroid Build Coastguard Worker
49*1fa6dee9SAndroid Build Coastguard Worker	// Try to get existing glob from the stored results
50*1fa6dee9SAndroid Build Coastguard Worker	c.globLock.Lock()
51*1fa6dee9SAndroid Build Coastguard Worker	g, exists := c.globs[key]
52*1fa6dee9SAndroid Build Coastguard Worker	c.globLock.Unlock()
53*1fa6dee9SAndroid Build Coastguard Worker
54*1fa6dee9SAndroid Build Coastguard Worker	if exists {
55*1fa6dee9SAndroid Build Coastguard Worker		// Glob has already been done, double check it is identical
56*1fa6dee9SAndroid Build Coastguard Worker		verifyGlob(key, pattern, excludes, g)
57*1fa6dee9SAndroid Build Coastguard Worker		// Return a copy so that modifications don't affect the cached value.
58*1fa6dee9SAndroid Build Coastguard Worker		return slices.Clone(g.Matches), nil
59*1fa6dee9SAndroid Build Coastguard Worker	}
60*1fa6dee9SAndroid Build Coastguard Worker
61*1fa6dee9SAndroid Build Coastguard Worker	// Get a globbed file list
62*1fa6dee9SAndroid Build Coastguard Worker	result, err := c.fs.Glob(pattern, excludes, pathtools.FollowSymlinks)
63*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
64*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
65*1fa6dee9SAndroid Build Coastguard Worker	}
66*1fa6dee9SAndroid Build Coastguard Worker
67*1fa6dee9SAndroid Build Coastguard Worker	// Store the results
68*1fa6dee9SAndroid Build Coastguard Worker	c.globLock.Lock()
69*1fa6dee9SAndroid Build Coastguard Worker	if g, exists = c.globs[key]; !exists {
70*1fa6dee9SAndroid Build Coastguard Worker		c.globs[key] = result
71*1fa6dee9SAndroid Build Coastguard Worker	}
72*1fa6dee9SAndroid Build Coastguard Worker	c.globLock.Unlock()
73*1fa6dee9SAndroid Build Coastguard Worker
74*1fa6dee9SAndroid Build Coastguard Worker	if exists {
75*1fa6dee9SAndroid Build Coastguard Worker		// Getting the list raced with another goroutine, throw away the results and use theirs
76*1fa6dee9SAndroid Build Coastguard Worker		verifyGlob(key, pattern, excludes, g)
77*1fa6dee9SAndroid Build Coastguard Worker		// Return a copy so that modifications don't affect the cached value.
78*1fa6dee9SAndroid Build Coastguard Worker		return slices.Clone(g.Matches), nil
79*1fa6dee9SAndroid Build Coastguard Worker	}
80*1fa6dee9SAndroid Build Coastguard Worker
81*1fa6dee9SAndroid Build Coastguard Worker	// Return a copy so that modifications don't affect the cached value.
82*1fa6dee9SAndroid Build Coastguard Worker	return slices.Clone(result.Matches), nil
83*1fa6dee9SAndroid Build Coastguard Worker}
84*1fa6dee9SAndroid Build Coastguard Worker
85*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Context) Globs() pathtools.MultipleGlobResults {
86*1fa6dee9SAndroid Build Coastguard Worker	keys := make([]globKey, 0, len(c.globs))
87*1fa6dee9SAndroid Build Coastguard Worker	for k := range c.globs {
88*1fa6dee9SAndroid Build Coastguard Worker		keys = append(keys, k)
89*1fa6dee9SAndroid Build Coastguard Worker	}
90*1fa6dee9SAndroid Build Coastguard Worker
91*1fa6dee9SAndroid Build Coastguard Worker	sort.Slice(keys, func(i, j int) bool {
92*1fa6dee9SAndroid Build Coastguard Worker		if keys[i].pattern != keys[j].pattern {
93*1fa6dee9SAndroid Build Coastguard Worker			return keys[i].pattern < keys[j].pattern
94*1fa6dee9SAndroid Build Coastguard Worker		}
95*1fa6dee9SAndroid Build Coastguard Worker		return keys[i].excludes < keys[j].excludes
96*1fa6dee9SAndroid Build Coastguard Worker	})
97*1fa6dee9SAndroid Build Coastguard Worker
98*1fa6dee9SAndroid Build Coastguard Worker	globs := make(pathtools.MultipleGlobResults, len(keys))
99*1fa6dee9SAndroid Build Coastguard Worker	for i, key := range keys {
100*1fa6dee9SAndroid Build Coastguard Worker		globs[i] = c.globs[key]
101*1fa6dee9SAndroid Build Coastguard Worker	}
102*1fa6dee9SAndroid Build Coastguard Worker
103*1fa6dee9SAndroid Build Coastguard Worker	return globs
104*1fa6dee9SAndroid Build Coastguard Worker}
105*1fa6dee9SAndroid Build Coastguard Worker
106*1fa6dee9SAndroid Build Coastguard Worker// globKey combines a pattern and a list of excludes into a hashable struct to be used as a key in
107*1fa6dee9SAndroid Build Coastguard Worker// a map.
108*1fa6dee9SAndroid Build Coastguard Workertype globKey struct {
109*1fa6dee9SAndroid Build Coastguard Worker	pattern  string
110*1fa6dee9SAndroid Build Coastguard Worker	excludes string
111*1fa6dee9SAndroid Build Coastguard Worker}
112*1fa6dee9SAndroid Build Coastguard Worker
113*1fa6dee9SAndroid Build Coastguard Worker// globToKey converts a pattern and an excludes list into a globKey struct that is hashable and
114*1fa6dee9SAndroid Build Coastguard Worker// usable as a key in a map.
115*1fa6dee9SAndroid Build Coastguard Workerfunc globToKey(pattern string, excludes []string) globKey {
116*1fa6dee9SAndroid Build Coastguard Worker	return globKey{pattern, strings.Join(excludes, "|")}
117*1fa6dee9SAndroid Build Coastguard Worker}
118