|
|
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package buildutil
import ( "bufio" "bytes" "fmt" "go/build" "io" "io/ioutil" "path/filepath" "strconv" "strings" )
// OverlayContext overlays a build.Context with additional files from
// a map. Files in the map take precedence over other files.
//
// In addition to plain string comparison, two file names are
// considered equal if their base names match and their directory
// components point at the same directory on the file system. That is,
// symbolic links are followed for directories, but not files.
//
// A common use case for OverlayContext is to allow editors to pass in
// a set of unsaved, modified files.
//
// Currently, only the Context.OpenFile function will respect the
// overlay. This may change in the future.
func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context { // TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
rc := func(data []byte) (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewBuffer(data)), nil }
copy := *orig // make a copy
ctxt := © ctxt.OpenFile = func(path string) (io.ReadCloser, error) { // Fast path: names match exactly.
if content, ok := overlay[path]; ok { return rc(content) }
// Slow path: check for same file under a different
// alias, perhaps due to a symbolic link.
for filename, content := range overlay { if sameFile(path, filename) { return rc(content) } }
return OpenFile(orig, path) } return ctxt }
// ParseOverlayArchive parses an archive containing Go files and their
// contents. The result is intended to be used with OverlayContext.
//
//
// Archive format
//
// The archive consists of a series of files. Each file consists of a
// name, a decimal file size and the file contents, separated by
// newlinews. No newline follows after the file contents.
func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) { overlay := make(map[string][]byte) r := bufio.NewReader(archive) for { // Read file name.
filename, err := r.ReadString('\n') if err != nil { if err == io.EOF { break // OK
} return nil, fmt.Errorf("reading archive file name: %v", err) } filename = filepath.Clean(strings.TrimSpace(filename))
// Read file size.
sz, err := r.ReadString('\n') if err != nil { return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err) } sz = strings.TrimSpace(sz) size, err := strconv.ParseUint(sz, 10, 32) if err != nil { return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err) }
// Read file content.
content := make([]byte, size) if _, err := io.ReadFull(r, content); err != nil { return nil, fmt.Errorf("reading archive file %s: %v", filename, err) } overlay[filename] = content }
return overlay, nil }
|