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 15 package blueprint 16 17 import ( 18 "fmt" 19 "reflect" 20 "sort" 21 ) 22 23 type GlobPath struct { 24 Pattern string 25 Excludes []string 26 Files []string 27 Deps []string 28 Name string 29 } 30 31 func verifyGlob(fileName, pattern string, excludes []string, g GlobPath) { 32 if pattern != g.Pattern { 33 panic(fmt.Errorf("Mismatched patterns %q and %q for glob file %q", pattern, g.Pattern, fileName)) 34 } 35 if !reflect.DeepEqual(g.Excludes, excludes) { 36 panic(fmt.Errorf("Mismatched excludes %v and %v for glob file %q", excludes, g.Excludes, fileName)) 37 } 38 } 39 40 func (c *Context) glob(pattern string, excludes []string) ([]string, error) { 41 fileName := globToFileName(pattern, excludes) 42 43 // Try to get existing glob from the stored results 44 c.globLock.Lock() 45 g, exists := c.globs[fileName] 46 c.globLock.Unlock() 47 48 if exists { 49 // Glob has already been done, double check it is identical 50 verifyGlob(fileName, pattern, excludes, g) 51 return g.Files, nil 52 } 53 54 // Get a globbed file list 55 files, deps, err := c.fs.Glob(pattern, excludes) 56 if err != nil { 57 return nil, err 58 } 59 60 // Store the results 61 c.globLock.Lock() 62 if g, exists = c.globs[fileName]; !exists { 63 c.globs[fileName] = GlobPath{pattern, excludes, files, deps, fileName} 64 } 65 c.globLock.Unlock() 66 67 // Getting the list raced with another goroutine, throw away the results and use theirs 68 if exists { 69 verifyGlob(fileName, pattern, excludes, g) 70 return g.Files, nil 71 } 72 73 return files, nil 74 } 75 76 func (c *Context) Globs() []GlobPath { 77 fileNames := make([]string, 0, len(c.globs)) 78 for k := range c.globs { 79 fileNames = append(fileNames, k) 80 } 81 sort.Strings(fileNames) 82 83 globs := make([]GlobPath, len(fileNames)) 84 for i, fileName := range fileNames { 85 globs[i] = c.globs[fileName] 86 } 87 88 return globs 89 } 90 91 func globToString(pattern string) string { 92 ret := "" 93 for _, c := range pattern { 94 switch { 95 case c >= 'a' && c <= 'z', 96 c >= 'A' && c <= 'Z', 97 c >= '0' && c <= '9', 98 c == '_', c == '-', c == '/': 99 ret += string(c) 100 default: 101 ret += "_" 102 } 103 } 104 105 return ret 106 } 107 108 func globToFileName(pattern string, excludes []string) string { 109 ret := globToString(pattern) 110 for _, e := range excludes { 111 ret += "__" + globToString(e) 112 } 113 return ret + ".glob" 114 } 115