-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathmain.go
276 lines (207 loc) · 7.1 KB
/
main.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package main
import (
"context"
"flag"
"fmt"
"os"
"path"
"runtime/pprof"
"github.com/google/subcommands"
"github.com/loov/goda/internal/cut"
"github.com/loov/goda/internal/exec"
"github.com/loov/goda/internal/graph"
"github.com/loov/goda/internal/list"
"github.com/loov/goda/internal/pkgset"
"github.com/loov/goda/internal/tree"
"github.com/loov/goda/internal/weight"
"github.com/loov/goda/internal/weightdiff"
)
func main() {
cpuProfile := flag.String("cpuprofile", "", "profile cpu usage")
cmds := subcommands.NewCommander(flag.CommandLine, path.Base(os.Args[0]))
cmds.Register(cmds.HelpCommand(), "")
cmds.Register(&list.Command{}, "")
cmds.Register(&tree.Command{}, "")
cmds.Register(&exec.Command{}, "")
cmds.Register(&weight.Command{}, "")
cmds.Register(&weightdiff.Command{}, "")
cmds.Register(&graph.Command{}, "")
cmds.Register(&cut.Command{}, "")
cmds.Register(&ExprHelp{}, "")
cmds.Register(&FormatHelp{}, "")
flag.Parse()
os.Exit(func() int {
ctx := context.Background()
if *cpuProfile != "" {
f, err := os.Create(*cpuProfile)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to create file: %v\n", err)
return -1
}
defer func() { _ = f.Close() }()
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Fprintf(os.Stderr, "could not start CPU profile: %v\n", err)
}
defer pprof.StopCPUProfile()
}
return int(cmds.Execute(ctx))
}())
}
type ExprHelp struct{}
func (*ExprHelp) Name() string { return "expr" }
func (*ExprHelp) Synopsis() string { return "Help about package expressions" }
func (*ExprHelp) Usage() string {
return `Package expressions allow to specify calculations with dependencies.
The examples use X, Y and Z as placeholders for packages, packages paths or
package expressions.
# Basic operations:
There are a few basic operations specified for manipulating sets of packages.
X Y Z;
X + Y + Z; add(X, Y, Z); or(X, Y, Z)
all packages that match X, Y or Z
X - Y - Z; subtract(X, Y, Z); exclude(X, Y, Z)
packages that match X but not Y or Z
shared(X, Y, Z); intersect(X, Y, Z)
packages that match all of X, Y and Z
xor(X, Y);
packages that match one of X or Y but not both
# Selectors:
Selectors allow selecting parts of the dependency tree. They are
applied in left to right order.
X:all
select X and all of its direct and indirect dependencies
X:import, X:imp
select direct imports of X
X:import:all, X:imp:all
select direct and indirect dependencies of X; X not included
X:source
select packages not imported by any package in X
X:-source
shorthand for (X - X:source)
X:main
select packages in X named main
X:test
select test packages of X
# Functions:
reach(X, Y);
packages from X that can reach a package in Y
incoming(X, Y);
packages from X that directly import a package in Y, including Y
transitive(X);
a transitive reduction in package dependencies
# Tags and OS:
test=1(X);
include tests when resolving X
goos=linux(X):
set goos to "linux" tests when resolving X
purego=1(X):
add tag "purego" for resolving X
# Example expressions:
github.com/loov/goda:import
all direct dependencies for the "github.com/loov/goda" package
shared(github.com/loov/goda/pkgset:all, github.com/loov/goda/templates:all)
packages directly or indirectly imported by both
"github.com/loov/goda/pkgset" and "github.com/loov/goda/templates"
github.com/loov/goda/...:all - golang.org/x/tools/...
all of goda's dependencies excluding golang.org/x/tools packages
reach(github.com/loov/goda/...:all, golang.org/x/tools/go/packages)
packages in github.com/loov/goda/ that use golang.org/x/tools/go/packages
`
}
func (*ExprHelp) SetFlags(f *flag.FlagSet) {}
func (cmd *ExprHelp) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
if f.NArg() == 0 {
return subcommands.ExitUsageError
}
result, err := pkgset.Parse(ctx, f.Args())
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return subcommands.ExitFailure
}
fmt.Fprintln(os.Stdout, result.Tree(0))
return subcommands.ExitSuccess
}
type FormatHelp struct{}
func (*FormatHelp) Name() string { return "format" }
func (*FormatHelp) Synopsis() string { return "Help about formatting" }
func (*FormatHelp) Usage() string {
return `Formatting allows to add useful information about packages.
Formatting uses -f flag for specifying the output of each package.
goda uses https://pkg.go.dev/text/template for templating and it allows
for extensive formatting.
Each package node in goda has information about the package itself,
and its statistics. Additionally there is a summary of downstream
and upstream statistics:
type Node struct {
*Package
ImportsNodes []*Node
Stat Stat // Stats about the current node.
Up Stat // Stats about upstream nodes.
Down Stat // Stats about downstream nodes.
}
type Package struct {
ID string // ID is a unique identifier for a package,
PkgPath string // PkgPath is the full import path of the package.
Module *packages.Module
}
type Module struct {
Path string // module path
Version string // module version
Main bool // is this the main module?
}
This is not the full list of information about the node, however,
this is the most useful. To see inspect the structures in depth,
it's possible to use:
goda list -f "{{ printf \"%#v\" .Package }}" .
Statistics for package contains the following information:
type Stat struct {
PackageCount int64
AllFiles Source
Go Source
OtherFiles Source
Decls Decls
Tokens Tokens
}
The source information contains the following information:
type Source struct {
Files int // Files count in this stat.
Binary int // Binary file count.
Size memory.Bytes // Size in bytes of all files.
Lines int // Count of non-empty lines.
Blank int // Count of empty lines.
}
As an example, to print total size of non-go files in a package:
goda list -f "{{.ID}} {{.Stat.OtherFiles.Size}}" ./...:all
It's also possible to see information about the ast tokens and
declarations, which can be used as an approximation of the final
binary size.
type Decls struct {
Func int64
Type int64
Const int64
Var int64
Other int64
}
type Tokens struct {
Code int64
Comment int64
Basic int64
}
"goda cut" command additionally contains:
type Node struct {
Cut stat.Stat
Imports []*Node
ImportedBy []*Node
InDegree int // alias for len(ImportedBy)
OutDegree int // alias for len(Imports)
...
}
This contains summary of packages that would be removed when that
package would deleted from the project.
`
}
func (*FormatHelp) SetFlags(f *flag.FlagSet) {}
func (cmd *FormatHelp) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
fmt.Println("Run \"goda help format\" to see help about formatting.")
return subcommands.ExitUsageError
}