forked from sourcegraph/srclib
/
units_cmd.go
165 lines (137 loc) · 3.92 KB
/
units_cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package src
import (
"fmt"
"log"
"path/filepath"
"strings"
"sourcegraph.com/sourcegraph/srclib/config"
"sourcegraph.com/sourcegraph/srclib/scan"
"sourcegraph.com/sourcegraph/srclib/toolchain"
"sourcegraph.com/sourcegraph/srclib/unit"
)
func init() {
c, err := CLI.AddCommand("units",
"lists source units",
`Lists source units in the repository or directory tree rooted at DIR (or the current directory if DIR is not specified).`,
&unitsCmd,
)
if err != nil {
log.Fatal(err)
}
setDefaultRepoURIOpt(c)
setDefaultRepoSubdirOpt(c)
}
// scanUnitsIntoConfig uses cfg to scan for source units. It modifies
// cfg.SourceUnits, merging the scanned source units with those already present
// in cfg.
func scanUnitsIntoConfig(cfg *config.Repository, configOpt config.Options, execOpt ToolchainExecOpt, quiet bool) error {
scanners := make([]toolchain.Tool, len(cfg.Scanners))
for i, scannerRef := range cfg.Scanners {
scanner, err := toolchain.OpenTool(scannerRef.Toolchain, scannerRef.Subcmd, execOpt.ToolchainMode())
if err != nil {
return err
}
scanners[i] = scanner
}
units, err := scan.ScanMulti(scanners, scan.Options{Options: configOpt, Quiet: quiet}, cfg.Config)
if err != nil {
return err
}
// Merge the repo/tree config with each source unit's config.
if cfg.Config == nil {
cfg.Config = map[string]interface{}{}
}
for _, u := range units {
for k, v := range cfg.Config {
if uv, present := u.Config[k]; present {
log.Printf("Both the scanned source unit %q and the Srcfile specify a Config key %q. Using the value from the scanned source unit (%+v).", u.ID(), k, uv)
} else {
if u.Config == nil {
u.Config = map[string]interface{}{}
}
u.Config[k] = v
}
}
}
// collect manually specified source units by ID
manualUnits := make(map[unit.ID]*unit.SourceUnit, len(cfg.SourceUnits))
for _, u := range cfg.SourceUnits {
manualUnits[u.ID()] = u
xf, err := unit.ExpandPaths(".", u.Files)
if err != nil {
return err
}
u.Files = xf
}
for _, u := range units {
if mu, present := manualUnits[u.ID()]; present {
log.Printf("Found manually specified source unit %q with same ID as scanned source unit. Using manually specified unit, ignoring scanned source unit.", mu.ID())
continue
}
unitDir := u.Dir
if unitDir == "" && len(u.Files) > 0 {
// in case the unit doesn't specify a Dir, obtain it from the first file
unitDir = filepath.Dir(u.Files[0])
}
// heed SkipDirs
if pathHasAnyPrefix(unitDir, cfg.SkipDirs) {
continue
}
skip := false
for _, skipUnit := range cfg.SkipUnits {
if u.Name == skipUnit.Name && u.Type == skipUnit.Type {
skip = true
break
}
}
if skip {
continue
}
cfg.SourceUnits = append(cfg.SourceUnits, u)
}
return nil
}
type UnitsCmd struct {
config.Options
ToolchainExecOpt `group:"execution"`
Output struct {
Output string `short:"o" long:"output" description:"output format" default:"text" value-name:"text|json"`
} `group:"output"`
Args struct {
Dir Directory `name:"DIR" default:"." description:"root directory of tree to list units in"`
} `positional-args:"yes"`
}
var unitsCmd UnitsCmd
func (c *UnitsCmd) Execute(args []string) error {
if c.Args.Dir == "" {
c.Args.Dir = "."
}
cfg, err := getInitialConfig(c.Options, c.Args.Dir)
if err != nil {
return err
}
if err := scanUnitsIntoConfig(cfg, c.Options, c.ToolchainExecOpt, false); err != nil {
return err
}
if c.Output.Output == "json" {
PrintJSON(cfg.SourceUnits, "")
} else {
for _, u := range cfg.SourceUnits {
fmt.Printf("%-50s %s\n", u.Name, u.Type)
}
}
return nil
}
func pathHasAnyPrefix(path string, prefixes []string) bool {
for _, prefix := range prefixes {
if pathHasPrefix(path, prefix) {
return true
}
}
return false
}
func pathHasPrefix(path, prefix string) bool {
path = filepath.Clean(path)
prefix = filepath.Clean(prefix)
return prefix == "." || path == prefix || strings.HasPrefix(path, prefix+"/")
}