1// Copyright 2015 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package blueprint 16 17import ( 18 "fmt" 19 "slices" 20 "sort" 21 "strings" 22 23 "github.com/google/blueprint/pathtools" 24) 25 26func verifyGlob(key globKey, pattern string, excludes []string, g pathtools.GlobResult) { 27 if pattern != g.Pattern { 28 panic(fmt.Errorf("Mismatched patterns %q and %q for glob key %q", pattern, g.Pattern, key)) 29 } 30 if len(excludes) != len(g.Excludes) { 31 panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) 32 } 33 34 for i := range excludes { 35 if g.Excludes[i] != excludes[i] { 36 panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) 37 } 38 } 39} 40 41func (c *Context) glob(pattern string, excludes []string) ([]string, error) { 42 // Sort excludes so that two globs with the same excludes in a different order reuse the same 43 // key. Make a copy first to avoid modifying the caller's version. 44 excludes = slices.Clone(excludes) 45 sort.Strings(excludes) 46 47 key := globToKey(pattern, excludes) 48 49 // Try to get existing glob from the stored results 50 c.globLock.Lock() 51 g, exists := c.globs[key] 52 c.globLock.Unlock() 53 54 if exists { 55 // Glob has already been done, double check it is identical 56 verifyGlob(key, pattern, excludes, g) 57 // Return a copy so that modifications don't affect the cached value. 58 return slices.Clone(g.Matches), nil 59 } 60 61 // Get a globbed file list 62 result, err := c.fs.Glob(pattern, excludes, pathtools.FollowSymlinks) 63 if err != nil { 64 return nil, err 65 } 66 67 // Store the results 68 c.globLock.Lock() 69 if g, exists = c.globs[key]; !exists { 70 c.globs[key] = result 71 } 72 c.globLock.Unlock() 73 74 if exists { 75 // Getting the list raced with another goroutine, throw away the results and use theirs 76 verifyGlob(key, pattern, excludes, g) 77 // Return a copy so that modifications don't affect the cached value. 78 return slices.Clone(g.Matches), nil 79 } 80 81 // Return a copy so that modifications don't affect the cached value. 82 return slices.Clone(result.Matches), nil 83} 84 85func (c *Context) Globs() pathtools.MultipleGlobResults { 86 keys := make([]globKey, 0, len(c.globs)) 87 for k := range c.globs { 88 keys = append(keys, k) 89 } 90 91 sort.Slice(keys, func(i, j int) bool { 92 if keys[i].pattern != keys[j].pattern { 93 return keys[i].pattern < keys[j].pattern 94 } 95 return keys[i].excludes < keys[j].excludes 96 }) 97 98 globs := make(pathtools.MultipleGlobResults, len(keys)) 99 for i, key := range keys { 100 globs[i] = c.globs[key] 101 } 102 103 return globs 104} 105 106// globKey combines a pattern and a list of excludes into a hashable struct to be used as a key in 107// a map. 108type globKey struct { 109 pattern string 110 excludes string 111} 112 113// globToKey converts a pattern and an excludes list into a globKey struct that is hashable and 114// usable as a key in a map. 115func globToKey(pattern string, excludes []string) globKey { 116 return globKey{pattern, strings.Join(excludes, "|")} 117} 118