gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/go/buildutil/overlay.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package buildutil
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"go/build"
    12  	"io"
    13  	"io/ioutil"
    14  	"path/filepath"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  // OverlayContext overlays a build.Context with additional files from
    20  // a map. Files in the map take precedence over other files.
    21  //
    22  // In addition to plain string comparison, two file names are
    23  // considered equal if their base names match and their directory
    24  // components point at the same directory on the file system. That is,
    25  // symbolic links are followed for directories, but not files.
    26  //
    27  // A common use case for OverlayContext is to allow editors to pass in
    28  // a set of unsaved, modified files.
    29  //
    30  // Currently, only the Context.OpenFile function will respect the
    31  // overlay. This may change in the future.
    32  func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context {
    33  	// TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
    34  
    35  	rc := func(data []byte) (io.ReadCloser, error) {
    36  		return ioutil.NopCloser(bytes.NewBuffer(data)), nil
    37  	}
    38  
    39  	copy := *orig // make a copy
    40  	ctxt := &copy
    41  	ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
    42  		// Fast path: names match exactly.
    43  		if content, ok := overlay[path]; ok {
    44  			return rc(content)
    45  		}
    46  
    47  		// Slow path: check for same file under a different
    48  		// alias, perhaps due to a symbolic link.
    49  		for filename, content := range overlay {
    50  			if sameFile(path, filename) {
    51  				return rc(content)
    52  			}
    53  		}
    54  
    55  		return OpenFile(orig, path)
    56  	}
    57  	return ctxt
    58  }
    59  
    60  // ParseOverlayArchive parses an archive containing Go files and their
    61  // contents. The result is intended to be used with OverlayContext.
    62  //
    63  //
    64  // Archive format
    65  //
    66  // The archive consists of a series of files. Each file consists of a
    67  // name, a decimal file size and the file contents, separated by
    68  // newlinews. No newline follows after the file contents.
    69  func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
    70  	overlay := make(map[string][]byte)
    71  	r := bufio.NewReader(archive)
    72  	for {
    73  		// Read file name.
    74  		filename, err := r.ReadString('\n')
    75  		if err != nil {
    76  			if err == io.EOF {
    77  				break // OK
    78  			}
    79  			return nil, fmt.Errorf("reading archive file name: %v", err)
    80  		}
    81  		filename = filepath.Clean(strings.TrimSpace(filename))
    82  
    83  		// Read file size.
    84  		sz, err := r.ReadString('\n')
    85  		if err != nil {
    86  			return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
    87  		}
    88  		sz = strings.TrimSpace(sz)
    89  		size, err := strconv.ParseUint(sz, 10, 32)
    90  		if err != nil {
    91  			return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
    92  		}
    93  
    94  		// Read file content.
    95  		content := make([]byte, size)
    96  		if _, err := io.ReadFull(r, content); err != nil {
    97  			return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
    98  		}
    99  		overlay[filename] = content
   100  	}
   101  
   102  	return overlay, nil
   103  }