forked from bytbox/gomake
/
godep.go
200 lines (184 loc) · 4.67 KB
/
godep.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
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
. "container/vector"
"fmt"
"go/ast"
"go/parser"
"opts"
"os"
"path"
"strings"
)
var showVersion = opts.LongFlag("version", "display version information")
var showNeeded = opts.Flag("n", "need", "display external dependencies")
var progName = "godep"
var roots = map[string]string{}
func main() {
opts.Usage = "[file1.go [...]]"
opts.Description =
`construct and print a dependency tree for the given source files.`
// parse and handle options
opts.Parse()
if *showVersion {
ShowVersion()
os.Exit(0)
}
// if there are no files, generate a list
if len(opts.Args) == 0 {
path.Walk(".", GoFileFinder{}, nil)
} else {
for _, fname := range opts.Args {
files.Push(fname)
}
}
// for each file, list dependencies
for _, fname := range files {
file, err := parser.ParseFile(fname, nil, parser.ImportsOnly)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
HandleFile(fname, file)
}
PrintAutoNotice()
FindMain()
if *showNeeded {
PrintNeeded(".EXTERNAL: ", ".a")
}
// in any case, print as a comment
PrintNeeded("# external packages: ", "")
PrintDeps()
}
type Package struct {
files *StringVector
packages map[string]string
hasMain bool
}
var packages = map[string]Package{}
func FindMain() {
// for each file in the main package
if pkg, ok := packages["main"]; ok {
for _, fname := range *pkg.files {
file, _ := parser.ParseFile(fname, nil, 0)
ast.Walk(&MainCheckVisitor{fname}, file)
}
}
}
// PrintNeeded prints out a list of external dependencies to standard output.
func PrintNeeded(pre, ppost string) {
// dependencies already displayed
done := map[string]bool{}
// start the list
fmt.Print(pre)
// for each package
for _, pkg := range packages {
// print all packages for which we don't have the source
for _, pkgname := range pkg.packages {
if _, ok := packages[pkgname]; !ok && !done[pkgname] {
fmt.Printf("%s%s ", pkgname, ppost)
done[pkgname] = true
}
}
}
fmt.Print("\n")
}
// PrintDeps prints out the dependency lists to standard output.
func PrintDeps() {
// for each package
for pkgname, pkg := range packages {
if pkgname != "main" {
// start the list
fmt.Printf("%s.a: ", pkgname)
// print all the files
for _, fname := range *pkg.files {
fmt.Printf("%s ", fname)
}
// print all packages for which we have the source
// exception: if -n was supplied, print all packages
for _, pkgname := range pkg.packages {
_, ok := packages[pkgname]
if ok || *showNeeded {
fmt.Printf("%s.a ", pkgname)
}
}
fmt.Printf("\n")
}
}
common := StringVector{}
// for the main package
if main, ok := packages["main"]; ok {
// consider all files not found in 'roots' to be common to
// everything in this package
for _, fname := range *main.files {
if app, ok := roots[fname]; ok {
fmt.Printf("%s: %s.${O}\n", app, app)
} else {
common.Push(fname)
}
}
for _, fname := range *main.files {
if app, ok := roots[fname]; ok {
// dependencies already displayed
done := map[string]bool{}
// print the file
fmt.Printf("%s.${O}: %s ", app, fname)
// print the common files
for _, cfile := range common {
fmt.Printf("%s ", cfile)
}
// print all packages for which we have the
// source, or, if -n was supplied, print all
for _, pkgname := range main.packages {
_, ok := packages[pkgname]
if ok || (*showNeeded && !done[pkgname]) {
fmt.Printf("%s.a ", pkgname)
done[pkgname] = true
}
}
fmt.Printf("\n")
}
}
}
}
func HandleFile(fname string, file *ast.File) {
pkgname := file.Name.Name
if pkg, ok := packages[pkgname]; ok {
pkg.files.Push(fname)
} else {
packages[pkgname] = Package{&StringVector{}, map[string]string{}, false}
packages[pkgname].files.Push(fname)
}
ast.Walk(&ImportVisitor{packages[pkgname]}, file)
}
type ImportVisitor struct {
pkg Package
}
func (v ImportVisitor) Visit(node interface{}) ast.Visitor {
// check the type of the node
if spec, ok := node.(*ast.ImportSpec); ok {
ppath := path.Clean(strings.Trim(string(spec.Path.Value), "\""))
if _, ok = v.pkg.packages[ppath]; !ok {
v.pkg.packages[ppath]=ppath
}
}
return v
}
type MainCheckVisitor struct {
fname string
}
func addRoot(filename string) {
fparts := strings.Split(filename, ".", -1)
basename := fparts[0]
roots[filename] = basename
}
func (v MainCheckVisitor) Visit(node interface{}) ast.Visitor {
if decl, ok := node.(*ast.FuncDecl); ok {
if decl.Name.Name == "main" {
addRoot(v.fname)
}
}
return v
}