Home | History | Annotate | Download | only in blueprint
      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