From bd1a5cd1a59bff09fa53022436490eb03fb0dc4c Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Mon, 7 Dec 2015 17:19:14 -0800 Subject: [PATCH] Replace `go list` Main motivation is so that we can attempt to pick up packages across architectures and to have more control over the list of packages. I consider this the first in several structural changes. Most additions are inside of list.go. For each run of listPackage a depScanner is made to track already seen packages (inspired by Scanner). That list is not cached between executions. The current implementation of listPackage attempts to resolve vendor/ a bit better than we used to, but other changes need to be implemented elsewhere in godep before that can be fully realized. All *existing* tests pass, but this doesn't mean it actually works *better*. A test was added to show that we fix #271. --- Changelog.md | 4 + Godeps/Godeps.json | 9 + .../src/github.com/kr/pretty/.gitignore | 4 + .../src/github.com/kr/pretty/License | 21 + .../src/github.com/kr/pretty/Readme | 9 + .../src/github.com/kr/pretty/diff.go | 158 +++++++ .../src/github.com/kr/pretty/formatter.go | 337 +++++++++++++ .../src/github.com/kr/pretty/pretty.go | 98 ++++ .../src/github.com/kr/pretty/zero.go | 41 ++ .../_workspace/src/github.com/kr/text/License | 19 + .../_workspace/src/github.com/kr/text/Readme | 3 + .../src/github.com/kr/text/colwriter/Readme | 5 + .../github.com/kr/text/colwriter/column.go | 147 ++++++ .../_workspace/src/github.com/kr/text/doc.go | 3 + .../src/github.com/kr/text/indent.go | 74 +++ .../src/github.com/kr/text/mc/Readme | 9 + .../src/github.com/kr/text/mc/mc.go | 63 +++ .../_workspace/src/github.com/kr/text/wrap.go | 86 ++++ diff.go | 8 +- get.go | 7 +- list.go | 442 ++++++++++++++++++ main.go | 1 + msg.go | 42 ++ pkg.go | 70 +-- restore.go | 9 +- save.go | 5 +- save_test.go | 71 ++- update.go | 24 +- update_test.go | 5 +- version.go | 2 +- 30 files changed, 1703 insertions(+), 73 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/.gitignore create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/License create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/Readme create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/diff.go create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/formatter.go create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/pretty.go create mode 100644 Godeps/_workspace/src/github.com/kr/pretty/zero.go create mode 100644 Godeps/_workspace/src/github.com/kr/text/License create mode 100644 Godeps/_workspace/src/github.com/kr/text/Readme create mode 100644 Godeps/_workspace/src/github.com/kr/text/colwriter/Readme create mode 100644 Godeps/_workspace/src/github.com/kr/text/colwriter/column.go create mode 100644 Godeps/_workspace/src/github.com/kr/text/doc.go create mode 100644 Godeps/_workspace/src/github.com/kr/text/indent.go create mode 100644 Godeps/_workspace/src/github.com/kr/text/mc/Readme create mode 100644 Godeps/_workspace/src/github.com/kr/text/mc/mc.go create mode 100644 Godeps/_workspace/src/github.com/kr/text/wrap.go create mode 100644 list.go create mode 100644 msg.go diff --git a/Changelog.md b/Changelog.md index 2da3bff..912c0fa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v33 2015/12/07 + +* Replace the use of `go list`. This is a large change although all existing tests pass. + # v32 2015/12/02 * Eval Symlinks in Contains() check. diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index faaba54..2250d33 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -6,6 +6,15 @@ "ImportPath": "github.com/kr/fs", "Rev": "2788f0dbd16903de03cb8186e5c7d97b69ad387b" }, + { + "ImportPath": "github.com/kr/pretty", + "Comment": "go.weekly.2011-12-22-24-gf31442d", + "Rev": "f31442d60e51465c69811e2107ae978868dbea5c" + }, + { + "ImportPath": "github.com/kr/text", + "Rev": "6807e777504f54ad073ecef66747de158294b639" + }, { "ImportPath": "github.com/pmezard/go-difflib/difflib", "Rev": "f78a839676152fd9f4863704f5d516195c18fc14" diff --git a/Godeps/_workspace/src/github.com/kr/pretty/.gitignore b/Godeps/_workspace/src/github.com/kr/pretty/.gitignore new file mode 100644 index 0000000..1f0a99f --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/.gitignore @@ -0,0 +1,4 @@ +[568].out +_go* +_test* +_obj diff --git a/Godeps/_workspace/src/github.com/kr/pretty/License b/Godeps/_workspace/src/github.com/kr/pretty/License new file mode 100644 index 0000000..05c783c --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/License @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright 2012 Keith Rarick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/kr/pretty/Readme b/Godeps/_workspace/src/github.com/kr/pretty/Readme new file mode 100644 index 0000000..c589fc6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/Readme @@ -0,0 +1,9 @@ +package pretty + + import "github.com/kr/pretty" + + Package pretty provides pretty-printing for Go values. + +Documentation + + http://godoc.org/github.com/kr/pretty diff --git a/Godeps/_workspace/src/github.com/kr/pretty/diff.go b/Godeps/_workspace/src/github.com/kr/pretty/diff.go new file mode 100644 index 0000000..8fe8e24 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/diff.go @@ -0,0 +1,158 @@ +package pretty + +import ( + "fmt" + "io" + "reflect" +) + +type sbuf []string + +func (s *sbuf) Write(b []byte) (int, error) { + *s = append(*s, string(b)) + return len(b), nil +} + +// Diff returns a slice where each element describes +// a difference between a and b. +func Diff(a, b interface{}) (desc []string) { + Fdiff((*sbuf)(&desc), a, b) + return desc +} + +// Fdiff writes to w a description of the differences between a and b. +func Fdiff(w io.Writer, a, b interface{}) { + diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b)) +} + +type diffWriter struct { + w io.Writer + l string // label +} + +func (w diffWriter) printf(f string, a ...interface{}) { + var l string + if w.l != "" { + l = w.l + ": " + } + fmt.Fprintf(w.w, l+f, a...) +} + +func (w diffWriter) diff(av, bv reflect.Value) { + if !av.IsValid() && bv.IsValid() { + w.printf("nil != %#v", bv.Interface()) + return + } + if av.IsValid() && !bv.IsValid() { + w.printf("%#v != nil", av.Interface()) + return + } + if !av.IsValid() && !bv.IsValid() { + return + } + + at := av.Type() + bt := bv.Type() + if at != bt { + w.printf("%v != %v", at, bt) + return + } + + // numeric types, including bool + if at.Kind() < reflect.Array { + a, b := av.Interface(), bv.Interface() + if a != b { + w.printf("%#v != %#v", a, b) + } + return + } + + switch at.Kind() { + case reflect.String: + a, b := av.Interface(), bv.Interface() + if a != b { + w.printf("%q != %q", a, b) + } + case reflect.Ptr: + switch { + case av.IsNil() && !bv.IsNil(): + w.printf("nil != %v", bv.Interface()) + case !av.IsNil() && bv.IsNil(): + w.printf("%v != nil", av.Interface()) + case !av.IsNil() && !bv.IsNil(): + w.diff(av.Elem(), bv.Elem()) + } + case reflect.Struct: + for i := 0; i < av.NumField(); i++ { + w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i)) + } + case reflect.Slice: + lenA := av.Len() + lenB := bv.Len() + if lenA != lenB { + w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB) + break + } + for i := 0; i < lenA; i++ { + w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i)) + } + case reflect.Map: + ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys()) + for _, k := range ak { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.printf("%q != (missing)", av.MapIndex(k)) + } + for _, k := range both { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.diff(av.MapIndex(k), bv.MapIndex(k)) + } + for _, k := range bk { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.printf("(missing) != %q", bv.MapIndex(k)) + } + case reflect.Interface: + w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface())) + default: + if !reflect.DeepEqual(av.Interface(), bv.Interface()) { + w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface())) + } + } +} + +func (d diffWriter) relabel(name string) (d1 diffWriter) { + d1 = d + if d.l != "" && name[0] != '[' { + d1.l += "." + } + d1.l += name + return d1 +} + +func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) { + for _, av := range a { + inBoth := false + for _, bv := range b { + if reflect.DeepEqual(av.Interface(), bv.Interface()) { + inBoth = true + both = append(both, av) + break + } + } + if !inBoth { + ak = append(ak, av) + } + } + for _, bv := range b { + inBoth := false + for _, av := range a { + if reflect.DeepEqual(av.Interface(), bv.Interface()) { + inBoth = true + break + } + } + if !inBoth { + bk = append(bk, bv) + } + } + return +} diff --git a/Godeps/_workspace/src/github.com/kr/pretty/formatter.go b/Godeps/_workspace/src/github.com/kr/pretty/formatter.go new file mode 100644 index 0000000..c991dd9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/formatter.go @@ -0,0 +1,337 @@ +package pretty + +import ( + "fmt" + "io" + "reflect" + "strconv" + "text/tabwriter" + + "github.com/tools/godep/Godeps/_workspace/src/github.com/kr/text" +) + +const ( + limit = 50 +) + +type formatter struct { + x interface{} + force bool + quote bool +} + +// Formatter makes a wrapper, f, that will format x as go source with line +// breaks and tabs. Object f responds to the "%v" formatting verb when both the +// "#" and " " (space) flags are set, for example: +// +// fmt.Sprintf("%# v", Formatter(x)) +// +// If one of these two flags is not set, or any other verb is used, f will +// format x according to the usual rules of package fmt. +// In particular, if x satisfies fmt.Formatter, then x.Format will be called. +func Formatter(x interface{}) (f fmt.Formatter) { + return formatter{x: x, quote: true} +} + +func (fo formatter) String() string { + return fmt.Sprint(fo.x) // unwrap it +} + +func (fo formatter) passThrough(f fmt.State, c rune) { + s := "%" + for i := 0; i < 128; i++ { + if f.Flag(i) { + s += string(i) + } + } + if w, ok := f.Width(); ok { + s += fmt.Sprintf("%d", w) + } + if p, ok := f.Precision(); ok { + s += fmt.Sprintf(".%d", p) + } + s += string(c) + fmt.Fprintf(f, s, fo.x) +} + +func (fo formatter) Format(f fmt.State, c rune) { + if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { + w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) + p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} + p.printValue(reflect.ValueOf(fo.x), true, fo.quote) + w.Flush() + return + } + fo.passThrough(f, c) +} + +type printer struct { + io.Writer + tw *tabwriter.Writer + visited map[visit]int + depth int +} + +func (p *printer) indent() *printer { + q := *p + q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) + q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) + return &q +} + +func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { + if showType { + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, "(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } +} + +// printValue must keep track of already-printed pointer values to avoid +// infinite recursion. +type visit struct { + v uintptr + typ reflect.Type +} + +func (p *printer) printValue(v reflect.Value, showType, quote bool) { + if p.depth > 10 { + io.WriteString(p, "!%v(DEPTH EXCEEDED)") + return + } + + switch v.Kind() { + case reflect.Bool: + p.printInline(v, v.Bool(), showType) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.printInline(v, v.Int(), showType) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.printInline(v, v.Uint(), showType) + case reflect.Float32, reflect.Float64: + p.printInline(v, v.Float(), showType) + case reflect.Complex64, reflect.Complex128: + fmt.Fprintf(p, "%#v", v.Complex()) + case reflect.String: + p.fmtString(v.String(), quote) + case reflect.Map: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + keys := v.MapKeys() + for i := 0; i < v.Len(); i++ { + showTypeInStruct := true + k := keys[i] + mv := v.MapIndex(k) + pp.printValue(k, false, true) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = t.Elem().Kind() == reflect.Interface + pp.printValue(mv, showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Struct: + t := v.Type() + if v.CanAddr() { + addr := v.UnsafeAddr() + vis := visit{addr, t} + if vd, ok := p.visited[vis]; ok && vd < p.depth { + p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) + break // don't print v again + } + p.visited[vis] = p.depth + } + + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.NumField(); i++ { + showTypeInStruct := true + if f := t.Field(i); f.Name != "" { + io.WriteString(pp, f.Name) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = labelType(f.Type) + } + pp.printValue(getField(v, i), showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.NumField()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Interface: + switch e := v.Elem(); { + case e.Kind() == reflect.Invalid: + io.WriteString(p, "nil") + case e.IsValid(): + pp := *p + pp.depth++ + pp.printValue(e, showType, true) + default: + io.WriteString(p, v.Type().String()) + io.WriteString(p, "(nil)") + } + case reflect.Array, reflect.Slice: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + if v.Kind() == reflect.Slice && v.IsNil() && showType { + io.WriteString(p, "(nil)") + break + } + if v.Kind() == reflect.Slice && v.IsNil() { + io.WriteString(p, "nil") + break + } + writeByte(p, '{') + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.Len(); i++ { + showTypeInSlice := t.Elem().Kind() == reflect.Interface + pp.printValue(v.Index(i), showTypeInSlice, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + writeByte(p, '}') + case reflect.Ptr: + e := v.Elem() + if !e.IsValid() { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + io.WriteString(p, ")(nil)") + } else { + pp := *p + pp.depth++ + writeByte(pp, '&') + pp.printValue(e, true, true) + } + case reflect.Chan: + x := v.Pointer() + if showType { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, ")(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } + case reflect.Func: + io.WriteString(p, v.Type().String()) + io.WriteString(p, " {...}") + case reflect.UnsafePointer: + p.printInline(v, v.Pointer(), showType) + case reflect.Invalid: + io.WriteString(p, "nil") + } +} + +func canInline(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map: + return !canExpand(t.Elem()) + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + if canExpand(t.Field(i).Type) { + return false + } + } + return true + case reflect.Interface: + return false + case reflect.Array, reflect.Slice: + return !canExpand(t.Elem()) + case reflect.Ptr: + return false + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + return false + } + return true +} + +func canExpand(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map, reflect.Struct, + reflect.Interface, reflect.Array, reflect.Slice, + reflect.Ptr: + return true + } + return false +} + +func labelType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Interface, reflect.Struct: + return true + } + return false +} + +func (p *printer) fmtString(s string, quote bool) { + if quote { + s = strconv.Quote(s) + } + io.WriteString(p, s) +} + +func tryDeepEqual(a, b interface{}) bool { + defer func() { recover() }() + return reflect.DeepEqual(a, b) +} + +func writeByte(w io.Writer, b byte) { + w.Write([]byte{b}) +} + +func getField(v reflect.Value, i int) reflect.Value { + val := v.Field(i) + if val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() + } + return val +} diff --git a/Godeps/_workspace/src/github.com/kr/pretty/pretty.go b/Godeps/_workspace/src/github.com/kr/pretty/pretty.go new file mode 100644 index 0000000..d3df868 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/pretty.go @@ -0,0 +1,98 @@ +// Package pretty provides pretty-printing for Go values. This is +// useful during debugging, to avoid wrapping long output lines in +// the terminal. +// +// It provides a function, Formatter, that can be used with any +// function that accepts a format string. It also provides +// convenience wrappers for functions in packages fmt and log. +package pretty + +import ( + "fmt" + "io" + "log" +) + +// Errorf is a convenience wrapper for fmt.Errorf. +// +// Calling Errorf(f, x, y) is equivalent to +// fmt.Errorf(f, Formatter(x), Formatter(y)). +func Errorf(format string, a ...interface{}) error { + return fmt.Errorf(format, wrap(a, false)...) +} + +// Fprintf is a convenience wrapper for fmt.Fprintf. +// +// Calling Fprintf(w, f, x, y) is equivalent to +// fmt.Fprintf(w, f, Formatter(x), Formatter(y)). +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) { + return fmt.Fprintf(w, format, wrap(a, false)...) +} + +// Log is a convenience wrapper for log.Printf. +// +// Calling Log(x, y) is equivalent to +// log.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Log(a ...interface{}) { + log.Print(wrap(a, true)...) +} + +// Logf is a convenience wrapper for log.Printf. +// +// Calling Logf(f, x, y) is equivalent to +// log.Printf(f, Formatter(x), Formatter(y)). +func Logf(format string, a ...interface{}) { + log.Printf(format, wrap(a, false)...) +} + +// Logln is a convenience wrapper for log.Printf. +// +// Calling Logln(x, y) is equivalent to +// log.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Logln(a ...interface{}) { + log.Println(wrap(a, true)...) +} + +// Print pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Print(a ...interface{}) (n int, errno error) { + return fmt.Print(wrap(a, true)...) +} + +// Printf is a convenience wrapper for fmt.Printf. +// +// Calling Printf(f, x, y) is equivalent to +// fmt.Printf(f, Formatter(x), Formatter(y)). +func Printf(format string, a ...interface{}) (n int, errno error) { + return fmt.Printf(format, wrap(a, false)...) +} + +// Println pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Println(a ...interface{}) (n int, errno error) { + return fmt.Println(wrap(a, true)...) +} + +// Sprintf is a convenience wrapper for fmt.Sprintf. +// +// Calling Sprintf(f, x, y) is equivalent to +// fmt.Sprintf(f, Formatter(x), Formatter(y)). +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, wrap(a, false)...) +} + +func wrap(a []interface{}, force bool) []interface{} { + w := make([]interface{}, len(a)) + for i, x := range a { + w[i] = formatter{x: x, force: force} + } + return w +} diff --git a/Godeps/_workspace/src/github.com/kr/pretty/zero.go b/Godeps/_workspace/src/github.com/kr/pretty/zero.go new file mode 100644 index 0000000..abb5b6f --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/pretty/zero.go @@ -0,0 +1,41 @@ +package pretty + +import ( + "reflect" +) + +func nonzero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() != 0 + case reflect.Float32, reflect.Float64: + return v.Float() != 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() != complex(0, 0) + case reflect.String: + return v.String() != "" + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if nonzero(getField(v, i)) { + return true + } + } + return false + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if nonzero(v.Index(i)) { + return true + } + } + return false + case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func: + return !v.IsNil() + case reflect.UnsafePointer: + return v.Pointer() != 0 + } + return true +} diff --git a/Godeps/_workspace/src/github.com/kr/text/License b/Godeps/_workspace/src/github.com/kr/text/License new file mode 100644 index 0000000..480a328 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/License @@ -0,0 +1,19 @@ +Copyright 2012 Keith Rarick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/kr/text/Readme b/Godeps/_workspace/src/github.com/kr/text/Readme new file mode 100644 index 0000000..7e6e7c0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/Readme @@ -0,0 +1,3 @@ +This is a Go package for manipulating paragraphs of text. + +See http://go.pkgdoc.org/github.com/kr/text for full documentation. diff --git a/Godeps/_workspace/src/github.com/kr/text/colwriter/Readme b/Godeps/_workspace/src/github.com/kr/text/colwriter/Readme new file mode 100644 index 0000000..1c1f4e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/colwriter/Readme @@ -0,0 +1,5 @@ +Package colwriter provides a write filter that formats +input lines in multiple columns. + +The package is a straightforward translation from +/src/cmd/draw/mc.c in Plan 9 from User Space. diff --git a/Godeps/_workspace/src/github.com/kr/text/colwriter/column.go b/Godeps/_workspace/src/github.com/kr/text/colwriter/column.go new file mode 100644 index 0000000..7302ce9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/colwriter/column.go @@ -0,0 +1,147 @@ +// Package colwriter provides a write filter that formats +// input lines in multiple columns. +// +// The package is a straightforward translation from +// /src/cmd/draw/mc.c in Plan 9 from User Space. +package colwriter + +import ( + "bytes" + "io" + "unicode/utf8" +) + +const ( + tab = 4 +) + +const ( + // Print each input line ending in a colon ':' separately. + BreakOnColon uint = 1 << iota +) + +// A Writer is a filter that arranges input lines in as many columns as will +// fit in its width. Tab '\t' chars in the input are translated to sequences +// of spaces ending at multiples of 4 positions. +// +// If BreakOnColon is set, each input line ending in a colon ':' is written +// separately. +// +// The Writer assumes that all Unicode code points have the same width; this +// may not be true in some fonts. +type Writer struct { + w io.Writer + buf []byte + width int + flag uint +} + +// NewWriter allocates and initializes a new Writer writing to w. +// Parameter width controls the total number of characters on each line +// across all columns. +func NewWriter(w io.Writer, width int, flag uint) *Writer { + return &Writer{ + w: w, + width: width, + flag: flag, + } +} + +// Write writes p to the writer w. The only errors returned are ones +// encountered while writing to the underlying output stream. +func (w *Writer) Write(p []byte) (n int, err error) { + var linelen int + var lastWasColon bool + for i, c := range p { + w.buf = append(w.buf, c) + linelen++ + if c == '\t' { + w.buf[len(w.buf)-1] = ' ' + for linelen%tab != 0 { + w.buf = append(w.buf, ' ') + linelen++ + } + } + if w.flag&BreakOnColon != 0 && c == ':' { + lastWasColon = true + } else if lastWasColon { + if c == '\n' { + pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'}) + if pos < 0 { + pos = 0 + } + line := w.buf[pos:] + w.buf = w.buf[:pos] + if err = w.columnate(); err != nil { + if len(line) < i { + return i - len(line), err + } + return 0, err + } + if n, err := w.w.Write(line); err != nil { + if r := len(line) - n; r < i { + return i - r, err + } + return 0, err + } + } + lastWasColon = false + } + if c == '\n' { + linelen = 0 + } + } + return len(p), nil +} + +// Flush should be called after the last call to Write to ensure that any data +// buffered in the Writer is written to output. +func (w *Writer) Flush() error { + return w.columnate() +} + +func (w *Writer) columnate() error { + words := bytes.Split(w.buf, []byte{'\n'}) + w.buf = nil + if len(words[len(words)-1]) == 0 { + words = words[:len(words)-1] + } + maxwidth := 0 + for _, wd := range words { + if n := utf8.RuneCount(wd); n > maxwidth { + maxwidth = n + } + } + maxwidth++ // space char + wordsPerLine := w.width / maxwidth + if wordsPerLine <= 0 { + wordsPerLine = 1 + } + nlines := (len(words) + wordsPerLine - 1) / wordsPerLine + for i := 0; i < nlines; i++ { + col := 0 + endcol := 0 + for j := i; j < len(words); j += nlines { + endcol += maxwidth + _, err := w.w.Write(words[j]) + if err != nil { + return err + } + col += utf8.RuneCount(words[j]) + if j+nlines < len(words) { + for col < endcol { + _, err := w.w.Write([]byte{' '}) + if err != nil { + return err + } + col++ + } + } + } + _, err := w.w.Write([]byte{'\n'}) + if err != nil { + return err + } + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/kr/text/doc.go b/Godeps/_workspace/src/github.com/kr/text/doc.go new file mode 100644 index 0000000..cf4c198 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/doc.go @@ -0,0 +1,3 @@ +// Package text provides rudimentary functions for manipulating text in +// paragraphs. +package text diff --git a/Godeps/_workspace/src/github.com/kr/text/indent.go b/Godeps/_workspace/src/github.com/kr/text/indent.go new file mode 100644 index 0000000..4ebac45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/indent.go @@ -0,0 +1,74 @@ +package text + +import ( + "io" +) + +// Indent inserts prefix at the beginning of each non-empty line of s. The +// end-of-line marker is NL. +func Indent(s, prefix string) string { + return string(IndentBytes([]byte(s), []byte(prefix))) +} + +// IndentBytes inserts prefix at the beginning of each non-empty line of b. +// The end-of-line marker is NL. +func IndentBytes(b, prefix []byte) []byte { + var res []byte + bol := true + for _, c := range b { + if bol && c != '\n' { + res = append(res, prefix...) + } + res = append(res, c) + bol = c == '\n' + } + return res +} + +// Writer indents each line of its input. +type indentWriter struct { + w io.Writer + bol bool + pre [][]byte + sel int + off int +} + +// NewIndentWriter makes a new write filter that indents the input +// lines. Each line is prefixed in order with the corresponding +// element of pre. If there are more lines than elements, the last +// element of pre is repeated for each subsequent line. +func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer { + return &indentWriter{ + w: w, + pre: pre, + bol: true, + } +} + +// The only errors returned are from the underlying indentWriter. +func (w *indentWriter) Write(p []byte) (n int, err error) { + for _, c := range p { + if w.bol { + var i int + i, err = w.w.Write(w.pre[w.sel][w.off:]) + w.off += i + if err != nil { + return n, err + } + } + _, err = w.w.Write([]byte{c}) + if err != nil { + return n, err + } + n++ + w.bol = c == '\n' + if w.bol { + w.off = 0 + if w.sel < len(w.pre)-1 { + w.sel++ + } + } + } + return n, nil +} diff --git a/Godeps/_workspace/src/github.com/kr/text/mc/Readme b/Godeps/_workspace/src/github.com/kr/text/mc/Readme new file mode 100644 index 0000000..519ddc0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/mc/Readme @@ -0,0 +1,9 @@ +Command mc prints in multiple columns. + + Usage: mc [-] [-N] [file...] + +Mc splits the input into as many columns as will fit in N +print positions. If the output is a tty, the default N is +the number of characters in a terminal line; otherwise the +default N is 80. Under option - each input line ending in +a colon ':' is printed separately. diff --git a/Godeps/_workspace/src/github.com/kr/text/mc/mc.go b/Godeps/_workspace/src/github.com/kr/text/mc/mc.go new file mode 100644 index 0000000..0fe119d --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/mc/mc.go @@ -0,0 +1,63 @@ +// Command mc prints in multiple columns. +// +// Usage: mc [-] [-N] [file...] +// +// Mc splits the input into as many columns as will fit in N +// print positions. If the output is a tty, the default N is +// the number of characters in a terminal line; otherwise the +// default N is 80. Under option - each input line ending in +// a colon ':' is printed separately. +package main + +import ( + "io" + "log" + "os" + "strconv" + + "github.com/kr/pty" + "github.com/tools/godep/Godeps/_workspace/src/github.com/kr/text/colwriter" +) + +func main() { + var width int + var flag uint + args := os.Args[1:] + for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' { + if len(args[0]) > 1 { + width, _ = strconv.Atoi(args[0][1:]) + } else { + flag |= colwriter.BreakOnColon + } + args = args[1:] + } + if width < 1 { + _, width, _ = pty.Getsize(os.Stdout) + } + if width < 1 { + width = 80 + } + + w := colwriter.NewWriter(os.Stdout, width, flag) + if len(args) > 0 { + for _, s := range args { + if f, err := os.Open(s); err == nil { + copyin(w, f) + f.Close() + } else { + log.Println(err) + } + } + } else { + copyin(w, os.Stdin) + } +} + +func copyin(w *colwriter.Writer, r io.Reader) { + if _, err := io.Copy(w, r); err != nil { + log.Println(err) + } + if err := w.Flush(); err != nil { + log.Println(err) + } +} diff --git a/Godeps/_workspace/src/github.com/kr/text/wrap.go b/Godeps/_workspace/src/github.com/kr/text/wrap.go new file mode 100644 index 0000000..1c85cd2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kr/text/wrap.go @@ -0,0 +1,86 @@ +package text + +import ( + "bytes" + "math" +) + +var ( + nl = []byte{'\n'} + sp = []byte{' '} +) + +const defaultPenalty = 1e5 + +// Wrap wraps s into a paragraph of lines of length lim, with minimal +// raggedness. +func Wrap(s string, lim int) string { + return string(WrapBytes([]byte(s), lim)) +} + +// WrapBytes wraps b into a paragraph of lines of length lim, with minimal +// raggedness. +func WrapBytes(b []byte, lim int) []byte { + words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp) + var lines [][]byte + for _, line := range WrapWords(words, 1, lim, defaultPenalty) { + lines = append(lines, bytes.Join(line, sp)) + } + return bytes.Join(lines, nl) +} + +// WrapWords is the low-level line-breaking algorithm, useful if you need more +// control over the details of the text wrapping process. For most uses, either +// Wrap or WrapBytes will be sufficient and more convenient. +// +// WrapWords splits a list of words into lines with minimal "raggedness", +// treating each byte as one unit, accounting for spc units between adjacent +// words on each line, and attempting to limit lines to lim units. Raggedness +// is the total error over all lines, where error is the square of the +// difference of the length of the line and lim. Too-long lines (which only +// happen when a single word is longer than lim units) have pen penalty units +// added to the error. +func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte { + n := len(words) + + length := make([][]int, n) + for i := 0; i < n; i++ { + length[i] = make([]int, n) + length[i][i] = len(words[i]) + for j := i + 1; j < n; j++ { + length[i][j] = length[i][j-1] + spc + len(words[j]) + } + } + + nbrk := make([]int, n) + cost := make([]int, n) + for i := range cost { + cost[i] = math.MaxInt32 + } + for i := n - 1; i >= 0; i-- { + if length[i][n-1] <= lim { + cost[i] = 0 + nbrk[i] = n + } else { + for j := i + 1; j < n; j++ { + d := lim - length[i][j-1] + c := d*d + cost[j] + if length[i][j-1] > lim { + c += pen // too-long lines get a worse penalty + } + if c < cost[i] { + cost[i] = c + nbrk[i] = j + } + } + } + } + + var lines [][][]byte + i := 0 + for i < n { + lines = append(lines, words[i:nbrk[i]]) + i = nbrk[i] + } + return lines +} diff --git a/diff.go b/diff.go index 9f78c9c..7e5e768 100644 --- a/diff.go +++ b/diff.go @@ -9,16 +9,22 @@ import ( ) var cmdDiff = &Command{ - Usage: "diff", + Usage: "diff [-d]", Short: "shows the diff between current and previously saved set of dependencies", Long: ` Shows the difference, in a unified diff format, between the current set of dependencies and those generated on a previous 'go save' execution. + +If -d is given, debug output is enabled (you probably don't want this). `, Run: runDiff, } +func init() { + cmdDiff.Flag.BoolVar(&debug, "d", false, "enable debug output") +} + func runDiff(cmd *Command, args []string) { gold, err := loadDefaultGodepsFile() if err != nil { diff --git a/get.go b/get.go index bcc15a4..1704d77 100644 --- a/get.go +++ b/get.go @@ -7,7 +7,7 @@ import ( ) var cmdGet = &Command{ - Usage: "get [-v] [-t] [packages]", + Usage: "get [-v] [-d] [-t] [packages]", Short: "download and install packages with specified dependencies", Long: ` Get downloads to GOPATH the packages named by the import paths, and installs @@ -16,7 +16,9 @@ them with the dependencies specified in their Godeps files. If any of the packages do not have Godeps files, those are installed as if by go get. -If -verbose is given, verbose output is enabled. +If -v is given, verbose output is enabled. + +If -d is given, debug output is enabled (you probably don't want this, see -v above). If -t is given, dependencies of test files are also downloaded and installed. @@ -29,6 +31,7 @@ var getT bool func init() { cmdGet.Flag.BoolVar(&verbose, "v", false, "enable verbose output") + cmdGet.Flag.BoolVar(&debug, "d", false, "enable debug output") cmdGet.Flag.BoolVar(&getT, "t", false, "get test dependencies") } diff --git a/list.go b/list.go new file mode 100644 index 0000000..f61c117 --- /dev/null +++ b/list.go @@ -0,0 +1,442 @@ +package main + +import ( + "fmt" + "go/build" + "log" + "os" + "path/filepath" + "sort" + "strings" + + pathpkg "path" +) + +var ( + buildContext = build.Default + gorootSrc = filepath.Join(buildContext.GOROOT, "src") +) + +func init() { + buildContext.UseAllFiles = true +} + +// packageContext is used to track an import and which package imported it. +type packageContext struct { + pkg *build.Package // package that imports the import + imp string // import +} + +// depScanner tracks the processed and to be processed packageContexts +type depScanner struct { + processed []packageContext + todo []packageContext +} + +// Next package and import to process +func (ds *depScanner) Next() (*build.Package, string) { + c := ds.todo[0] + ds.processed = append(ds.processed, c) + ds.todo = ds.todo[1:] + return c.pkg, c.imp +} + +// Continue looping? +func (ds *depScanner) Continue() bool { + if len(ds.todo) > 0 { + return true + } + return false +} + +// Add a package and imports to the depScanner. Skips already processed package/import combos +func (ds *depScanner) Add(pkg *build.Package, imports ...string) { +Next: + for _, i := range imports { + if i == "C" { + i = "runtime/cgo" + } + pc := packageContext{pkg, i} + for _, epc := range ds.processed { + if epc == pc { + debugln("ctxts epc == pc, skipping", epc, pc) + continue Next + } + if pc.pkg.Dir == epc.pkg.Dir && pc.imp == epc.imp { + debugln("ctxts epc.pkg.Dir == pc.pkg.Dir && pc.imp == epc.imp, skipping", epc.pkg.Dir, pc.imp) + continue Next + } + } + for _, epc := range ds.todo { + if epc == pc { + debugln("ctxts epc == pc, skipping", epc, pc) + continue Next + } + } + debugln("Adding pc:", pc) + ds.todo = append(ds.todo, pc) + } +} + +func checkGoroot(p *build.Package, err error) (*build.Package, error) { + if p.Goroot && err != nil { + buildContext.UseAllFiles = false + p, err = buildContext.Import(p.ImportPath, p.Dir, 0) + buildContext.UseAllFiles = true + } + return p, err +} + +// listPackage specified by path +func listPackage(path string) (*Package, error) { + var dir string + var lp *build.Package + var err error + deps := make(map[string]bool) + imports := make(map[string]bool) + if build.IsLocalImport(path) { + dir = path + if !filepath.IsAbs(dir) { + if abs, err := filepath.Abs(dir); err == nil { + // interpret relative to current directory + dir = abs + } + } + lp, err = buildContext.ImportDir(dir, 0) + } else { + dir, err = os.Getwd() + if err != nil { + return nil, err + } + lp, err = buildContext.Import(path, dir, 0) + lp, err = checkGoroot(lp, err) + } + p := &Package{ + Dir: lp.Dir, + Root: lp.Root, + ImportPath: lp.ImportPath, + XTestImports: lp.XTestImports, + TestImports: lp.TestImports, + GoFiles: lp.GoFiles, + CgoFiles: lp.CgoFiles, + TestGoFiles: lp.TestGoFiles, + XTestGoFiles: lp.XTestGoFiles, + IgnoredGoFiles: lp.IgnoredGoFiles, + } + p.Standard = lp.Goroot && lp.ImportPath != "" && !strings.Contains(lp.ImportPath, ".") + if err != nil { + return p, err + } + debugln("Looking For Package:", path, "in", dir) + ppln(lp) + + ds := depScanner{} + ds.Add(lp, lp.Imports...) + for ds.Continue() { + ip, i := ds.Next() + + debugf("Processing import %s for %s\n", i, ip.Dir) + // We need to check to see if the import exists in vendor/ folders up the hierachy of the importing package, + var dp *build.Package + if VendorExperiment && !ip.Goroot { + for base := ip.Dir; base != ip.Root; base = filepath.Dir(base) { + dir := filepath.Join(base, "vendor", i) + debugln("dir:", dir) + dp, err = buildContext.ImportDir(dir, 0) + if err != nil { + if os.IsNotExist(err) { + continue + } + debugln(err.Error()) + ppln(err) + panic("Unknown error attempt to find vendor/") + } + goto Found + } + } + // Wasn't found above, so resolve it using the build.Context + dp, err = buildContext.Import(i, ip.Dir, 0) + if err != nil { + if dp.Goroot { + // If it's in the GOROOT we can probably recover + switch err.(type) { + case *build.MultiplePackageError: + debugln("MultiplePackageError, importing Goroot package, trying w/o UseAllFiles") + dp, err = checkGoroot(dp, err) + default: + ppln(err) + debugln(err.Error()) + panic("Unknown error importing GOROOT package: " + dp.ImportPath) + } + } else { + debugln("Warning: Error importing dependent package") + ppln(err) + } + } + Found: + ppln(dp) + if dp.Goroot { + // Treat packages discovered to be in the GOROOT as if the package we're looking for is importing them + ds.Add(lp, dp.Imports...) + } else { + ds.Add(dp, dp.Imports...) + } + debugln("lp:", lp) + debugln("ip:", ip) + if lp == ip { + debugln("lp == ip") + imports[dp.ImportPath] = true + } + deps[dp.ImportPath] = true + } + for k := range deps { + p.Deps = append(p.Deps, k) + } + for k := range imports { + p.Imports = append(p.Imports, k) + } + sort.Strings(p.Imports) + sort.Strings(p.Deps) + debugln("Looking For Package:", path, "in", dir) + ppln(p) + return p, nil +} + +// All of the following functions were vendored from go proper. Locations are noted in comments, but may change in future Go versions. + +// importPaths returns the import paths to use for the given command line. +// $GOROOT/src/cmd/main.go:366 +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, allPackagesInFS(a)...) + } else { + out = append(out, allPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +// $GOROOT/src/cmd/main.go:332 +func importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + pathpkg.Clean(a) + if a == "./." { + a = "." + } + } else { + a = pathpkg.Clean(a) + } + if a == "all" || a == "std" || a == "cmd" { + out = append(out, allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +// $GOROOT/src/cmd/main.go:620 +func allPackagesInFS(pattern string) []string { + pkgs := matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages), +// "cmd" (standard commands), or a path including "...". +// $GOROOT/src/cmd/main.go:542 +func allPackages(pattern string) []string { + pkgs := matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// $GOROOT/src/cmd/main.go:554 +func matchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if pattern != "all" && pattern != "std" && pattern != "cmd" { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !buildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + for _, src := range buildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != gorootSrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { + // The name "std" is only the standard library. + // If the name has a dot, assume it's a domain name for go get, + // and if the name is cmd, it's the root of the command tree. + return filepath.SkipDir + } + if !treeCanMatch(name) { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +// $GOROOT/src/cmd/main.go:527 +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +// $GOROOT/src/cmd/main.go:489 +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// $GOROOT/src/cmd/go/main.go:631 +func matchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := pathpkg.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} diff --git a/main.go b/main.go index ab7fabc..abd0ed2 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( ) var verbose bool // Verbose flag for commands that support it +var debug bool // Debug flag for commands that support it // Command is an implementation of a godep command // like godep save or godep go. diff --git a/msg.go b/msg.go new file mode 100644 index 0000000..71b142a --- /dev/null +++ b/msg.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "github.com/tools/godep/Godeps/_workspace/src/github.com/kr/pretty" +) + +func debugln(a ...interface{}) (int, error) { + if debug { + return fmt.Println(a...) + } + return 0, nil +} + +func debugf(format string, a ...interface{}) (int, error) { + if debug { + return fmt.Printf(format, a...) + } + return 0, nil +} + +func pp(a ...interface{}) (int, error) { + if debug { + return pretty.Print(a...) + } + return 0, nil +} + +func ppln(a ...interface{}) (int, error) { + if debug { + return pretty.Println(a...) + } + return 0, nil +} + +func ppf(format string, a ...interface{}) (int, error) { + if debug { + return pretty.Printf(format, a...) + } + return 0, nil +} diff --git a/pkg.go b/pkg.go index 5db6ba8..c6f26cd 100644 --- a/pkg.go +++ b/pkg.go @@ -1,10 +1,8 @@ package main import ( - "encoding/json" - "io" - "os" - "os/exec" + "regexp" + "strings" ) // Package represents a Go package. @@ -14,6 +12,7 @@ type Package struct { ImportPath string Deps []string Standard bool + Processed bool GoFiles []string CgoFiles []string @@ -27,46 +26,33 @@ type Package struct { Error struct { Err string } + + // --- New stuff for now + Imports []string } // LoadPackages loads the named packages using go list -json. -// Unlike the go tool, an empty argument list is treated as -// an empty list; "." must be given explicitly if desired. -func LoadPackages(name ...string) (a []*Package, err error) { - if len(name) == 0 { +// Unlike the go tool, an empty argument list is treated as an empty list; "." +// must be given explicitly if desired. +// IgnoredGoFiles will be processed and their dependencies resolved recursively +// Files with a build tag of `ignore` are skipped. Files with other build tags +// are however processed. +func LoadPackages(names ...string) (a []*Package, err error) { + if len(names) == 0 { return nil, nil } - args := []string{"list", "-e", "-json"} - cmd := exec.Command("go", append(args, name...)...) - r, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - return nil, err - } - d := json.NewDecoder(r) - for { - info := new(Package) - err = d.Decode(info) - if err == io.EOF { - break - } + for _, i := range importPaths(names) { + p, err := listPackage(i) if err != nil { - info.Error.Err = err.Error() + return nil, err } - a = append(a, info) - } - err = cmd.Wait() - if err != nil { - return nil, err + a = append(a, p) } return a, nil } -func (p *Package) allGoFiles() (a []string) { +func (p *Package) allGoFiles() []string { + var a []string a = append(a, p.GoFiles...) a = append(a, p.CgoFiles...) a = append(a, p.TestGoFiles...) @@ -74,3 +60,21 @@ func (p *Package) allGoFiles() (a []string) { a = append(a, p.IgnoredGoFiles...) return a } + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +// Taken from $GOROOT/src/cmd/go/main.go. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) + } +} diff --git a/restore.go b/restore.go index 216ed03..ca9c82a 100644 --- a/restore.go +++ b/restore.go @@ -6,18 +6,21 @@ import ( ) var cmdRestore = &Command{ - Usage: "restore [-v]", + Usage: "restore [-v] [-d]", Short: "check out listed dependency versions in GOPATH", Long: ` Restore checks out the Godeps-specified version of each package in GOPATH. If -v is given, verbose output is enabled. + +If -d is given, debug output is enabled (you probably don't want this, see -v above). `, Run: runRestore, } func init() { cmdRestore.Flag.BoolVar(&verbose, "v", false, "enable verbose output") + cmdRestore.Flag.BoolVar(&debug, "d", false, "enable debug output") } func runRestore(cmd *Command, args []string) { @@ -29,7 +32,7 @@ func runRestore(cmd *Command, args []string) { for _, dep := range g.Deps { err := download(dep) if err != nil { - log.Printf("restore, during download dep %q: %v\n", dep.ImportPath, err) + log.Printf("error downloading dep (%s): %s\n", dep.ImportPath, err.Error()) hadError = true } } @@ -37,7 +40,7 @@ func runRestore(cmd *Command, args []string) { for _, dep := range g.Deps { err := restore(dep) if err != nil { - log.Printf("restore, during restore dep %q: %v\n", dep.ImportPath, err) + log.Printf("error restoring dep (%s): %s\n", dep.ImportPath, err.Error()) hadError = true } } diff --git a/save.go b/save.go index 06995be..9101166 100644 --- a/save.go +++ b/save.go @@ -17,7 +17,7 @@ import ( ) var cmdSave = &Command{ - Usage: "save [-r] [-v] [-t] [packages]", + Usage: "save [-r] [-v] [-d] [-t] [packages]", Short: "list and copy dependencies into Godeps", Long: ` @@ -52,6 +52,8 @@ vendor experiment. If -v is given, verbose output is enabled. +If -d is given, debug output is enabled (you probably don't want this, see -v). + If -t is given, test files (*_test.go files + testdata directories) are also saved. @@ -66,6 +68,7 @@ var ( func init() { cmdSave.Flag.BoolVar(&verbose, "v", false, "enable verbose output") + cmdSave.Flag.BoolVar(&debug, "d", false, "enable debug output") cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths") cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files") } diff --git a/save_test.go b/save_test.go index 469474a..afe0111 100644 --- a/save_test.go +++ b/save_test.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "reflect" + "runtime" "strings" "testing" "text/template" @@ -22,19 +23,21 @@ type node struct { } var ( - pkgtpl = template.Must(template.New("package").Parse(`package {{.Name}} + pkgtpl = template.Must(template.New("package").Parse(`{{ if .Tags }}{{printf "// +build %s\n\n" .Tags }}{{end}}package {{.Name}} import ( {{range .Imports}} {{printf "%q" .}} {{end}}) -`)) +`)) //`Hack: Fix Atom highlighting + ) -func pkg(name string, pkg ...string) string { +func pkg(name string, imports ...string) string { v := struct { Name string + Tags string Imports []string - }{name, pkg} + }{name, "", imports} var buf bytes.Buffer err := pkgtpl.Execute(&buf, v) if err != nil { @@ -43,10 +46,32 @@ func pkg(name string, pkg ...string) string { return buf.String() } +func pkgWithImpossibleTag(name string, imports ...string) string { + v := struct { + Name string + Tags string + Imports []string + }{name, impossibleTag(), imports} + var buf bytes.Buffer + err := pkgtpl.Execute(&buf, v) + if err != nil { + panic(err) + } + return buf.String() +} + +func impossibleTag() string { + return "!" + runtime.GOOS +} + func decl(name string) string { return "var " + name + " int\n" } +func setGOPATH(paths ...string) { + buildContext.GOPATH = strings.Join(paths, string(os.PathListSeparator)) +} + func godeps(importpath string, keyval ...string) *Godeps { g := &Godeps{ ImportPath: importpath, @@ -1116,6 +1141,39 @@ func TestSave(t *testing.T) { }, }, }, + { // two packages, one in a subdirectory that's included only on other OS + cwd: "C", + start: []*node{ + { + "C", + "", + []*node{ + {"main.go", pkg("main", "D"), nil}, + {"+git", "", nil}, + }, + }, + { + "D", + "", + []*node{ + {"main.go", pkgWithImpossibleTag("D", "D/P"), nil}, + {"P/main.go", pkg("P"), nil}, + {"+git", "D1", nil}, + }, + }, + }, + want: []*node{ + {"C/main.go", pkg("main", "D"), nil}, + {"C/Godeps/_workspace/src/D/main.go", pkgWithImpossibleTag("D", "D/P"), nil}, + {"C/Godeps/_workspace/src/D/P/main.go", pkg("P"), nil}, + }, + wdep: Godeps{ + ImportPath: "C", + Deps: []Dependency{ + {ImportPath: "D", Comment: "D1"}, + }, + }, + }, } wd, err := os.Getwd() @@ -1143,10 +1201,7 @@ func TestSave(t *testing.T) { } root1 := filepath.Join(wd, scratch, "r1") root2 := filepath.Join(wd, scratch, "r2") - err = os.Setenv("GOPATH", root1+string(os.PathListSeparator)+root2) - if err != nil { - panic(err) - } + setGOPATH(root1, root2) saveR = test.flagR saveT = test.flagT err = save(test.args) diff --git a/update.go b/update.go index 9dcba2e..1fff009 100644 --- a/update.go +++ b/update.go @@ -6,13 +6,12 @@ import ( "log" "path" "path/filepath" - "regexp" "strconv" "strings" ) var cmdUpdate = &Command{ - Usage: "update [packages]", + Usage: "update [-d] [packages]", Short: "use different revision of selected packages", Long: ` Update changes the named dependency packages to use the @@ -21,12 +20,15 @@ be copied into Godeps and the new revision will be written to the manifest. For more about specifying packages, see 'go help packages'. + +If -d is given, debug output is enabled (you probably don't want this). `, Run: runUpdate, } func init() { cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update") + cmdUpdate.Flag.BoolVar(&debug, "d", false, "enable debug output") } func runUpdate(cmd *Command, args []string) { @@ -129,24 +131,6 @@ func markMatches(pat string, deps []Dependency) (matched bool) { return matched } -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Taken from $GOROOT/src/cmd/go/main.go. -func matchPattern(pattern string) func(name string) bool { - re := regexp.QuoteMeta(pattern) - re = strings.Replace(re, `\.\.\.`, `.*`, -1) - // Special case: foo/... matches foo too. - if strings.HasSuffix(re, `/.*`) { - re = re[:len(re)-len(`/.*`)] + `(/.*)?` - } - reg := regexp.MustCompile(`^` + re + `$`) - return func(name string) bool { - return reg.MatchString(name) - } -} - // LoadVCSAndUpdate loads and updates a set of dependencies. func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, error) { var err1 error diff --git a/update_test.go b/update_test.go index 92dfa6f..65f6a29 100644 --- a/update_test.go +++ b/update_test.go @@ -420,10 +420,7 @@ func TestUpdate(t *testing.T) { if err != nil { panic(err) } - err = os.Setenv("GOPATH", filepath.Join(wd, gopath)) - if err != nil { - panic(err) - } + setGOPATH(filepath.Join(wd, gopath)) log.SetOutput(ioutil.Discard) err = update(test.args) log.SetOutput(os.Stderr) diff --git a/version.go b/version.go index f8d10ee..a154914 100644 --- a/version.go +++ b/version.go @@ -5,7 +5,7 @@ import ( "runtime" ) -const version = 32 +const version = 33 var cmdVersion = &Command{ Usage: "version",