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