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.
19 // OverlayContext overlays a build.Context with additional files from
20 // a map. Files in the map take precedence over other files.
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.
27 // A common use case for OverlayContext is to allow editors to pass in
28 // a set of unsaved, modified files.
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
35 rc := func(data []byte) (io.ReadCloser, error) {
36 return ioutil.NopCloser(bytes.NewBuffer(data)), nil
39 copy := *orig // make a copy
41 ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
42 // Fast path: names match exactly.
43 if content, ok := overlay[path]; ok {
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) {
55 return OpenFile(orig, path)
60 // ParseOverlayArchive parses an archive containing Go files and their
61 // contents. The result is intended to be used with OverlayContext.
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 // newlines. 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)
74 filename, err := r.ReadString('\n')
79 return nil, fmt.Errorf("reading archive file name: %v", err)
81 filename = filepath.Clean(strings.TrimSpace(filename))
84 sz, err := r.ReadString('\n')
86 return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
88 sz = strings.TrimSpace(sz)
89 size, err := strconv.ParseUint(sz, 10, 32)
91 return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
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)
99 overlay[filename] = content