src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

overlay.go (2930B)


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