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