// 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 // newlines. 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 }