Skip to content

Commit 6700b01

Browse files
committed
Introduce the estimate command
related to issue #89
1 parent 35c0dc7 commit 6700b01

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

estimate.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"os"
9+
"os/exec"
10+
"path/filepath"
11+
"strings"
12+
13+
"golang.org/x/tools/go/vcs"
14+
)
15+
16+
func get(gopath, repo string) error {
17+
done := make(chan struct{})
18+
defer close(done)
19+
go progressSize("go get", filepath.Join(gopath, "src"), done)
20+
21+
// As per https://groups.google.com/forum/#!topic/golang-nuts/N5apfenE4m4,
22+
// the arguments to “go get” are packages, not repositories. Hence, we
23+
// specify “gopkg/...” in order to cover all packages.
24+
// As a concrete example, github.com/jacobsa/util is a repository we want
25+
// to package into a single Debian package, and using “go get -d
26+
// github.com/jacobsa/util” fails because there are no buildable go files
27+
// in the top level of that repository.
28+
cmd := exec.Command("go", "get", "-d", "-t", repo+"/...")
29+
cmd.Stderr = os.Stderr
30+
cmd.Env = append([]string{
31+
fmt.Sprintf("GOPATH=%s", gopath),
32+
}, passthroughEnv()...)
33+
return cmd.Run()
34+
}
35+
36+
func estimate(importpath string) error {
37+
// construct a separate GOPATH in a temporary directory
38+
gopath, err := ioutil.TempDir("", "dh-make-golang")
39+
if err != nil {
40+
return err
41+
}
42+
defer os.RemoveAll(gopath)
43+
44+
if err := get(gopath, importpath); err != nil {
45+
return err
46+
}
47+
48+
// Use digraph(1) to obtain the forward transitive closure of the repo in
49+
// question.
50+
cmd := exec.Command("/bin/sh", "-c", "go list -f '{{.ImportPath}}{{.Imports}}' ... | tr '[]' ' ' | digraph forward $(go list "+importpath+"/...)")
51+
cmd.Stderr = os.Stderr
52+
cmd.Env = append([]string{
53+
fmt.Sprintf("GOPATH=%s", gopath),
54+
}, passthroughEnv()...)
55+
out, err := cmd.Output()
56+
if err != nil {
57+
return fmt.Errorf("%v: %v", cmd.Args, err)
58+
}
59+
60+
closure := make(map[string]bool)
61+
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
62+
closure[line] = true
63+
}
64+
65+
// Remove standard lib packages
66+
cmd = exec.Command("go", "list", "std")
67+
cmd.Stderr = os.Stderr
68+
cmd.Env = append([]string{
69+
fmt.Sprintf("GOPATH=%s", gopath),
70+
}, passthroughEnv()...)
71+
72+
out, err = cmd.Output()
73+
if err != nil {
74+
return fmt.Errorf("%v: %v", cmd.Args, err)
75+
}
76+
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
77+
delete(closure, line)
78+
}
79+
80+
delete(closure, "C") // would fail resolving anyway
81+
82+
// Resolve all packages to the root of their repository.
83+
roots := make(map[string]bool)
84+
for dep := range closure {
85+
rr, err := vcs.RepoRootForImportPath(dep, false)
86+
if err != nil {
87+
log.Printf("Could not determine repo path for import path %q: %v\n", dep, err)
88+
continue
89+
}
90+
91+
roots[rr.Root] = true
92+
}
93+
94+
// Filter out all already-packaged ones:
95+
golangBinaries, err := getGolangBinaries()
96+
if err != nil {
97+
return nil
98+
}
99+
100+
for importpath, binary := range golangBinaries {
101+
if roots[importpath] {
102+
log.Printf("found %s in Debian package %s", importpath, binary)
103+
delete(roots, importpath)
104+
}
105+
}
106+
107+
if len(roots) == 0 {
108+
log.Printf("%s is already fully packaged in Debian", importpath)
109+
return nil
110+
}
111+
log.Printf("Bringing %s to Debian requires packaging the following Go packages:", importpath)
112+
for importpath := range roots {
113+
fmt.Println(importpath)
114+
}
115+
116+
return nil
117+
}
118+
119+
func execEstimate(args []string) {
120+
fs := flag.NewFlagSet("search", flag.ExitOnError)
121+
122+
err := fs.Parse(args)
123+
if err != nil {
124+
log.Fatal(err)
125+
}
126+
127+
if fs.NArg() != 1 {
128+
fmt.Fprintf(os.Stderr, "Usage: %s estimate <importpath>\n", os.Args[0])
129+
fmt.Fprintf(os.Stderr, "Estimates the work necessary to bring <importpath> into Debian by printing all currently unpacked repositories\n")
130+
fmt.Fprintf(os.Stderr, "Example: %s estimate github.com/Debian/dh-make-golang\n", os.Args[0])
131+
os.Exit(1)
132+
}
133+
134+
// TODO: support the -git_revision flag
135+
136+
if err := estimate(fs.Arg(0)); err != nil {
137+
log.Fatal(err)
138+
}
139+
}

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ func main() {
1919
execSearch(args[1:])
2020
case "create-salsa-project":
2121
execCreateSalsaProject(args[1:])
22+
case "estimate":
23+
execEstimate(args[1:])
2224
default:
2325
execMake(args)
2426
}

make.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ func (u *upstream) findDependencies(gopath, repo string) error {
197197
rr, err := vcs.RepoRootForImportPath(dep, false)
198198
if err != nil {
199199
log.Printf("Could not determine repo path for import path %q: %v\n", dep, err)
200+
continue
200201
}
201202

202203
roots[rr.Root] = true

0 commit comments

Comments
 (0)