xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/embedcfg.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Daspackage main
16*9bb1b549SSpandan Das
17*9bb1b549SSpandan Dasimport (
18*9bb1b549SSpandan Das	"encoding/json"
19*9bb1b549SSpandan Das	"errors"
20*9bb1b549SSpandan Das	"fmt"
21*9bb1b549SSpandan Das	"io/ioutil"
22*9bb1b549SSpandan Das	"os"
23*9bb1b549SSpandan Das	"path"
24*9bb1b549SSpandan Das	"path/filepath"
25*9bb1b549SSpandan Das	"runtime"
26*9bb1b549SSpandan Das	"sort"
27*9bb1b549SSpandan Das	"strings"
28*9bb1b549SSpandan Das)
29*9bb1b549SSpandan Das
30*9bb1b549SSpandan Das// buildEmbedcfgFile writes an embedcfg file to be read by the compiler.
31*9bb1b549SSpandan Das// An embedcfg file can be used in Go 1.16 or higher if the "embed" package
32*9bb1b549SSpandan Das// is imported and there are one or more //go:embed comments in .go files.
33*9bb1b549SSpandan Das// The embedcfg file maps //go:embed patterns to actual file names.
34*9bb1b549SSpandan Das//
35*9bb1b549SSpandan Das// The embedcfg file will be created in workDir, and its name is returned.
36*9bb1b549SSpandan Das// The caller is responsible for deleting it. If no embedcfg file is needed,
37*9bb1b549SSpandan Das// "" is returned with no error.
38*9bb1b549SSpandan Das//
39*9bb1b549SSpandan Das// All source files listed in goSrcs with //go:embed comments must be in one
40*9bb1b549SSpandan Das// of the directories in embedRootDirs (not in a subdirectory). Embed patterns
41*9bb1b549SSpandan Das// are evaluated relative to the source directory. Embed sources (embedSrcs)
42*9bb1b549SSpandan Das// outside those directories are ignored, since they can't be matched by any
43*9bb1b549SSpandan Das// valid pattern.
44*9bb1b549SSpandan Dasfunc buildEmbedcfgFile(goSrcs []fileInfo, embedSrcs, embedRootDirs []string, workDir string) (string, error) {
45*9bb1b549SSpandan Das	// Check whether this package uses embedding and whether the toolchain
46*9bb1b549SSpandan Das	// supports it (Go 1.16+). With Go 1.15 and lower, we'll try to compile
47*9bb1b549SSpandan Das	// without an embedcfg file, and the compiler will complain the "embed"
48*9bb1b549SSpandan Das	// package is missing.
49*9bb1b549SSpandan Das	var major, minor int
50*9bb1b549SSpandan Das	if n, err := fmt.Sscanf(runtime.Version(), "go%d.%d", &major, &minor); n != 2 || err != nil {
51*9bb1b549SSpandan Das		// Can't parse go version. Maybe it's a development version; fall through.
52*9bb1b549SSpandan Das	} else if major < 1 || (major == 1 && minor < 16) {
53*9bb1b549SSpandan Das		return "", nil
54*9bb1b549SSpandan Das	}
55*9bb1b549SSpandan Das	importEmbed := false
56*9bb1b549SSpandan Das	haveEmbed := false
57*9bb1b549SSpandan Das	for _, src := range goSrcs {
58*9bb1b549SSpandan Das		if len(src.embeds) > 0 {
59*9bb1b549SSpandan Das			haveEmbed = true
60*9bb1b549SSpandan Das			rootDir := findInRootDirs(src.filename, embedRootDirs)
61*9bb1b549SSpandan Das			if rootDir == "" || strings.Contains(src.filename[len(rootDir)+1:], string(filepath.Separator)) {
62*9bb1b549SSpandan Das				// Report an error if a source files appears in a subdirectory of
63*9bb1b549SSpandan Das				// another source directory. In this situation, the same file could be
64*9bb1b549SSpandan Das				// referenced with different paths.
65*9bb1b549SSpandan Das				return "", fmt.Errorf("%s: source files with //go:embed should be in same directory. Allowed directories are:\n\t%s",
66*9bb1b549SSpandan Das					src.filename,
67*9bb1b549SSpandan Das					strings.Join(embedRootDirs, "\n\t"))
68*9bb1b549SSpandan Das			}
69*9bb1b549SSpandan Das		}
70*9bb1b549SSpandan Das		for _, imp := range src.imports {
71*9bb1b549SSpandan Das			if imp.path == "embed" {
72*9bb1b549SSpandan Das				importEmbed = true
73*9bb1b549SSpandan Das			}
74*9bb1b549SSpandan Das		}
75*9bb1b549SSpandan Das	}
76*9bb1b549SSpandan Das	if !importEmbed || !haveEmbed {
77*9bb1b549SSpandan Das		return "", nil
78*9bb1b549SSpandan Das	}
79*9bb1b549SSpandan Das
80*9bb1b549SSpandan Das	// Build a tree of embeddable files. This includes paths listed with
81*9bb1b549SSpandan Das	// -embedsrc. If one of those paths is a directory, the tree includes
82*9bb1b549SSpandan Das	// its files and subdirectories. Paths in the tree are relative to the
83*9bb1b549SSpandan Das	// path in embedRootDirs that contains them.
84*9bb1b549SSpandan Das	root, err := buildEmbedTree(embedSrcs, embedRootDirs)
85*9bb1b549SSpandan Das	if err != nil {
86*9bb1b549SSpandan Das		return "", err
87*9bb1b549SSpandan Das	}
88*9bb1b549SSpandan Das
89*9bb1b549SSpandan Das	// Resolve patterns to sets of files.
90*9bb1b549SSpandan Das	var embedcfg struct {
91*9bb1b549SSpandan Das		Patterns map[string][]string
92*9bb1b549SSpandan Das		Files    map[string]string
93*9bb1b549SSpandan Das	}
94*9bb1b549SSpandan Das	embedcfg.Patterns = make(map[string][]string)
95*9bb1b549SSpandan Das	embedcfg.Files = make(map[string]string)
96*9bb1b549SSpandan Das	for _, src := range goSrcs {
97*9bb1b549SSpandan Das		for _, embed := range src.embeds {
98*9bb1b549SSpandan Das			matchedPaths, matchedFiles, err := resolveEmbed(embed, root)
99*9bb1b549SSpandan Das			if err != nil {
100*9bb1b549SSpandan Das				return "", err
101*9bb1b549SSpandan Das			}
102*9bb1b549SSpandan Das			embedcfg.Patterns[embed.pattern] = matchedPaths
103*9bb1b549SSpandan Das			for i, rel := range matchedPaths {
104*9bb1b549SSpandan Das				embedcfg.Files[rel] = matchedFiles[i]
105*9bb1b549SSpandan Das			}
106*9bb1b549SSpandan Das		}
107*9bb1b549SSpandan Das	}
108*9bb1b549SSpandan Das
109*9bb1b549SSpandan Das	// Write the configuration to a JSON file.
110*9bb1b549SSpandan Das	embedcfgData, err := json.MarshalIndent(&embedcfg, "", "\t")
111*9bb1b549SSpandan Das	if err != nil {
112*9bb1b549SSpandan Das		return "", err
113*9bb1b549SSpandan Das	}
114*9bb1b549SSpandan Das	embedcfgName := filepath.Join(workDir, "embedcfg")
115*9bb1b549SSpandan Das	if err := ioutil.WriteFile(embedcfgName, embedcfgData, 0o666); err != nil {
116*9bb1b549SSpandan Das		return "", err
117*9bb1b549SSpandan Das	}
118*9bb1b549SSpandan Das	return embedcfgName, nil
119*9bb1b549SSpandan Das}
120*9bb1b549SSpandan Das
121*9bb1b549SSpandan Das// findInRootDirs returns a string from rootDirs which is a parent of the
122*9bb1b549SSpandan Das// file path p. If there is no such string, findInRootDirs returns "".
123*9bb1b549SSpandan Dasfunc findInRootDirs(p string, rootDirs []string) string {
124*9bb1b549SSpandan Das	dir := filepath.Dir(p)
125*9bb1b549SSpandan Das	for _, rootDir := range rootDirs {
126*9bb1b549SSpandan Das		if rootDir == dir ||
127*9bb1b549SSpandan Das			(strings.HasPrefix(dir, rootDir) && len(dir) > len(rootDir)+1 && dir[len(rootDir)] == filepath.Separator) {
128*9bb1b549SSpandan Das			return rootDir
129*9bb1b549SSpandan Das		}
130*9bb1b549SSpandan Das	}
131*9bb1b549SSpandan Das	return ""
132*9bb1b549SSpandan Das}
133*9bb1b549SSpandan Das
134*9bb1b549SSpandan Das// embedNode represents an embeddable file or directory in a tree.
135*9bb1b549SSpandan Dastype embedNode struct {
136*9bb1b549SSpandan Das	name       string                // base name
137*9bb1b549SSpandan Das	path       string                // absolute file path
138*9bb1b549SSpandan Das	children   map[string]*embedNode // non-nil for directory
139*9bb1b549SSpandan Das	childNames []string              // sorted
140*9bb1b549SSpandan Das}
141*9bb1b549SSpandan Das
142*9bb1b549SSpandan Das// add inserts file nodes into the tree rooted at f for the slash-separated
143*9bb1b549SSpandan Das// path src, relative to the absolute file path rootDir. If src points to a
144*9bb1b549SSpandan Das// directory, add recursively inserts nodes for its contents. If a node already
145*9bb1b549SSpandan Das// exists (for example, if a source file and a generated file have the same
146*9bb1b549SSpandan Das// name), add leaves the existing node in place.
147*9bb1b549SSpandan Dasfunc (n *embedNode) add(rootDir, src string) error {
148*9bb1b549SSpandan Das	// Create nodes for parents of src.
149*9bb1b549SSpandan Das	parent := n
150*9bb1b549SSpandan Das	parts := strings.Split(src, "/")
151*9bb1b549SSpandan Das	for _, p := range parts[:len(parts)-1] {
152*9bb1b549SSpandan Das		if parent.children[p] == nil {
153*9bb1b549SSpandan Das			parent.children[p] = &embedNode{
154*9bb1b549SSpandan Das				name:     p,
155*9bb1b549SSpandan Das				children: make(map[string]*embedNode),
156*9bb1b549SSpandan Das			}
157*9bb1b549SSpandan Das		}
158*9bb1b549SSpandan Das		parent = parent.children[p]
159*9bb1b549SSpandan Das	}
160*9bb1b549SSpandan Das
161*9bb1b549SSpandan Das	// Create a node for src. If src is a directory, recursively create nodes for
162*9bb1b549SSpandan Das	// its contents. Go embedding ignores symbolic links, but Bazel may use links
163*9bb1b549SSpandan Das	// for generated files and directories, so we follow them here.
164*9bb1b549SSpandan Das	var visit func(*embedNode, string, os.FileInfo) error
165*9bb1b549SSpandan Das	visit = func(parent *embedNode, path string, fi os.FileInfo) error {
166*9bb1b549SSpandan Das		base := filepath.Base(path)
167*9bb1b549SSpandan Das		if parent.children[base] == nil {
168*9bb1b549SSpandan Das			parent.children[base] = &embedNode{name: base, path: path}
169*9bb1b549SSpandan Das		}
170*9bb1b549SSpandan Das		if !fi.IsDir() {
171*9bb1b549SSpandan Das			return nil
172*9bb1b549SSpandan Das		}
173*9bb1b549SSpandan Das		node := parent.children[base]
174*9bb1b549SSpandan Das		node.children = make(map[string]*embedNode)
175*9bb1b549SSpandan Das		f, err := os.Open(path)
176*9bb1b549SSpandan Das		if err != nil {
177*9bb1b549SSpandan Das			return err
178*9bb1b549SSpandan Das		}
179*9bb1b549SSpandan Das		names, err := f.Readdirnames(0)
180*9bb1b549SSpandan Das		f.Close()
181*9bb1b549SSpandan Das		if err != nil {
182*9bb1b549SSpandan Das			return err
183*9bb1b549SSpandan Das		}
184*9bb1b549SSpandan Das		for _, name := range names {
185*9bb1b549SSpandan Das			cPath := filepath.Join(path, name)
186*9bb1b549SSpandan Das			cfi, err := os.Stat(cPath)
187*9bb1b549SSpandan Das			if err != nil {
188*9bb1b549SSpandan Das				return err
189*9bb1b549SSpandan Das			}
190*9bb1b549SSpandan Das			if err := visit(node, cPath, cfi); err != nil {
191*9bb1b549SSpandan Das				return err
192*9bb1b549SSpandan Das			}
193*9bb1b549SSpandan Das		}
194*9bb1b549SSpandan Das		return nil
195*9bb1b549SSpandan Das	}
196*9bb1b549SSpandan Das
197*9bb1b549SSpandan Das	path := filepath.Join(rootDir, src)
198*9bb1b549SSpandan Das	fi, err := os.Stat(path)
199*9bb1b549SSpandan Das	if err != nil {
200*9bb1b549SSpandan Das		return err
201*9bb1b549SSpandan Das	}
202*9bb1b549SSpandan Das	return visit(parent, path, fi)
203*9bb1b549SSpandan Das}
204*9bb1b549SSpandan Das
205*9bb1b549SSpandan Dasfunc (n *embedNode) isDir() bool {
206*9bb1b549SSpandan Das	return n.children != nil
207*9bb1b549SSpandan Das}
208*9bb1b549SSpandan Das
209*9bb1b549SSpandan Das// get returns a tree node, given a slash-separated path relative to the
210*9bb1b549SSpandan Das// receiver. get returns nil if no node exists with that path.
211*9bb1b549SSpandan Dasfunc (n *embedNode) get(path string) *embedNode {
212*9bb1b549SSpandan Das	if path == "." || path == "" {
213*9bb1b549SSpandan Das		return n
214*9bb1b549SSpandan Das	}
215*9bb1b549SSpandan Das	for _, part := range strings.Split(path, "/") {
216*9bb1b549SSpandan Das		n = n.children[part]
217*9bb1b549SSpandan Das		if n == nil {
218*9bb1b549SSpandan Das			return nil
219*9bb1b549SSpandan Das		}
220*9bb1b549SSpandan Das	}
221*9bb1b549SSpandan Das	return n
222*9bb1b549SSpandan Das}
223*9bb1b549SSpandan Das
224*9bb1b549SSpandan Dasvar errSkip = errors.New("skip")
225*9bb1b549SSpandan Das
226*9bb1b549SSpandan Das// walk calls fn on each node in the tree rooted at n in depth-first pre-order.
227*9bb1b549SSpandan Dasfunc (n *embedNode) walk(fn func(rel string, n *embedNode) error) error {
228*9bb1b549SSpandan Das	var visit func(string, *embedNode) error
229*9bb1b549SSpandan Das	visit = func(rel string, node *embedNode) error {
230*9bb1b549SSpandan Das		err := fn(rel, node)
231*9bb1b549SSpandan Das		if err == errSkip {
232*9bb1b549SSpandan Das			return nil
233*9bb1b549SSpandan Das		} else if err != nil {
234*9bb1b549SSpandan Das			return err
235*9bb1b549SSpandan Das		}
236*9bb1b549SSpandan Das		for _, name := range node.childNames {
237*9bb1b549SSpandan Das			if err := visit(path.Join(rel, name), node.children[name]); err != nil && err != errSkip {
238*9bb1b549SSpandan Das				return err
239*9bb1b549SSpandan Das			}
240*9bb1b549SSpandan Das		}
241*9bb1b549SSpandan Das		return nil
242*9bb1b549SSpandan Das	}
243*9bb1b549SSpandan Das	err := visit("", n)
244*9bb1b549SSpandan Das	if err == errSkip {
245*9bb1b549SSpandan Das		return nil
246*9bb1b549SSpandan Das	}
247*9bb1b549SSpandan Das	return err
248*9bb1b549SSpandan Das}
249*9bb1b549SSpandan Das
250*9bb1b549SSpandan Das// buildEmbedTree constructs a logical directory tree of embeddable files.
251*9bb1b549SSpandan Das// The tree may contain a mix of static and generated files from multiple
252*9bb1b549SSpandan Das// root directories. Directory artifacts are recursively expanded.
253*9bb1b549SSpandan Dasfunc buildEmbedTree(embedSrcs, embedRootDirs []string) (root *embedNode, err error) {
254*9bb1b549SSpandan Das	defer func() {
255*9bb1b549SSpandan Das		if err != nil {
256*9bb1b549SSpandan Das			err = fmt.Errorf("building tree of embeddable files in directories %s: %v", strings.Join(embedRootDirs, string(filepath.ListSeparator)), err)
257*9bb1b549SSpandan Das		}
258*9bb1b549SSpandan Das	}()
259*9bb1b549SSpandan Das
260*9bb1b549SSpandan Das	// Add each path to the tree.
261*9bb1b549SSpandan Das	root = &embedNode{name: "", children: make(map[string]*embedNode)}
262*9bb1b549SSpandan Das	for _, src := range embedSrcs {
263*9bb1b549SSpandan Das		rootDir := findInRootDirs(src, embedRootDirs)
264*9bb1b549SSpandan Das		if rootDir == "" {
265*9bb1b549SSpandan Das			// Embedded path cannot be matched by any valid pattern. Ignore.
266*9bb1b549SSpandan Das			continue
267*9bb1b549SSpandan Das		}
268*9bb1b549SSpandan Das		rel := filepath.ToSlash(src[len(rootDir)+1:])
269*9bb1b549SSpandan Das		if err := root.add(rootDir, rel); err != nil {
270*9bb1b549SSpandan Das			return nil, err
271*9bb1b549SSpandan Das		}
272*9bb1b549SSpandan Das	}
273*9bb1b549SSpandan Das
274*9bb1b549SSpandan Das	// Sort children in each directory node.
275*9bb1b549SSpandan Das	var visit func(*embedNode)
276*9bb1b549SSpandan Das	visit = func(node *embedNode) {
277*9bb1b549SSpandan Das		node.childNames = make([]string, 0, len(node.children))
278*9bb1b549SSpandan Das		for name, child := range node.children {
279*9bb1b549SSpandan Das			node.childNames = append(node.childNames, name)
280*9bb1b549SSpandan Das			visit(child)
281*9bb1b549SSpandan Das		}
282*9bb1b549SSpandan Das		sort.Strings(node.childNames)
283*9bb1b549SSpandan Das	}
284*9bb1b549SSpandan Das	visit(root)
285*9bb1b549SSpandan Das
286*9bb1b549SSpandan Das	return root, nil
287*9bb1b549SSpandan Das}
288*9bb1b549SSpandan Das
289*9bb1b549SSpandan Das// resolveEmbed matches a //go:embed pattern in a source file to a set of
290*9bb1b549SSpandan Das// embeddable files in the given tree.
291*9bb1b549SSpandan Dasfunc resolveEmbed(embed fileEmbed, root *embedNode) (matchedPaths, matchedFiles []string, err error) {
292*9bb1b549SSpandan Das	defer func() {
293*9bb1b549SSpandan Das		if err != nil {
294*9bb1b549SSpandan Das			err = fmt.Errorf("%v: could not embed %s: %v", embed.pos, embed.pattern, err)
295*9bb1b549SSpandan Das		}
296*9bb1b549SSpandan Das	}()
297*9bb1b549SSpandan Das
298*9bb1b549SSpandan Das	// Remove optional "all:" prefix from pattern and set matchAll flag if present.
299*9bb1b549SSpandan Das	// See https://pkg.go.dev/embed#hdr-Directives for details.
300*9bb1b549SSpandan Das	pattern := embed.pattern
301*9bb1b549SSpandan Das	var matchAll bool
302*9bb1b549SSpandan Das	if strings.HasPrefix(pattern, "all:") {
303*9bb1b549SSpandan Das		matchAll = true
304*9bb1b549SSpandan Das		pattern = pattern[4:]
305*9bb1b549SSpandan Das	}
306*9bb1b549SSpandan Das
307*9bb1b549SSpandan Das	// Check that the pattern has valid syntax.
308*9bb1b549SSpandan Das	if _, err := path.Match(pattern, ""); err != nil || !validEmbedPattern(pattern) {
309*9bb1b549SSpandan Das		return nil, nil, fmt.Errorf("invalid pattern syntax")
310*9bb1b549SSpandan Das	}
311*9bb1b549SSpandan Das
312*9bb1b549SSpandan Das	// Search for matching files.
313*9bb1b549SSpandan Das	err = root.walk(func(matchRel string, matchNode *embedNode) error {
314*9bb1b549SSpandan Das		if ok, _ := path.Match(pattern, matchRel); !ok {
315*9bb1b549SSpandan Das			// Non-matching file or directory.
316*9bb1b549SSpandan Das			return nil
317*9bb1b549SSpandan Das		}
318*9bb1b549SSpandan Das
319*9bb1b549SSpandan Das		// TODO: Should check that directories along path do not begin a new module
320*9bb1b549SSpandan Das		// (do not contain a go.mod).
321*9bb1b549SSpandan Das		// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;l=2158;drc=261fe25c83a94fc3defe064baed3944cd3d16959
322*9bb1b549SSpandan Das		for dir := matchRel; len(dir) > 1; dir = filepath.Dir(dir) {
323*9bb1b549SSpandan Das			if base := path.Base(matchRel); isBadEmbedName(base) {
324*9bb1b549SSpandan Das				what := "file"
325*9bb1b549SSpandan Das				if matchNode.isDir() {
326*9bb1b549SSpandan Das					what = "directory"
327*9bb1b549SSpandan Das				}
328*9bb1b549SSpandan Das				if dir == matchRel {
329*9bb1b549SSpandan Das					return fmt.Errorf("cannot embed %s %s: invalid name %s", what, matchRel, base)
330*9bb1b549SSpandan Das				} else {
331*9bb1b549SSpandan Das					return fmt.Errorf("cannot embed %s %s: in invalid directory %s", what, matchRel, base)
332*9bb1b549SSpandan Das				}
333*9bb1b549SSpandan Das			}
334*9bb1b549SSpandan Das		}
335*9bb1b549SSpandan Das
336*9bb1b549SSpandan Das		if !matchNode.isDir() {
337*9bb1b549SSpandan Das			// Matching file. Add to list.
338*9bb1b549SSpandan Das			matchedPaths = append(matchedPaths, matchRel)
339*9bb1b549SSpandan Das			matchedFiles = append(matchedFiles, matchNode.path)
340*9bb1b549SSpandan Das			return nil
341*9bb1b549SSpandan Das		}
342*9bb1b549SSpandan Das
343*9bb1b549SSpandan Das		// Matching directory. Recursively add all files in subdirectories.
344*9bb1b549SSpandan Das		// Don't add hidden files or directories (starting with "." or "_"),
345*9bb1b549SSpandan Das		// unless "all:" prefix was set.
346*9bb1b549SSpandan Das		// See golang/go#42328.
347*9bb1b549SSpandan Das		matchTreeErr := matchNode.walk(func(childRel string, childNode *embedNode) error {
348*9bb1b549SSpandan Das			// TODO: Should check that directories along path do not begin a new module
349*9bb1b549SSpandan Das			// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;l=2158;drc=261fe25c83a94fc3defe064baed3944cd3d16959
350*9bb1b549SSpandan Das			if childRel != "" {
351*9bb1b549SSpandan Das				base := path.Base(childRel)
352*9bb1b549SSpandan Das				if isBadEmbedName(base) || (!matchAll && (strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_"))) {
353*9bb1b549SSpandan Das					if childNode.isDir() {
354*9bb1b549SSpandan Das						return errSkip
355*9bb1b549SSpandan Das					}
356*9bb1b549SSpandan Das					return nil
357*9bb1b549SSpandan Das				}
358*9bb1b549SSpandan Das			}
359*9bb1b549SSpandan Das			if !childNode.isDir() {
360*9bb1b549SSpandan Das				matchedPaths = append(matchedPaths, path.Join(matchRel, childRel))
361*9bb1b549SSpandan Das				matchedFiles = append(matchedFiles, childNode.path)
362*9bb1b549SSpandan Das			}
363*9bb1b549SSpandan Das			return nil
364*9bb1b549SSpandan Das		})
365*9bb1b549SSpandan Das		if matchTreeErr != nil {
366*9bb1b549SSpandan Das			return matchTreeErr
367*9bb1b549SSpandan Das		}
368*9bb1b549SSpandan Das		return errSkip
369*9bb1b549SSpandan Das	})
370*9bb1b549SSpandan Das	if err != nil && err != errSkip {
371*9bb1b549SSpandan Das		return nil, nil, err
372*9bb1b549SSpandan Das	}
373*9bb1b549SSpandan Das	if len(matchedPaths) == 0 {
374*9bb1b549SSpandan Das		return nil, nil, fmt.Errorf("no matching files found")
375*9bb1b549SSpandan Das	}
376*9bb1b549SSpandan Das	return matchedPaths, matchedFiles, nil
377*9bb1b549SSpandan Das}
378*9bb1b549SSpandan Das
379*9bb1b549SSpandan Dasfunc validEmbedPattern(pattern string) bool {
380*9bb1b549SSpandan Das	return pattern != "." && fsValidPath(pattern)
381*9bb1b549SSpandan Das}
382*9bb1b549SSpandan Das
383*9bb1b549SSpandan Das// validPath reports whether the given path name
384*9bb1b549SSpandan Das// is valid for use in a call to Open.
385*9bb1b549SSpandan Das// Path names passed to open are unrooted, slash-separated
386*9bb1b549SSpandan Das// sequences of path elements, like “x/y/z”.
387*9bb1b549SSpandan Das// Path names must not contain a “.” or “..” or empty element,
388*9bb1b549SSpandan Das// except for the special case that the root directory is named “.”.
389*9bb1b549SSpandan Das//
390*9bb1b549SSpandan Das// Paths are slash-separated on all systems, even Windows.
391*9bb1b549SSpandan Das// Backslashes must not appear in path names.
392*9bb1b549SSpandan Das//
393*9bb1b549SSpandan Das// Copied from io/fs.ValidPath in Go 1.16beta1.
394*9bb1b549SSpandan Dasfunc fsValidPath(name string) bool {
395*9bb1b549SSpandan Das	if name == "." {
396*9bb1b549SSpandan Das		// special case
397*9bb1b549SSpandan Das		return true
398*9bb1b549SSpandan Das	}
399*9bb1b549SSpandan Das
400*9bb1b549SSpandan Das	// Iterate over elements in name, checking each.
401*9bb1b549SSpandan Das	for {
402*9bb1b549SSpandan Das		i := 0
403*9bb1b549SSpandan Das		for i < len(name) && name[i] != '/' {
404*9bb1b549SSpandan Das			if name[i] == '\\' {
405*9bb1b549SSpandan Das				return false
406*9bb1b549SSpandan Das			}
407*9bb1b549SSpandan Das			i++
408*9bb1b549SSpandan Das		}
409*9bb1b549SSpandan Das		elem := name[:i]
410*9bb1b549SSpandan Das		if elem == "" || elem == "." || elem == ".." {
411*9bb1b549SSpandan Das			return false
412*9bb1b549SSpandan Das		}
413*9bb1b549SSpandan Das		if i == len(name) {
414*9bb1b549SSpandan Das			return true // reached clean ending
415*9bb1b549SSpandan Das		}
416*9bb1b549SSpandan Das		name = name[i+1:]
417*9bb1b549SSpandan Das	}
418*9bb1b549SSpandan Das}
419*9bb1b549SSpandan Das
420*9bb1b549SSpandan Das// isBadEmbedName reports whether name is the base name of a file that
421*9bb1b549SSpandan Das// can't or won't be included in modules and therefore shouldn't be treated
422*9bb1b549SSpandan Das// as existing for embedding.
423*9bb1b549SSpandan Das//
424*9bb1b549SSpandan Das// TODO: This should use the equivalent of golang.org/x/mod/module.CheckFilePath instead of fsValidPath.
425*9bb1b549SSpandan Das// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;l=2200;drc=261fe25c83a94fc3defe064baed3944cd3d16959
426*9bb1b549SSpandan Dasfunc isBadEmbedName(name string) bool {
427*9bb1b549SSpandan Das	if !fsValidPath(name) {
428*9bb1b549SSpandan Das		return true
429*9bb1b549SSpandan Das	}
430*9bb1b549SSpandan Das	switch name {
431*9bb1b549SSpandan Das	// Empty string should be impossible but make it bad.
432*9bb1b549SSpandan Das	case "":
433*9bb1b549SSpandan Das		return true
434*9bb1b549SSpandan Das	// Version control directories won't be present in module.
435*9bb1b549SSpandan Das	case ".bzr", ".hg", ".git", ".svn":
436*9bb1b549SSpandan Das		return true
437*9bb1b549SSpandan Das	}
438*9bb1b549SSpandan Das	return false
439*9bb1b549SSpandan Das}
440