@@ -3,14 +3,17 @@ package main
3
3
import (
4
4
"flag"
5
5
"fmt"
6
+ "go/build"
6
7
"io/ioutil"
7
8
"log"
8
9
"os"
9
10
"os/exec"
10
11
"path/filepath"
12
+ "sort"
11
13
"strings"
12
14
13
15
"golang.org/x/tools/go/vcs"
16
+ "golang.org/x/tools/refactor/importgraph"
14
17
)
15
18
16
19
func get (gopath , repo string ) error {
@@ -77,72 +80,94 @@ func estimate(importpath string) error {
77
80
}
78
81
}
79
82
80
- // Use digraph(1) to obtain the forward transitive closure of the repo in
81
- // question.
82
- cmd := exec .Command ("/bin/sh" , "-c" , "go list -f '{{.ImportPath}}{{.Imports}}{{.TestImports}}{{.XTestImports}}' ... | tr '[]' ' ' | digraph forward $(go list " + importpath + "/...)" )
83
+ // Remove standard lib packages
84
+ cmd := exec .Command ("go" , "list" , "std" )
83
85
cmd .Stderr = os .Stderr
84
86
cmd .Env = append ([]string {
85
87
fmt .Sprintf ("GOPATH=%s" , gopath ),
86
88
}, passthroughEnv ()... )
89
+
87
90
out , err := cmd .Output ()
88
91
if err != nil {
89
92
return fmt .Errorf ("%v: %v" , cmd .Args , err )
90
93
}
91
-
92
- closure := make (map [string ]bool )
94
+ stdlib := make (map [string ]bool )
93
95
for _ , line := range strings .Split (strings .TrimSpace (string (out )), "\n " ) {
94
- closure [line ] = true
96
+ stdlib [line ] = true
95
97
}
96
98
97
- // Remove standard lib packages
98
- cmd = exec .Command ("go" , "list" , "std" )
99
- cmd .Stderr = os .Stderr
100
- cmd .Env = append ([]string {
101
- fmt .Sprintf ("GOPATH=%s" , gopath ),
102
- }, passthroughEnv ()... )
99
+ stdlib ["C" ] = true // would fail resolving anyway
103
100
104
- out , err = cmd .Output ()
101
+ // Filter out all already-packaged ones:
102
+ golangBinaries , err := getGolangBinaries ()
105
103
if err != nil {
106
- return fmt .Errorf ("%v: %v" , cmd .Args , err )
107
- }
108
- for _ , line := range strings .Split (strings .TrimSpace (string (out )), "\n " ) {
109
- delete (closure , line )
104
+ return nil
110
105
}
111
106
112
- delete (closure , "C" ) // would fail resolving anyway
107
+ build .Default .GOPATH = gopath
108
+ forward , _ , errors := importgraph .Build (& build .Default )
109
+ if len (errors ) > 0 {
110
+ lines := make ([]string , 0 , len (errors ))
111
+ for importPath , err := range errors {
112
+ lines = append (lines , fmt .Sprintf ("%s: %v" , importPath , err ))
113
+ }
114
+ return fmt .Errorf ("could not load packages: %v" , strings .Join (lines , "\n " ))
115
+ }
113
116
114
- // Resolve all packages to the root of their repository.
115
- roots := make (map [string ]bool )
116
- for dep := range closure {
117
- rr , err := vcs .RepoRootForImportPath (dep , false )
117
+ var lines []string
118
+ seen := make (map [string ]bool )
119
+ rrseen := make (map [string ]bool )
120
+ node := func (importPath string , indent int ) {
121
+ rr , err := vcs .RepoRootForImportPath (importPath , false )
118
122
if err != nil {
119
- log .Printf ("Could not determine repo path for import path %q: %v\n " , dep , err )
120
- continue
123
+ log .Printf ("Could not determine repo path for import path %q: %v\n " , importPath , err )
124
+ return
121
125
}
122
-
123
- roots [rr .Root ] = true
126
+ if rrseen [rr .Root ] {
127
+ return
128
+ }
129
+ rrseen [rr .Root ] = true
130
+ if _ , ok := golangBinaries [rr .Root ]; ok {
131
+ return // already packaged in Debian
132
+ }
133
+ lines = append (lines , fmt .Sprintf ("%s%s" , strings .Repeat (" " , indent ), rr .Root ))
124
134
}
125
-
126
- // Filter out all already-packaged ones:
127
- golangBinaries , err := getGolangBinaries ()
128
- if err != nil {
129
- return nil
135
+ var visit func (x string , indent int )
136
+ visit = func (x string , indent int ) {
137
+ if seen [x ] {
138
+ return
139
+ }
140
+ seen [x ] = true
141
+ if ! stdlib [x ] {
142
+ node (x , indent )
143
+ }
144
+ for y := range forward [x ] {
145
+ visit (y , indent + 1 )
146
+ }
130
147
}
131
148
132
- for importpath , binary := range golangBinaries {
133
- if roots [importpath ] {
134
- log .Printf ("found %s in Debian package %s" , importpath , binary )
135
- delete (roots , importpath )
149
+ keys := make ([]string , 0 , len (forward ))
150
+ for key := range forward {
151
+ keys = append (keys , key )
152
+ }
153
+ sort .Strings (keys )
154
+ for _ , key := range keys {
155
+ if ! strings .HasPrefix (key , importpath ) {
156
+ continue
157
+ }
158
+ if seen [key ] {
159
+ continue // already covered in a previous visit call
136
160
}
161
+ visit (key , 0 )
137
162
}
138
163
139
- if len (roots ) == 0 {
164
+ if len (lines ) == 0 {
140
165
log .Printf ("%s is already fully packaged in Debian" , importpath )
141
166
return nil
142
167
}
143
168
log .Printf ("Bringing %s to Debian requires packaging the following Go packages:" , importpath )
144
- for importpath := range roots {
145
- fmt .Println (importpath )
169
+ for _ , line := range lines {
170
+ fmt .Println (line )
146
171
}
147
172
148
173
return nil
0 commit comments