...

Source file src/golang.org/x/tools/go/buildutil/overlay.go

Documentation: golang.org/x/tools/go/buildutil

     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  // # Archive format
    64  //
    65  // The archive consists of a series of files. Each file consists of a
    66  // name, a decimal file size and the file contents, separated by
    67  // newlines. No newline follows after the file contents.
    68  func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
    69  	overlay := make(map[string][]byte)
    70  	r := bufio.NewReader(archive)
    71  	for {
    72  		// Read file name.
    73  		filename, err := r.ReadString('\n')
    74  		if err != nil {
    75  			if err == io.EOF {
    76  				break // OK
    77  			}
    78  			return nil, fmt.Errorf("reading archive file name: %v", err)
    79  		}
    80  		filename = filepath.Clean(strings.TrimSpace(filename))
    81  
    82  		// Read file size.
    83  		sz, err := r.ReadString('\n')
    84  		if err != nil {
    85  			return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
    86  		}
    87  		sz = strings.TrimSpace(sz)
    88  		size, err := strconv.ParseUint(sz, 10, 32)
    89  		if err != nil {
    90  			return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
    91  		}
    92  
    93  		// Read file content.
    94  		content := make([]byte, size)
    95  		if _, err := io.ReadFull(r, content); err != nil {
    96  			return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
    97  		}
    98  		overlay[filename] = content
    99  	}
   100  
   101  	return overlay, nil
   102  }
   103  

View as plain text