Source file src/cmd/go/internal/imports/scan.go

     1  // Copyright 2018 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.
     4  
     5  package imports
     6  
     7  import (
     8  	"fmt"
     9  	"io/fs"
    10  	"path/filepath"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"cmd/go/internal/fsys"
    16  )
    17  
    18  func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
    19  	infos, err := fsys.ReadDir(dir)
    20  	if err != nil {
    21  		return nil, nil, err
    22  	}
    23  	var files []string
    24  	for _, info := range infos {
    25  		name := info.Name()
    26  
    27  		// If the directory entry is a symlink, stat it to obtain the info for the
    28  		// link target instead of the link itself.
    29  		if info.Mode()&fs.ModeSymlink != 0 {
    30  			info, err = fsys.Stat(filepath.Join(dir, name))
    31  			if err != nil {
    32  				continue // Ignore broken symlinks.
    33  			}
    34  		}
    35  
    36  		if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
    37  			files = append(files, filepath.Join(dir, name))
    38  		}
    39  	}
    40  	return scanFiles(files, tags, false)
    41  }
    42  
    43  func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) {
    44  	return scanFiles(files, tags, true)
    45  }
    46  
    47  func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) {
    48  	imports := make(map[string]bool)
    49  	testImports := make(map[string]bool)
    50  	numFiles := 0
    51  Files:
    52  	for _, name := range files {
    53  		r, err := fsys.Open(name)
    54  		if err != nil {
    55  			return nil, nil, err
    56  		}
    57  		var list []string
    58  		data, err := ReadImports(r, false, &list)
    59  		r.Close()
    60  		if err != nil {
    61  			return nil, nil, fmt.Errorf("reading %s: %v", name, err)
    62  		}
    63  
    64  		// import "C" is implicit requirement of cgo tag.
    65  		// When listing files on the command line (explicitFiles=true)
    66  		// we do not apply build tag filtering but we still do apply
    67  		// cgo filtering, so no explicitFiles check here.
    68  		// Why? Because we always have, and it's not worth breaking
    69  		// that behavior now.
    70  		for _, path := range list {
    71  			if path == `"C"` && !tags["cgo"] && !tags["*"] {
    72  				continue Files
    73  			}
    74  		}
    75  
    76  		if !explicitFiles && !ShouldBuild(data, tags) {
    77  			continue
    78  		}
    79  		numFiles++
    80  		m := imports
    81  		if strings.HasSuffix(name, "_test.go") {
    82  			m = testImports
    83  		}
    84  		for _, p := range list {
    85  			q, err := strconv.Unquote(p)
    86  			if err != nil {
    87  				continue
    88  			}
    89  			m[q] = true
    90  		}
    91  	}
    92  	if numFiles == 0 {
    93  		return nil, nil, ErrNoGo
    94  	}
    95  	return keys(imports), keys(testImports), nil
    96  }
    97  
    98  var ErrNoGo = fmt.Errorf("no Go source files")
    99  
   100  func keys(m map[string]bool) []string {
   101  	list := make([]string, 0, len(m))
   102  	for k := range m {
   103  		list = append(list, k)
   104  	}
   105  	sort.Strings(list)
   106  	return list
   107  }
   108  

View as plain text