Left: | ||
Right: |
OLD | NEW |
---|---|
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "bufio" | 4 "bufio" |
5 "bytes" | 5 "bytes" |
6 "encoding/hex" | 6 "encoding/hex" |
7 "errors" | 7 "errors" |
8 "flag" | 8 "flag" |
9 "fmt" | 9 "fmt" |
10 "go/build" | 10 "go/build" |
11 "os" | 11 "os" |
12 "os/exec" | 12 "os/exec" |
13 "path/filepath" | 13 "path/filepath" |
14 "regexp" | 14 "regexp" |
15 "sort" | 15 "sort" |
16 "strconv" | 16 "strconv" |
17 "strings" | 17 "strings" |
18 "sync" | 18 "sync" |
19 "time" | 19 "time" |
20 | 20 |
21 "github.com/kisielk/gotool" | 21 "github.com/kisielk/gotool" |
22 ) | 22 ) |
23 | 23 |
24 var ( | 24 var ( |
25 revFile = flag.String("u", "", "update dependencies") | 25 revFile = flag.String("u", "", "update dependencies") |
26 updateMany = flag.Bool("U", false, "update many dependencies") | |
26 testDeps = flag.Bool("t", false, "include testing dependencies") | 27 testDeps = flag.Bool("t", false, "include testing dependencies") |
27 printCommands = flag.Bool("x", false, "show executed commands") | 28 printCommands = flag.Bool("x", false, "show executed commands") |
28 dryRun = flag.Bool("n", false, "print but do not execute update c ommands") | 29 dryRun = flag.Bool("n", false, "print but do not execute update c ommands") |
29 _ = flag.Bool("f", true, "(deprecated, superceded by -F) whe n updating, try to fetch deps if the update fails") | 30 _ = flag.Bool("f", true, "(deprecated, superceded by -F) whe n updating, try to fetch deps if the update fails") |
30 noFetch = flag.Bool("F", false, "when updating, do not try to fetc h deps if the update fails") | 31 noFetch = flag.Bool("F", false, "when updating, do not try to fetc h deps if the update fails") |
31 parallel = flag.Int("P", 1, "max number of concurrent updates") | 32 parallel = flag.Int("P", 1, "max number of concurrent updates") |
32 ) | 33 ) |
33 | 34 |
34 var exitCode = 0 | 35 var exitCode = 0 |
35 | 36 |
36 var buildContext = build.Default | 37 var buildContext = build.Default |
37 | 38 |
38 var usage = ` | 39 var usage = ` |
39 Usage: | 40 Usage: |
40 godeps [flags] [pkg ...] | 41 godeps [flags] [pkg ...] |
41 godeps -u file [flags] | 42 godeps -u file [flags] |
43 godeps -U [flags] file... | |
mattyw
2015/01/27 10:51:26
There should be more information about how it work
rog
2015/01/27 17:59:55
Done.
| |
42 | 44 |
43 In the first form of usage (without the -u flag), godeps prints to | 45 In the first form of usage (without the -u flag), godeps prints to |
44 standard output a list of all the source dependencies of the named | 46 standard output a list of all the source dependencies of the named |
45 packages (or the package in the current directory if none is given). | 47 packages (or the package in the current directory if none is given). |
46 If there is ambiguity in the source-control systems used, godeps will | 48 If there is ambiguity in the source-control systems used, godeps will |
47 print all the available versions and an error, exiting with a false | 49 print all the available versions and an error, exiting with a false |
48 status. It is up to the user to remove lines from the output to make | 50 status. It is up to the user to remove lines from the output to make |
49 the output suitable for input to godeps -u. | 51 the output suitable for input to godeps -u. |
50 | 52 |
51 In the second form, godeps updates source to versions specified by | 53 In the second form, godeps updates source to versions specified by |
52 the -u file argument, which should hold version information in the | 54 the -u file argument, which should hold version information in the |
53 same form printed by godeps. It is an error if the file contains more | 55 same form printed by godeps. It is an error if the file contains more |
54 than one line for the same package root. If a specified revision is not | 56 than one line for the same package root. If a specified revision is not |
55 currently available, godeps will attempt to fetch it, unless the -F flag | 57 currently available, godeps will attempt to fetch it, unless the -F flag |
56 is provided. | 58 is provided. |
57 `[1:] | 59 `[1:] |
58 | 60 |
59 func main() { | 61 func main() { |
60 flag.Usage = func() { | 62 flag.Usage = func() { |
61 fmt.Fprintf(os.Stderr, "%s\n", usage) | 63 fmt.Fprintf(os.Stderr, "%s\n", usage) |
62 flag.PrintDefaults() | 64 flag.PrintDefaults() |
63 os.Exit(2) | 65 os.Exit(2) |
64 } | 66 } |
65 flag.Parse() | 67 flag.Parse() |
66 » if *revFile != "" { | 68 » if *revFile != "" || *updateMany { |
67 » » if flag.NArg() != 0 { | 69 » » var files []string |
68 » » » flag.Usage() | 70 » » if *revFile != "" { |
71 » » » if flag.NArg() != 0 { | |
72 » » » » flag.Usage() | |
73 » » » } | |
74 » » » files = []string{*revFile} | |
75 » » } else { | |
76 » » » if flag.NArg() == 0 { | |
77 » » » » flag.Usage() | |
78 » » » } | |
79 » » » files = flag.Args() | |
69 } | 80 } |
70 » » update(*revFile) | 81 » » update(files) |
71 } else { | 82 } else { |
72 pkgs := flag.Args() | 83 pkgs := flag.Args() |
73 if len(pkgs) == 0 { | 84 if len(pkgs) == 0 { |
74 pkgs = []string{"."} | 85 pkgs = []string{"."} |
75 } | 86 } |
76 pkgs = gotool.ImportPaths(pkgs) | 87 pkgs = gotool.ImportPaths(pkgs) |
77 for _, info := range list(pkgs, *testDeps) { | 88 for _, info := range list(pkgs, *testDeps) { |
78 fmt.Println(info) | 89 fmt.Println(info) |
79 } | 90 } |
80 } | 91 } |
81 os.Exit(exitCode) | 92 os.Exit(exitCode) |
82 } | 93 } |
83 | 94 |
84 func update(file string) { | 95 func update(files []string) { |
85 » projects, err := parseDepFile(file) | 96 » fp := make(map[string]map[string]*depInfo) |
86 » if err != nil { | 97 » for _, file := range files { |
87 » » errorf("cannot parse %q: %v", file, err) | 98 » » projects, err := parseDepFile(file) |
88 » » return | 99 » » if err != nil { |
100 » » » errorf("cannot parse %q: %v", file, err) | |
101 » » » return | |
102 » » } | |
103 » » fp[file] = projects | |
89 } | 104 } |
105 projects := reconcileDeps(fp) | |
90 // First get info on all the projects, make sure their working | 106 // First get info on all the projects, make sure their working |
91 // directories are all clean and prune out the ones which | 107 // directories are all clean and prune out the ones which |
92 // don't need updating. | 108 // don't need updating. |
93 for proj, info := range projects { | 109 for proj, info := range projects { |
94 if info.notThere { | 110 if info.notThere { |
95 if *noFetch { | 111 if *noFetch { |
96 errorf("%q does not exist", proj) | 112 errorf("%q does not exist", proj) |
97 delete(projects, proj) | 113 delete(projects, proj) |
98 } | 114 } |
99 continue | 115 continue |
(...skipping 10 matching lines...) Expand all Loading... | |
110 continue | 126 continue |
111 } | 127 } |
112 if currentInfo.revid == info.revid { | 128 if currentInfo.revid == info.revid { |
113 // No need to update. | 129 // No need to update. |
114 delete(projects, proj) | 130 delete(projects, proj) |
115 } | 131 } |
116 } | 132 } |
117 updateProjects(projects) | 133 updateProjects(projects) |
118 } | 134 } |
119 | 135 |
136 type fileDepInfo struct { | |
137 file string | |
138 *depInfo | |
139 } | |
140 | |
141 // reconcileDeps takes dependencies from a bunch | |
142 // of different dependency files and returns a single | |
143 // unified project-to-info map containing all the dependencies, | |
144 // each one at the latest version found. | |
145 func reconcileDeps(fp map[string]map[string]*depInfo) map[string]*depInfo { | |
146 m := make(map[string]byLatestDep) | |
147 for file, projects := range fp { | |
148 for _, info := range projects { | |
149 m[info.project] = append(m[info.project], fileDepInfo{ | |
150 file: file, | |
151 depInfo: info, | |
152 }) | |
153 } | |
154 } | |
155 r := make(map[string]*depInfo) | |
156 for project, infos := range m { | |
157 sort.Sort(infos) | |
158 r[project] = infos[0].depInfo | |
159 for _, info := range infos { | |
160 if !info.notThere && info.revid != infos[0].revid { | |
161 fmt.Printf("warning: ignoring older revno %q for %s in %s\n", info.revno, project, info.file) | |
162 } | |
163 } | |
164 } | |
165 return r | |
166 } | |
167 | |
168 // byLatestDep can be used to sort dependency infos | |
169 // into latest-first order. | |
170 type byLatestDep []fileDepInfo | |
171 | |
172 func (s byLatestDep) Len() int { return len(s) } | |
173 func (s byLatestDep) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
174 func (s byLatestDep) Less(i, j int) bool { | |
175 i0, i1 := s[i], s[j] | |
176 if i0.revid != i1.revid { | |
177 if i0.vcs.Kind() != i1.vcs.Kind() { | |
178 return i0.vcs.Kind() < i1.vcs.Kind() | |
179 } | |
180 return i0.vcs.Later(i0.depInfo.VCSInfo, i1.depInfo.VCSInfo) | |
181 } | |
182 return i0.file < i1.file | |
183 } | |
184 | |
120 func updateProjects(projects map[string]*depInfo) { | 185 func updateProjects(projects map[string]*depInfo) { |
121 limit := make(chan struct{}, *parallel) | 186 limit := make(chan struct{}, *parallel) |
122 type result struct { | 187 type result struct { |
123 info *depInfo | 188 info *depInfo |
124 err error | 189 err error |
125 } | 190 } |
126 results := make(chan result) | 191 results := make(chan result) |
127 for _, info := range projects { | 192 for _, info := range projects { |
128 info := info | 193 info := info |
129 go func() { | 194 go func() { |
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
509 // testing dependencies are not transitively included. | 574 // testing dependencies are not transitively included. |
510 ctxt.walkDeps(impPath, false) | 575 ctxt.walkDeps(impPath, false) |
511 } | 576 } |
512 } | 577 } |
513 | 578 |
514 type VCS interface { | 579 type VCS interface { |
515 Kind() string | 580 Kind() string |
516 Info(dir string) (VCSInfo, error) | 581 Info(dir string) (VCSInfo, error) |
517 Update(dir, revid string) error | 582 Update(dir, revid string) error |
518 Fetch(dir string) error | 583 Fetch(dir string) error |
584 Later(i0, i1 VCSInfo) bool | |
519 } | 585 } |
520 | 586 |
521 type VCSInfo struct { | 587 type VCSInfo struct { |
522 revid string | 588 revid string |
523 revno string // optional | 589 revno string // optional |
524 clean bool | 590 clean bool |
525 } | 591 } |
526 | 592 |
527 var metadataDirs = map[string]VCS{ | 593 var metadataDirs = map[string]VCS{ |
528 ".bzr": bzrVCS{}, | 594 ".bzr": bzrVCS{}, |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
580 func (gitVCS) Update(dir string, revid string) error { | 646 func (gitVCS) Update(dir string, revid string) error { |
581 _, err := runUpdateCmd(dir, "git", "checkout", revid) | 647 _, err := runUpdateCmd(dir, "git", "checkout", revid) |
582 return err | 648 return err |
583 } | 649 } |
584 | 650 |
585 func (gitVCS) Fetch(dir string) error { | 651 func (gitVCS) Fetch(dir string) error { |
586 _, err := runCmd(dir, "git", "fetch") | 652 _, err := runCmd(dir, "git", "fetch") |
587 return err | 653 return err |
588 } | 654 } |
589 | 655 |
656 func (gitVCS) Later(i0, i1 VCSInfo) bool { | |
657 if i0.revno == "" || i1.revno == "" { | |
658 return i0.revno != "" | |
659 } | |
660 t0, err0 := time.Parse(time.RFC3339, i0.revno) | |
661 if err0 != nil { | |
662 fmt.Printf("warning: cannot parse time stamp %s: %v", i0.revno, err0) | |
663 } | |
664 t1, err1 := time.Parse(time.RFC3339, i1.revno) | |
665 if err1 != nil { | |
666 fmt.Printf("warning: cannot parse time stamp %s: %v", i1.revno, err1) | |
667 } | |
668 if err0 != nil || err1 != nil { | |
669 return err0 == nil | |
670 } | |
671 return t0.After(t1) | |
672 } | |
673 | |
590 type bzrVCS struct{} | 674 type bzrVCS struct{} |
591 | 675 |
592 func (bzrVCS) Kind() string { | 676 func (bzrVCS) Kind() string { |
593 return "bzr" | 677 return "bzr" |
594 } | 678 } |
595 | 679 |
596 var validBzrInfo = regexp.MustCompile(`^([0-9]+) ([^ \t]+)$`) | 680 var validBzrInfo = regexp.MustCompile(`^([0-9]+) ([^ \t]+)$`) |
597 var shelveLine = regexp.MustCompile(`^[0-9]+ (shelves exist|shelf exists)\.`) | 681 var shelveLine = regexp.MustCompile(`^[0-9]+ (shelves exist|shelf exists)\.`) |
598 | 682 |
599 func (bzrVCS) Info(dir string) (VCSInfo, error) { | 683 func (bzrVCS) Info(dir string) (VCSInfo, error) { |
(...skipping 29 matching lines...) Expand all Loading... | |
629 func (bzrVCS) Update(dir string, revid string) error { | 713 func (bzrVCS) Update(dir string, revid string) error { |
630 _, err := runUpdateCmd(dir, "bzr", "update", "-r", "revid:"+revid) | 714 _, err := runUpdateCmd(dir, "bzr", "update", "-r", "revid:"+revid) |
631 return err | 715 return err |
632 } | 716 } |
633 | 717 |
634 func (bzrVCS) Fetch(dir string) error { | 718 func (bzrVCS) Fetch(dir string) error { |
635 _, err := runCmd(dir, "bzr", "pull") | 719 _, err := runCmd(dir, "bzr", "pull") |
636 return err | 720 return err |
637 } | 721 } |
638 | 722 |
723 func (bzrVCS) Later(i0, i1 VCSInfo) bool { | |
724 return laterIntRevno(i0, i1) | |
725 } | |
726 | |
639 var validHgInfo = regexp.MustCompile(`^([a-f0-9]+) ([0-9]+)$`) | 727 var validHgInfo = regexp.MustCompile(`^([a-f0-9]+) ([0-9]+)$`) |
640 | 728 |
641 type hgVCS struct{} | 729 type hgVCS struct{} |
642 | 730 |
643 func (hgVCS) Info(dir string) (VCSInfo, error) { | 731 func (hgVCS) Info(dir string) (VCSInfo, error) { |
644 out, err := runCmd(dir, "hg", "log", "-l", "1", "-r", ".", "--template", "{node} {rev}") | 732 out, err := runCmd(dir, "hg", "log", "-l", "1", "-r", ".", "--template", "{node} {rev}") |
645 if err != nil { | 733 if err != nil { |
646 return VCSInfo{}, err | 734 return VCSInfo{}, err |
647 } | 735 } |
648 m := validHgInfo.FindStringSubmatch(strings.TrimSpace(out)) | 736 m := validHgInfo.FindStringSubmatch(strings.TrimSpace(out)) |
(...skipping 19 matching lines...) Expand all Loading... | |
668 func (hgVCS) Update(dir string, revid string) error { | 756 func (hgVCS) Update(dir string, revid string) error { |
669 _, err := runUpdateCmd(dir, "hg", "update", revid) | 757 _, err := runUpdateCmd(dir, "hg", "update", revid) |
670 return err | 758 return err |
671 } | 759 } |
672 | 760 |
673 func (hgVCS) Fetch(dir string) error { | 761 func (hgVCS) Fetch(dir string) error { |
674 _, err := runCmd(dir, "hg", "pull") | 762 _, err := runCmd(dir, "hg", "pull") |
675 return err | 763 return err |
676 } | 764 } |
677 | 765 |
766 func (hgVCS) Later(i0, i1 VCSInfo) bool { | |
767 return laterIntRevno(i0, i1) | |
768 } | |
769 | |
770 func laterIntRevno(i0, i1 VCSInfo) bool { | |
771 if i0.revno == "" || i1.revno == "" { | |
772 return i0.revno != "" | |
773 } | |
774 r0, err0 := strconv.Atoi(i0.revno) | |
775 if err0 != nil { | |
776 fmt.Printf("warning: cannot parse revision number %s: %v", i0.re vno, err0) | |
mattyw
2015/01/27 10:51:26
So if we can't parse a revision id we always assum
rog
2015/01/27 17:59:55
That's not quite right. If we can't parse a revisi
| |
777 } | |
778 r1, err1 := strconv.Atoi(i1.revno) | |
779 if err1 != nil { | |
780 fmt.Printf("warning: cannot parse revision number %s: %v", i1.re vno, err1) | |
781 } | |
782 if err0 != nil || err1 != nil { | |
783 return err0 == nil | |
784 } | |
785 return r0 > r1 | |
786 } | |
787 | |
678 func runUpdateCmd(dir string, name string, args ...string) (string, error) { | 788 func runUpdateCmd(dir string, name string, args ...string) (string, error) { |
679 if *dryRun { | 789 if *dryRun { |
680 printShellCommand(dir, name, args) | 790 printShellCommand(dir, name, args) |
681 return "", nil | 791 return "", nil |
682 } | 792 } |
683 return runCmd(dir, name, args...) | 793 return runCmd(dir, name, args...) |
684 } | 794 } |
685 | 795 |
686 func runCmd(dir string, name string, args ...string) (string, error) { | 796 func runCmd(dir string, name string, args ...string) (string, error) { |
687 var outData, errData bytes.Buffer | 797 var outData, errData bytes.Buffer |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
725 buf.WriteString(" ") | 835 buf.WriteString(" ") |
726 buf.WriteString(shquote(arg)) | 836 buf.WriteString(shquote(arg)) |
727 } | 837 } |
728 fmt.Fprintf(os.Stderr, "%s\n", buf.Bytes()) | 838 fmt.Fprintf(os.Stderr, "%s\n", buf.Bytes()) |
729 } | 839 } |
730 | 840 |
731 func shquote(s string) string { | 841 func shquote(s string) string { |
732 // single-quote becomes single-quote, double-quote, single-quote, double -quote, single-quote | 842 // single-quote becomes single-quote, double-quote, single-quote, double -quote, single-quote |
733 return `'` + strings.Replace(s, `'`, `'"'"'`, -1) + `'` | 843 return `'` + strings.Replace(s, `'`, `'"'"'`, -1) + `'` |
734 } | 844 } |
OLD | NEW |