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