pax_global_header00006660000000000000000000000064126255650620014523gustar00rootroot0000000000000052 comment=a40e0ad20dda5dfee3465ff3615dcbf86f1aa797 gb-0.3.2/000077500000000000000000000000001262556506200121155ustar00rootroot00000000000000gb-0.3.2/.gitignore000066400000000000000000000000211262556506200140760ustar00rootroot00000000000000!* testdata/bin/ gb-0.3.2/.travis.yml000066400000000000000000000011161262556506200142250ustar00rootroot00000000000000language: go go_import_path: github.com/constabulary/gb go: - 1.5.1 - 1.4.3 - tip sudo: false addons: apt: packages: - libcap-dev # integration-tests/service.v1 ssh_known_hosts: - bitbucket.org install: - go get -t -v ./... - git clone --quiet --single-branch --depth 1 https://github.com/constabulary/integration-tests.git ../integration-tests - echo '#!/bin/bash' > "$GOPATH/bin/sudo" && echo 'echo >&2 attempted sudo "$@"' >> "$GOPATH/bin/sudo" && chmod +x "$GOPATH/bin/sudo" script: - go test -v ./... - ../integration-tests/run-all.bash gb-0.3.2/LICENSE000066400000000000000000000020701262556506200131210ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 constabulary 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. gb-0.3.2/README.md000066400000000000000000000041321262556506200133740ustar00rootroot00000000000000# gb ### Build status Unix: [![travis-cs status](https://travis-ci.org/constabulary/gb.svg "travis-ci status")](https://travis-ci.org/constabulary/gb) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/rjttg1agmp2sra3h/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/gb/branch/master) `gb` is a proof of concept replacement build tool for the [Go programming language](https://golang.org). I gave a talk about `gb` and the rational for its creation at GDG Berlin in April 2015, [video](https://www.youtube.com/watch?v=c3dW80eO88I) and [slides](http://go-talks.appspot.com/github.com/davecheney/presentations/reproducible-builds.slide#1). ## Project based `gb` operates on the concept of a project. A gb project is a workspace for all the Go code that is required to build your project. A gb project is a folder on disk that contains a subdirectory named src/. That's it, no environment variables to set. For the rest of this document we'll refer to your gb project as $PROJECT. You can create as many projects as you like and move between them simply by changing directories. ## Installation go get github.com/constabulary/gb/... ## Read more gb has its own site, [getgb.io](http://getgb.io/), head over there for more information. ## Contributing ### Road map #### Completed - [Cross Compilation](https://github.com/constabulary/gb/milestones/cross-compilation) - Tag handling, unify -tags, ENVVARS and GOOS/GOARCH into a single format for binary names and pkg cache #### Todo - 0.3 series: gb test improvements, test output, ~~flag handling~~ (done) - [Race detector support](https://github.com/constabulary/gb/issues/96) - 0.4 series: gb vendor updates and bug fixes - 0.5 series: new package resolver (replace go/build) ### Big ticket items Big ticket items that are not on the road map yet - Package BuildID support (make stale detection work like the Go 1.5) - `gccgo` toolchain support. We welcome pull requests, bug fixes and issue reports. Before proposing a large change, please discuss your change by raising an issue. gb-0.3.2/action_test.go000066400000000000000000000044541262556506200147670ustar00rootroot00000000000000package gb import ( "reflect" "sort" "testing" ) func TestBuildAction(t *testing.T) { actions := func(a ...*Action) []*Action { return a } var tests = []struct { pkg string action *Action err error }{{ pkg: "a", action: &Action{ Name: "build: a", Deps: actions(&Action{Name: "compile: a"}), }, }, { pkg: "b", action: &Action{ Name: "build: b", Deps: []*Action{ &Action{ Name: "link: b", Deps: []*Action{ &Action{ Name: "compile: b", Deps: []*Action{ &Action{ Name: "compile: a", }}, }, }}, }, }, }, { pkg: "c", action: &Action{ Name: "build: c", Deps: []*Action{ &Action{ Name: "compile: c", Deps: []*Action{ &Action{ Name: "compile: a", }, &Action{ Name: "compile: d.v1", }}, }}, }, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !sameErr(err, tt.err) { t.Errorf("ctx.ResolvePackage(%v): want %v, got %v", tt.pkg, tt.err, err) continue } if err != nil { continue } got, err := BuildPackages(pkg) if !sameErr(err, tt.err) { t.Errorf("BuildAction(%v): want %v, got %v", tt.pkg, tt.err, err) continue } deleteTasks(got) if !reflect.DeepEqual(tt.action, got) { t.Errorf("BuildAction(%v): want %#v, got %#v", tt.pkg, tt.action, got) } // double underpants sameAction(t, got, tt.action) } } func sameAction(t *testing.T, want, got *Action) { if want.Name != got.Name { t.Errorf("sameAction: names do not match, want: %v, got %v", want.Name, got.Name) return } if len(want.Deps) != len(got.Deps) { t.Errorf("sameAction(%v, %v): deps: len(want): %v, len(got): %v", want.Name, got.Name, len(want.Deps), len(got.Deps)) return } w, g := make(map[string]*Action), make(map[string]*Action) for _, a := range want.Deps { w[a.Name] = a } for _, a := range got.Deps { g[a.Name] = a } var wk []string for k := range w { wk = append(wk, k) } sort.Strings(wk) for _, a := range wk { g, ok := g[a] if !ok { t.Errorf("sameAction(%v, %v): deps: want %v, got nil", want.Name, got.Name, a) continue } sameAction(t, w[a], g) } } func deleteTasks(a *Action) { for _, d := range a.Deps { deleteTasks(d) } a.Run = nil } gb-0.3.2/appveyor.yml000066400000000000000000000020531262556506200145050ustar00rootroot00000000000000version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\constabulary\gb shallow_clone: true # for startup speed environment: GOPATH: C:\gopath platform: - x64 #- x86 # the "x86" platform still gives us GOARCH=amd64 :/ # TODO(tianon) we have 32bit Go installed at C:\go-x86 and 32bit mingw at both C:\msys64\mingw32 and C:\MinGW, so we could do something # http://www.appveyor.com/docs/installed-software install: # some helpful output for debugging builds - go version - go env # pre-installed MinGW at C:\MinGW is 32bit only # but MSYS2 at C:\msys64 has mingw64 - set PATH=C:\msys64\mingw64\bin;%PATH% - gcc --version - g++ --version # need bzr for several tests - choco install bzr - set PATH=C:\Program Files (x86)\Bazaar;%PATH% - bzr --version # TODO(tianon) - git clone --depth 1 https://github.com/constabulary/integration-tests.git build_script: - go install -v ./... test_script: - set PATH=C:\gopath\bin;%PATH% - gb help - go test -v ./... #artifacts: # - path: '%GOPATH%\bin\*.exe' deploy: off gb-0.3.2/build.go000066400000000000000000000213541262556506200135500ustar00rootroot00000000000000package gb import ( "fmt" "path/filepath" "runtime" "strings" "time" "github.com/constabulary/gb/debug" ) // Build builds each of pkgs in succession. If pkg is a command, then the results of build include // linking the final binary into pkg.Context.Bindir(). func Build(pkgs ...*Package) error { build, err := BuildPackages(pkgs...) if err != nil { return err } return ExecuteConcurrent(build, runtime.NumCPU(), nil) } // BuildPackages produces a tree of *Actions that can be executed to build // a *Package. // BuildPackages walks the tree of *Packages and returns a corresponding // tree of *Actions representing the steps required to build *Package // and any of its dependencies func BuildPackages(pkgs ...*Package) (*Action, error) { if len(pkgs) < 1 { return nil, fmt.Errorf("no packages supplied") } targets := make(map[string]*Action) // maps package importpath to build action names := func(pkgs []*Package) []string { var names []string for _, pkg := range pkgs { names = append(names, pkg.ImportPath) } return names } // create top level build action to unify all packages t0 := time.Now() build := Action{ Name: fmt.Sprintf("build: %s", strings.Join(names(pkgs), ",")), Run: func() error { debug.Debugf("build duration: %v %v", time.Since(t0), pkgs[0].Statistics.String()) return nil }, } for _, pkg := range pkgs { if len(pkg.GoFiles)+len(pkg.CgoFiles) == 0 { debug.Debugf("skipping %v: no go files", pkg.ImportPath) continue } a, err := BuildPackage(targets, pkg) if err != nil { return nil, err } if a == nil { // nothing to do continue } build.Deps = append(build.Deps, a) } return &build, nil } // BuildPackage returns an Action representing the steps required to // build this package. func BuildPackage(targets map[string]*Action, pkg *Package) (*Action, error) { // if this action is already present in the map, return it // rather than creating a new action. if a, ok := targets[pkg.ImportPath]; ok { return a, nil } // step 0. are we stale ? // if this package is not stale, then by definition none of its // dependencies are stale, so ignore this whole tree. if !pkg.Stale { return nil, nil } // step 1. build dependencies deps, err := BuildDependencies(targets, pkg) if err != nil { return nil, err } // step 2. build this package build, err := Compile(pkg, deps...) if err != nil { return nil, err } if build == nil { panic("build action was nil") // shouldn't happen } // record the final action as the action that represents // building this package. targets[pkg.ImportPath] = build return build, nil } // Compile returns an Action representing the steps required to compile this package. func Compile(pkg *Package, deps ...*Action) (*Action, error) { var gofiles []string gofiles = append(gofiles, pkg.GoFiles...) // step 1. are there any .c files that we have to run cgo on ? var ofiles []string // additional ofiles to pack if len(pkg.CgoFiles) > 0 { cgoACTION, cgoOFILES, cgoGOFILES, err := cgo(pkg) if err != nil { return nil, err } gofiles = append(gofiles, cgoGOFILES...) ofiles = append(ofiles, cgoOFILES...) deps = append(deps, cgoACTION) } if len(gofiles) == 0 { return nil, fmt.Errorf("compile %q: no go files supplied", pkg.ImportPath) } // step 2. compile all the go files for this package, including pkg.CgoFiles compile := Action{ Name: fmt.Sprintf("compile: %s", pkg.ImportPath), Deps: deps, Run: func() error { return gc(pkg, gofiles) }, } // step 3. are there any .s files to assemble. var assemble []*Action for _, sfile := range pkg.SFiles { sfile := sfile ofile := filepath.Join(pkg.Workdir(), pkg.ImportPath, stripext(sfile)+".6") assemble = append(assemble, &Action{ Name: fmt.Sprintf("asm: %s/%s", pkg.ImportPath, sfile), Run: func() error { t0 := time.Now() err := pkg.tc.Asm(pkg, pkg.Dir, ofile, filepath.Join(pkg.Dir, sfile)) pkg.Record("asm", time.Since(t0)) return err }, // asm depends on compile because compile will generate the local go_asm.h Deps: []*Action{&compile}, }) ofiles = append(ofiles, ofile) } build := &compile // Do we need to pack ? Yes, replace build action with pack. if len(ofiles) > 0 { pack := Action{ Name: fmt.Sprintf("pack: %s", pkg.ImportPath), Deps: []*Action{ &compile, }, Run: func() error { // collect .o files, ofiles always starts with the gc compiled object. // TODO(dfc) objfile(pkg) should already be at the top of this set ofiles = append( []string{objfile(pkg)}, ofiles..., ) // pack t0 := time.Now() err := pkg.tc.Pack(pkg, ofiles...) pkg.Record("pack", time.Since(t0)) return err }, } pack.Deps = append(pack.Deps, assemble...) build = &pack } // should this package be cached // TODO(dfc) pkg.SkipInstall should become Install if !pkg.SkipInstall && pkg.Scope != "test" { build = &Action{ Name: fmt.Sprintf("install: %s", pkg.ImportPath), Deps: []*Action{build}, Run: func() error { return copyfile(pkgfile(pkg), objfile(pkg)) }, } } // if this is a main package, add a link stage if pkg.isMain() { build = &Action{ Name: fmt.Sprintf("link: %s", pkg.ImportPath), Deps: []*Action{build}, Run: func() error { return link(pkg) }, } } if pkg.Scope != "test" { // if this package is not compiled in test scope, then // log the name of the package when complete. build.Run = logInfoFn(build.Run, pkg.ImportPath) } return build, nil } func logInfoFn(fn func() error, s string) func() error { return func() error { err := fn() fmt.Println(s) return err } } // BuildDependencies returns a slice of Actions representing the steps required // to build all dependant packages of this package. func BuildDependencies(targets map[string]*Action, pkg *Package) ([]*Action, error) { var deps []*Action pkgs := pkg.Imports() if pkg.isMain() { extra := []string{ // all binaries depend on runtime, even if they do not // explicitly import it. "runtime", } for _, i := range extra { if pkg.shouldignore(i) { continue } p, err := pkg.ResolvePackage(i) if err != nil { return nil, err } pkgs = append(pkgs, p) } } for _, i := range pkgs { a, err := BuildPackage(targets, i) if err != nil { return nil, err } if a == nil { // no action required for this Package continue } deps = append(deps, a) } return deps, nil } func gc(pkg *Package, gofiles []string) error { t0 := time.Now() includes := pkg.IncludePaths() importpath := pkg.ImportPath if pkg.Scope == "test" && pkg.ExtraIncludes != "" { // TODO(dfc) gross includes = append([]string{pkg.ExtraIncludes}, includes...) } for i := range gofiles { if filepath.IsAbs(gofiles[i]) { // terrible hack for cgo files which come with an absolute path continue } fullpath := filepath.Join(pkg.Dir, gofiles[i]) path, err := filepath.Rel(pkg.Dir, fullpath) if err == nil { gofiles[i] = path } else { gofiles[i] = fullpath } } err := pkg.tc.Gc(pkg, includes, importpath, pkg.Dir, objfile(pkg), gofiles) pkg.Record("gc", time.Since(t0)) return err } func link(pkg *Package) error { t0 := time.Now() target := pkg.Binfile() if err := mkdir(filepath.Dir(target)); err != nil { return err } includes := pkg.IncludePaths() if pkg.Scope == "test" && pkg.ExtraIncludes != "" { // TODO(dfc) gross includes = append([]string{pkg.ExtraIncludes}, includes...) } err := pkg.tc.Ld(pkg, includes, target, objfile(pkg)) pkg.Record("link", time.Since(t0)) return err } // Workdir returns the working directory for a package. func Workdir(pkg *Package) string { switch pkg.Scope { case "test": ip := strings.TrimSuffix(filepath.FromSlash(pkg.ImportPath), "_test") return filepath.Join(pkg.Workdir(), ip, "_test", filepath.Dir(filepath.FromSlash(pkg.ImportPath))) default: return filepath.Join(pkg.Workdir(), filepath.Dir(filepath.FromSlash(pkg.ImportPath))) } } // objfile returns the name of the object file for this package func objfile(pkg *Package) string { return filepath.Join(Workdir(pkg), objname(pkg)) } func objname(pkg *Package) string { if pkg.isMain() { return filepath.Join(filepath.Base(filepath.FromSlash(pkg.ImportPath)), "main.a") } return filepath.Base(filepath.FromSlash(pkg.ImportPath)) + ".a" } func pkgname(pkg *Package) string { switch pkg.Scope { case "test": return filepath.Base(filepath.FromSlash(pkg.ImportPath)) default: if pkg.Name == "main" { return filepath.Base(filepath.FromSlash(pkg.ImportPath)) } return pkg.Name } } func binname(pkg *Package) string { switch { case pkg.Scope == "test": return pkg.Name + ".test" case pkg.Name == "main": return filepath.Base(filepath.FromSlash(pkg.ImportPath)) default: panic("binname called with non main package: " + pkg.ImportPath) } } gb-0.3.2/build_test.go000066400000000000000000000166631262556506200146160ustar00rootroot00000000000000package gb import ( "errors" "fmt" "go/build" "io/ioutil" "os" "path/filepath" "reflect" "sort" "testing" ) func TestBuild(t *testing.T) { opts := func(o ...func(*Context) error) []func(*Context) error { return o } tests := []struct { pkg string opts []func(*Context) error err error }{{ pkg: "a", err: nil, }, { pkg: "b", // actually command err: nil, }, { pkg: "c", err: nil, }, { pkg: "d.v1", err: nil, }, { pkg: "x", err: errors.New("import cycle detected: x -> y -> x"), }, { pkg: "cgomain", err: nil, }, { pkg: "cgotest", err: nil, }, { pkg: "notestfiles", err: nil, }, { pkg: "cgoonlynotest", err: nil, }, { pkg: "testonly", err: nil, }, { pkg: "extestonly", err: nil, }, { pkg: "mainnoruntime", err: nil, }, { pkg: "h", // imports "blank", which is blank, see issue #131 err: fmt.Errorf("no buildable Go source files in %s", filepath.Join(getwd(t), "testdata", "src", "blank")), }, { pkg: "cppmain", }, { pkg: "tags1", opts: opts(Tags("x")), // excludes the test file in package err: fmt.Errorf("no buildable Go source files in %s", filepath.Join(getwd(t), "testdata", "src", "tags1")), }, { pkg: "tags2", err: fmt.Errorf("no buildable Go source files in %s", filepath.Join(getwd(t), "testdata", "src", "tags2")), }, { pkg: "tags2", opts: opts(Tags("x")), }} proj := testProject(t) for _, tt := range tests { ctx, err := proj.NewContext(tt.opts...) ctx.Force = true ctx.SkipInstall = true defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !sameErr(err, tt.err) { t.Errorf("ctx.ResolvePackage(%v): want %v, got %v", tt.pkg, tt.err, err) continue } if err != nil { continue } if err := Build(pkg); !sameErr(err, tt.err) { t.Errorf("ctx.Build(%v): want %v, got %v", tt.pkg, tt.err, err) } } } func TestBuildPackage(t *testing.T) { tests := []struct { pkg string err error }{{ pkg: "a", err: nil, }, { pkg: "b", // actually command err: nil, }, { pkg: "c", err: nil, }, { pkg: "d.v1", err: nil, }, { pkg: "cgomain", err: nil, }, { pkg: "cgotest", err: nil, }, { pkg: "notestfiles", err: nil, }, { pkg: "cgoonlynotest", err: nil, }, { pkg: "testonly", err: errors.New(`compile "testonly": no go files supplied`), }, { pkg: "extestonly", err: errors.New(`compile "extestonly": no go files supplied`), }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Errorf("ctx.ResolvePackage(%v): %v", tt.pkg, err) continue } targets := make(map[string]*Action) if _, err := BuildPackage(targets, pkg); !sameErr(err, tt.err) { t.Errorf("ctx.BuildPackage(%v): want %v, got %v", tt.pkg, tt.err, err) } } } func TestBuildPackages(t *testing.T) { tests := []struct { pkgs []string actions []string err error }{{ pkgs: []string{"a", "b", "c"}, actions: []string{"compile: a", "compile: c", "link: b"}, }, { pkgs: []string{"cgotest", "cgomain", "notestfiles", "cgoonlynotest", "testonly", "extestonly"}, actions: []string{"compile: notestfiles", "link: cgomain", "pack: cgoonlynotest", "pack: cgotest"}, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() var pkgs []*Package for _, pkg := range tt.pkgs { pkg, err := ctx.ResolvePackage(pkg) if err != nil { t.Errorf("ctx.ResolvePackage(%v): %v", pkg, err) continue } pkgs = append(pkgs, pkg) } a, err := BuildPackages(pkgs...) if !sameErr(err, tt.err) { t.Errorf("ctx.BuildPackages(%v): want %v, got %v", pkgs, tt.err, err) } var names []string for _, a := range a.Deps { names = append(names, a.Name) } sort.Strings(names) if !reflect.DeepEqual(tt.actions, names) { t.Errorf("ctx.BuildPackages(%v): want %v, got %v", pkgs, tt.actions, names) } } } func TestObjfile(t *testing.T) { var tests = []struct { pkg string // package name want string // objfile result }{ {pkg: "b", want: "b/main.a"}, {pkg: "nested/a", want: "nested/a.a"}, {pkg: "nested/b", want: "nested/b.a"}, } for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Fatal(err) } got := objfile(pkg) want := filepath.Join(ctx.Workdir(), tt.want) if want != got { t.Errorf("(%s).Objdir(): want %s, got %s", tt.pkg, want, got) } } } func TestCgoobjdir(t *testing.T) { var tests = []struct { pkg string // package name want string // objdir result }{ {pkg: "b", want: "b/_cgo"}, {pkg: "nested/a", want: "nested/a/_cgo"}, {pkg: "nested/b", want: "nested/b/_cgo"}, } ctx := testContext(t) defer ctx.Destroy() for _, tt := range tests { pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Fatal(err) } got := cgoworkdir(pkg) want := filepath.Join(ctx.Workdir(), tt.want) if want != got { t.Errorf("(%s).cgoobjdir(): want %s, got %s", tt.pkg, want, got) } } } func TestWorkdir(t *testing.T) { var tests = []struct { pkg string // package name want string // objdir result }{ {pkg: "b", want: ""}, {pkg: "nested/a", want: "nested"}, {pkg: "nested/b", want: "nested"}, } ctx := testContext(t) defer ctx.Destroy() for _, tt := range tests { pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Error(err) continue } got := Workdir(pkg) want := filepath.Join(ctx.Workdir(), tt.want) if want != got { t.Errorf("Workdir(Package{Name: %v, ImportPath: %v, Scope: %v}): want %s, got %s", pkg.Name, pkg.ImportPath, pkg.Scope, want, got) } } } func TestPkgname(t *testing.T) { var tests = []struct { pkg *Package want string }{{ pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "main", }, }, want: "main", }, { pkg: &Package{ Package: &build.Package{ Name: "a", ImportPath: "main", }, }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a", }, }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "testmain", }, }, want: "testmain", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "main", }, Scope: "test", }, want: "main", }, { pkg: &Package{ Package: &build.Package{ Name: "a", ImportPath: "main", }, Scope: "test", }, want: "main", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a", }, Scope: "test", }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a/a", }, Scope: "test", }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "testmain", }, Scope: "test", }, want: "testmain", }} for _, tt := range tests { got := pkgname(tt.pkg) if got != tt.want { t.Errorf("pkgname(Package{Name:%q, ImportPath: %q, Scope:%q}): got %v, want %v", tt.pkg.Name, tt.pkg.ImportPath, tt.pkg.Scope, got, tt.want) } } } func sameErr(e1, e2 error) bool { if e1 != nil && e2 != nil { return e1.Error() == e2.Error() } return e1 == e2 } func getwd(t *testing.T) string { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } return cwd } func mktemp(t *testing.T) string { s, err := mktmp() if err != nil { t.Fatal(err) } return s } func mktmp() (string, error) { return ioutil.TempDir("", "gb-test-") } gb-0.3.2/cgo.go000066400000000000000000000341671262556506200132270ustar00rootroot00000000000000package gb import ( "bytes" "fmt" "io" "os" "path/filepath" "runtime" "strconv" "strings" "time" ) func cgo(pkg *Package) (*Action, []string, []string, error) { switch { case gc14: return cgo14(pkg) case gc15: return cgo15(pkg) default: return nil, nil, nil, fmt.Errorf("unsupported Go version: %v", runtime.Version) } } // cgo produces a an Action representing the cgo steps // an ofile representing the result of the cgo steps // a set of .go files for compilation, and an error. func cgo14(pkg *Package) (*Action, []string, []string, error) { // collect cflags and ldflags from the package // the environment, and pkg-config. cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := cflags(pkg, false) pcCFLAGS, pcLDFLAGS, err := pkgconfig(pkg) if err != nil { return nil, nil, nil, err } cgoCFLAGS = append(cgoCFLAGS, pcCFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) runcgo1 := []*Action{ &Action{ Name: "runcgo1: " + pkg.ImportPath, Run: func() error { return runcgo1(pkg, cgoCFLAGS, cgoLDFLAGS) }, }} workdir := cgoworkdir(pkg) defun := filepath.Join(workdir, "_cgo_defun.o") rundefun := Action{ Name: "cc: " + pkg.ImportPath + ": _cgo_defun_c", Deps: runcgo1, Run: func() error { return pkg.tc.Cc(pkg, defun, filepath.Join(workdir, "_cgo_defun.c")) }, } cgofiles := []string{filepath.Join(workdir, "_cgo_gotypes.go")} for _, f := range pkg.CgoFiles { cgofiles = append(cgofiles, filepath.Join(workdir, stripext(f)+".cgo1.go")) } cfiles := []string{ filepath.Join(workdir, "_cgo_main.c"), filepath.Join(workdir, "_cgo_export.c"), } cfiles = append(cfiles, pkg.CFiles...) for _, f := range pkg.CgoFiles { cfiles = append(cfiles, filepath.Join(workdir, stripext(f)+".cgo2.c")) } cflags := append(cgoCPPFLAGS, cgoCFLAGS...) cxxflags := append(cgoCPPFLAGS, cgoCXXFLAGS...) gcc1, ofiles := cgocc(pkg, cflags, cxxflags, cfiles, pkg.CXXFiles, runcgo1...) ofile := filepath.Join(filepath.Dir(ofiles[0]), "_cgo_.o") gcc2 := Action{ Name: "gccld: " + pkg.ImportPath + ": _cgo_.o", Deps: gcc1, Run: func() error { return gccld(pkg, cgoCFLAGS, cgoLDFLAGS, ofile, ofiles) }, } dynout := filepath.Join(workdir, "_cgo_import.c") imports := stripext(dynout) + ".o" runcgo2 := Action{ Name: "runcgo2: " + pkg.ImportPath, Deps: []*Action{&gcc2}, Run: func() error { if err := runcgo2(pkg, dynout, ofile); err != nil { return err } return pkg.tc.Cc(pkg, imports, dynout) }, } allo := filepath.Join(filepath.Dir(ofiles[0]), "_all.o") action := Action{ Name: "rungcc3: " + pkg.ImportPath, Deps: []*Action{&runcgo2, &rundefun}, Run: func() error { return rungcc3(pkg, pkg.Dir, allo, ofiles[1:]) // skip _cgo_main.o }, } return &action, []string{defun, imports, allo}, cgofiles, nil } // cgo produces a an Action representing the cgo steps // an ofile representing the result of the cgo steps // a set of .go files for compilation, and an error. func cgo15(pkg *Package) (*Action, []string, []string, error) { // collect cflags and ldflags from the package // the environment, and pkg-config. cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := cflags(pkg, false) pcCFLAGS, pcLDFLAGS, err := pkgconfig(pkg) if err != nil { return nil, nil, nil, err } cgoCFLAGS = append(cgoCFLAGS, pcCFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) runcgo1 := []*Action{ &Action{ Name: "runcgo1: " + pkg.ImportPath, Run: func() error { return runcgo1(pkg, cgoCFLAGS, cgoLDFLAGS) }, }, } workdir := cgoworkdir(pkg) cgofiles := []string{filepath.Join(workdir, "_cgo_gotypes.go")} for _, f := range pkg.CgoFiles { cgofiles = append(cgofiles, filepath.Join(workdir, stripext(f)+".cgo1.go")) } cfiles := []string{ filepath.Join(workdir, "_cgo_main.c"), filepath.Join(workdir, "_cgo_export.c"), } cfiles = append(cfiles, pkg.CFiles...) for _, f := range pkg.CgoFiles { cfiles = append(cfiles, filepath.Join(workdir, stripext(f)+".cgo2.c")) } cflags := append(cgoCPPFLAGS, cgoCFLAGS...) cxxflags := append(cgoCPPFLAGS, cgoCXXFLAGS...) gcc1, ofiles := cgocc(pkg, cflags, cxxflags, cfiles, pkg.CXXFiles, runcgo1...) ofile := filepath.Join(filepath.Dir(ofiles[0]), "_cgo_.o") gcc2 := Action{ Name: "gccld: " + pkg.ImportPath + ": _cgo_.o", Deps: gcc1, Run: func() error { return gccld(pkg, cgoCFLAGS, cgoLDFLAGS, ofile, ofiles) }, } dynout := filepath.Join(workdir, "_cgo_import.go") runcgo2 := Action{ Name: "runcgo2: " + pkg.ImportPath, Deps: []*Action{&gcc2}, Run: func() error { return runcgo2(pkg, dynout, ofile) }, } cgofiles = append(cgofiles, dynout) allo := filepath.Join(filepath.Dir(ofiles[0]), "_all.o") action := Action{ Name: "rungcc3: " + pkg.ImportPath, Deps: []*Action{&runcgo2}, Run: func() error { return rungcc3(pkg, pkg.Dir, allo, ofiles[1:]) // skip _cgo_main.o }, } return &action, []string{allo}, cgofiles, nil } // cgocc compiles all .c files. // TODO(dfc) cxx not done func cgocc(pkg *Package, cflags, cxxflags, cfiles, cxxfiles []string, deps ...*Action) ([]*Action, []string) { workdir := cgoworkdir(pkg) var cc []*Action var ofiles []string for _, cfile := range cfiles { cfile := cfile ofile := filepath.Join(workdir, stripext(filepath.Base(cfile))+".o") ofiles = append(ofiles, ofile) cc = append(cc, &Action{ Name: "rungcc1: " + pkg.ImportPath + ": " + cfile, Deps: deps, Run: func() error { return rungcc1(pkg, cflags, ofile, cfile) }, }) } for _, cxxfile := range cxxfiles { cxxfile := cxxfile ofile := filepath.Join(workdir, stripext(filepath.Base(cxxfile))+".o") ofiles = append(ofiles, ofile) cc = append(cc, &Action{ Name: "rung++1: " + pkg.ImportPath + ": " + cxxfile, Deps: deps, Run: func() error { return rungpp1(pkg, cxxflags, ofile, cxxfile) }, }) } return cc, ofiles } // rungcc1 invokes gcc to compile cfile into ofile func rungcc1(pkg *Package, cgoCFLAGS []string, ofile, cfile string) error { args := []string{"-g", "-O2", "-I", pkg.Dir, "-I", filepath.Dir(ofile), } args = append(args, cgoCFLAGS...) args = append(args, "-o", ofile, "-c", cfile, ) t0 := time.Now() gcc := gccCmd(pkg, pkg.Dir) var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, gcc[0], append(gcc[1:], args...)...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } pkg.Record(gcc[0], time.Since(t0)) return err } // rungpp1 invokes g++ to compile cfile into ofile func rungpp1(pkg *Package, cgoCFLAGS []string, ofile, cfile string) error { args := []string{"-g", "-O2", "-I", pkg.Dir, "-I", filepath.Dir(ofile), } args = append(args, cgoCFLAGS...) args = append(args, "-o", ofile, "-c", cfile, ) t0 := time.Now() gxx := gxxCmd(pkg, pkg.Dir) var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, gxx[0], append(gxx[1:], args...)...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } pkg.Record(gxx[0], time.Since(t0)) return err } // gccld links the o files from rungcc1 into a single _cgo_.o. func gccld(pkg *Package, cgoCFLAGS, cgoLDFLAGS []string, ofile string, ofiles []string) error { args := []string{} args = append(args, "-o", ofile) args = append(args, ofiles...) args = append(args, cgoLDFLAGS...) // this has to go at the end, because reasons! t0 := time.Now() var cmd []string if len(pkg.CXXFiles) > 0 || len(pkg.SwigCXXFiles) > 0 { cmd = gxxCmd(pkg, pkg.Dir) } else { cmd = gccCmd(pkg, pkg.Dir) } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, cmd[0], append(cmd[1:], args...)...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } pkg.Record("gccld", time.Since(t0)) return err } // rungcc3 links all previous ofiles together with libgcc into a single _all.o. func rungcc3(pkg *Package, dir string, ofile string, ofiles []string) error { args := []string{} args = append(args, "-o", ofile) args = append(args, ofiles...) args = append(args, "-Wl,-r", "-nostdlib") var cmd []string if len(pkg.CXXFiles) > 0 || len(pkg.SwigCXXFiles) > 0 { cmd = gxxCmd(pkg, dir) } else { cmd = gccCmd(pkg, dir) } if !strings.HasPrefix(cmd[0], "clang") { libgcc, err := libgcc(pkg.Context) if err != nil { return nil } args = append(args, libgcc) } t0 := time.Now() var buf bytes.Buffer err := runOut(&buf, dir, nil, cmd[0], append(cmd[1:], args...)...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } pkg.Record("gcc3", time.Since(t0)) return err } // libgcc returns the value of gcc -print-libgcc-file-name. func libgcc(ctx *Context) (string, error) { args := []string{ "-print-libgcc-file-name", } var buf bytes.Buffer cmd := gccCmd(&Package{Context: ctx}, "") // TODO(dfc) hack err := runOut(&buf, ".", nil, cmd[0], args...) return strings.TrimSpace(buf.String()), err } func cgotool(ctx *Context) string { // TODO(dfc) need ctx.GOROOT method return filepath.Join(ctx.Context.GOROOT, "pkg", "tool", ctx.gohostos+"_"+ctx.gohostarch, "cgo") } // envList returns the value of the given environment variable broken // into fields, using the default value when the variable is empty. func envList(key, def string) []string { v := os.Getenv(key) if v == "" { v = def } return strings.Fields(v) } // Return the flags to use when invoking the C or C++ compilers, or cgo. func cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) { var defaults string if def { defaults = "-g -O2" } cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) return } // call pkg-config and return the cflags and ldflags. func pkgconfig(p *Package) ([]string, []string, error) { if len(p.CgoPkgConfig) == 0 { return nil, nil, nil // nothing to do } args := []string{ "--cflags", } args = append(args, p.CgoPkgConfig...) var out bytes.Buffer err := runOut(&out, p.Dir, nil, "pkg-config", args...) if err != nil { return nil, nil, err } cflags := strings.Fields(out.String()) args = []string{ "--libs", } args = append(args, p.CgoPkgConfig...) out.Reset() err = runOut(&out, p.Dir, nil, "pkg-config", args...) if err != nil { return nil, nil, err } ldflags := strings.Fields(out.String()) return cflags, ldflags, nil } func quoteFlags(flags []string) []string { quoted := make([]string, len(flags)) for i, f := range flags { quoted[i] = strconv.Quote(f) } return quoted } // runcgo1 invokes the cgo tool to process pkg.CgoFiles. func runcgo1(pkg *Package, cflags, ldflags []string) error { cgo := cgotool(pkg.Context) workdir := cgoworkdir(pkg) if err := mkdir(workdir); err != nil { return err } args := []string{"-objdir", workdir} switch { case gc14: args = append(args, "--", "-I", pkg.Dir, ) case gc15: args = append(args, "-importpath", pkg.ImportPath, "--", "-I", workdir, "-I", pkg.Dir, ) default: return fmt.Errorf("unsupported Go version: %v", runtime.Version) } args = append(args, cflags...) args = append(args, pkg.CgoFiles...) cgoenv := []string{ "CGO_CFLAGS=" + strings.Join(quoteFlags(cflags), " "), "CGO_LDFLAGS=" + strings.Join(quoteFlags(ldflags), " "), } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, cgoenv, cgo, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } // runcgo2 invokes the cgo tool to create _cgo_import.go func runcgo2(pkg *Package, dynout, ofile string) error { cgo := cgotool(pkg.Context) workdir := cgoworkdir(pkg) args := []string{ "-objdir", workdir, } switch { case gc14: args = append(args, "-dynimport", ofile, "-dynout", dynout, ) case gc15: args = append(args, "-dynpackage", pkg.Name, "-dynimport", ofile, "-dynout", dynout, ) default: return fmt.Errorf("unsuppored Go version: %v", runtime.Version) } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, cgo, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } // cgoworkdir returns the cgo working directory for this package. func cgoworkdir(pkg *Package) string { return filepath.Join(Workdir(pkg), pkgname(pkg), "_cgo") } // gccCmd returns a gcc command line prefix. func gccCmd(pkg *Package, objdir string) []string { return ccompilerCmd(pkg, "CC", defaultCC, objdir) } // gxxCmd returns a g++ command line prefix. func gxxCmd(pkg *Package, objdir string) []string { return ccompilerCmd(pkg, "CXX", defaultCXX, objdir) } // ccompilerCmd returns a command line prefix for the given environment // variable and using the default command when the variable is empty. func ccompilerCmd(pkg *Package, envvar, defcmd, objdir string) []string { compiler := envList(envvar, defcmd) a := []string{compiler[0]} if objdir != "" { a = append(a, "-I", objdir) } a = append(a, compiler[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" if pkg.gotargetos != "windows" { a = append(a, "-fPIC") } a = append(a, gccArchArgs(pkg.gotargetarch)...) // gcc-4.5 and beyond require explicit "-pthread" flag // for multithreading with pthread library. switch pkg.gotargetos { case "windows": a = append(a, "-mthreads") default: a = append(a, "-pthread") } if strings.Contains(a[0], "clang") { // disable ASCII art in clang errors, if possible a = append(a, "-fno-caret-diagnostics") // clang is too smart about command-line arguments a = append(a, "-Qunused-arguments") } // disable word wrapping in error messages a = append(a, "-fmessage-length=0") // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See https://golang.org/issue/3253. if pkg.gotargetos == "darwin" { a = append(a, "-fno-common") } return a } // gccArchArgs returns arguments to pass to gcc based on the architecture. func gccArchArgs(goarch string) []string { switch goarch { case "386": return []string{"-m32"} case "amd64", "amd64p32": return []string{"-m64"} case "arm": return []string{"-marm"} // not thumb } return nil } gb-0.3.2/cmd/000077500000000000000000000000001262556506200126605ustar00rootroot00000000000000gb-0.3.2/cmd/cmd.go000066400000000000000000000051241262556506200137540ustar00rootroot00000000000000// Package command holds support functions and types for writing gb and gb plugins package cmd import ( "flag" "fmt" "os" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/debug" ) // Command represents a subcommand, or plugin that is executed within // a gb project. type Command struct { // Name of the command Name string // UsageLine demonstrates how to use this command UsageLine string // Single line description of the purpose of the command Short string // Description of this command Long string // Run is invoked with a Context derived from the Project and arguments // left over after flag parsing. Run func(ctx *gb.Context, args []string) error // AddFlags installs additional flags to be parsed before Run. AddFlags func(fs *flag.FlagSet) // Allow plugins to modify arguments FlagParse func(fs *flag.FlagSet, args []string) error // ParseArgs provides an alternative method to parse arguments. // By default, arguments will be parsed as import paths with // ImportPaths ParseArgs func(ctx *gb.Context, cwd string, args []string) []string } // Runnable indicates this is a command that can be involved. // Non runnable commands are only informational. func (c *Command) Runnable() bool { return c.Run != nil } // Hidden indicates this is a command which is hidden from help / alldoc.go. func (c *Command) Hidden() bool { return c.Name == "depset" } // RunCommand detects the project root, parses flags and runs the Command. func RunCommand(fs *flag.FlagSet, cmd *Command, projectroot, goroot string, args []string) error { if cmd.AddFlags != nil { cmd.AddFlags(fs) } if err := fs.Parse(args); err != nil { fs.Usage() os.Exit(1) } args = fs.Args() // reset to the remaining arguments ctx, err := NewContext(projectroot, gb.GcToolchain()) if err != nil { return fmt.Errorf("unable to construct context: %v", err) } defer ctx.Destroy() debug.Debugf("args: %v", args) return cmd.Run(ctx, args) } // NewContext creates a gb.Context for the project root. func NewContext(projectroot string, options ...func(*gb.Context) error) (*gb.Context, error) { if projectroot == "" { return nil, fmt.Errorf("project root is blank") } root, err := FindProjectroot(projectroot) if err != nil { return nil, fmt.Errorf("could not locate project root: %v", err) } project := gb.NewProject(root, gb.SourceDir(filepath.Join(root, "src")), gb.SourceDir(filepath.Join(root, "vendor", "src")), ) debug.Debugf("project root %q", project.Projectdir()) return project.NewContext(options...) } func mkdir(path string) error { return os.MkdirAll(path, 0755) } gb-0.3.2/cmd/env.go000066400000000000000000000011251262556506200137760ustar00rootroot00000000000000package cmd import ( "fmt" "log" "os" "strings" ) func MustGetwd() string { wd, err := os.Getwd() if err != nil { log.Fatalf("unable to determine current working directory: %v", err) } return wd } // MergeEnv merges args into env, overwriting entries. func MergeEnv(env []string, args map[string]string) []string { m := make(map[string]string) for _, e := range env { v := strings.SplitN(e, "=", 2) m[v[0]] = v[1] } for k, v := range args { m[k] = v } env = make([]string, 0, len(m)) for k, v := range m { env = append(env, fmt.Sprintf("%s=%s", k, v)) } return env } gb-0.3.2/cmd/env_test.go000066400000000000000000000015511262556506200150400ustar00rootroot00000000000000package cmd import "testing" func TestMergeEnv(t *testing.T) { envTests := []struct { env []string args map[string]string want []string }{ { env: nil, args: nil, want: nil, }, { env: []string{`FOO=BAR`, `BAZ="QUXX"`}, args: nil, want: []string{`FOO=BAR`, `BAZ="QUXX"`}, }, { env: []string{`FOO=BAR`, `BAZ="QUXX"`}, args: map[string]string{"BLORT": "false", "BAZ": "QUXX"}, want: []string{`FOO=BAR`, `BAZ=QUXX`, `BLORT=false`}, }, } for _, tt := range envTests { got := MergeEnv(tt.env, tt.args) compare(t, tt.want, got) } } func compare(t *testing.T, want, got []string) { w, g := set(want), set(got) for k := range w { if w[k] != g[k] { t.Errorf("want %v, got %v", k, g[k]) } } } func set(v []string) map[string]bool { m := make(map[string]bool) for _, s := range v { m[s] = true } return m } gb-0.3.2/cmd/gb-vendor/000077500000000000000000000000001262556506200145435ustar00rootroot00000000000000gb-0.3.2/cmd/gb-vendor/alldocs.go000066400000000000000000000062341262556506200165200ustar00rootroot00000000000000// DO NOT EDIT THIS FILE. //go:generate gb vendor help documentation /* gb-vendor, a gb plugin to manage your vendored dependencies. Usage: gb vendor command [arguments] The commands are: fetch fetch a remote dependency update update a local dependency list lists dependencies, one per line delete deletes a local dependency purge purges all unreferenced dependencies restore restore dependencies from the manifest Use "gb vendor help [command]" for more information about a command. Additional help topics: Use "gb vendor help [topic]" for more information about that topic. Fetch a remote dependency Usage: gb vendor fetch [-branch branch | -revision rev | -tag tag] [-precaire] [-no-recurse] importpath fetch vendors an upstream import path. The import path may include a url scheme. This may be useful when fetching dependencies from private repositories that cannot be probed. Flags: -branch branch fetch from the name branch. If not supplied the default upstream branch will be used. -no-recurse do not fetch recursively. -tag tag fetch the specified tag. If not supplied the default upstream branch will be used. -revision rev fetch the specific revision from the branch (if supplied). If no revision supplied, the latest available will be supplied. -precaire allow the use of insecure protocols. Update a local dependency Usage: gb vendor update [-all] import gb vendor update will replaces the source with the latest available from the head of the master branch. Updating from one copy of a dependency to another comes with several restrictions. The first is you can only update to the head of the branch your dependency was vendered from, switching branches is not supported. The second restriction is if you have used -tag or -revision while vendoring a dependency, your dependency is "headless" (to borrow a term from git) and cannot be updated. To update across branches, or from one tag/revision to another, you must first use gb vendor delete to remove the dependency, then gb vendor fetch [-tag | -revision | -branch ] [-precaire] to replace it. Flags: -all will update all dependencies in the manifest, otherwise only the dependency supplied. -precaire allow the use of insecure protocols. Lists dependencies, one per line Usage: gb vendor list [-f format] gb vendor list formats lists the contents of the manifest file. The output Flags: -f controls the template used for printing each manifest entry. If not supplied the default value is "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}" Deletes a local dependency Usage: gb vendor delete [-all] importpath delete removes a dependency from $PROJECT/vendor/src and the vendor manifest Flags: -all remove all dependencies Purges all unreferenced dependencies Usage: gb vendor purge gb vendor purge will remove all unreferenced dependencies Restore dependencies from the manifest Usage: gb vendor restore [-precaire] Restore vendor dependecies. Flags: -precaire allow the use of insecure protocols. */ package main gb-0.3.2/cmd/gb-vendor/delete.go000066400000000000000000000037151262556506200163420ustar00rootroot00000000000000package main import ( "flag" "fmt" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/fileutils" "github.com/constabulary/gb/vendor" ) var ( // gb vendor delete flags // delete all dependencies deleteAll bool ) func addDeleteFlags(fs *flag.FlagSet) { fs.BoolVar(&deleteAll, "all", false, "delete all dependencies") } var cmdDelete = &cmd.Command{ Name: "delete", UsageLine: "delete [-all] importpath", Short: "deletes a local dependency", Long: `delete removes a dependency from $PROJECT/vendor/src and the vendor manifest Flags: -all remove all dependencies `, Run: func(ctx *gb.Context, args []string) error { if len(args) != 1 && !deleteAll { return fmt.Errorf("delete: import path or --all flag is missing") } else if len(args) == 1 && deleteAll { return fmt.Errorf("delete: you cannot specify path and --all flag at once") } m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } var dependencies []vendor.Dependency if deleteAll { dependencies = make([]vendor.Dependency, len(m.Dependencies)) copy(dependencies, m.Dependencies) } else { p := args[0] dependency, err := m.GetDependencyForImportpath(p) if err != nil { return fmt.Errorf("could not get dependency: %v", err) } dependencies = append(dependencies, dependency) } for _, d := range dependencies { path := d.Importpath if err := m.RemoveDependency(d); err != nil { return fmt.Errorf("dependency could not be deleted: %v", err) } if err := fileutils.RemoveAll(filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(path))); err != nil { // TODO(dfc) need to apply vendor.cleanpath here to remove indermediate directories. return fmt.Errorf("dependency could not be deleted: %v", err) } } return vendor.WriteManifest(manifestFile(ctx), m) }, AddFlags: addDeleteFlags, } gb-0.3.2/cmd/gb-vendor/fetch.go000066400000000000000000000143471262556506200161740ustar00rootroot00000000000000package main import ( "flag" "fmt" "net/url" "path/filepath" "runtime" "sort" "go/build" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/vendor" ) var ( // gb vendor fetch command flags branch string // revision (commit) revision string tag string noRecurse bool // Container variable to house the value of the no-recurse flag. recurse bool // should we fetch recursively insecure bool // Allow the use of insecure protocols ) func addFetchFlags(fs *flag.FlagSet) { fs.StringVar(&branch, "branch", "", "branch of the package") fs.StringVar(&revision, "revision", "", "revision of the package") fs.StringVar(&tag, "tag", "", "tag of the package") fs.BoolVar(&noRecurse, "no-recurse", false, "do not fetch recursively") fs.BoolVar(&insecure, "precaire", false, "allow the use of insecure protocols") } var cmdFetch = &cmd.Command{ Name: "fetch", UsageLine: "fetch [-branch branch | -revision rev | -tag tag] [-precaire] [-no-recurse] importpath", Short: "fetch a remote dependency", Long: `fetch vendors an upstream import path. The import path may include a url scheme. This may be useful when fetching dependencies from private repositories that cannot be probed. Flags: -branch branch fetch from the name branch. If not supplied the default upstream branch will be used. -no-recurse do not fetch recursively. -tag tag fetch the specified tag. If not supplied the default upstream branch will be used. -revision rev fetch the specific revision from the branch (if supplied). If no revision supplied, the latest available will be supplied. -precaire allow the use of insecure protocols. `, Run: func(ctx *gb.Context, args []string) error { switch len(args) { case 0: return fmt.Errorf("fetch: import path missing") case 1: path := args[0] recurse = !noRecurse return fetch(ctx, path, recurse) default: return fmt.Errorf("more than one import path supplied") } }, AddFlags: addFetchFlags, } func fetch(ctx *gb.Context, path string, recurse bool) error { m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } repo, extra, err := vendor.DeduceRemoteRepo(path, insecure) if err != nil { return err } // strip of any scheme portion from the path, it is already // encoded in the repo. path = stripscheme(path) if m.HasImportpath(path) { return fmt.Errorf("%s is already vendored", path) } wc, err := repo.Checkout(branch, tag, revision) if err != nil { return err } rev, err := wc.Revision() if err != nil { return err } branch, err := wc.Branch() if err != nil { return err } dep := vendor.Dependency{ Importpath: path, Repository: repo.URL(), Revision: rev, Branch: branch, Path: extra, } if err := m.AddDependency(dep); err != nil { return err } dst := filepath.Join(ctx.Projectdir(), "vendor", "src", dep.Importpath) src := filepath.Join(wc.Dir(), dep.Path) if err := vendor.Copypath(dst, src); err != nil { return err } if err := vendor.WriteManifest(manifestFile(ctx), m); err != nil { return err } if err := wc.Destroy(); err != nil { return err } if !recurse { return nil } // if we are recursing, overwrite branch, tag and revision // values so recursive fetching checks out from HEAD. branch = "" tag = "" revision = "" for done := false; !done; { paths := []struct { Root, Prefix string }{ {filepath.Join(runtime.GOROOT(), "src"), ""}, {filepath.Join(ctx.Projectdir(), "src"), ""}, } m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return err } for _, d := range m.Dependencies { paths = append(paths, struct{ Root, Prefix string }{filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(d.Importpath)), filepath.FromSlash(d.Importpath)}) } dsm, err := vendor.LoadPaths(paths...) if err != nil { return err } is, ok := dsm[filepath.Join(ctx.Projectdir(), "vendor", "src", path)] if !ok { return fmt.Errorf("unable to locate depset for %q", path) } missing := findMissing(pkgs(is.Pkgs), dsm) switch len(missing) { case 0: done = true default: // sort keys in ascending order, so the shortest missing import path // with be fetched first. keys := keys(missing) sort.Strings(keys) pkg := keys[0] fmt.Println("fetching recursive dependency", pkg) if err := fetch(ctx, pkg, false); err != nil { return err } } } return nil } func keys(m map[string]bool) []string { var s []string for k := range m { s = append(s, k) } return s } func pkgs(m map[string]*vendor.Pkg) []*vendor.Pkg { var p []*vendor.Pkg for _, v := range m { p = append(p, v) } return p } func findMissing(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { missing := make(map[string]bool) imports := make(map[string]*vendor.Pkg) for _, s := range dsm { for _, p := range s.Pkgs { imports[p.ImportPath] = p } } // make fake C package for cgo imports["C"] = &vendor.Pkg{ Depset: nil, // probably a bad idea Package: &build.Package{ Name: "C", }, } stk := make(map[string]bool) push := func(v string) { if stk[v] { panic(fmt.Sprintln("import loop:", v, stk)) } stk[v] = true } pop := func(v string) { if !stk[v] { panic(fmt.Sprintln("impossible pop:", v, stk)) } delete(stk, v) } // checked records import paths who's dependencies are all present checked := make(map[string]bool) var fn func(string) fn = func(importpath string) { p, ok := imports[importpath] if !ok { missing[importpath] = true return } // have we already walked this arm, if so, skip it if checked[importpath] { return } sz := len(missing) push(importpath) for _, i := range p.Imports { if i == importpath { continue } fn(i) } // if the size of the missing map has not changed // this entire subtree is complete, mark it as such if len(missing) == sz { checked[importpath] = true } pop(importpath) } for _, pkg := range pkgs { fn(pkg.ImportPath) } return missing } // stripscheme removes any scheme components from url like paths. func stripscheme(path string) string { u, err := url.Parse(path) if err != nil { panic(err) } return u.Host + u.Path } gb-0.3.2/cmd/gb-vendor/help.go000066400000000000000000000046751262556506200160360ustar00rootroot00000000000000package main import ( "bufio" "bytes" "fmt" "io" "os" "strings" "text/template" "unicode" "unicode/utf8" "github.com/constabulary/gb/cmd" ) var helpTemplate = `{{if .Runnable}}usage: gb vendor {{.UsageLine}} {{end}}{{.Long | trim}} ` // help implements the 'help' command. func help(args []string) { if len(args) == 0 { printUsage(os.Stdout) return } if len(args) != 1 { fmt.Fprintf(os.Stderr, "usage: gb vendor help command\n\nToo many arguments given.\n") os.Exit(2) } arg := args[0] // 'gb vendor help documentation' generates alldocs.go. if arg == "documentation" { var buf bytes.Buffer printUsage(&buf) usage := &cmd.Command{Long: buf.String()} f, _ := os.Create("alldocs.go") tmpl(f, documentationTemplate, append([]*cmd.Command{usage}, commands...)) f.Close() return } for _, cmd := range commands { if cmd.Name == arg { tmpl(os.Stdout, helpTemplate, cmd) // not exit 2: succeeded at 'gb help cmd'. return } } fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'gb help'.\n", arg) os.Exit(2) // failed at 'gb help cmd' } var usageTemplate = `gb-vendor, a gb plugin to manage your vendored dependencies. Usage: gb vendor command [arguments] The commands are: {{range .}}{{if .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "gb vendor help [command]" for more information about a command. Additional help topics: {{range .}}{{if not .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "gb vendor help [topic]" for more information about that topic. ` var documentationTemplate = `// DO NOT EDIT THIS FILE. //go:generate gb vendor help documentation /* {{range .}}{{if .Short}}{{.Short | capitalize}} {{end}}{{if .Runnable}}Usage: gb vendor {{.UsageLine}} {{end}}{{.Long | trim}} {{end}}*/ package main ` // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) { t := template.New("top") t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) template.Must(t.Parse(text)) if err := t.Execute(w, data); err != nil { panic(err) } } func capitalize(s string) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToTitle(r)) + s[n:] } func printUsage(w io.Writer) { bw := bufio.NewWriter(w) tmpl(bw, usageTemplate, commands) bw.Flush() } func usage() { printUsage(os.Stderr) os.Exit(2) } gb-0.3.2/cmd/gb-vendor/list.go000066400000000000000000000025001262556506200160420ustar00rootroot00000000000000package main import ( "flag" "fmt" "html/template" "os" "text/tabwriter" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/vendor" ) var format string var cmdList = &cmd.Command{ Name: "list", UsageLine: "list [-f format]", Short: "lists dependencies, one per line", Long: `gb vendor list formats lists the contents of the manifest file. The output Flags: -f controls the template used for printing each manifest entry. If not supplied the default value is "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}" `, Run: func(ctx *gb.Context, args []string) error { m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } tmpl, err := template.New("list").Parse(format) if err != nil { return fmt.Errorf("unable to parse template %q: %v", format, err) } w := tabwriter.NewWriter(os.Stdout, 1, 2, 1, ' ', 0) for _, dep := range m.Dependencies { if err := tmpl.Execute(w, dep); err != nil { return fmt.Errorf("unable to execute template: %v", err) } fmt.Fprintln(w) } return w.Flush() }, AddFlags: func(fs *flag.FlagSet) { fs.StringVar(&format, "f", "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}", "format template") }, } gb-0.3.2/cmd/gb-vendor/main.go000066400000000000000000000041321262556506200160160ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/debug" ) var ( fs = flag.NewFlagSet(os.Args[0], flag.ExitOnError) projectroot = os.Getenv("GB_PROJECT_DIR") args []string ) func init() { fs.Usage = usage } var commands = []*cmd.Command{ cmdFetch, cmdUpdate, cmdList, cmdDelete, cmdPurge, cmdRestore, } func main() { fatalf := func(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...) os.Exit(1) } args := os.Args[1:] switch { case len(args) < 1, args[0] == "-h", args[0] == "-help": printUsage(os.Stdout) os.Exit(0) case args[0] == "help": help(args[1:]) return case projectroot == "": fatalf("don't run this binary directly, it is meant to be run as 'gb vendor ...'") default: } root, err := cmd.FindProjectroot(projectroot) if err != nil { fatalf("could not locate project root: %v", err) } project := gb.NewProject(root, gb.SourceDir(filepath.Join(root, "src")), gb.SourceDir(filepath.Join(root, "vendor", "src")), ) debug.Debugf("project root %q", project.Projectdir()) for _, command := range commands { if command.Name == args[0] && command.Runnable() { // add extra flags if necessary if command.AddFlags != nil { command.AddFlags(fs) } if command.FlagParse != nil { err = command.FlagParse(fs, args) } else { err = fs.Parse(args[1:]) } if err != nil { fatalf("could not parse flags: %v", err) } args = fs.Args() // reset args to the leftovers from fs.Parse debug.Debugf("args: %v", args) ctx, err := project.NewContext( gb.GcToolchain(), ) if err != nil { fatalf("unable to construct context: %v", err) } defer ctx.Destroy() if err := command.Run(ctx, args); err != nil { fatalf("command %q failed: %v", command.Name, err) } return } } fatalf("unknown command %q ", args[0]) } const manifestfile = "manifest" func manifestFile(ctx *gb.Context) string { return filepath.Join(ctx.Projectdir(), "vendor", manifestfile) } gb-0.3.2/cmd/gb-vendor/purge.go000066400000000000000000000032401262556506200162130ustar00rootroot00000000000000package main import ( "fmt" "path/filepath" "strings" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/fileutils" "github.com/constabulary/gb/vendor" ) var cmdPurge = &cmd.Command{ Name: "purge", UsageLine: "purge", Short: "purges all unreferenced dependencies", Long: `gb vendor purge will remove all unreferenced dependencies `, Run: func(ctx *gb.Context, args []string) error { m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } imports, err := vendor.ParseImports(ctx.Projectdir()) if err != nil { return fmt.Errorf("import could not be parsed: %v", err) } var hasImportWithPrefix = func(d string) bool { for i := range imports { if strings.HasPrefix(i, d) { return true } } return false } dependencies := make([]vendor.Dependency, len(m.Dependencies)) copy(dependencies, m.Dependencies) for _, d := range dependencies { if !hasImportWithPrefix(d.Importpath) { dep, err := m.GetDependencyForImportpath(d.Importpath) if err != nil { return fmt.Errorf("could not get get dependency: %v", err) } if err := m.RemoveDependency(dep); err != nil { return fmt.Errorf("dependency could not be removed: %v", err) } if err := fileutils.RemoveAll(filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(d.Importpath))); err != nil { // TODO(dfc) need to apply vendor.cleanpath here to remove indermediate directories. return fmt.Errorf("dependency could not be deleted: %v", err) } } } return vendor.WriteManifest(manifestFile(ctx), m) }, } gb-0.3.2/cmd/gb-vendor/restore.go000066400000000000000000000026301262556506200165560ustar00rootroot00000000000000package main import ( "flag" "fmt" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/vendor" ) func addRestoreFlags(fs *flag.FlagSet) { fs.BoolVar(&insecure, "precaire", false, "allow the use of insecure protocols") } var cmdRestore = &cmd.Command{ Name: "restore", UsageLine: "restore [-precaire]", Short: "restore dependencies from the manifest", Long: `Restore vendor dependecies. Flags: -precaire allow the use of insecure protocols. `, Run: func(ctx *gb.Context, args []string) error { return restore(ctx) }, AddFlags: addRestoreFlags, } func restore(ctx *gb.Context) error { m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } for _, dep := range m.Dependencies { fmt.Printf("Getting %s\n", dep.Importpath) repo, _, err := vendor.DeduceRemoteRepo(dep.Importpath, insecure) if err != nil { return fmt.Errorf("Could not process dependency: %s", err) } wc, err := repo.Checkout("", "", dep.Revision) if err != nil { return fmt.Errorf("Could not retrieve dependency: %s", err) } dst := filepath.Join(ctx.Projectdir(), "vendor", "src", dep.Importpath) src := filepath.Join(wc.Dir(), dep.Path) if err := vendor.Copypath(dst, src); err != nil { return err } if err := wc.Destroy(); err != nil { return err } } return nil } gb-0.3.2/cmd/gb-vendor/update.go000066400000000000000000000072651262556506200163660ustar00rootroot00000000000000package main import ( "flag" "fmt" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/fileutils" "github.com/constabulary/gb/vendor" ) var ( // gb vendor update flags // update all dependencies updateAll bool ) func addUpdateFlags(fs *flag.FlagSet) { fs.BoolVar(&updateAll, "all", false, "update all dependencies") fs.BoolVar(&insecure, "precaire", false, "allow the use of insecure protocols") } var cmdUpdate = &cmd.Command{ Name: "update", UsageLine: "update [-all] import", Short: "update a local dependency", Long: `gb vendor update will replaces the source with the latest available from the head of the master branch. Updating from one copy of a dependency to another comes with several restrictions. The first is you can only update to the head of the branch your dependency was vendered from, switching branches is not supported. The second restriction is if you have used -tag or -revision while vendoring a dependency, your dependency is "headless" (to borrow a term from git) and cannot be updated. To update across branches, or from one tag/revision to another, you must first use gb vendor delete to remove the dependency, then gb vendor fetch [-tag | -revision | -branch ] [-precaire] to replace it. Flags: -all will update all dependencies in the manifest, otherwise only the dependency supplied. -precaire allow the use of insecure protocols. `, Run: func(ctx *gb.Context, args []string) error { if len(args) != 1 && !updateAll { return fmt.Errorf("update: import path or --all flag is missing") } else if len(args) == 1 && updateAll { return fmt.Errorf("update: you cannot specify path and --all flag at once") } m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } var dependencies []vendor.Dependency if updateAll { dependencies = make([]vendor.Dependency, len(m.Dependencies)) copy(dependencies, m.Dependencies) } else { p := args[0] dependency, err := m.GetDependencyForImportpath(p) if err != nil { return fmt.Errorf("could not get dependency: %v", err) } dependencies = append(dependencies, dependency) } for _, d := range dependencies { err = m.RemoveDependency(d) if err != nil { return fmt.Errorf("dependency could not be deleted from manifest: %v", err) } repo, extra, err := vendor.DeduceRemoteRepo(d.Importpath, insecure) if err != nil { return fmt.Errorf("could not determine repository for import %q", d.Importpath) } wc, err := repo.Checkout(d.Branch, "", "") if err != nil { return err } rev, err := wc.Revision() if err != nil { return err } branch, err := wc.Branch() if err != nil { return err } dep := vendor.Dependency{ Importpath: d.Importpath, Repository: repo.URL(), Revision: rev, Branch: branch, Path: extra, } if err := fileutils.RemoveAll(filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(d.Importpath))); err != nil { // TODO(dfc) need to apply vendor.cleanpath here to remove indermediate directories. return fmt.Errorf("dependency could not be deleted: %v", err) } dst := filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(dep.Importpath)) src := filepath.Join(wc.Dir(), dep.Path) if err := vendor.Copypath(dst, src); err != nil { return err } if err := m.AddDependency(dep); err != nil { return err } if err := vendor.WriteManifest(manifestFile(ctx), m); err != nil { return err } if err := wc.Destroy(); err != nil { return err } } return nil }, AddFlags: addUpdateFlags, } gb-0.3.2/cmd/gb/000077500000000000000000000000001262556506200132505ustar00rootroot00000000000000gb-0.3.2/cmd/gb/alldocs.go000066400000000000000000000120231262556506200152160ustar00rootroot00000000000000// DO NOT EDIT THIS FILE. //go:generate gb help documentation /* gb, a project based build tool for the Go programming language. Usage: gb command [arguments] The commands are: build build a package doc show documentation for a package or symbol env print project environment variables generate generate Go files by processing source info info returns information about this project list list the packages named by the importpaths test test packages Use "gb help [command]" for more information about a command. Additional help topics: plugin plugin information project gb project layout Use "gb help [topic]" for more information about that topic. Build a package Usage: gb build [build flags] [packages] Build compiles the packages named by the import paths, along with their dependencies. Flags: -f ignore cached packages if present, new packages built will overwrite any cached packages. This effectively disables incremental compilation. -F do not cache packages, cached packages will still be used for incremental compilation. -f -F is advised to disable the package caching system. -q decreases verbosity, effectively raising the output level to ERROR. In a successful build, no output will be displayed. -P The number of build jobs to run in parallel, including test execution. By default this is the number of CPUs visible to gb. -R sets the base of the project root search path from the current working directory to the value supplied. Effectively gb changes working directory to this path before searching for the project root. -dotfile if provided, gb will output a dot formatted file of the build steps to be performed. -ldflags 'flag list' arguments to pass on each linker invocation. -gcflags 'arg list' arguments to pass on each compile invocation. -tags 'tag list' additional build tags. The list flags accept a space-separated list of strings. To embed spaces in an element in the list, surround it with either single or double quotes. For more about where packages and binaries are installed, run 'gb help project'. Show documentation for a package or symbol Usage: gb doc [.] Doc shows documentation for a package or symbol. See 'go help doc'. Print project environment variables Usage: gb env Env prints project environment variables. Generate Go files by processing source Usage: gb generate [-run regexp] [file.go... | packages] Generate runs commands described by directives within existing files. Those commands can run any process, but the intent is to create or update Go source files, for instance by running yacc. See 'go help generate'. Info returns information about this project Usage: gb info info returns information about this project. info returns 0 if the project is well formed, and non zero otherwise. List the packages named by the importpaths Usage: gb list [-s] [-f format] [-json] [packages] List lists packages imported by the project. The default output shows the package import paths: % gb list github.com/constabulary/... github.com/constabulary/gb github.com/constabulary/gb/cmd github.com/constabulary/gb/cmd/gb github.com/constabulary/gb/cmd/gb-env github.com/constabulary/gb/cmd/gb-list Flags: -f alternate format for the list, using the syntax of package template. The default output is equivalent to -f '{{.ImportPath}}'. The struct being passed to the template is currently an instance of gb.Package. This structure is under active development and it's contents are not guaranteed to be stable. -s read format template from STDIN. -json prints output in structured JSON format. WARNING: gb.Package structure is not stable and will change in the future! Plugin information gb supports git style plugins. A gb plugin is anything in the $PATH with the prefix gb-. In other words gb-something, becomes gb something. gb plugins are executed from the parent gb process with the environment variable, GB_PROJECT_DIR set to the root of the current project. gb plugins can be executed directly but this is rarely useful, so authors should attempt to diagnose this by looking for the presence of the GB_PROJECT_DIR environment key. Gb project layout A gb project is defined as any directory that contains a src/ subdirectory. gb automatically detects the root of the project by looking at the current working directory and walking backwards until it finds a directory that contains a src/ subdirectory. In the event you wish to override this auto detection mechanism, the -R flag can be used to supply a project root. See http://getgb.io/docs/project for details Test packages Usage: gb test [build flags] [packages] [flags for test binary] Test automates testing the packages named by the import paths. 'gb test' recompiles each package along with any files with names matching the file pattern "*_test.go". See 'go help test'. */ package main gb-0.3.2/cmd/gb/build.go000066400000000000000000000064471262556506200147110ustar00rootroot00000000000000package main import ( "flag" "os" "runtime" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(BuildCmd) } var ( // build flags // should we build all packages in this project. // defaults to true when build is invoked from the project root. A bool // should we perform a release build +release tag ? // defaults to false, +debug. R bool // force rebuild of packages F bool // skip caching of packages FF bool ldflags, gcflags []string P int // number of executors to run in parallel dotfile string // path to dot output file buildtags []string ) func addBuildFlags(fs *flag.FlagSet) { // TODO(dfc) this should accept a *gb.Context fs.BoolVar(&A, "a", false, "build all packages in this project") fs.BoolVar(&R, "r", false, "perform a release build") fs.BoolVar(&F, "f", false, "rebuild up-to-date packages") fs.BoolVar(&FF, "F", false, "do not cache built packages") fs.IntVar(&P, "P", runtime.NumCPU(), "number of parallel jobs") fs.Var((*stringsFlag)(&ldflags), "ldflags", "flags passed to the linker") fs.Var((*stringsFlag)(&gcflags), "gcflags", "flags passed to the compiler") fs.StringVar(&dotfile, "dotfile", "", "path to dot output file") fs.Var((*stringsFlag)(&buildtags), "tags", "") } var BuildCmd = &cmd.Command{ Name: "build", Short: "build a package", UsageLine: "build [build flags] [packages]", Long: ` Build compiles the packages named by the import paths, along with their dependencies. Flags: -f ignore cached packages if present, new packages built will overwrite any cached packages. This effectively disables incremental compilation. -F do not cache packages, cached packages will still be used for incremental compilation. -f -F is advised to disable the package caching system. -q decreases verbosity, effectively raising the output level to ERROR. In a successful build, no output will be displayed. -P The number of build jobs to run in parallel, including test execution. By default this is the number of CPUs visible to gb. -R sets the base of the project root search path from the current working directory to the value supplied. Effectively gb changes working directory to this path before searching for the project root. -dotfile if provided, gb will output a dot formatted file of the build steps to be performed. -ldflags 'flag list' arguments to pass on each linker invocation. -gcflags 'arg list' arguments to pass on each compile invocation. -tags 'tag list' additional build tags. The list flags accept a space-separated list of strings. To embed spaces in an element in the list, surround it with either single or double quotes. For more about where packages and binaries are installed, run 'gb help project'. `, Run: func(ctx *gb.Context, args []string) error { // TODO(dfc) run should take a *gb.Context not a *gb.Project ctx.Force = F ctx.SkipInstall = FF pkgs, err := gb.ResolvePackages(ctx, args...) if err != nil { return err } build, err := gb.BuildPackages(pkgs...) if err != nil { return err } if dotfile != "" { f, err := os.Create(dotfile) if err != nil { return err } defer f.Close() printActions(f, build) } startSigHandlers() return gb.ExecuteConcurrent(build, P, interrupted) }, AddFlags: addBuildFlags, } gb-0.3.2/cmd/gb/depset.go000066400000000000000000000106421262556506200150660ustar00rootroot00000000000000package main import ( "fmt" "go/build" "path/filepath" "runtime" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/vendor" ) func init() { registerCommand(&cmd.Command{ Name: "depset", Run: depset, }) } func depset(ctx *gb.Context, args []string) error { paths := []struct { Root, Prefix string }{ {filepath.Join(runtime.GOROOT(), "src"), ""}, {filepath.Join(ctx.Projectdir(), "src"), ""}, } m, err := vendor.ReadManifest(filepath.Join("vendor", "manifest")) if err != nil { return err } for _, d := range m.Dependencies { paths = append(paths, struct{ Root, Prefix string }{filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(d.Importpath)), filepath.FromSlash(d.Importpath)}) } dsm, err := vendor.LoadPaths(paths...) if err != nil { return err } for _, set := range dsm { fmt.Printf("%s (%s)\n", set.Root, set.Prefix) for _, p := range set.Pkgs { fmt.Printf("\t%s (%s)\n", p.ImportPath, p.Name) fmt.Printf("\t\timports: %s\n", p.Imports) } } root := paths[1] // $PROJECT/src rs := dsm[root.Root].Pkgs fmt.Println("missing:") for missing := range findMissing(pkgs(rs), dsm) { fmt.Printf("\t%s\n", missing) } fmt.Println("orphaned:") for orphan := range findOrphaned(pkgs(rs), dsm) { fmt.Printf("\t%s\n", orphan) } return nil } func keys(m map[string]bool) []string { var s []string for k := range m { s = append(s, k) } return s } func pkgs(m map[string]*vendor.Pkg) []*vendor.Pkg { var p []*vendor.Pkg for _, v := range m { p = append(p, v) } return p } func findMissing(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { missing := make(map[string]bool) imports := make(map[string]*vendor.Pkg) for _, s := range dsm { for _, p := range s.Pkgs { imports[p.ImportPath] = p } } // make fake C package for cgo imports["C"] = &vendor.Pkg{ Depset: nil, // probably a bad idea Package: &build.Package{ Name: "C", }, } stk := make(map[string]bool) push := func(v string) { if stk[v] { panic(fmt.Sprintln("import loop:", v, stk)) } stk[v] = true } pop := func(v string) { if !stk[v] { panic(fmt.Sprintln("impossible pop:", v, stk)) } delete(stk, v) } // checked records import paths who's dependencies are all present checked := make(map[string]bool) var fn func(string) fn = func(importpath string) { p, ok := imports[importpath] if !ok { missing[importpath] = true return } // have we already walked this arm, if so, skip it if checked[importpath] { return } sz := len(missing) push(importpath) for _, i := range p.Imports { if i == importpath { continue } fn(i) } // if the size of the missing map has not changed // this entire subtree is complete, mark it as such if len(missing) == sz { checked[importpath] = true } pop(importpath) } for _, pkg := range pkgs { fn(pkg.ImportPath) } return missing } func findOrphaned(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { missing := make(map[string]bool) imports := make(map[string]*vendor.Pkg) for _, s := range dsm { for _, p := range s.Pkgs { imports[p.ImportPath] = p } } orphans := make(map[string]bool) for k := range dsm { orphans[k] = true } // make fake C package for cgo imports["C"] = &vendor.Pkg{ Depset: new(vendor.Depset), Package: &build.Package{ Name: "C", }, } stk := make(map[string]bool) push := func(v string) { if stk[v] { panic(fmt.Sprintln("import loop:", v, stk)) } stk[v] = true } pop := func(v string) { if !stk[v] { panic(fmt.Sprintln("impossible pop:", v, stk)) } delete(stk, v) } // checked records import paths who's dependencies are all present checked := make(map[string]bool) var fn func(string) fn = func(importpath string) { p, ok := imports[importpath] if !ok { missing[importpath] = true return } // delete the pkg's depset, as it is referenced delete(orphans, p.Depset.Root) // have we already walked this arm, if so, skip it if checked[importpath] { return } sz := len(missing) push(importpath) for _, i := range p.Imports { if i == importpath { continue } fn(i) } // if the size of the missing map has not changed // this entire subtree is complete, mark it as such if len(missing) == sz { checked[importpath] = true } pop(importpath) } for _, pkg := range pkgs { fn(pkg.ImportPath) } return orphans } gb-0.3.2/cmd/gb/doc.go000066400000000000000000000017441262556506200143520ustar00rootroot00000000000000package main import ( "fmt" "os" "os/exec" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(&cmd.Command{ Name: "doc", UsageLine: `doc [.]`, Short: "show documentation for a package or symbol", Long: ` Doc shows documentation for a package or symbol. See 'go help doc'. `, Run: func(ctx *gb.Context, args []string) error { env := cmd.MergeEnv(os.Environ(), map[string]string{ "GOPATH": fmt.Sprintf("%s:%s", ctx.Projectdir(), filepath.Join(ctx.Projectdir(), "vendor")), }) if len(args) == 0 { args = append(args, ".") } args = append([]string{filepath.Join(ctx.Context.GOROOT, "bin", "godoc")}, args...) cmd := exec.Cmd{ Path: args[0], Args: args, Env: env, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } return cmd.Run() }, ParseArgs: func(_ *gb.Context, _ string, args []string) []string { return args }, }) } gb-0.3.2/cmd/gb/dot.go000066400000000000000000000011201262556506200143570ustar00rootroot00000000000000package main import ( "fmt" "io" "strings" "github.com/constabulary/gb" ) func printActions(w io.Writer, a *gb.Action) { fmt.Fprintf(w, "digraph %q {\n", a.Name) seen := make(map[*gb.Action]bool) print0(w, seen, a) fmt.Fprintf(w, "}\n") } func print0(w io.Writer, seen map[*gb.Action]bool, a *gb.Action) { if seen[a] { return } split := func(s string) string { return strings.Replace(strings.Replace(s, ": ", "\n", -1), ",", "\n", -1) } for _, d := range a.Deps { print0(w, seen, d) fmt.Fprintf(w, "%q -> %q;\n", split(a.Name), split(d.Name)) } seen[a] = true } gb-0.3.2/cmd/gb/env.go000066400000000000000000000013631262556506200143720ustar00rootroot00000000000000package main import ( "fmt" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(EnvCmd) } var EnvCmd = &cmd.Command{ Name: "env", UsageLine: `env`, Short: "print project environment variables", Long: ` Env prints project environment variables. `, Run: env, } func env(ctx *gb.Context, args []string) error { env := makeenv(ctx) for _, e := range env { fmt.Printf("%s=%q\n", e.name, e.val) } return nil } type envvar struct { name, val string } func findenv(env []envvar, name string) string { for _, e := range env { if e.name == name { return e.val } } return "" } func makeenv(ctx *gb.Context) []envvar { return []envvar{ {"GB_PROJECT_DIR", ctx.Projectdir()}, } } gb-0.3.2/cmd/gb/flag.go000066400000000000000000000023141262556506200145100ustar00rootroot00000000000000// Copyright 2011 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 "fmt" type stringsFlag []string func (v *stringsFlag) Set(s string) error { var err error *v, err = splitQuotedFields(s) if *v == nil { *v = []string{} } return err } func splitQuotedFields(s string) ([]string, error) { // Split fields allowing '' or "" around elements. // Quotes further inside the string do not count. var f []string for len(s) > 0 { for len(s) > 0 && isSpaceByte(s[0]) { s = s[1:] } if len(s) == 0 { break } // Accepted quoted string. No unescaping inside. if s[0] == '"' || s[0] == '\'' { quote := s[0] s = s[1:] i := 0 for i < len(s) && s[i] != quote { i++ } if i >= len(s) { return nil, fmt.Errorf("unterminated %c string", quote) } f = append(f, s[:i]) s = s[i+1:] continue } i := 0 for i < len(s) && !isSpaceByte(s[i]) { i++ } f = append(f, s[:i]) s = s[i:] } return f, nil } func (v *stringsFlag) String() string { return "" } func isSpaceByte(c byte) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } gb-0.3.2/cmd/gb/generate.go000066400000000000000000000020261262556506200153710ustar00rootroot00000000000000package main import ( "fmt" "os" "os/exec" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(GenerateCmd) } var GenerateCmd = &cmd.Command{ Name: "generate", UsageLine: "generate [-run regexp] [file.go... | packages]", Short: "generate Go files by processing source", Long: ` Generate runs commands described by directives within existing files. Those commands can run any process, but the intent is to create or update Go source files, for instance by running yacc. See 'go help generate'. `, Run: func(ctx *gb.Context, args []string) error { env := cmd.MergeEnv(os.Environ(), map[string]string{ "GOPATH": fmt.Sprintf("%s:%s", ctx.Projectdir(), filepath.Join(ctx.Projectdir(), "vendor")), }) args = append([]string{filepath.Join(ctx.Context.GOROOT, "bin", "go"), "generate"}, args...) cmd := exec.Cmd{ Path: args[0], Args: args, Env: env, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } return cmd.Run() }, } gb-0.3.2/cmd/gb/help.go000066400000000000000000000066401262556506200145350ustar00rootroot00000000000000package main import ( "bufio" "bytes" "fmt" "io" "os" "sort" "strings" "text/template" "unicode" "unicode/utf8" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(helpProject) } var helpProject = &cmd.Command{ Name: "project", Short: "gb project layout", Long: `A gb project is defined as any directory that contains a src/ subdirectory. gb automatically detects the root of the project by looking at the current working directory and walking backwards until it finds a directory that contains a src/ subdirectory. In the event you wish to override this auto detection mechanism, the -R flag can be used to supply a project root. See http://getgb.io/docs/project for details`, } var helpTemplate = `{{if .Runnable}}usage: gb {{.UsageLine}} {{end}}{{.Long | trim}} ` // help implements the 'help' command. func help(args []string) { if len(args) == 0 { printUsage(os.Stdout) // not exit 2: succeeded at 'gb help'. return } if len(args) != 1 { fmt.Fprintf(os.Stderr, "usage: gb help command\n\nToo many arguments given.\n") os.Exit(2) // failed at 'gb help' } arg := args[0] // 'gb help documentation' generates alldocs.go. if arg == "documentation" { var buf bytes.Buffer printUsage(&buf) usage := &cmd.Command{Long: buf.String()} f, _ := os.Create("alldocs.go") tmpl(f, documentationTemplate, append([]*cmd.Command{usage}, sortedCommands()...)) f.Close() return } for _, cmd := range commands { if cmd.Name == arg { tmpl(os.Stdout, helpTemplate, cmd) // not exit 2: succeeded at 'gb help cmd'. return } } fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'gb help'.\n", arg) os.Exit(2) // failed at 'gb help cmd' } var usageTemplate = `gb, a project based build tool for the Go programming language. Usage: gb command [arguments] The commands are: {{range .}}{{if .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "gb help [command]" for more information about a command. Additional help topics: {{range .}}{{if not .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "gb help [topic]" for more information about that topic. ` var documentationTemplate = `// DO NOT EDIT THIS FILE. //go:generate gb help documentation /* {{range .}}{{if .Short}}{{.Short | capitalize}} {{end}}{{if .Runnable}}Usage: gb {{.UsageLine}} {{end}}{{.Long | trim}} {{end}}*/ package main ` // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) { t := template.New("top") t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) template.Must(t.Parse(text)) if err := t.Execute(w, data); err != nil { panic(err) } } func capitalize(s string) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToTitle(r)) + s[n:] } func printUsage(w io.Writer) { bw := bufio.NewWriter(w) tmpl(bw, usageTemplate, sortedCommands()) bw.Flush() } func sortedCommands() []*cmd.Command { // TODO(dfc) drop this and make main.commands a []*cmd.Command var sortedKeys []string for k := range commands { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) var cmds []*cmd.Command for _, c := range sortedKeys { // skip hidden commands if commands[c].Hidden() { continue } cmds = append(cmds, commands[c]) } return cmds } func usage() { printUsage(os.Stderr) os.Exit(2) } gb-0.3.2/cmd/gb/info.go000066400000000000000000000017061262556506200145360ustar00rootroot00000000000000package main import ( "fmt" "path/filepath" "strings" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(&cmd.Command{ Name: "info", UsageLine: `info`, Short: "info returns information about this project", Long: `info returns information about this project. info returns 0 if the project is well formed, and non zero otherwise. `, Run: info, AddFlags: addBuildFlags, }) } func info(ctx *gb.Context, args []string) error { fmt.Printf("GB_PROJECT_DIR=%q\n", ctx.Projectdir()) fmt.Printf("GB_SRC_PATH=%q\n", joinlist(ctx.Srcdirs()...)) fmt.Printf("GB_PKG_DIR=%q\n", ctx.Pkgdir()) fmt.Printf("GB_BIN_SUFFIX=%q\n", ctx.Suffix()) return nil } // joinlist joins path elements using the os specific separator. // TODO(dfc) it probably gets this wrong on windows in some circumstances. func joinlist(paths ...string) string { return strings.Join(paths, string(filepath.ListSeparator)) } gb-0.3.2/cmd/gb/list.go000066400000000000000000000063151262556506200145570ustar00rootroot00000000000000package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "os" "strings" "text/template" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) var ( projectroot string format string formatStdin bool jsonOutput bool ) func init() { registerCommand(&cmd.Command{ Name: "list", UsageLine: `list [-s] [-f format] [-json] [packages]`, Short: "list the packages named by the importpaths", Long: ` List lists packages imported by the project. The default output shows the package import paths: % gb list github.com/constabulary/... github.com/constabulary/gb github.com/constabulary/gb/cmd github.com/constabulary/gb/cmd/gb github.com/constabulary/gb/cmd/gb-env github.com/constabulary/gb/cmd/gb-list Flags: -f alternate format for the list, using the syntax of package template. The default output is equivalent to -f '{{.ImportPath}}'. The struct being passed to the template is currently an instance of gb.Package. This structure is under active development and it's contents are not guaranteed to be stable. -s read format template from STDIN. -json prints output in structured JSON format. WARNING: gb.Package structure is not stable and will change in the future! `, Run: list, AddFlags: func(fs *flag.FlagSet) { fs.StringVar(&format, "f", "{{.ImportPath}}", "format template") fs.BoolVar(&formatStdin, "s", false, "read format from stdin") fs.BoolVar(&jsonOutput, "json", false, "outputs json. WARNING: gb.Package structure is not stable and will change in future") }, }) } func list(ctx *gb.Context, args []string) error { if formatStdin { var formatBuffer bytes.Buffer io.Copy(&formatBuffer, os.Stdin) format = formatBuffer.String() } pkgs, err := gb.ResolvePackages(ctx, args...) if err != nil { log.Fatalf("unable to resolve: %v", err) } if jsonOutput { views := make([]*PackageView, 0, len(pkgs)) for _, pkg := range pkgs { views = append(views, NewPackageView(pkg)) } encoder := json.NewEncoder(os.Stdout) if err := encoder.Encode(views); err != nil { return fmt.Errorf("Error occurred during json encoding: %v", err) } } else { fm := template.FuncMap{ "join": strings.Join, } tmpl, err := template.New("list").Funcs(fm).Parse(format) if err != nil { return fmt.Errorf("unable to parse template %q: %v", format, err) } for _, pkg := range pkgs { if err := tmpl.Execute(os.Stdout, pkg); err != nil { return fmt.Errorf("unable to execute template: %v", err) } fmt.Fprintln(os.Stdout) } } return nil } // PackageView represents a package shown by list command in JSON format. // It is not stable and may be subject to change. type PackageView struct { Dir string ImportPath string Name string Root string GoFiles []string Imports []string TestGoFiles []string TestImports []string } // NewPackageView creates a *PackageView from gb Package. func NewPackageView(pkg *gb.Package) *PackageView { return &PackageView{ Dir: pkg.Dir, ImportPath: pkg.ImportPath, Name: pkg.Name, Root: pkg.Root, GoFiles: pkg.GoFiles, Imports: pkg.Package.Imports, TestGoFiles: pkg.TestGoFiles, TestImports: pkg.TestImports, } } gb-0.3.2/cmd/gb/main.go000066400000000000000000000056661262556506200145400ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "os/exec" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/debug" ) var ( fs = flag.NewFlagSet(os.Args[0], flag.ExitOnError) cwd string args []string ) const ( // enable to keep working directory noDestroyContext = false ) func init() { fs.StringVar(&cwd, "R", cmd.MustGetwd(), "set the project root") // actually the working directory to start the project root search fs.Usage = usage } var commands = make(map[string]*cmd.Command) // registerCommand registers a command for main. // registerCommand should only be called from init(). func registerCommand(command *cmd.Command) { commands[command.Name] = command } func fatalf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...) os.Exit(1) } func main() { args := os.Args if len(args) < 2 || args[1] == "-h" { fs.Usage() os.Exit(1) } name := args[1] if name == "help" { help(args[2:]) return } command, ok := commands[name] if (command != nil && !command.Runnable()) || !ok { plugin, err := lookupPlugin(name) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: unknown command %q\n", name) fs.Usage() os.Exit(1) } command = &cmd.Command{ Run: func(ctx *gb.Context, args []string) error { args = append([]string{plugin}, args...) env := cmd.MergeEnv(os.Environ(), map[string]string{ "GB_PROJECT_DIR": ctx.Projectdir(), }) cmd := exec.Cmd{ Path: plugin, Args: args, Env: env, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } return cmd.Run() }, // plugin should not interpret arguments ParseArgs: func(_ *gb.Context, _ string, args []string) []string { return args }, } } // add extra flags if necessary if command.AddFlags != nil { command.AddFlags(fs) } var err error if command.FlagParse != nil { err = command.FlagParse(fs, args) } else { err = fs.Parse(args[2:]) } if err != nil { fatalf("could not parse flags: %v", err) } args = fs.Args() // reset args to the leftovers from fs.Parse if command == commands["plugin"] { args = append([]string{name}, args...) } cwd, err := filepath.Abs(cwd) // if cwd was passed in via -R, make sure it is absolute if err != nil { fatalf("could not make project root absolute: %v", err) } ctx, err := cmd.NewContext( cwd, // project root gb.GcToolchain(), gb.Gcflags(gcflags...), gb.Ldflags(ldflags...), gb.Tags(buildtags...), ) if err != nil { fatalf("unable to construct context: %v", err) } if !noDestroyContext { defer ctx.Destroy() } if command.ParseArgs != nil { args = command.ParseArgs(ctx, ctx.Projectdir(), args) } else { args = cmd.ImportPaths(ctx, cwd, args) } debug.Debugf("args: %v", args) if err := command.Run(ctx, args); err != nil { if !noDestroyContext { defer ctx.Destroy() } fatalf("command %q failed: %v", name, err) } return } gb-0.3.2/cmd/gb/plugin.go000066400000000000000000000015711262556506200151010ustar00rootroot00000000000000package main import ( "fmt" "os/exec" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(PluginCmd) } var PluginCmd = &cmd.Command{ Name: "plugin", Short: "plugin information", Long: `gb supports git style plugins. A gb plugin is anything in the $PATH with the prefix gb-. In other words gb-something, becomes gb something. gb plugins are executed from the parent gb process with the environment variable, GB_PROJECT_DIR set to the root of the current project. gb plugins can be executed directly but this is rarely useful, so authors should attempt to diagnose this by looking for the presence of the GB_PROJECT_DIR environment key. `, } func lookupPlugin(arg string) (string, error) { plugin := "gb-" + arg path, err := exec.LookPath(plugin) if err != nil { return "", fmt.Errorf("plugin: unable to locate %q: %v", plugin, err) } return path, nil } gb-0.3.2/cmd/gb/signal.go000066400000000000000000000011741262556506200150570ustar00rootroot00000000000000// Copyright 2012 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 ( "os" "os/signal" "sync" ) // interrupted is closed, if go process is interrupted. var interrupted = make(chan struct{}) // processSignals setups signal handler. func processSignals() { sig := make(chan os.Signal) signal.Notify(sig, signalsToIgnore...) go func() { <-sig close(interrupted) }() } var onceProcessSignals sync.Once // startSigHandlers start signal handlers. func startSigHandlers() { onceProcessSignals.Do(processSignals) } gb-0.3.2/cmd/gb/signal_notunix.go000066400000000000000000000006011262556506200166350ustar00rootroot00000000000000// Copyright 2012 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. // +build plan9 windows package main import ( "os" ) var signalsToIgnore = []os.Signal{os.Interrupt} // signalTrace is the signal to send to make a Go program // crash with a stack trace. var signalTrace os.Signal = nil gb-0.3.2/cmd/gb/signal_unix.go000066400000000000000000000007261262556506200161240ustar00rootroot00000000000000// Copyright 2012 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. // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris package main import ( "os" "syscall" ) var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} // signalTrace is the signal to send to make a Go program // crash with a stack trace. var signalTrace os.Signal = syscall.SIGQUIT gb-0.3.2/cmd/gb/stringset.go000066400000000000000000000016031262556506200156210ustar00rootroot00000000000000package main // union returns the union of a and b. func union(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { r[k] = true } for k := range b { r[k] = true } return r } // intersection returns the intersection of a and b. func intersection(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { if b[k] { r[k] = true } } return r } // difference returns the symetric difference of a and b. func difference(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { if !b[k] { r[k] = true } } for k := range b { if !a[k] { r[k] = true } } return r } // contains returns true if a contains all the elements in s. func contains(a map[string]bool, s ...string) bool { var r bool for _, e := range s { if !a[e] { return false } r = true } return r } gb-0.3.2/cmd/gb/stringset_test.go000066400000000000000000000052141262556506200166620ustar00rootroot00000000000000package main import "testing" import "reflect" func set(args ...string) map[string]bool { r := make(map[string]bool) for _, a := range args { r[a] = true } return r } func TestUnion(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set("b"), }, { a: set("a"), b: nil, want: set("a"), }, { a: set("a"), b: set("b"), want: set("b", "a"), }, { a: set("c"), b: set("c"), want: set("c"), }} for _, tt := range tests { got := union(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("union(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestIntersection(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set(), }, { a: set("a"), b: nil, want: set(), }, { a: set("a"), b: set("b"), want: set(), }, { a: set("c"), b: set("c"), want: set("c"), }, { a: set("a", "c"), b: set("b", "c"), want: set("c"), }} for _, tt := range tests { got := intersection(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("intersection(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestDifference(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set("b"), }, { a: set("a"), b: nil, want: set("a"), }, { a: set("a"), b: set("b"), want: set("a", "b"), }, { a: set("c"), b: set("c"), want: set(), }, { a: set("a", "c"), b: set("b", "c"), want: set("a", "b"), }} for _, tt := range tests { got := difference(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("difference(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestContains(t *testing.T) { tests := []struct { a map[string]bool s []string want bool }{{ a: nil, s: nil, want: false, }, { a: set("a"), s: nil, want: false, }, { a: set("a"), s: []string{"a"}, want: true, }, { a: set("a"), s: []string{"b"}, want: false, }, { a: set("a", "b"), s: []string{"b"}, want: true, }, { a: set("a"), s: []string{"a", "b"}, want: false, }, { a: set("a", "b", "c"), s: []string{"a", "b"}, want: true, }, { a: set("a", "b", "c"), s: []string{"x", "b"}, want: false, }, { a: set("a", "b", "c"), s: []string{"b", "c", "d"}, want: false, }} for _, tt := range tests { got := contains(tt.a, tt.s...) if !reflect.DeepEqual(tt.want, got) { t.Errorf("contains(%v, %v) want: %v, got %v", tt.a, tt.s, tt.want, got) } } } gb-0.3.2/cmd/gb/test.go000066400000000000000000000040661262556506200145640ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "sort" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/test" ) func init() { registerCommand(TestCmd) } var ( tfs []string // Arguments passed to the test binary testProfile bool testCover bool testCoverMode string testCoverPkg string ) func addTestFlags(fs *flag.FlagSet) { addBuildFlags(fs) fs.BoolVar(&testCover, "cover", false, "enable coverage analysis") fs.StringVar(&testCoverMode, "covermode", "set", "Set covermode: set (default), count, atomic") fs.StringVar(&testCoverPkg, "coverpkg", "", "enable coverage analysis") } var TestCmd = &cmd.Command{ Name: "test", UsageLine: "test [build flags] [packages] [flags for test binary]", Short: "test packages", Long: ` Test automates testing the packages named by the import paths. 'gb test' recompiles each package along with any files with names matching the file pattern "*_test.go". See 'go help test'. `, Run: func(ctx *gb.Context, args []string) error { ctx.Force = F ctx.SkipInstall = FF r := test.TestResolver(ctx) // gb build builds packages in dependency order, however // gb test tests packages in alpha order. This matches the // expected behaviour from go test; tests are executed in // stable order. sort.Strings(args) pkgs, err := gb.ResolvePackages(r, args...) if err != nil { return err } test, err := test.TestPackages(TestFlags(tfs), pkgs...) if err != nil { return err } if dotfile != "" { f, err := os.Create(dotfile) if err != nil { return err } defer f.Close() printActions(f, test) } startSigHandlers() return gb.ExecuteConcurrent(test, P, interrupted) }, AddFlags: addTestFlags, FlagParse: func(flags *flag.FlagSet, args []string) error { var err error args, tfs, err = TestFlagsExtraParse(args[2:]) if err != nil { fmt.Fprintf(os.Stderr, "gb test: %s\n", err) fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n") os.Exit(2) } return flags.Parse(args) }, } gb-0.3.2/cmd/gb/testflag.go000066400000000000000000000116051262556506200154130ustar00rootroot00000000000000package main import ( "errors" "fmt" "strconv" "strings" ) // testFlagSpec defines a flag we know about. type testFlagSpec struct { boolVar bool // True if the flag is type bool passToTest bool // pass to Test passToAll bool // pass to test plugin and test binary present bool // The flag has been seen } // testFlagDefn is the set of flags we process. var testFlagDefn = map[string]*testFlagSpec{ // local to the test plugin "cover": {boolVar: true}, "coverpkg": {}, "covermode": {}, "a": {boolVar: true}, "r": {boolVar: true}, "f": {boolVar: true}, "F": {boolVar: true}, "P": {}, "ldflags": {}, "gcflags": {}, "dotfile": {}, // Passed to the test binary "q": {boolVar: true, passToTest: true}, "v": {boolVar: true, passToTest: true}, "bench": {passToTest: true}, "benchmem": {boolVar: true, passToTest: true}, "benchtime": {passToTest: true}, "coverprofile": {passToTest: true}, "cpu": {passToTest: true}, "cpuprofile": {passToTest: true}, "memprofile": {passToTest: true}, "memprofilerate": {passToTest: true}, "blockprofile": {passToTest: true}, "blockprofilerate": {passToTest: true}, "outputdir": {passToTest: true}, "parallel": {passToTest: true}, "run": {passToTest: true}, "short": {boolVar: true, passToTest: true}, "timeout": {passToTest: true}, } // TestFlags appends "-test." for flags that are passed to the test binary. func TestFlags(testArgs []string) []string { var targs []string for _, arg := range testArgs { var nArg, nVal, fArg string fArg = arg if !strings.Contains(arg, "-test.") { nArg = strings.TrimPrefix(arg, "-") if strings.Contains(nArg, "=") { nArgVal := strings.Split(nArg, "=") nArg, nVal = nArgVal[0], nArgVal[1] } if val, ok := testFlagDefn[nArg]; ok { // Special handling for -q, needs to be -test.v when passed to the test if nArg == "q" { nArg = "v" } if val.passToTest || val.passToAll { fArg = "-test." + nArg } } if nVal != "" { fArg = fArg + "=" + nVal } } targs = append(targs, fArg) } return targs } // TestFlagsExtraParse is used to separate known arguments from unknown // arguments passed on the command line. Returns a string slice of test plugin // arguments (parseArgs), and a slice of string arguments for the test binary // (extraArgs). An error is returned if an argument is used twice, or an // argument value is incorrect. func TestFlagsExtraParse(args []string) (parseArgs []string, extraArgs []string, err error) { argsLen := len(args) for x := 0; x < argsLen; x++ { nArg := args[x] val, ok := testFlagDefn[strings.TrimPrefix(nArg, "-")] if !strings.HasPrefix(nArg, "-") || (ok && !val.passToTest) { err = setArgFound(nArg) if err != nil { return } parseArgs = append(parseArgs, nArg) if ok && val.passToAll { extraArgs = append(extraArgs, nArg) } continue } var hadTestPrefix bool hasEqual := strings.Contains(nArg, "=") if !hasEqual && (x+1 < argsLen && !strings.HasPrefix(args[x+1], "-")) { if strings.Contains(nArg, "-test.") { hadTestPrefix = true nArg = strings.TrimPrefix(nArg, "-test.") } else { nArg = strings.TrimPrefix(nArg, "-") } err = setArgFound(nArg) if err != nil { return } // Check the spec for arguments that consume the next argument if val, ok := testFlagDefn[nArg]; ok { if !val.boolVar { nArg = nArg + "=" + args[x+1] x++ } } } else if hasEqual { // The argument has an embedded value, here we can do some basic // checking. sArgs := strings.Split(nArg, "=") tArg, tVal := strings.TrimPrefix(sArgs[0], "-"), sArgs[1] if val, ok := testFlagDefn[tArg]; ok { if val.boolVar { if err = checkBoolFlag(tVal); err != nil { return } } if !val.passToTest { parseArgs = append(parseArgs, nArg) continue } } } // Append "-" to the argument, and "-test." if "-test." was previously // trimmed. if nArg[0] != '-' { pre := "-" if hadTestPrefix { pre = "-test." } nArg = pre + nArg } extraArgs = append(extraArgs, nArg) } return } // setArgFound checks the argument spec to see if arg has already been // encountered. If it has, then an error is returned. func setArgFound(arg string) error { var err error nArg := strings.TrimPrefix(arg, "-") if val, ok := testFlagDefn[nArg]; ok { if val.present { err = fmt.Errorf("%q flag may be set only once", arg) } else { testFlagDefn[nArg].present = true } } return err } // checkBoolFlag checks the value to ensure it is a boolean, if not an error is // returned. func checkBoolFlag(value string) error { var nErr error _, err := strconv.ParseBool(value) if err != nil { nErr = errors.New("illegal bool flag value " + value) } return nErr } gb-0.3.2/cmd/gb/testflag_test.go000066400000000000000000000126261262556506200164560ustar00rootroot00000000000000package main import ( "errors" "reflect" "testing" ) func TestTestFlagsPreParse(t *testing.T) { tests := []struct { args []string // The command line arguments to parse pargs []string // The expected arguments for flag.Parse eargs []string // The expected "extra" arguments to pass to the test binary err error }{ { args: []string{"-q", "-test", "-debug"}, eargs: []string{"-q", "-test", "-debug"}, }, { args: []string{"-v", "-debug", "package_name"}, pargs: []string{"package_name"}, eargs: []string{"-v", "-debug"}, }, { args: []string{"-q", "-debug", "package_name"}, pargs: []string{"package_name"}, eargs: []string{"-q", "-debug"}, }, { args: []string{"-bench"}, eargs: []string{"-bench"}, }, { args: []string{"-bench=."}, eargs: []string{"-bench=."}, }, { args: []string{"-bench", "."}, eargs: []string{"-bench=."}, }, { args: []string{"-bench", "Test*"}, eargs: []string{"-bench=Test*"}, }, { args: []string{"-benchmem"}, eargs: []string{"-benchmem"}, }, { args: []string{"-benchtime", "2s"}, eargs: []string{"-benchtime=2s"}, }, { args: []string{"-blockprofile", "profile"}, eargs: []string{"-blockprofile=profile"}, }, { args: []string{"-blockprofile", "profile", "-cover"}, pargs: []string{"-cover"}, eargs: []string{"-blockprofile=profile"}, }, { args: []string{"-blockprofilerate", "2"}, eargs: []string{"-blockprofilerate=2"}, }, { args: []string{"-coverprofile", "c.out"}, eargs: []string{"-coverprofile=c.out"}, }, { args: []string{"-cpu", "1"}, eargs: []string{"-cpu=1"}, }, { args: []string{"-cpu", "1"}, eargs: []string{"-cpu=1"}, }, { args: []string{"-timeout"}, eargs: []string{"-timeout"}, }, { args: []string{"-timeout", "2s"}, eargs: []string{"-timeout=2s"}, }, { args: []string{"-test.run", "test"}, eargs: []string{"-test.run=test"}, }, { args: []string{"-test.bench", "Test*"}, eargs: []string{"-test.bench=Test*"}, }, { args: []string{"-test.bench=Test*"}, eargs: []string{"-test.bench=Test*"}, }, { args: []string{"-test.run", "Test*", "-test.run", "Test2*"}, err: errors.New("\"run\" flag may be set only once"), }, { args: []string{"-cover=true"}, pargs: []string{"-cover=true"}, }, { args: []string{"-cover=false"}, pargs: []string{"-cover=false"}, }, { args: []string{"-cover=notabool"}, err: errors.New("illegal bool flag value notabool"), }, { args: []string{"-run", "Test*", "-run", "Test2*"}, err: errors.New("\"run\" flag may be set only once"), }, { args: []string{"-short"}, eargs: []string{"-short"}, }, { args: []string{"-memprofilerate", "1"}, eargs: []string{"-memprofilerate=1"}, }, { args: []string{"-coverpkg", "package"}, pargs: []string{"-coverpkg", "package"}, }, { args: []string{"-P", "1"}, pargs: []string{"-P", "1"}, }, { args: []string{"-P=1", "-short"}, pargs: []string{"-P=1"}, eargs: []string{"-short"}, }} for _, tt := range tests { for k, v := range testFlagDefn { if v.present { testFlagDefn[k].present = false } } pargs, eargs, err := TestFlagsExtraParse(tt.args) if tt.err != nil && (err == nil || (err != nil && tt.err.Error() != err.Error())) { t.Errorf("TestExtraFlags(%v): want err = '%v', got = '%v'", tt.args, tt.err, err) } else if tt.err == nil && (!reflect.DeepEqual(pargs, tt.pargs) || !reflect.DeepEqual(eargs, tt.eargs)) { t.Errorf("TestExtraFlags(%v): want (%v,%v), got (%v,%v)", tt.args, tt.pargs, tt.eargs, pargs, eargs) } } } func TestTestFlags(t *testing.T) { tests := []struct { eargs []string // Extra test binary arguments targs []string // The expected test binary arguments }{ { eargs: []string{"-q", "-debug"}, targs: []string{"-test.v", "-debug"}, }, { eargs: []string{"-v", "-debug"}, targs: []string{"-test.v", "-debug"}, }, { eargs: []string{"-bench"}, targs: []string{"-test.bench"}, }, { eargs: []string{"-bench", "."}, targs: []string{"-test.bench", "."}, }, { eargs: []string{"-bench='Test*'"}, targs: []string{"-test.bench='Test*'"}, }, { eargs: []string{"-benchmem"}, targs: []string{"-test.benchmem"}, }, { eargs: []string{"-benchtime"}, targs: []string{"-test.benchtime"}, }, { eargs: []string{"-benchtime", "2s"}, targs: []string{"-test.benchtime", "2s"}, }, { eargs: []string{"-benchtime=2s"}, targs: []string{"-test.benchtime=2s"}, }, { eargs: []string{"-blockprofile", "profile"}, targs: []string{"-test.blockprofile", "profile"}, }, { eargs: []string{"-blockprofile=profile"}, targs: []string{"-test.blockprofile=profile"}, }, { eargs: []string{"-blockprofile"}, targs: []string{"-test.blockprofile"}, }, { eargs: []string{"-cpuprofile"}, targs: []string{"-test.cpuprofile"}, }, { eargs: []string{"-memprofile"}, targs: []string{"-test.memprofile"}, }, { eargs: []string{"-short"}, targs: []string{"-test.short"}, }, { eargs: []string{"-memprofilerate", "1"}, targs: []string{"-test.memprofilerate", "1"}, }, { eargs: []string{"-test.run=test"}, targs: []string{"-test.run=test"}, }, { eargs: []string{"-test.short"}, targs: []string{"-test.short"}, }} for _, tt := range tests { targs := TestFlags(tt.eargs) if !reflect.DeepEqual(targs, tt.targs) { t.Errorf("TestFlags(%v): want %v, got %v", tt.eargs, tt.targs, targs) } } } gb-0.3.2/cmd/import.go000066400000000000000000000024421262556506200145230ustar00rootroot00000000000000package cmd import ( "path" "path/filepath" "strings" ) type Context interface { Srcdirs() []string AllPackages(string) []string } // importPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func importPathsNoDotExpansion(ctx Context, cwd string, args []string) []string { srcdir, _ := filepath.Rel(ctx.Srcdirs()[0], cwd) if srcdir == ".." { srcdir = "." } if len(args) == 0 { args = []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) } if a == "all" || a == "std" { out = append(out, ctx.AllPackages(a)...) continue } a = path.Join(srcdir, path.Clean(a)) out = append(out, a) } return out } // importPaths returns the import paths to use for the given command line. func ImportPaths(ctx Context, cwd string, args []string) []string { args = importPathsNoDotExpansion(ctx, cwd, args) var out []string for _, a := range args { if strings.Contains(a, "...") { out = append(out, ctx.AllPackages(a)...) continue } out = append(out, a) } return out } gb-0.3.2/cmd/import_test.go000066400000000000000000000036421262556506200155650ustar00rootroot00000000000000package cmd import ( "os" "path/filepath" "reflect" "testing" ) type context struct { srcdirs, allpkgs []string } func (c *context) Srcdirs() []string { return c.srcdirs } func (c *context) AllPackages(pattern string) []string { return c.allpkgs } func testdata(args ...string) string { cwd, _ := os.Getwd() return filepath.Join(append([]string{cwd, "testdata"}, args...)...) } // l constructs a []string func l(args ...string) []string { return args } // p constructs a path func p(args ...string) string { return filepath.Join(args...) } func TestImportPaths(t *testing.T) { var tests = []struct { ctx context cwd string args []string want []string }{ { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("a", "b", "c", p("c", "d")), }, cwd: testdata("src"), args: nil, want: l("a", "b", "c", p("c", "d")), }, { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("a", "b", "c", p("c", "d")), }, cwd: testdata("src"), args: l("..."), want: l("a", "b", "c", p("c", "d")), }, { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("c", p("c", "d")), }, cwd: testdata("src", "c"), args: nil, want: l("c", p("c", "d")), }, { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("a", "b", "c", p("c", "d")), }, cwd: testdata("src"), args: l("c"), want: l("c"), }, { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("a", "b", "c", p("c", "d")), }, cwd: testdata("src"), args: l("c", "b"), want: l("c", "b"), }, { ctx: context{ srcdirs: l(testdata("src")), allpkgs: l("c", p("c", "d")), }, cwd: testdata("src"), args: l("c/..."), want: l("c", p("c", "d")), }, } for _, tt := range tests { got := ImportPaths(&tt.ctx, tt.cwd, tt.args) if !reflect.DeepEqual(got, tt.want) { t.Errorf("ImportPaths(%v): got %v, want %v", tt.args, got, tt.want) } } } gb-0.3.2/cmd/path.go000066400000000000000000000013001262556506200141350ustar00rootroot00000000000000package cmd import ( "fmt" "os" "path/filepath" ) // FindProjectroot works upwards from path seaching for the // src/ directory which identifies the project root. func FindProjectroot(path string) (string, error) { if path == "" { return "", fmt.Errorf("project root is blank") } start := path for path != filepath.Dir(path) { root := filepath.Join(path, "src") if _, err := os.Stat(root); err != nil { if os.IsNotExist(err) { path = filepath.Dir(path) continue } return "", err } path, err := filepath.EvalSymlinks(path) if err != nil { return "", err } return path, nil } return "", fmt.Errorf("could not find project root in %q or its parents", start) } gb-0.3.2/cmd/path_test.go000066400000000000000000000027121262556506200152040ustar00rootroot00000000000000package cmd import ( "fmt" "io/ioutil" "os" "path/filepath" "testing" ) var join = filepath.Join // makeTestData constructs func makeTestdata(t *testing.T) string { tempdir, err := filepath.EvalSymlinks(os.TempDir()) if err != nil { t.Fatal(err) } root, err := ioutil.TempDir(tempdir, "path-test") if err != nil { t.Fatal(err) } mkdir := func(args ...string) string { path := join(args...) if err := os.MkdirAll(path, 0777); err != nil { t.Fatal(err) } return path } mkfile := func(path string, content string) { if err := ioutil.WriteFile(path, []byte(content), 0755); err != nil { t.Fatal(err) } } srcdir := mkdir(root, "src") mkfile(join(mkdir(srcdir, "a"), "a.go"), "package a") return root } func TestFindProjectroot(t *testing.T) { root := makeTestdata(t) defer os.RemoveAll(root) tests := []struct { path string want string err error }{ {path: root, want: root}, {path: join(root, "src"), want: root}, {path: join(join(root, "src"), "a"), want: root}, {path: join(root, ".."), err: fmt.Errorf("could not find project root in %q or its parents", join(root, ".."))}, } for _, tt := range tests { got, err := FindProjectroot(tt.path) if got != tt.want || !sameErr(err, tt.err) { t.Errorf("FindProjectroot(%v): want: %v, %v, got %v, %v", tt.path, tt.want, tt.err, got, err) } } } func sameErr(e1, e2 error) bool { if e1 != nil && e2 != nil { return e1.Error() == e2.Error() } return e1 == e2 } gb-0.3.2/context.go000066400000000000000000000234261262556506200141370ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "io" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "sort" "strings" "sync" "time" "github.com/constabulary/gb/debug" ) // Context represents an execution of one or more Targets inside a Project. type Context struct { *Project Context *build.Context workdir string tc Toolchain gohostos, gohostarch string // GOOS and GOARCH for this host gotargetos, gotargetarch string // GOOS and GOARCH for the target Statistics Force bool // force rebuild of packages SkipInstall bool // do not cache compiled packages pkgs map[string]*Package // map of package paths to resolved packages gcflags []string // flags passed to the compiler ldflags []string // flags passed to the linker linkmode, buildmode string // link and build modes buildtags []string // build tags } // GOOS configures the Context to use goos as the target os. func GOOS(goos string) func(*Context) error { return func(c *Context) error { if goos == "" { return fmt.Errorf("goos cannot be blank") } c.gotargetos = goos return nil } } // GOARCH configures the Context to use goarch as the target arch. func GOARCH(goarch string) func(*Context) error { return func(c *Context) error { if goarch == "" { return fmt.Errorf("goarch cannot be blank") } c.gotargetarch = goarch return nil } } // Tags configured the context to use these additional build tags func Tags(tags ...string) func(*Context) error { return func(c *Context) error { c.buildtags = make([]string, len(tags)) copy(c.buildtags, tags) return nil } } // NewContext returns a new build context from this project. // By default this context will use the gc toolchain with the // host's GOOS and GOARCH values. func (p *Project) NewContext(opts ...func(*Context) error) (*Context, error) { if len(p.srcdirs) == 0 { return nil, fmt.Errorf("no source directories supplied") } envOr := func(key, def string) string { if v := os.Getenv(key); v != "" { return v } else { return def } } defaults := []func(*Context) error{ // must come before GcToolchain() func(c *Context) error { c.gohostos = runtime.GOOS c.gohostarch = runtime.GOARCH c.gotargetos = envOr("GOOS", runtime.GOOS) c.gotargetarch = envOr("GOARCH", runtime.GOARCH) return nil }, GcToolchain(), } workdir, err := ioutil.TempDir("", "gb") if err != nil { return nil, err } ctx := Context{ Project: p, workdir: workdir, pkgs: make(map[string]*Package), buildmode: "exe", } for _, opt := range append(defaults, opts...) { err := opt(&ctx) if err != nil { return nil, err } } // sort build tags to ensure the ctxSring and Suffix is stable sort.Strings(ctx.buildtags) // backfill enbedded go/build.Context ctx.Context = &build.Context{ GOOS: ctx.gotargetos, GOARCH: ctx.gotargetarch, GOROOT: runtime.GOROOT(), GOPATH: togopath(p.Srcdirs()), Compiler: runtime.Compiler, // TODO(dfc) probably unused // Make sure we use the same set of release tags as go/build ReleaseTags: build.Default.ReleaseTags, BuildTags: ctx.buildtags, CgoEnabled: build.Default.CgoEnabled, } return &ctx, nil } // Gcflags sets options passed to the compiler. func Gcflags(flags ...string) func(*Context) error { return func(c *Context) error { c.gcflags = flags return nil } } // Ldflags sets options passed to the linker. func Ldflags(flags ...string) func(*Context) error { return func(c *Context) error { c.ldflags = flags return nil } } // IncludePaths returns the include paths visible in this context. func (c *Context) IncludePaths() []string { return []string{ c.workdir, c.Pkgdir(), } } // Pkgdir returns the path to precompiled packages. func (c *Context) Pkgdir() string { return filepath.Join(c.Project.Pkgdir(), c.ctxString()) } // Suffix returns the suffix (if any) for binaries produced // by this context. func (c *Context) Suffix() string { suffix := c.ctxString() if suffix != "" { suffix = "-" + suffix } return suffix } // Workdir returns the path to this Context's working directory. func (c *Context) Workdir() string { return c.workdir } // ResolvePackage resolves the package at path using the current context. func (c *Context) ResolvePackage(path string) (*Package, error) { if path == "." { return nil, fmt.Errorf("%q is not a package", filepath.Join(c.rootdir, "src")) } path, err := relImportPath(filepath.Join(c.rootdir, "src"), path) if err != nil { return nil, err } return loadPackage(c, nil, path) } // Destroy removes the temporary working files of this context. func (c *Context) Destroy() error { debug.Debugf("removing work directory: %v", c.workdir) return os.RemoveAll(c.workdir) } // ctxString returns a string representation of the unique properties // of the context. func (c *Context) ctxString() string { v := []string{ c.gotargetos, c.gotargetarch, } v = append(v, c.buildtags...) return strings.Join(v, "-") } func runOut(output io.Writer, dir string, env []string, command string, args ...string) error { cmd := exec.Command(command, args...) cmd.Dir = dir cmd.Stdout = output cmd.Stderr = os.Stderr cmd.Env = mergeEnvLists(env, envForDir(cmd.Dir)) debug.Debugf("cd %s; %s", cmd.Dir, cmd.Args) err := cmd.Run() return err } // Statistics records the various Durations type Statistics struct { sync.Mutex stats map[string]time.Duration } func (s *Statistics) Record(name string, d time.Duration) { s.Lock() defer s.Unlock() if s.stats == nil { s.stats = make(map[string]time.Duration) } s.stats[name] += d } func (s *Statistics) Total() time.Duration { s.Lock() defer s.Unlock() var d time.Duration for _, v := range s.stats { d += v } return d } func (s *Statistics) String() string { s.Lock() defer s.Unlock() return fmt.Sprintf("%v", s.stats) } // 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. 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) } } // hasPathPrefix reports whether the path s begins with the // elements in prefix. 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 } } // treeCanMatchPattern(pattern)(name) reports whether // name or children of name can possibly match pattern. // Pattern is the same limited glob accepted by matchPattern. 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) } } // AllPackages returns all the packages that can be found under the $PROJECT/src directory. // The pattern is a path including "...". func (c *Context) AllPackages(pattern string) []string { return matchPackages(c, pattern) } // shouldignore tests if the package should be ignored. func (c *Context) shouldignore(p string) bool { if c.isCrossCompile() { return p == "C" || p == "unsafe" } return stdlib[p] } func (c *Context) isCrossCompile() bool { return c.gohostos != c.gotargetos || c.gohostarch != c.gotargetarch } func matchPackages(c *Context, pattern string) []string { debug.Debugf("matchPackages: %v %v", c.srcdirs[0].Root, pattern) match := func(string) bool { return true } treeCanMatch := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) treeCanMatch = treeCanMatchPattern(pattern) } var pkgs []string for _, dir := range c.srcdirs[:1] { src := filepath.Clean(dir.Root) + string(filepath.Separator) filepath.Walk(src, 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, ".") { return filepath.SkipDir } if !treeCanMatch(name) { return filepath.SkipDir } if !match(name) { return nil } _, err = c.Context.Import(".", path, 0) if err != nil { if _, noGo := err.(*build.NoGoError); noGo { return nil } } pkgs = append(pkgs, name) return nil }) } return pkgs } // envForDir returns a copy of the environment // suitable for running in the given directory. // The environment is the current process's environment // but with an updated $PWD, so that an os.Getwd in the // child will be faster. func envForDir(dir string) []string { env := os.Environ() // Internally we only use rooted paths, so dir is rooted. // Even if dir is not rooted, no harm done. return mergeEnvLists([]string{"PWD=" + dir}, env) } // mergeEnvLists merges the two environment lists such that // variables with the same name in "in" replace those in "out". func mergeEnvLists(in, out []string) []string { NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] for i, outkv := range out { if strings.HasPrefix(outkv, k) { out[i] = inkv continue NextVar } } out = append(out, inkv) } return out } gb-0.3.2/context_test.go000066400000000000000000000033141262556506200151700ustar00rootroot00000000000000package gb import ( "runtime" "runtime/debug" "strings" "testing" ) func testImportCycle(pkg string, t *testing.T) { ctx := testContext(t) debug.SetMaxStack(1 << 18) _, err := ctx.ResolvePackage(pkg) if strings.Index(err.Error(), "cycle detected") == -1 { t.Errorf("ctx.ResolvePackage returned wrong error. Expected cycle detection, got: %v", err) } if err == nil { t.Errorf("ctx.ResolvePackage should have returned an error for cycle, returned nil") } } func TestOneElementCycleDetection(t *testing.T) { testImportCycle("cycle0", t) } func TestSimpleCycleDetection(t *testing.T) { testImportCycle("cycle1/a", t) } func TestLongCycleDetection(t *testing.T) { testImportCycle("cycle2/a", t) } func TestContextCtxString(t *testing.T) { opts := func(o ...func(*Context) error) []func(*Context) error { return o } join := func(s ...string) string { return strings.Join(s, "-") } tests := []struct { opts []func(*Context) error want string }{ {nil, join(runtime.GOOS, runtime.GOARCH)}, {opts(GOOS("windows")), join("windows", runtime.GOARCH)}, {opts(GOARCH("arm64")), join(runtime.GOOS, "arm64")}, {opts(GOARCH("arm64"), GOOS("plan9")), join("plan9", "arm64")}, {opts(Tags()), join(runtime.GOOS, runtime.GOARCH)}, {opts(Tags("sphinx", "leon")), join(runtime.GOOS, runtime.GOARCH, "leon", "sphinx")}, {opts(Tags("sphinx", "leon"), GOARCH("ppc64le")), join(runtime.GOOS, "ppc64le", "leon", "sphinx")}, } proj := testProject(t) for _, tt := range tests { ctx, err := proj.NewContext(tt.opts...) if err != nil { t.Fatal(err) } defer ctx.Destroy() got := ctx.ctxString() if got != tt.want { t.Errorf("NewContext(%q).ctxString(): got %v, want %v", tt.opts, got, tt.want) } } } gb-0.3.2/debug/000077500000000000000000000000001262556506200132035ustar00rootroot00000000000000gb-0.3.2/debug/debug.go000066400000000000000000000013661262556506200146260ustar00rootroot00000000000000// debug provides a light weight debug facility. // Usage is via the DEBUG environment variable. Any non empty value will // enable debug logging. For example // // DEBUG=. gb // // The period is a hint that the value passed to DEBUG is a regex, which matches // files, or packages present in the file part of the file/line pair logged as a // prefix of the log line. (not implemented yet) // // Debug output is send to os.Stderr, there is no facility to change this. package debug import ( "fmt" "log" "os" ) var debug = os.Getenv("DEBUG") var logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile) func Debugf(format string, args ...interface{}) { if len(debug) == 0 { return } logger.Output(2, fmt.Sprintf(format, args...)) } gb-0.3.2/defaultcc.go000066400000000000000000000001311262556506200143710ustar00rootroot00000000000000// +build !darwin,!freebsd package gb const defaultCC = "gcc" const defaultCXX = "g++" gb-0.3.2/defaultcc_bsd.go000066400000000000000000000001351262556506200152250ustar00rootroot00000000000000// +build darwin freebsd package gb const defaultCC = "clang" const defaultCXX = "clang++" gb-0.3.2/example_test.go000066400000000000000000000014741262556506200151440ustar00rootroot00000000000000package gb_test import ( "log" "path/filepath" "github.com/constabulary/gb" ) func ExampleNewPackage() { // Every project begins with a project root. // Normally you'd check this out of source control. root := filepath.Join("home", "dfc", "devel", "demo") // Create a new Project passing in the source directories // under this project's root. proj := gb.NewProject(root, gb.SourceDir(filepath.Join(root, "src")), // $PROJECT/src gb.SourceDir(filepath.Join(root, "vendor", "src")), // $PROJECT/vendor/src ) // Create a new Context from the Project. A Context holds // the state of a specific compilation or test within the Project. ctx, err := proj.NewContext() if err != nil { log.Fatal("Could not create new context:", err) } // Always remember to clean up your Context ctx.Destroy() } gb-0.3.2/executor.go000066400000000000000000000041461262556506200143070ustar00rootroot00000000000000package gb import ( "errors" "sync" ) // Execute executes a tree of *Actions sequentually in depth first order. func Execute(a *Action) error { seen := make(map[*Action]error) return execute(seen, a) } func execute(seen map[*Action]error, a *Action) error { // step 0, have we been here before if err, ok := seen[a]; ok { return err } // step 1, build all dependencies for _, d := range a.Deps { if err := execute(seen, d); err != nil { return err } } // step 2, now execute ourselves err := a.Run() seen[a] = err return err } // ExecuteConcurrent executes all actions in a tree concurrently. // Each Action will wait until its dependant actions are complete. func ExecuteConcurrent(a *Action, n int, interrupt <-chan struct{}) error { var mu sync.Mutex // protects seen seen := make(map[*Action]chan error) get := func(result chan error) error { err := <-result result <- err return err } permits := make(chan bool, n) for i := 0; i < cap(permits); i++ { permits <- true } // wg tracks all the outstanding actions var wg sync.WaitGroup var execute func(map[*Action]chan error, *Action) chan error execute = func(seen map[*Action]chan error, a *Action) chan error { // step 0, have we seen this action before ? mu.Lock() if result, ok := seen[a]; ok { // yes! return the result channel so others can wait // on our action mu.Unlock() return result } // step 1, we're the first to run this action result := make(chan error, 1) seen[a] = result mu.Unlock() // queue all dependant actions. var results []chan error for _, dep := range a.Deps { results = append(results, execute(seen, dep)) } wg.Add(1) go func() { defer wg.Done() // wait for dependant actions for _, r := range results { if err := get(r); err != nil { result <- err return } } // wait for a permit and execute our action select { case <-permits: result <- a.Run() permits <- true case <-interrupt: result <- errors.New("interrupted") return } }() return result } err := get(execute(seen, a)) wg.Wait() return err } gb-0.3.2/executor_test.go000066400000000000000000000077041262556506200153510ustar00rootroot00000000000000package gb import ( "errors" "fmt" "io" "path/filepath" "reflect" "testing" ) func TestExecuteBuildAction(t *testing.T) { tests := []struct { pkg string err error }{{ pkg: "a", err: nil, }, { pkg: "b", // actually command err: nil, }, { pkg: "c", err: nil, }, { pkg: "d.v1", err: nil, }, { pkg: "x", err: errors.New("import cycle detected: x -> y -> x"), }, { pkg: "h", // imports "blank", which is blank, see issue #131 err: fmt.Errorf("no buildable Go source files in %s", filepath.Join(getwd(t), "testdata", "src", "blank")), }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !sameErr(err, tt.err) { t.Errorf("ctx.ResolvePackage(%v): want %v, got %v", tt.pkg, tt.err, err) continue } if err != nil { continue } action, err := BuildPackages(pkg) if err != nil { t.Errorf("BuildAction(%v): ", tt.pkg, err) continue } if err := Execute(action); !sameErr(err, tt.err) { t.Errorf("Execute(%v): want: %v, got %v", action.Name, tt.err, err) } } } func niltask() error { return nil } var executorTests = []struct { action *Action // root action err error // expected error }{{ action: &Action{ Name: "no error", Run: niltask, }, }, { action: &Action{ Name: "root error", Run: func() error { return io.EOF }, }, err: io.EOF, }, { action: &Action{ Name: "child, child, error", Run: func() error { return fmt.Errorf("I should not have been called") }, Deps: []*Action{&Action{ Name: "child, error", Run: niltask, Deps: []*Action{&Action{ Name: "error", Run: func() error { return io.EOF }, }}, }}, }, err: io.EOF, }, { action: &Action{ Name: "once only", Run: func() error { if c1 != 1 || c2 != 1 || c3 != 1 { return fmt.Errorf("unexpected count, c1: %v, c2: %v, c3: %v", c1, c2, c3) } return nil }, Deps: []*Action{createDag()}, }, }, { action: &Action{ Name: "failure count", Run: func() error { return fmt.Errorf("I should not have been called") }, Deps: []*Action{createFailDag()}, }, err: fmt.Errorf("task3 called 1 time"), }} func createDag() *Action { task1 := func() error { c1++; return nil } task2 := func() error { c2++; return nil } task3 := func() error { c3++; return nil } action1 := Action{Name: "c1", Run: task1} action2 := Action{Name: "c2", Run: task2} action3 := Action{Name: "c3", Run: task3} action1.Deps = append(action1.Deps, &action2, &action3) action2.Deps = append(action2.Deps, &action3) return &action1 } func createFailDag() *Action { task1 := func() error { c1++; return nil } task2 := func() error { c2++; return fmt.Errorf("task2 called %v time", c2) } task3 := func() error { c3++; return fmt.Errorf("task3 called %v time", c3) } action1 := Action{Name: "c1", Run: task1} action2 := Action{Name: "c2", Run: task2} action3 := Action{Name: "c3", Run: task3} action1.Deps = append(action1.Deps, &action2, &action3) action2.Deps = append(action2.Deps, &action3) return &action1 } var c1, c2, c3 int func executeReset() { c1 = 0 c2 = 0 c3 = 0 // reset executor test variables } func TestExecute(t *testing.T) { for _, tt := range executorTests { executeReset() got := Execute(tt.action) if !reflect.DeepEqual(got, tt.err) { t.Errorf("Execute: %v: want err: %v, got err %v", tt.action.Name, tt.err, got) } } } func testExecuteConcurrentN(t *testing.T, n int) { for _, tt := range executorTests { executeReset() got := ExecuteConcurrent(tt.action, n, nil) // no interrupt ch if !reflect.DeepEqual(got, tt.err) { t.Errorf("ExecuteConcurrent(%v): %v: want err: %v, got err %v", n, tt.action.Name, tt.err, got) } } } func TestExecuteConcurrent1(t *testing.T) { testExecuteConcurrentN(t, 1) } func TestExecuteConcurrent2(t *testing.T) { testExecuteConcurrentN(t, 2) } func TestExecuteConcurrent4(t *testing.T) { testExecuteConcurrentN(t, 4) } func TestExecuteConcurrent7(t *testing.T) { testExecuteConcurrentN(t, 7) } gb-0.3.2/fileutils/000077500000000000000000000000001262556506200141155ustar00rootroot00000000000000gb-0.3.2/fileutils/_testdata/000077500000000000000000000000001262556506200160655ustar00rootroot00000000000000gb-0.3.2/fileutils/_testdata/copyfile/000077500000000000000000000000001262556506200176775ustar00rootroot00000000000000gb-0.3.2/fileutils/_testdata/copyfile/a/000077500000000000000000000000001262556506200201175ustar00rootroot00000000000000gb-0.3.2/fileutils/_testdata/copyfile/a/rick000077700000000000000000000000001262556506200260252/never/going/to/give/you/upustar00rootroot00000000000000gb-0.3.2/fileutils/fileutils.go000066400000000000000000000043041262556506200164450ustar00rootroot00000000000000// package fileutils provides utililty methods to copy and move files and directories. package fileutils import ( "fmt" "io" "os" "path/filepath" "runtime" "strings" ) const debugCopypath = true const debugCopyfile = false // Copypath copies the contents of src to dst, excluding any file or // directory that starts with a period. func Copypath(dst string, src string) error { err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if strings.HasPrefix(filepath.Base(path), ".") { if info.IsDir() { return filepath.SkipDir } return nil } if info.IsDir() { return nil } if info.Mode()&os.ModeSymlink != 0 { if debugCopypath { fmt.Printf("skipping symlink: %v\n", path) } return nil } dst := filepath.Join(dst, path[len(src):]) return copyfile(dst, path) }) if err != nil { // if there was an error during copying, remove the partial copy. RemoveAll(dst) } return err } func copyfile(dst, src string) error { err := mkdir(filepath.Dir(dst)) if err != nil { return fmt.Errorf("copyfile: mkdirall: %v", err) } r, err := os.Open(src) if err != nil { return fmt.Errorf("copyfile: open(%q): %v", src, err) } defer r.Close() w, err := os.Create(dst) if err != nil { return fmt.Errorf("copyfile: create(%q): %v", dst, err) } defer w.Close() if debugCopyfile { fmt.Printf("copyfile(dst: %v, src: %v)\n", dst, src) } _, err = io.Copy(w, r) return err } // RemoveAll removes path and any children it contains. Unlike os.RemoveAll it // deletes read only files on Windows. func RemoveAll(path string) error { if runtime.GOOS == "windows" { // Simple case: if Remove works, we're done. err := os.Remove(path) if err == nil || os.IsNotExist(err) { return nil } // make sure all files are writable so we can delete them filepath.Walk(path, func(path string, info os.FileInfo, err error) error { if err != nil { // walk gave us some error, give it back. return err } mode := info.Mode() if mode|0200 == mode { return nil } return os.Chmod(path, mode|0200) }) } return os.RemoveAll(path) } func mkdir(path string) error { return os.MkdirAll(path, 0755) } gb-0.3.2/fileutils/fileutils_test.go000066400000000000000000000010201262556506200174740ustar00rootroot00000000000000package fileutils import ( "io/ioutil" "path/filepath" "runtime" "testing" ) func TestCopypathSkipsSymlinks(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("no symlinks on windows y'all") } dst := mktemp(t) defer RemoveAll(dst) src := filepath.Join("_testdata", "copyfile", "a") if err := Copypath(dst, src); err != nil { t.Fatalf("copypath(%s, %s): %v", dst, src, err) } } func mktemp(t *testing.T) string { s, err := ioutil.TempDir("", "fileutils_test") if err != nil { t.Fatal(err) } return s } gb-0.3.2/fileutils/path_test.go000066400000000000000000000054611262556506200164450ustar00rootroot00000000000000// Copyright 2009 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 fileutils import ( "os" "runtime" "testing" ) var isReadonlyError = func(error) bool { return false } func TestRemoveAll(t *testing.T) { tmpDir := os.TempDir() // Work directory. path := tmpDir + "/_TestRemoveAll_" fpath := path + "/file" dpath := path + "/dir" // Make directory with 1 file and remove. if err := os.MkdirAll(path, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", path, err) } fd, err := os.Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } fd.Close() if err = RemoveAll(path); err != nil { t.Fatalf("RemoveAll %q (first): %s", path, err) } if _, err = os.Lstat(path); err == nil { t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path) } // Make directory with file and subdirectory and remove. if err = os.MkdirAll(dpath, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", dpath, err) } fd, err = os.Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } fd.Close() fd, err = os.Create(dpath + "/file") if err != nil { t.Fatalf("create %q: %s", fpath, err) } fd.Close() if err = RemoveAll(path); err != nil { t.Fatalf("RemoveAll %q (second): %s", path, err) } if _, err := os.Lstat(path); err == nil { t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path) } // Determine if we should run the following test. testit := true if runtime.GOOS == "windows" { // Chmod is not supported under windows. testit = false } else { // Test fails as root. testit = os.Getuid() != 0 } if testit { // Make directory with file and subdirectory and trigger error. if err = os.MkdirAll(dpath, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", dpath, err) } for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { fd, err = os.Create(s) if err != nil { t.Fatalf("create %q: %s", s, err) } fd.Close() } if err = os.Chmod(dpath, 0); err != nil { t.Fatalf("Chmod %q 0: %s", dpath, err) } // No error checking here: either RemoveAll // will or won't be able to remove dpath; // either way we want to see if it removes fpath // and path/zzz. Reasons why RemoveAll might // succeed in removing dpath as well include: // * running as root // * running on a file system without permissions (FAT) RemoveAll(path) os.Chmod(dpath, 0777) for _, s := range []string{fpath, path + "/zzz"} { if _, err = os.Lstat(s); err == nil { t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) } } } if err = RemoveAll(path); err != nil { t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) } if _, err = os.Lstat(path); err == nil { t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) } } gb-0.3.2/gb.go000066400000000000000000000057621262556506200130460ustar00rootroot00000000000000// Package gb is a tool kit for building Go packages and programs. // // The executable, cmd/gb, is located in the respective subdirectory // along with several plugin programs. package gb import ( "fmt" "io" "os" "path/filepath" "strings" "github.com/constabulary/gb/debug" ) // Toolchain represents a standardised set of command line tools // used to build and test Go programs. type Toolchain interface { Gc(pkg *Package, searchpaths []string, importpath, srcdir, outfile string, files []string) error Asm(pkg *Package, srcdir, ofile, sfile string) error Pack(pkg *Package, afiles ...string) error Ld(*Package, []string, string, string) error Cc(pkg *Package, ofile string, cfile string) error // compiler returns the location of the compiler for .go source code compiler() string // linker returns the location of the linker for this toolchain linker() string } // Actions and Tasks. // // Actions and Tasks allow gb to separate the role of describing the // order in which work will be done, from the work itself. // Actions are the former, they describe the graph of dependencies // between actions, and thus the work to be done. By traversing the action // graph, we can do the work, executing Tasks in a sane order. // // Tasks describe the work to be done, without being concerned with // the order in which the work is done -- that is up to the code that // places Tasks into actions. Tasks also know more intimate details about // filesystems, processes, file lists, etc, that Actions do not. // // Action graphs (they are not strictly trees as branchs converge on base actions) // contain only work to be performed, there are no Actions with empty Tasks // or Tasks which do no work. // // Actions are executed by Executors, but can also be transformed, mutated, // or even graphed. // An Action describes a task to be performed and a set // of Actions that the task depends on. type Action struct { // Name describes the action. Name string // Deps identifies the Actions that this Action depends. Deps []*Action // Run identifies the task that this action represents. Run func() error } func mkdir(path string) error { return os.MkdirAll(path, 0755) } func copyfile(dst, src string) error { err := mkdir(filepath.Dir(dst)) if err != nil { return fmt.Errorf("copyfile: mkdirall: %v", err) } r, err := os.Open(src) if err != nil { return fmt.Errorf("copyfile: open(%q): %v", src, err) } defer r.Close() w, err := os.Create(dst) if err != nil { return fmt.Errorf("copyfile: create(%q): %v", dst, err) } defer w.Close() debug.Debugf("copyfile(dst: %v, src: %v)", dst, src) _, err = io.Copy(w, r) return err } // joinlist joins a []string representing path items // using the operating system specific list separator. func joinlist(l []string) string { return strings.Join(l, string(filepath.ListSeparator)) } // stripext strips the extension from a filename. // The extension is defined by filepath.Ext. func stripext(path string) string { return path[:len(path)-len(filepath.Ext(path))] } gb-0.3.2/gb14.go000066400000000000000000000001031262556506200131730ustar00rootroot00000000000000// +build !go1.5 package gb const ( gc14 = true gc15 = false ) gb-0.3.2/gb15.go000066400000000000000000000001021262556506200131730ustar00rootroot00000000000000// +build go1.5 package gb const ( gc14 = false gc15 = true ) gb-0.3.2/gb_test.go000066400000000000000000000005531262556506200140760ustar00rootroot00000000000000package gb import "testing" func TestStripext(t *testing.T) { tests := []struct { path, want string }{ {"a.txt", "a"}, {"a.a.txt", "a.a"}, {"Makefile", "Makefile"}, {"", ""}, {"/", "/"}, } for _, tt := range tests { got := stripext(tt.path) if got != tt.want { t.Errorf("stripext(%q): want: %v, got: %v", tt.path, tt.want, got) } } } gb-0.3.2/gc.go000066400000000000000000000123111262556506200130330ustar00rootroot00000000000000package gb import ( "bytes" "fmt" "go/build" "io" "io/ioutil" "os" "path/filepath" "runtime" ) // gc toolchain type gcToolchain struct { gc, cc, ld, as, pack string } func GcToolchain() func(c *Context) error { return func(c *Context) error { // TODO(dfc) this should come from the context, not the runtime. goroot := runtime.GOROOT() if gc14 && (c.gohostos != c.gotargetos || c.gohostarch != c.gotargetarch) { // cross-compliation is not supported yet #31 return fmt.Errorf("cross compilation from host %s/%s to target %s/%s not supported with Go 1.4", c.gohostos, c.gohostarch, c.gotargetos, c.gotargetarch) } tooldir := filepath.Join(goroot, "pkg", "tool", c.gohostos+"_"+c.gohostarch) exe := "" if c.gohostos == "windows" { exe += ".exe" } switch { case gc14: archchar, err := build.ArchChar(c.gotargetarch) if err != nil { return err } c.tc = &gcToolchain{ gc: filepath.Join(tooldir, archchar+"g"+exe), ld: filepath.Join(tooldir, archchar+"l"+exe), as: filepath.Join(tooldir, archchar+"a"+exe), cc: filepath.Join(tooldir, archchar+"c"+exe), pack: filepath.Join(tooldir, "pack"+exe), } case gc15: c.tc = &gcToolchain{ gc: filepath.Join(tooldir, "compile"+exe), ld: filepath.Join(tooldir, "link"+exe), as: filepath.Join(tooldir, "asm"+exe), pack: filepath.Join(tooldir, "pack"+exe), } default: return fmt.Errorf("unsupported Go version: %v", runtime.Version) } return nil } } func (t *gcToolchain) Asm(pkg *Package, srcdir, ofile, sfile string) error { args := []string{"-o", ofile, "-D", "GOOS_" + pkg.gotargetos, "-D", "GOARCH_" + pkg.gotargetarch} switch { case gc14: includedir := filepath.Join(pkg.Context.Context.GOROOT, "pkg", pkg.gotargetos+"_"+pkg.gotargetarch) args = append(args, "-I", includedir) case gc15: odir := filepath.Join(filepath.Dir(ofile)) includedir := filepath.Join(runtime.GOROOT(), "pkg", "include") args = append(args, "-I", odir, "-I", includedir) default: return fmt.Errorf("unsupported Go version: %v", runtime.Version) } args = append(args, sfile) if err := mkdir(filepath.Dir(ofile)); err != nil { return fmt.Errorf("gc:asm: %v", err) } var buf bytes.Buffer err := runOut(&buf, srcdir, nil, t.as, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } func (t *gcToolchain) Ld(pkg *Package, searchpaths []string, outfile, afile string) error { // to ensure we don't write a partial binary, link the binary to a temporary file in // in the target directory, then rename. dir := filepath.Dir(outfile) tmp, err := ioutil.TempFile(dir, ".gb-link") if err != nil { return err } tmp.Close() args := append(pkg.ldflags, "-o", tmp.Name()) for _, d := range searchpaths { args = append(args, "-L", d) } if gc15 { args = append(args, "-buildmode", pkg.buildmode) } args = append(args, afile) if err := mkdir(dir); err != nil { return err } var buf bytes.Buffer if err = runOut(&buf, ".", nil, t.ld, args...); err != nil { os.Remove(tmp.Name()) // remove partial file fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) return err } return os.Rename(tmp.Name(), outfile) } func (t *gcToolchain) Cc(pkg *Package, ofile, cfile string) error { if gc15 { return fmt.Errorf("gc15 does not support cc") } args := []string{ "-F", "-V", "-w", "-trimpath", pkg.Workdir(), "-I", Workdir(pkg), "-I", filepath.Join(pkg.Context.Context.GOROOT, "pkg", pkg.gohostos+"_"+pkg.gohostarch), // for runtime.h "-o", ofile, "-D", "GOOS_" + pkg.gotargetos, "-D", "GOARCH_" + pkg.gotargetarch, cfile, } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, t.cc, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } func (t *gcToolchain) Pack(pkg *Package, afiles ...string) error { args := []string{"r"} args = append(args, afiles...) dir := filepath.Dir(afiles[0]) var buf bytes.Buffer err := runOut(&buf, dir, nil, t.pack, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } func (t *gcToolchain) compiler() string { return t.gc } func (t *gcToolchain) linker() string { return t.ld } func (t *gcToolchain) Gc(pkg *Package, searchpaths []string, importpath, srcdir, outfile string, files []string) error { args := append(pkg.gcflags, "-p", importpath, "-pack") args = append(args, "-o", outfile) for _, d := range searchpaths { args = append(args, "-I", d) } if pkg.Standard && pkg.ImportPath == "runtime" { // runtime compiles with a special gc flag to emit // additional reflect type data. args = append(args, "-+") } if pkg.Complete() { args = append(args, "-complete") } else if gc15 { asmhdr := filepath.Join(filepath.Dir(outfile), pkg.Name, "go_asm.h") args = append(args, "-asmhdr", asmhdr) } args = append(args, files...) if err := mkdir(filepath.Join(filepath.Dir(outfile), pkg.Name)); err != nil { return fmt.Errorf("gc:gc: %v", err) } var buf bytes.Buffer err := runOut(&buf, srcdir, nil, t.gc, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } gb-0.3.2/install.go000066400000000000000000000044571262556506200141240ustar00rootroot00000000000000package gb import ( "os" "path/filepath" "time" ) // pkgdir returns the destination for object cached for this Package. func pkgdir(pkg *Package) string { if pkg.Scope == "test" { panic("pkgdir called with test scope") } return filepath.Join(pkg.Pkgdir(), filepath.Dir(filepath.FromSlash(pkg.ImportPath))) } func pkgfile(pkg *Package) string { return filepath.Join(pkgdir(pkg), filepath.Base(filepath.FromSlash(pkg.ImportPath))+".a") } // isStale returns true if the source pkg is considered to be stale with // respect to its installed version. func isStale(pkg *Package) bool { if pkg.Force { return true } // tests are always stale, they are never installed if pkg.Scope == "test" { return true } // Package is stale if completely unbuilt. var built time.Time if fi, err := os.Stat(pkgfile(pkg)); err == nil { built = fi.ModTime() } if built.IsZero() { return true } olderThan := func(file string) bool { fi, err := os.Stat(file) return err != nil || fi.ModTime().After(built) } newerThan := func(file string) bool { fi, err := os.Stat(file) return err != nil || fi.ModTime().Before(built) } // As a courtesy to developers installing new versions of the compiler // frequently, define that packages are stale if they are // older than the compiler, and commands if they are older than // the linker. This heuristic will not work if the binaries are // back-dated, as some binary distributions may do, but it does handle // a very common case. if olderThan(pkg.tc.compiler()) { return true } if pkg.IsCommand() && olderThan(pkg.tc.linker()) { return true } // Package is stale if a dependency is newer. for _, p := range pkg.Imports() { if olderThan(pkgfile(p)) { return true } } // if the main package is up to date but _newer_ than the binary (which // could have been removed), then consider it stale. if pkg.isMain() && newerThan(pkg.Binfile()) { return true } srcs := stringList(pkg.GoFiles, pkg.CFiles, pkg.CXXFiles, pkg.MFiles, pkg.HFiles, pkg.SFiles, pkg.CgoFiles, pkg.SysoFiles, pkg.SwigFiles, pkg.SwigCXXFiles) for _, src := range srcs { if olderThan(filepath.Join(pkg.Dir, src)) { return true } } return false } func stringList(args ...[]string) []string { var l []string for _, arg := range args { l = append(l, arg...) } return l } gb-0.3.2/install_test.go000066400000000000000000000023601262556506200151520ustar00rootroot00000000000000package gb import ( "os" "path/filepath" "testing" ) func TestStale(t *testing.T) { var tests = []struct { pkgs []string stale map[string]bool }{{ pkgs: []string{"a"}, stale: map[string]bool{ "a": true, }, }, { pkgs: []string{"a", "b"}, stale: map[string]bool{ "a": false, "b": true, }, }, { pkgs: []string{"a", "b"}, stale: map[string]bool{ "a": false, "b": false, }, }} root := mktemp(t) defer os.RemoveAll(root) proj := Project{ rootdir: root, srcdirs: []Srcdir{{ Root: filepath.Join(getwd(t), "testdata", "src"), }}, } newctx := func() *Context { ctx, err := proj.NewContext( GcToolchain(), ) if err != nil { t.Fatal(err) } return ctx } resolve := func(ctx *Context, pkg string) *Package { p, err := ctx.ResolvePackage(pkg) if err != nil { t.Fatal(err) } return p } for _, tt := range tests { ctx := newctx() defer ctx.Destroy() for _, pkg := range tt.pkgs { resolve(ctx, pkg) } for p, s := range tt.stale { pkg := resolve(ctx, p) if pkg.Stale != s { t.Errorf("%q.Stale: got %v, want %v", pkg.Name, pkg.Stale, s) } } for _, pkg := range tt.pkgs { if err := Build(resolve(ctx, pkg)); err != nil { t.Fatal(err) } } } } gb-0.3.2/package.go000066400000000000000000000101371262556506200140410ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "path/filepath" "strings" ) // Package represents a resolved package from the Project with respect to the Context. type Package struct { *Context *build.Package Scope string // scope: build, test, etc ExtraIncludes string // hook for test Stale bool // is the package out of date wrt. its cached copy Standard bool // is this package part of the standard library } // NewPackage creates a resolved Package. func NewPackage(ctx *Context, p *build.Package) *Package { pkg := Package{ Context: ctx, Package: p, } // seed pkg.c so calling result never blocks pkg.Stale = isStale(&pkg) return &pkg } // isMain returns true if this is a command, not being built in test scope, and // not the testmain itself. func (p *Package) isMain() bool { switch p.Scope { case "test": return strings.HasSuffix(p.ImportPath, "testmain") default: return p.Name == "main" } } // Imports returns the Pacakges that this Package depends on. func (p *Package) Imports() []*Package { pkgs := make([]*Package, 0, len(p.Package.Imports)) for _, i := range p.Package.Imports { if p.shouldignore(i) { continue } pkg, ok := p.pkgs[i] if !ok { panic("could not locate package: " + i) } pkgs = append(pkgs, pkg) } return pkgs } func (p *Package) String() string { return fmt.Sprintf("%v", struct { Name, ImportPath, Dir string }{ p.Name, p.ImportPath, p.Dir, }) } // Complete indicates if this is a pure Go package func (p *Package) Complete() bool { // If we're giving the compiler the entire package (no C etc files), tell it that, // so that it can give good error messages about forward declarations. // Exceptions: a few standard packages have forward declarations for // pieces supplied behind-the-scenes by package runtime. extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { case "bytes", "net", "os", "runtime/pprof", "sync", "time": extFiles++ } } return extFiles == 0 } // Binfile returns the destination of the compiled target of this command. func (pkg *Package) Binfile() string { // TODO(dfc) should have a check for package main, or should be merged in to objfile. var target string switch pkg.Scope { case "test": target = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test", binname(pkg)) default: target = filepath.Join(pkg.Bindir(), binname(pkg)) } // if this is a cross compile or there are build tags, add ctxString. if pkg.isCrossCompile() { target += "-" + pkg.ctxString() } else if len(pkg.buildtags) > 0 { target += "-" + strings.Join(pkg.buildtags, "-") } if pkg.gotargetos == "windows" { target += ".exe" } return target } // loadPackage recursively resolves path and its imports and if successful // stores those packages in the Context's internal package cache. func loadPackage(c *Context, stack []string, path string) (*Package, error) { if build.IsLocalImport(path) { // sanity check return nil, fmt.Errorf("%q is not a valid import path", path) } if pkg, ok := c.pkgs[path]; ok { // already loaded, just return return pkg, nil } push := func(path string) { stack = append(stack, path) } pop := func(path string) { stack = stack[:len(stack)-1] } onStack := func(path string) bool { for _, p := range stack { if p == path { return true } } return false } p, err := c.Context.Import(path, c.Projectdir(), 0) if err != nil { return nil, err } standard := p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") push(path) var stale bool for _, i := range p.Imports { if c.shouldignore(i) { continue } if onStack(i) { push(i) return nil, fmt.Errorf("import cycle detected: %s", strings.Join(stack, " -> ")) } pkg, err := loadPackage(c, stack, i) if err != nil { return nil, err } stale = stale || pkg.Stale } pop(path) pkg := Package{ Context: c, Package: p, Standard: standard, } pkg.Stale = stale || isStale(&pkg) c.pkgs[path] = &pkg return &pkg, nil } gb-0.3.2/package_test.go000066400000000000000000000065301262556506200151020ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "path/filepath" "runtime" "testing" ) func testProject(t *testing.T) *Project { cwd := getwd(t) root := filepath.Join(cwd, "testdata") return NewProject(root, SourceDir(filepath.Join(root, "src")), ) } func testContext(t *testing.T) *Context { prj := testProject(t) ctx, err := prj.NewContext() if err != nil { t.Fatal(err) } ctx.Force = true ctx.SkipInstall = true return ctx } func TestResolvePackage(t *testing.T) { ctx := testContext(t) defer ctx.Destroy() _, err := ctx.ResolvePackage("a") if err != nil { t.Fatal(err) } } func TestPackageBinfile(t *testing.T) { opts := func(o ...func(*Context) error) []func(*Context) error { return o } gotargetos := "windows" if runtime.GOOS == "windows" { gotargetos = "linux" } gotargetarch := "386" if runtime.GOARCH == "386" { gotargetarch = "amd64" } var tests = []struct { pkg string // package name opts []func(*Context) error want string // binfile result }{{ pkg: "b", want: "b", }, { pkg: "b", opts: opts(GOOS(gotargetos)), want: fmt.Sprintf("b-%v-%v", gotargetos, runtime.GOARCH), }, { pkg: "b", opts: opts(GOARCH(gotargetarch)), want: fmt.Sprintf("b-%v-%v", runtime.GOOS, gotargetarch), }, { pkg: "b", opts: opts(GOARCH(gotargetarch), GOOS(gotargetos)), want: fmt.Sprintf("b-%v-%v", gotargetos, gotargetarch), }, { pkg: "b", opts: opts(Tags("lol")), want: "b-lol", }, { pkg: "b", opts: opts(GOARCH(gotargetarch), GOOS(gotargetos), Tags("lol")), want: fmt.Sprintf("b-%v-%v-lol", gotargetos, gotargetarch), }} proj := testProject(t) for i, tt := range tests { ctx, err := proj.NewContext(tt.opts...) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Fatal(err) } got := pkg.Binfile() want := filepath.Join(ctx.Bindir(), tt.want) if pkg.gotargetos == "windows" { want += ".exe" } if want != got { t.Errorf("test %v: (%s).Binfile(): want %s, got %s", i+1, tt.pkg, want, got) } } } func TestPackageIsMain(t *testing.T) { var tests = []struct { pkg *Package want bool }{{ pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "main", }, }, want: true, }, { pkg: &Package{ Package: &build.Package{ Name: "a", ImportPath: "main", }, }, want: false, }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a", }, }, want: true, }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "testmain", }, }, want: true, }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "main", }, Scope: "test", }, want: false, }, { pkg: &Package{ Package: &build.Package{ Name: "a", ImportPath: "main", }, Scope: "test", }, want: false, }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a", }, Scope: "test", }, want: false, }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "testmain", }, Scope: "test", }, want: true, }} for _, tt := range tests { got := tt.pkg.isMain() if got != tt.want { t.Errorf("Package{Name:%q, ImportPath: %q, Scope:%q}.isMain(): got %v, want %v", tt.pkg.Name, tt.pkg.ImportPath, tt.pkg.Scope, got, tt.want) } } } gb-0.3.2/project.go000066400000000000000000000032271262556506200141160ustar00rootroot00000000000000package gb import ( "path/filepath" ) // Project represents a gb project. A gb project has a simlar layout to // a $GOPATH workspace. Each gb project has a standard directory layout // starting at the project root, which we'll refer too as $PROJECT. // // $PROJECT/ - the project root // $PROJECT/src/ - base directory for the source of packages // $PROJECT/bin/ - base directory for the compiled binaries type Project struct { rootdir string srcdirs []Srcdir } func togopath(srcdirs []string) string { var s []string for _, srcdir := range srcdirs { s = append(s, filepath.Dir(srcdir)) } return joinlist(s) } func SourceDir(root string) func(*Project) { return func(p *Project) { p.srcdirs = append(p.srcdirs, Srcdir{Root: root}) } } func NewProject(root string, options ...func(*Project)) *Project { proj := Project{ rootdir: root, } for _, opt := range options { opt(&proj) } return &proj } // Pkgdir returns the path to precompiled packages. func (p *Project) Pkgdir() string { return filepath.Join(p.rootdir, "pkg") } // Projectdir returns the path root of this project. func (p *Project) Projectdir() string { return p.rootdir } // Srcdirs returns the path to the source directories. // The first source directory will always be // filepath.Join(Projectdir(), "src") // but there may be additional directories. func (p *Project) Srcdirs() []string { var dirs []string for _, s := range p.srcdirs { dirs = append(dirs, s.Root) } return dirs } // Bindir returns the path for compiled programs. func (p *Project) Bindir() string { return filepath.Join(p.rootdir, "bin") } gb-0.3.2/resolve.go000066400000000000000000000015541262556506200141300ustar00rootroot00000000000000package gb import ( "fmt" "path/filepath" ) // Resolver resolves packages. type Resolver interface { // ResolvePackage resolves the import path to a *Package ResolvePackage(path string) (*Package, error) } // ResolvePackages resolves import paths to packages. func ResolvePackages(r Resolver, paths ...string) ([]*Package, error) { var pkgs []*Package for _, path := range paths { pkg, err := r.ResolvePackage(path) if err != nil { return pkgs, fmt.Errorf("failed to resolve import path %q: %v", path, err) } pkgs = append(pkgs, pkg) } return pkgs, nil } func relImportPath(root, path string) (string, error) { if isRel(path) { return filepath.Rel(root, path) } return path, nil } // isRel returns if an import path is relative or absolute. func isRel(path string) bool { // TODO(dfc) should this be strings.StartsWith(".") return path == "." } gb-0.3.2/resolve_test.go000066400000000000000000000042431262556506200151650ustar00rootroot00000000000000package gb import ( "fmt" "io/ioutil" "os" "path/filepath" "testing" ) func TestResolvePackages(t *testing.T) { cwd := getwd(t) root := filepath.Join(cwd, "testdata", "src") tests := []struct { paths []string err error }{ {paths: []string{"a"}}, {paths: []string{"."}, err: fmt.Errorf("failed to resolve import path %q: %q is not a package", ".", root)}, {paths: []string{"h"}, err: fmt.Errorf("failed to resolve import path %q: no buildable Go source files in %s", "h", filepath.Join(root, "blank"))}, } for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() _, err := ResolvePackages(ctx, tt.paths...) if !sameErr(err, tt.err) { t.Errorf("ResolvePackage(%v): want: %v, got %v", tt.paths, tt.err, err) } } } var join = filepath.Join // makeTestData constructs func makeTestdata(t *testing.T) string { tempdir, err := filepath.EvalSymlinks(os.TempDir()) if err != nil { t.Fatal(err) } root, err := ioutil.TempDir(tempdir, "path-test") if err != nil { t.Fatal(err) } mkdir := func(args ...string) string { path := join(args...) if err := os.MkdirAll(path, 0777); err != nil { t.Fatal(err) } return path } mkfile := func(path string, content string) { if err := ioutil.WriteFile(path, []byte(content), 0755); err != nil { t.Fatal(err) } } srcdir := mkdir(root, "src") mkfile(join(mkdir(srcdir, "a"), "a.go"), "package a") return root } func TestRelImportPath(t *testing.T) { tests := []struct { root, path, want string }{ {"/project/src", "a", "a"}, // { "/project/src", "./a", "a"}, // TODO(dfc) this is relative // { "/project/src", "a/../b", "a"}, // TODO(dfc) so is this } for _, tt := range tests { got, _ := relImportPath(tt.root, tt.path) if got != tt.want { t.Errorf("relImportPath(%q, %q): want: %v, got: %v", tt.root, tt.path, tt.want, got) } } } func TestIsRel(t *testing.T) { tests := []struct { path string want bool }{ {".", true}, {"..", false}, // TODO(dfc) this is relative {"a/../b", false}, // TODO(dfc) this too } for _, tt := range tests { got := isRel(tt.path) if got != tt.want { t.Errorf("isRel(%q): want: %v, got: %v", tt.want, got) } } } gb-0.3.2/src.go000066400000000000000000000003211262556506200132270ustar00rootroot00000000000000package gb type Srcdir struct { // Root is the root directory of this Srcdir. Root string // Prefix is an optional import path prefix applied // to any package resolved via this Srcdir. Prefix string } gb-0.3.2/stdlib.go000066400000000000000000000077621262556506200137410ustar00rootroot00000000000000package gb // packages from the standard lib. They are excluded // from the package map. var stdlib = map[string]bool{ "C": true, "archive/tar": true, "archive/zip": true, "bufio": true, "bytes": true, "compress/bzip2": true, "compress/flate": true, "compress/gzip": true, "compress/lzw": true, "compress/zlib": true, "container/heap": true, "container/list": true, "container/ring": true, "crypto": true, "crypto/aes": true, "crypto/cipher": true, "crypto/des": true, "crypto/dsa": true, "crypto/ecdsa": true, "crypto/elliptic": true, "crypto/hmac": true, "crypto/md5": true, "crypto/rand": true, "crypto/rc4": true, "crypto/rsa": true, "crypto/sha1": true, "crypto/sha256": true, "crypto/sha512": true, "crypto/subtle": true, "crypto/tls": true, "crypto/x509": true, "crypto/x509/pkix": true, "database/sql": true, "database/sql/driver": true, "debug/dwarf": true, "debug/elf": true, "debug/gosym": true, "debug/macho": true, "debug/pe": true, "encoding": true, "encoding/ascii85": true, "encoding/asn1": true, "encoding/base32": true, "encoding/base64": true, "encoding/binary": true, "encoding/csv": true, "encoding/gob": true, "encoding/hex": true, "encoding/json": true, "encoding/pem": true, "encoding/xml": true, "errors": true, "expvar": true, "flag": true, "fmt": true, "go/ast": true, "go/build": true, "go/doc": true, "go/format": true, "go/parser": true, "go/printer": true, "go/scanner": true, "go/token": true, "hash": true, "hash/adler32": true, "hash/crc32": true, "hash/crc64": true, "hash/fnv": true, "html": true, "html/template": true, "image": true, "image/color": true, "image/draw": true, "image/gif": true, "image/jpeg": true, "image/png": true, "index/suffixarray": true, "io": true, "io/ioutil": true, "log": true, "log/syslog": true, "math": true, "math/big": true, "math/cmplx": true, "math/rand": true, "mime": true, "mime/multipart": true, "net": true, "net/http": true, "net/http/cgi": true, "net/http/cookiejar": true, "net/http/fcgi": true, "net/http/httptest": true, "net/http/httputil": true, "net/http/pprof": true, "net/mail": true, "net/rpc": true, "net/rpc/jsonrpc": true, "net/smtp": true, "net/textproto": true, "net/url": true, "os": true, "os/exec": true, "os/signal": true, "os/user": true, "path": true, "path/filepath": true, "reflect": true, "regexp": true, "regexp/syntax": true, "runtime": true, "runtime/cgo": true, "runtime/debug": true, "runtime/pprof": true, "sort": true, "strconv": true, "strings": true, "sync": true, "sync/atomic": true, "syscall": true, "testing": true, "testing/iotest": true, "testing/quick": true, "text/scanner": true, "text/tabwriter": true, "text/template": true, "text/template/parse": true, "time": true, "unicode": true, "unicode/utf16": true, "unicode/utf8": true, "unsafe": true, } gb-0.3.2/test/000077500000000000000000000000001262556506200130745ustar00rootroot00000000000000gb-0.3.2/test/gotest.go000066400000000000000000000211611262556506200147310ustar00rootroot00000000000000// Copyright 2011 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 test // imported from $GOROOT/src/cmd/go/test.go import ( "bytes" "errors" "go/ast" "go/build" "go/doc" "go/parser" "go/scanner" "go/token" "os" "path/filepath" "sort" "strings" "text/template" "unicode" "unicode/utf8" "github.com/constabulary/gb" "github.com/constabulary/gb/debug" ) type coverInfo struct { Package *gb.Package Vars map[string]*CoverVar } // CoverVar holds the name of the generated coverage variables targeting the named file. type CoverVar struct { File string // local file name Var string // name of count struct } var cwd, _ = os.Getwd() // shortPath returns an absolute or relative name for path, whatever is shorter. func shortPath(path string) string { if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) { return rel } return path } // isTestMain tells whether fn is a TestMain(m *testing.M) function. func isTestMain(fn *ast.FuncDecl) bool { if fn.Name.String() != "TestMain" || fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || fn.Type.Params == nil || len(fn.Type.Params.List) != 1 || len(fn.Type.Params.List[0].Names) > 1 { return false } ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) if !ok { return false } // We can't easily check that the type is *testing.M // because we don't know how testing has been imported, // but at least check that it's *M or *something.M. if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { return true } if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { return true } return false } // isTest tells whether name looks like a test (or benchmark, according to prefix). // It is a Test (say) if there is a character after Test that is not a lower-case letter. // We don't want TesticularCancer. func isTest(name, prefix string) bool { if !strings.HasPrefix(name, prefix) { return false } if len(name) == len(prefix) { // "Test" is ok return true } rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) return !unicode.IsLower(rune) } // loadTestFuncs returns the testFuncs describing the tests that will be run. func loadTestFuncs(ptest *build.Package) (*testFuncs, error) { t := &testFuncs{ Package: ptest, } debug.Debugf("loadTestFuncs: %v, %v", ptest.TestGoFiles, ptest.XTestGoFiles) for _, file := range ptest.TestGoFiles { if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil { return nil, err } } for _, file := range ptest.XTestGoFiles { if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil { return nil, err } } return t, nil } // writeTestmain writes the _testmain.go file for t to the file named out. func writeTestmain(out string, t *testFuncs) error { f, err := os.Create(out) if err != nil { return err } defer f.Close() if err := testmainTmpl.Execute(f, t); err != nil { return err } return nil } // expandScanner expands a scanner.List error into all the errors in the list. // The default Error method only shows the first error. func expandScanner(err error) error { // Look for parser errors. if err, ok := err.(scanner.ErrorList); ok { // Prepare error with \n before each message. // When printed in something like context: %v // this will put the leading file positions each on // its own line. It will also show all the errors // instead of just the first, as err.Error does. var buf bytes.Buffer for _, e := range err { e.Pos.Filename = shortPath(e.Pos.Filename) buf.WriteString("\n") buf.WriteString(e.Error()) } return errors.New(buf.String()) } return err } type testFuncs struct { Tests []testFunc Benchmarks []testFunc Examples []testFunc TestMain *testFunc Package *build.Package ImportTest bool NeedTest bool ImportXtest bool NeedXtest bool NeedCgo bool Cover []coverInfo } func (t *testFuncs) CoverMode() string { return "" } func (t *testFuncs) CoverEnabled() bool { return false } // Covered returns a string describing which packages are being tested for coverage. // If the covered package is the same as the tested package, it returns the empty string. // Otherwise it is a comma-separated human-readable list of packages beginning with // " in", ready for use in the coverage message. func (t *testFuncs) Covered() string { return "" } // Tested returns the name of the package being tested. func (t *testFuncs) Tested() string { return t.Package.Name } type testFunc struct { Package string // imported package name (_test or _xtest) Name string // function name Output string // output, for examples } var testFileSet = token.NewFileSet() func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) if err != nil { return expandScanner(err) } for _, d := range f.Decls { n, ok := d.(*ast.FuncDecl) if !ok { continue } if n.Recv != nil { continue } name := n.Name.String() switch { case isTestMain(n): if t.TestMain != nil { return errors.New("multiple definitions of TestMain") } t.TestMain = &testFunc{pkg, name, ""} *doImport, *seen = true, true case isTest(name, "Test"): t.Tests = append(t.Tests, testFunc{pkg, name, ""}) *doImport, *seen = true, true case isTest(name, "Benchmark"): t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""}) *doImport, *seen = true, true } } ex := doc.Examples(f) sort.Sort(byOrder(ex)) for _, e := range ex { *doImport = true // import test file whether executed or not if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output}) *seen = true } return nil } type byOrder []*doc.Example func (x byOrder) Len() int { return len(x) } func (x byOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order } var testmainTmpl = template.Must(template.New("main").Parse(` package main import ( {{if not .TestMain}} "os" {{end}} "regexp" "testing" {{if .ImportTest}} {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} {{end}} {{if .ImportXtest}} {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} {{end}} {{range $i, $p := .Cover}} _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} {{end}} {{if .NeedCgo}} _ "runtime/cgo" {{end}} ) var tests = []testing.InternalTest{ {{range .Tests}} {"{{.Name}}", {{.Package}}.{{.Name}}}, {{end}} } var benchmarks = []testing.InternalBenchmark{ {{range .Benchmarks}} {"{{.Name}}", {{.Package}}.{{.Name}}}, {{end}} } var examples = []testing.InternalExample{ {{range .Examples}} {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}}, {{end}} } var matchPat string var matchRe *regexp.Regexp func matchString(pat, str string) (result bool, err error) { if matchRe == nil || matchPat != pat { matchPat = pat matchRe, err = regexp.Compile(matchPat) if err != nil { return } } return matchRe.MatchString(str), nil } {{if .CoverEnabled}} // Only updated by init functions, so no need for atomicity. var ( coverCounters = make(map[string][]uint32) coverBlocks = make(map[string][]testing.CoverBlock) ) func init() { {{range $i, $p := .Cover}} {{range $file, $cover := $p.Vars}} coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) {{end}} {{end}} } func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { panic("coverage: mismatched sizes") } if coverCounters[fileName] != nil { // Already registered. return } coverCounters[fileName] = counter block := make([]testing.CoverBlock, len(counter)) for i := range counter { block[i] = testing.CoverBlock{ Line0: pos[3*i+0], Col0: uint16(pos[3*i+2]), Line1: pos[3*i+1], Col1: uint16(pos[3*i+2]>>16), Stmts: numStmts[i], } } coverBlocks[fileName] = block } {{end}} func main() { {{if .CoverEnabled}} testing.RegisterCover(testing.Cover{ Mode: {{printf "%q" .CoverMode}}, Counters: coverCounters, Blocks: coverBlocks, CoveredPackages: {{printf "%q" .Covered}}, }) {{end}} m := testing.MainStart(matchString, tests, benchmarks, examples) {{with .TestMain}} {{.Package}}.{{.Name}}(m) {{else}} os.Exit(m.Run()) {{end}} } `)) gb-0.3.2/test/resolve.go000066400000000000000000000013271262556506200151050ustar00rootroot00000000000000package test import ( "github.com/constabulary/gb" ) // TestResolver returns a gb.Resolver that resolves packages, their // dependencies including any internal or external test dependencies. func TestResolver(r gb.Resolver) gb.Resolver { return &testResolver{r} } type testResolver struct { gb.Resolver } func (r *testResolver) ResolvePackage(path string) (*gb.Package, error) { p, err := r.Resolver.ResolvePackage(path) if err != nil { return nil, err } var imports []string imports = append(imports, p.Package.TestImports...) imports = append(imports, p.Package.XTestImports...) for _, i := range imports { _, err := r.Resolver.ResolvePackage(i) if err != nil { return nil, err } } return p, nil } gb-0.3.2/test/test.go000066400000000000000000000147411262556506200144110ustar00rootroot00000000000000package test import ( "bytes" "fmt" "go/build" "io" "os" "os/exec" "path" "path/filepath" "strings" "time" "github.com/constabulary/gb" "github.com/constabulary/gb/debug" ) // Test returns a Target representing the result of compiling the // package pkg, and its dependencies, and linking it with the // test runner. func Test(flags []string, pkgs ...*gb.Package) error { test, err := TestPackages(flags, pkgs...) if err != nil { return err } return gb.Execute(test) } // TestPackages produces a graph of Actions that when executed build // and test the supplied packages. func TestPackages(flags []string, pkgs ...*gb.Package) (*gb.Action, error) { if len(pkgs) < 1 { return nil, fmt.Errorf("no test packages provided") } targets := make(map[string]*gb.Action) // maps package import paths to their test run action names := func(pkgs []*gb.Package) []string { var names []string for _, pkg := range pkgs { names = append(names, pkg.ImportPath) } return names } // create top level test action to root all test actions t0 := time.Now() test := gb.Action{ Name: fmt.Sprintf("test: %s", strings.Join(names(pkgs), ",")), Run: func() error { debug.Debugf("test duration: %v %v", time.Since(t0), pkgs[0].Statistics.String()) return nil }, } for _, pkg := range pkgs { a, err := TestPackage(targets, pkg, flags) if err != nil { return nil, err } if a == nil { // nothing to do ?? not even a test action ? continue } test.Deps = append(test.Deps, a) } return &test, nil } // TestPackage returns an Action representing the steps required to build // and test this Package. func TestPackage(targets map[string]*gb.Action, pkg *gb.Package, flags []string) (*gb.Action, error) { var gofiles []string gofiles = append(gofiles, pkg.GoFiles...) gofiles = append(gofiles, pkg.TestGoFiles...) var cgofiles []string cgofiles = append(cgofiles, pkg.CgoFiles...) var imports []string imports = append(imports, pkg.Package.Imports...) imports = append(imports, pkg.Package.TestImports...) name := pkg.Name if name == "main" { // rename the main package to its package name for testing. name = filepath.Base(filepath.FromSlash(pkg.ImportPath)) } // internal tests testpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath, Dir: pkg.Dir, SrcRoot: pkg.SrcRoot, GoFiles: gofiles, CFiles: pkg.CFiles, CgoFiles: cgofiles, TestGoFiles: pkg.TestGoFiles, // passed directly to buildTestMain XTestGoFiles: pkg.XTestGoFiles, // passed directly to buildTestMain CgoCFLAGS: pkg.CgoCFLAGS, CgoCPPFLAGS: pkg.CgoCPPFLAGS, CgoCXXFLAGS: pkg.CgoCXXFLAGS, CgoLDFLAGS: pkg.CgoLDFLAGS, CgoPkgConfig: pkg.CgoPkgConfig, Imports: imports, }) testpkg.Scope = "test" testpkg.Stale = true // only build the internal test if there is Go source or // internal test files. var testobj *gb.Action if len(testpkg.GoFiles)+len(testpkg.CgoFiles)+len(testpkg.TestGoFiles) > 0 { // build internal testpkg dependencies deps, err := gb.BuildDependencies(targets, testpkg) if err != nil { return nil, err } testobj, err = gb.Compile(testpkg, deps...) if err != nil { return nil, err } } // external tests if len(pkg.XTestGoFiles) > 0 { xtestpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, }) // build external test dependencies deps, err := gb.BuildDependencies(targets, xtestpkg) if err != nil { return nil, err } xtestpkg.Scope = "test" xtestpkg.Stale = true xtestpkg.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") // if there is an internal test object, add it as a dependency. if testobj != nil { deps = append(deps, testobj) } testobj, err = gb.Compile(xtestpkg, deps...) if err != nil { return nil, err } } testmainpkg, err := buildTestMain(testpkg) if err != nil { return nil, err } testmain, err := gb.Compile(testmainpkg, testobj) if err != nil { return nil, err } return &gb.Action{ Name: fmt.Sprintf("run: %s", testmainpkg.Binfile()), Deps: testmain.Deps, Run: func() error { // When used with the concurrent executor, building deps and // linking the test binary can cause a lot of disk space to be // pinned as linking will tend to occur more frequenty than retiring // tests. // // To solve this, we merge the testmain compile step (which includes // linking) and the test run and cleanup steps so they are executed // as one atomic operation. var output bytes.Buffer err := testmain.Run() // compile and link if err == nil { cmd := exec.Command(testmainpkg.Binfile(), flags...) cmd.Dir = pkg.Dir // tests run in the original source directory cmd.Stdout = &output cmd.Stderr = &output err = cmd.Run() // run test // test binaries can be very large, so always unlink the // binary after the test has run to free up temporary space // technically this is done by ctx.Destroy(), but freeing // the space earlier is important for projects with many // packages os.Remove(testmainpkg.Binfile()) } if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stdout, &output) } else { fmt.Println(pkg.ImportPath) } return err }, }, nil } func buildTestMain(pkg *gb.Package) (*gb.Package, error) { if pkg.Scope != "test" { return nil, fmt.Errorf("package %q is not test scoped", pkg.Name) } dir := gb.Workdir(pkg) if err := mkdir(dir); err != nil { return nil, fmt.Errorf("buildTestmain: %v", err) } tests, err := loadTestFuncs(pkg.Package) if err != nil { return nil, err } if len(pkg.Package.XTestGoFiles) > 0 { // if there are external tests ensure that we import the // test package into the final binary for side effects. tests.ImportXtest = true } if err := writeTestmain(filepath.Join(dir, "_testmain.go"), tests); err != nil { return nil, err } testmain := gb.NewPackage(pkg.Context, &build.Package{ Name: pkg.Name, ImportPath: path.Join(pkg.ImportPath, "testmain"), Dir: dir, SrcRoot: pkg.SrcRoot, GoFiles: []string{"_testmain.go"}, Imports: pkg.Package.Imports, }) testmain.Scope = "test" testmain.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") return testmain, nil } func mkdir(path string) error { return os.MkdirAll(path, 0755) } gb-0.3.2/test/test_test.go000066400000000000000000000114611262556506200154440ustar00rootroot00000000000000package test import ( "os" "path/filepath" "reflect" "sort" "strings" "testing" "time" "github.com/constabulary/gb" ) func TestTest(t *testing.T) { tests := []struct { pkg string testArgs []string ldflags []string err error }{ { pkg: "a", err: nil, }, { pkg: "b", err: nil, }, { pkg: "c", err: nil, }, { pkg: "e", err: nil, }, { pkg: "cmd/f", err: nil, }, { pkg: "extest", // test external tests err: nil, }, { pkg: "external_only_test", // issue 312 err: nil, }, { pkg: "notestfiles", err: nil, }, { pkg: "cgoonlynotest", err: nil, }, { pkg: "testonly", err: nil, }, { pkg: "extestonly", err: nil, }, { pkg: "g", // test that _test files can modify the internal package under test err: nil, }, { pkg: "ldflags", ldflags: []string{"-X", "ldflags.gitTagInfo", "banana", "-X", "ldflags.gitRevision", "f7926af2"}, }, { pkg: "cgotest", }, { pkg: "testflags", testArgs: []string{"-debug"}, }, { pkg: "main", // issue 375, a package called main }} for _, tt := range tests { ctx := testContext(t, gb.Ldflags(tt.ldflags...)) defer ctx.Destroy() r := TestResolver(ctx) pkg, err := r.ResolvePackage(tt.pkg) if err != nil { t.Errorf("ResolvePackage(%v): want %v, got %v", tt.pkg, tt.err, err) continue } if err := Test(tt.testArgs, pkg); err != tt.err { t.Errorf("Test(%v): want %v, got %v", tt.pkg, tt.err, err) time.Sleep(500 * time.Millisecond) } } } func TestTestPackage(t *testing.T) { tests := []struct { pkg string err error }{{ pkg: "a", err: nil, }, { pkg: "b", // actually command err: nil, }, { pkg: "c", err: nil, }, { pkg: "d.v1", err: nil, }, { pkg: "cgomain", err: nil, }, { pkg: "cgotest", err: nil, }, { pkg: "notestfiles", err: nil, }, { pkg: "cgoonlynotest", err: nil, }, { pkg: "testonly", err: nil, }, { pkg: "extestonly", err: nil, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Errorf("ctx.ResolvePackage(%v): %v", tt.pkg, err) continue } targets := make(map[string]*gb.Action) if _, err := TestPackage(targets, pkg, nil); !sameErr(err, tt.err) { t.Errorf("TestPackage(%v): want %v, got %v", tt.pkg, tt.err, err) } } } func TestTestPackages(t *testing.T) { tests := []struct { pkgs []string actions []string err error }{{ pkgs: []string{"a", "b", "c"}, actions: []string{ "run: $WORKDIR/a/testmain/_test/a.test$EXE", "run: $WORKDIR/b/testmain/_test/b.test$EXE", "run: $WORKDIR/c/testmain/_test/c.test$EXE", }, }, { pkgs: []string{"cgotest", "cgomain", "notestfiles", "cgoonlynotest", "testonly", "extestonly"}, actions: []string{ "run: $WORKDIR/cgomain/testmain/_test/cgomain.test$EXE", "run: $WORKDIR/cgoonlynotest/testmain/_test/cgoonly.test$EXE", "run: $WORKDIR/cgotest/testmain/_test/cgotest.test$EXE", "run: $WORKDIR/extestonly/testmain/_test/extestonly.test$EXE", "run: $WORKDIR/notestfiles/testmain/_test/notest.test$EXE", "run: $WORKDIR/testonly/testmain/_test/testonly.test$EXE", }, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() var pkgs []*gb.Package for _, pkg := range tt.pkgs { pkg, err := ctx.ResolvePackage(pkg) if err != nil { t.Errorf("ctx.ResolvePackage(%v): %v", pkg, err) continue } pkgs = append(pkgs, pkg) } a, err := TestPackages(nil, pkgs...) if !sameErr(err, tt.err) { t.Errorf("TestPackages(%v): want %v, got %v", pkgs, tt.err, err) } var actual []string for _, a := range a.Deps { actual = append(actual, a.Name) } sort.Strings(actual) var expected []string exe := "" if ctx.Context.GOOS == "windows" { exe = ".exe" } for _, s := range tt.actions { s = filepath.FromSlash(s) s = strings.Replace(s, "$WORKDIR", ctx.Workdir(), -1) s = strings.Replace(s, "$EXE", exe, -1) expected = append(expected, s) } if !reflect.DeepEqual(expected, actual) { t.Errorf("TestBuildPackages(%v): want %v, got %v", pkgs, expected, actual) } } } func getwd(t *testing.T) string { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } return cwd } func testProject(t *testing.T) *gb.Project { cwd := getwd(t) root := filepath.Join(cwd, "..", "testdata") return gb.NewProject(root, gb.SourceDir(filepath.Join(root, "src")), ) } func testContext(t *testing.T, opts ...func(*gb.Context) error) *gb.Context { prj := testProject(t) opts = append([]func(*gb.Context) error{gb.GcToolchain()}, opts...) ctx, err := prj.NewContext(opts...) if err != nil { t.Fatal(err) } ctx.Force = true ctx.SkipInstall = true return ctx } func sameErr(e1, e2 error) bool { if e1 != nil && e2 != nil { return e1.Error() == e2.Error() } return e1 == e2 } gb-0.3.2/testdata/000077500000000000000000000000001262556506200137265ustar00rootroot00000000000000gb-0.3.2/testdata/src/000077500000000000000000000000001262556506200145155ustar00rootroot00000000000000gb-0.3.2/testdata/src/a/000077500000000000000000000000001262556506200147355ustar00rootroot00000000000000gb-0.3.2/testdata/src/a/a.go000066400000000000000000000000311262556506200154760ustar00rootroot00000000000000package a const A = "A" gb-0.3.2/testdata/src/a/a_test.go000066400000000000000000000001061262556506200165400ustar00rootroot00000000000000package a import "testing" func TestA(t *testing.T) { t.Log("A") } gb-0.3.2/testdata/src/aprime/000077500000000000000000000000001262556506200157725ustar00rootroot00000000000000gb-0.3.2/testdata/src/aprime/a.go000066400000000000000000000000311262556506200165330ustar00rootroot00000000000000package a const A = "A" gb-0.3.2/testdata/src/b/000077500000000000000000000000001262556506200147365ustar00rootroot00000000000000gb-0.3.2/testdata/src/b/b.go000066400000000000000000000000701262556506200155030ustar00rootroot00000000000000package main import "a" func main() { println(a.A) } gb-0.3.2/testdata/src/blank/000077500000000000000000000000001262556506200156045ustar00rootroot00000000000000gb-0.3.2/testdata/src/blank/.turd000066400000000000000000000000001262556506200165510ustar00rootroot00000000000000gb-0.3.2/testdata/src/c/000077500000000000000000000000001262556506200147375ustar00rootroot00000000000000gb-0.3.2/testdata/src/c/c.go000066400000000000000000000001001262556506200154770ustar00rootroot00000000000000package c import "a" import "d.v1" const C = a.A var D = d.D gb-0.3.2/testdata/src/c/c_test.go000066400000000000000000000001041262556506200165420ustar00rootroot00000000000000package c import "testing" func TestC(t *testing.T) { t.Log(C) } gb-0.3.2/testdata/src/cgomain/000077500000000000000000000000001262556506200161325ustar00rootroot00000000000000gb-0.3.2/testdata/src/cgomain/main.go000066400000000000000000000004331262556506200174050ustar00rootroot00000000000000package main // typedef int (*intFunc) (); // // int // bridge_int_func(intFunc f) // { // return f(); // } // // int fortytwo() // { // return 42; // } import "C" import "fmt" func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f))) // Output: 42 } gb-0.3.2/testdata/src/cgoonlynotest/000077500000000000000000000000001262556506200174245ustar00rootroot00000000000000gb-0.3.2/testdata/src/cgoonlynotest/cgoonly.go000066400000000000000000000002371262556506200214270ustar00rootroot00000000000000package cgoonly /* int add(int x, int y) { return x+y; }; */ import "C" func Add(x, y int) int { return int(C.add(_Ctype_int(x), _Ctype_int(y))) } gb-0.3.2/testdata/src/cgotest/000077500000000000000000000000001262556506200161655ustar00rootroot00000000000000gb-0.3.2/testdata/src/cgotest/basic.go000066400000000000000000000056001262556506200175760ustar00rootroot00000000000000// 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. // Basic test cases for cgo. package cgotest /* #include #include #include #include #define SHIFT(x, y) ((x)<<(y)) #define KILO SHIFT(1, 10) #define UINT32VAL 0xc008427bU enum E { Enum1 = 1, Enum2 = 2, }; typedef unsigned char cgo_uuid_t[20]; void uuid_generate(cgo_uuid_t x) { x[0] = 0; } struct S { int x; }; extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } // issue 1222 typedef union { long align; } xxpthread_mutex_t; struct ibv_async_event { union { int x; } element; }; struct ibv_context { xxpthread_mutex_t mutex; }; int add(int x, int y) { return x+y; }; */ import "C" import ( "runtime" "syscall" "testing" "unsafe" ) const EINVAL = C.EINVAL /* test #define */ var KILO = C.KILO func uuidgen() { var uuid C.cgo_uuid_t C.uuid_generate(&uuid[0]) } func Strtol(s string, base int) (int, error) { p := C.CString(s) n, err := C.strtol(p, nil, C.int(base)) C.free(unsafe.Pointer(p)) return int(n), err } func Atol(s string) int { p := C.CString(s) n := C.atol(p) C.free(unsafe.Pointer(p)) return int(n) } func testConst(t *testing.T) { C.myConstFunc(nil, 0, nil) } func testEnum(t *testing.T) { if C.Enum1 != 1 || C.Enum2 != 2 { t.Error("bad enum", C.Enum1, C.Enum2) } } func testAtol(t *testing.T) { l := Atol("123") if l != 123 { t.Error("Atol 123: ", l) } } func testErrno(t *testing.T) { p := C.CString("no-such-file") m := C.CString("r") f, err := C.fopen(p, m) C.free(unsafe.Pointer(p)) C.free(unsafe.Pointer(m)) if err == nil { C.fclose(f) t.Fatalf("C.fopen: should fail") } if err != syscall.ENOENT { t.Fatalf("C.fopen: unexpected error: %v", err) } } func testMultipleAssign(t *testing.T) { p := C.CString("234") n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) if runtime.GOOS == "openbsd" { // Bug in OpenBSD strtol(3) - base > 36 succeeds. if (n != 0 && n != 239089) || m != 234 { t.Fatal("Strtol x2: ", n, m) } } else if n != 0 || m != 234 { t.Fatal("Strtol x2: ", n, m) } C.free(unsafe.Pointer(p)) } var ( cuint = (C.uint)(0) culong C.ulong cchar C.char ) type Context struct { ctx *C.struct_ibv_context } func benchCgoCall(b *testing.B) { const x = C.int(2) const y = C.int(3) for i := 0; i < b.N; i++ { C.add(x, y) } } // Issue 2470. func testUnsignedInt(t *testing.T) { a := (int64)(C.UINT32VAL) b := (int64)(0xc008427b) if a != b { t.Errorf("Incorrect unsigned int - got %x, want %x", a, b) } } // Static (build-time) test that syntax traversal visits all operands of s[i:j:k]. func sliceOperands(array [2000]int) { _ = array[C.KILO:C.KILO:C.KILO] // no type error } gb-0.3.2/testdata/src/cgotest/cgo_test.go000066400000000000000000000073361262556506200203340ustar00rootroot00000000000000// Copyright 2011 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 cgotest import "testing" // The actual test functions are in non-_test.go files // so that they can use cgo (import "C"). // These wrappers are here for gotest to find. // func TestAlign(t *testing.T) { testAlign(t) } func TestConst(t *testing.T) { testConst(t) } func TestEnum(t *testing.T) { testEnum(t) } func TestAtol(t *testing.T) { testAtol(t) } func TestErrno(t *testing.T) { testErrno(t) } func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } /*func TestCallback(t *testing.T) { testCallback(t) } func TestCallbackGC(t *testing.T) { testCallbackGC(t) } func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } func TestPanicFromC(t *testing.T) { testPanicFromC(t) } func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } func TestBlocking(t *testing.T) { testBlocking(t) } func Test1328(t *testing.T) { test1328(t) } func TestParallelSleep(t *testing.T) { testParallelSleep(t) } func TestSetEnv(t *testing.T) { testSetEnv(t) } func TestHelpers(t *testing.T) { testHelpers(t) } func TestLibgcc(t *testing.T) { testLibgcc(t) } func Test1635(t *testing.T) { test1635(t) } func TestPrintf(t *testing.T) { testPrintf(t) } func Test4029(t *testing.T) { test4029(t) } func TestBoolAlign(t *testing.T) { testBoolAlign(t) } func Test3729(t *testing.T) { test3729(t) } func Test3775(t *testing.T) { test3775(t) } func TestCthread(t *testing.T) { testCthread(t) } func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } func Test5227(t *testing.T) { test5227(t) } func TestCflags(t *testing.T) { testCflags(t) } func Test5337(t *testing.T) { test5337(t) } func Test5548(t *testing.T) { test5548(t) } func Test5603(t *testing.T) { test5603(t) } func Test6833(t *testing.T) { test6833(t) } func Test3250(t *testing.T) { test3250(t) } func TestCallbackStack(t *testing.T) { testCallbackStack(t) } func TestFpVar(t *testing.T) { testFpVar(t) } func Test4339(t *testing.T) { test4339(t) } func Test6390(t *testing.T) { test6390(t) } func Test5986(t *testing.T) { test5986(t) } func Test7665(t *testing.T) { test7665(t) } func TestNaming(t *testing.T) { testNaming(t) } func Test7560(t *testing.T) { test7560(t) } func Test5242(t *testing.T) { test5242(t) } func Test8092(t *testing.T) { test8092(t) } func Test7978(t *testing.T) { test7978(t) } func Test8694(t *testing.T) { test8694(t) } func Test8517(t *testing.T) { test8517(t) } func Test8811(t *testing.T) { test8811(t) } func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } func Test9026(t *testing.T) { test9026(t) } func Test9557(t *testing.T) { test9557(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } */ gb-0.3.2/testdata/src/cmd/000077500000000000000000000000001262556506200152605ustar00rootroot00000000000000gb-0.3.2/testdata/src/cmd/f/000077500000000000000000000000001262556506200155055ustar00rootroot00000000000000gb-0.3.2/testdata/src/cmd/f/main.go000066400000000000000000000000321262556506200167530ustar00rootroot00000000000000package main const X = 7 gb-0.3.2/testdata/src/cmd/f/main_test.go000066400000000000000000000001411262556506200200130ustar00rootroot00000000000000package main import "testing" func TestX(t *testing.T) { if X != 7 { t.Fatal("X != 7") } } gb-0.3.2/testdata/src/cppmain/000077500000000000000000000000001262556506200161445ustar00rootroot00000000000000gb-0.3.2/testdata/src/cppmain/main.cpp000066400000000000000000000002671262556506200176010ustar00rootroot00000000000000typedef int (*intFunc) (); extern "C" int bridge_int_func(intFunc f); extern "C" int fortytwo(); int bridge_int_func(intFunc f) { return f(); } int fortytwo() { return 42; } gb-0.3.2/testdata/src/cppmain/main.go000066400000000000000000000003351262556506200174200ustar00rootroot00000000000000package main /* typedef int (*intFunc) (); int bridge_int_func(intFunc f); int fortytwo(); */ import "C" import "fmt" func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f))) // Output: 42 } gb-0.3.2/testdata/src/cycle0/000077500000000000000000000000001262556506200156745ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle0/cycle.go000066400000000000000000000000371262556506200173220ustar00rootroot00000000000000package cycle import "cycle0" gb-0.3.2/testdata/src/cycle1/000077500000000000000000000000001262556506200156755ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle1/a/000077500000000000000000000000001262556506200161155ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle1/a/a.go000066400000000000000000000000421262556506200166600ustar00rootroot00000000000000package cyclea import "cycle1/b" gb-0.3.2/testdata/src/cycle1/b/000077500000000000000000000000001262556506200161165ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle1/b/b.go000066400000000000000000000000421262556506200166620ustar00rootroot00000000000000package cycleb import "cycle1/b" gb-0.3.2/testdata/src/cycle2/000077500000000000000000000000001262556506200156765ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle2/a/000077500000000000000000000000001262556506200161165ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle2/a/a.go000066400000000000000000000000431262556506200166620ustar00rootroot00000000000000package cycle2a import "cycle2/b" gb-0.3.2/testdata/src/cycle2/b/000077500000000000000000000000001262556506200161175ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle2/b/b.go000066400000000000000000000000431262556506200166640ustar00rootroot00000000000000package cycle2b import "cycle2/c" gb-0.3.2/testdata/src/cycle2/c/000077500000000000000000000000001262556506200161205ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle2/c/c.go000066400000000000000000000000431262556506200166660ustar00rootroot00000000000000package cycle2c import "cycle2/d" gb-0.3.2/testdata/src/cycle2/d/000077500000000000000000000000001262556506200161215ustar00rootroot00000000000000gb-0.3.2/testdata/src/cycle2/d/d.go000066400000000000000000000000431262556506200166700ustar00rootroot00000000000000package cycle2d import "cycle2/a" gb-0.3.2/testdata/src/d.v1/000077500000000000000000000000001262556506200152655ustar00rootroot00000000000000gb-0.3.2/testdata/src/d.v1/d.go000066400000000000000000000000271262556506200160360ustar00rootroot00000000000000package d var D = "d" gb-0.3.2/testdata/src/e/000077500000000000000000000000001262556506200147415ustar00rootroot00000000000000gb-0.3.2/testdata/src/e/e.go000066400000000000000000000000311262556506200155060ustar00rootroot00000000000000package e const E = "e" gb-0.3.2/testdata/src/e/e_test.go000066400000000000000000000001771262556506200165600ustar00rootroot00000000000000package e import "testing" import "f" // only imported in internal test scope func TestE(t *testing.T) { t.Log(f.F > 0.9) } gb-0.3.2/testdata/src/external_only_test/000077500000000000000000000000001262556506200204375ustar00rootroot00000000000000gb-0.3.2/testdata/src/external_only_test/x_test.go000066400000000000000000000001261262556506200222730ustar00rootroot00000000000000package x_test import "testing" func TestT(t *testing.T) { t.Log("all good brah") } gb-0.3.2/testdata/src/extest/000077500000000000000000000000001262556506200160315ustar00rootroot00000000000000gb-0.3.2/testdata/src/extest/q.go000066400000000000000000000000321262556506200166130ustar00rootroot00000000000000package extest var V = 1 gb-0.3.2/testdata/src/extest/q_test.go000066400000000000000000000001661262556506200176620ustar00rootroot00000000000000package extest import "testing" func TestV(t *testing.T) { if V != 0 { t.Fatalf("V: got %v, expected 0", V) } } gb-0.3.2/testdata/src/extest/q_test_test.go000066400000000000000000000001041262556506200207110ustar00rootroot00000000000000package extest_test import "extest" func init() { extest.V = 0 } gb-0.3.2/testdata/src/extestonly/000077500000000000000000000000001262556506200167335ustar00rootroot00000000000000gb-0.3.2/testdata/src/extestonly/x_test.go000066400000000000000000000001241262556506200205650ustar00rootroot00000000000000package extestonly_test import "testing" func TestT(t *testing.T) { t.Log("ok") } gb-0.3.2/testdata/src/f/000077500000000000000000000000001262556506200147425ustar00rootroot00000000000000gb-0.3.2/testdata/src/f/f.go000066400000000000000000000000311262556506200155100ustar00rootroot00000000000000package f const F = 1.0 gb-0.3.2/testdata/src/g/000077500000000000000000000000001262556506200147435ustar00rootroot00000000000000gb-0.3.2/testdata/src/g/export_test.go000066400000000000000000000000451262556506200176510ustar00rootroot00000000000000package g const WHATEVER = whatever gb-0.3.2/testdata/src/g/g.go000066400000000000000000000000361262556506200155170ustar00rootroot00000000000000package g const whatever = 0 gb-0.3.2/testdata/src/g/g_test.go000066400000000000000000000001441262556506200165560ustar00rootroot00000000000000package g_test import "g" import "testing" func TestWhatever(t *testing.T) { t.Log(g.WHATEVER) } gb-0.3.2/testdata/src/h/000077500000000000000000000000001262556506200147445ustar00rootroot00000000000000gb-0.3.2/testdata/src/h/h.go000066400000000000000000000001361262556506200155220ustar00rootroot00000000000000package h import "blank" // go/build.NoGoErr const b = blank.Blank // should be build error gb-0.3.2/testdata/src/ldflags/000077500000000000000000000000001262556506200161315ustar00rootroot00000000000000gb-0.3.2/testdata/src/ldflags/ldflags.go000066400000000000000000000000721262556506200200730ustar00rootroot00000000000000package ldflags var gitRevision string // set by linker gb-0.3.2/testdata/src/ldflags/ldflags_test.go000066400000000000000000000003751262556506200211400ustar00rootroot00000000000000package ldflags import "testing" var gitTagInfo string // set by linker func TestLdflags(t *testing.T) { if gitTagInfo != "banana" { t.Error("gitTagInfo:", gitTagInfo) } if gitRevision != "f7926af2" { t.Error("gitRevision:", gitRevision) } } gb-0.3.2/testdata/src/main/000077500000000000000000000000001262556506200154415ustar00rootroot00000000000000gb-0.3.2/testdata/src/main/main.go000066400000000000000000000000151262556506200167100ustar00rootroot00000000000000package main gb-0.3.2/testdata/src/mainnoruntime/000077500000000000000000000000001262556506200174025ustar00rootroot00000000000000gb-0.3.2/testdata/src/mainnoruntime/main.go000066400000000000000000000002601262556506200206530ustar00rootroot00000000000000package main // no imports, so nothing imports runtime directly or transitively // however the runtime contains the code for println. func main() { println("hello world") } gb-0.3.2/testdata/src/nested/000077500000000000000000000000001262556506200157775ustar00rootroot00000000000000gb-0.3.2/testdata/src/nested/a/000077500000000000000000000000001262556506200162175ustar00rootroot00000000000000gb-0.3.2/testdata/src/nested/a/a.go000066400000000000000000000000311262556506200167600ustar00rootroot00000000000000package a const A = `a` gb-0.3.2/testdata/src/nested/b/000077500000000000000000000000001262556506200162205ustar00rootroot00000000000000gb-0.3.2/testdata/src/nested/b/b.go000066400000000000000000000000311262556506200167620ustar00rootroot00000000000000package b const A = `b` gb-0.3.2/testdata/src/notestfiles/000077500000000000000000000000001262556506200170545ustar00rootroot00000000000000gb-0.3.2/testdata/src/notestfiles/notest.go000066400000000000000000000000361262556506200207160ustar00rootroot00000000000000package notest const X = "y" gb-0.3.2/testdata/src/tags1/000077500000000000000000000000001262556506200155345ustar00rootroot00000000000000gb-0.3.2/testdata/src/tags1/t.go000066400000000000000000000000511262556506200163220ustar00rootroot00000000000000// +build !x package tags1 const X = 1 gb-0.3.2/testdata/src/tags2/000077500000000000000000000000001262556506200155355ustar00rootroot00000000000000gb-0.3.2/testdata/src/tags2/t.go000066400000000000000000000000501262556506200163220ustar00rootroot00000000000000// +build x package tags2 const X = 2 gb-0.3.2/testdata/src/testflags/000077500000000000000000000000001262556506200165115ustar00rootroot00000000000000gb-0.3.2/testdata/src/testflags/testflags.go000066400000000000000000000000221262556506200210260ustar00rootroot00000000000000package testflags gb-0.3.2/testdata/src/testflags/testflags_test.go000066400000000000000000000003551262556506200220760ustar00rootroot00000000000000package testflags import ( "flag" "testing" ) var debug bool func init() { flag.BoolVar(&debug, "debug", false, "Enable debug output.") flag.Parse() } func TestDebug(t *testing.T) { if !debug { t.Error("debug not true!") } } gb-0.3.2/testdata/src/testonly/000077500000000000000000000000001262556506200163765ustar00rootroot00000000000000gb-0.3.2/testdata/src/testonly/x_test.go000066400000000000000000000001151262556506200202300ustar00rootroot00000000000000package testonly import "testing" func TestT(t *testing.T) { t.Log("ok") } gb-0.3.2/testdata/src/x/000077500000000000000000000000001262556506200147645ustar00rootroot00000000000000gb-0.3.2/testdata/src/x/x.go000066400000000000000000000000261262556506200155600ustar00rootroot00000000000000package x import "y" gb-0.3.2/testdata/src/y/000077500000000000000000000000001262556506200147655ustar00rootroot00000000000000gb-0.3.2/testdata/src/y/y.go000066400000000000000000000000261262556506200155620ustar00rootroot00000000000000package y import "x" gb-0.3.2/vendor/000077500000000000000000000000001262556506200134125ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/000077500000000000000000000000001262556506200153625ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/src/000077500000000000000000000000001262556506200161515ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/src/github.com/000077500000000000000000000000001262556506200202105ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/src/github.com/foo/000077500000000000000000000000001262556506200207735ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/src/github.com/foo/bar/000077500000000000000000000000001262556506200215375ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/src/github.com/foo/bar/main.go000066400000000000000000000004131262556506200230100ustar00rootroot00000000000000package main import ( "fmt" "github.com/quux/flobble" // "bitbucket.org/fwoop/ftang" // commented out, this is deliberate moo "github.com/lypo/moopo" ) import "github.com/hoo/wuu" func main() { fmt.Println(flobble.Q) fmt.Prinln(moo.Q) fmt.Println(wuu.Q) } gb-0.3.2/vendor/_testdata/vendor/000077500000000000000000000000001262556506200166575ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/000077500000000000000000000000001262556506200174465ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/bitbucket.org/000077500000000000000000000000001262556506200222105ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/bitbucket.org/fwoop/000077500000000000000000000000001262556506200233425ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/bitbucket.org/fwoop/ftang/000077500000000000000000000000001262556506200244415ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/bitbucket.org/fwoop/ftang/kthulu.go000066400000000000000000000000421262556506200263000ustar00rootroot00000000000000package ftang const CAT = "ack!" gb-0.3.2/vendor/_testdata/vendor/src/github.com/000077500000000000000000000000001262556506200215055ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/hoo/000077500000000000000000000000001262556506200222725ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/hoo/wuu/000077500000000000000000000000001262556506200231125ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/hoo/wuu/goo.go000066400000000000000000000000351262556506200242230ustar00rootroot00000000000000package wuu const Q = "hey" gb-0.3.2/vendor/_testdata/vendor/src/github.com/lypo/000077500000000000000000000000001262556506200224705ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/lypo/moopo/000077500000000000000000000000001262556506200236215ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/lypo/moopo/tropo.go000066400000000000000000000000361262556506200253120ustar00rootroot00000000000000package moopo const Q = "hi" gb-0.3.2/vendor/_testdata/vendor/src/github.com/quux/000077500000000000000000000000001262556506200225075ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/quux/flobble/000077500000000000000000000000001262556506200241145ustar00rootroot00000000000000gb-0.3.2/vendor/_testdata/vendor/src/github.com/quux/flobble/wobble.go000066400000000000000000000000431262556506200257120ustar00rootroot00000000000000package flobble const Q = "hello" gb-0.3.2/vendor/copy.go000066400000000000000000000026461262556506200147230ustar00rootroot00000000000000package vendor import ( "fmt" "io" "os" "path/filepath" "strings" "github.com/constabulary/gb/fileutils" ) const debugCopypath = true const debugCopyfile = false // Copypath copies the contents of src to dst, excluding any file or // directory that starts with a period. func Copypath(dst string, src string) error { err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if strings.HasPrefix(filepath.Base(path), ".") { if info.IsDir() { return filepath.SkipDir } return nil } if info.IsDir() { return nil } if info.Mode()&os.ModeSymlink != 0 { if debugCopypath { fmt.Printf("skipping symlink: %v\n", path) } return nil } dst := filepath.Join(dst, path[len(src):]) return copyfile(dst, path) }) if err != nil { // if there was an error during copying, remove the partial copy. fileutils.RemoveAll(dst) } return err } func copyfile(dst, src string) error { err := mkdir(filepath.Dir(dst)) if err != nil { return fmt.Errorf("copyfile: mkdirall: %v", err) } r, err := os.Open(src) if err != nil { return fmt.Errorf("copyfile: open(%q): %v", src, err) } defer r.Close() w, err := os.Create(dst) if err != nil { return fmt.Errorf("copyfile: create(%q): %v", dst, err) } defer w.Close() if debugCopyfile { fmt.Printf("copyfile(dst: %v, src: %v)\n", dst, src) } _, err = io.Copy(w, r) return err } gb-0.3.2/vendor/depset.go000066400000000000000000000043631262556506200152330ustar00rootroot00000000000000package vendor import ( "fmt" "go/build" "os" "path/filepath" "strings" ) // Pkg describes a Go package. type Pkg struct { *Depset *build.Package } // Depset describes a set of related Go packages. type Depset struct { Root string Prefix string Pkgs map[string]*Pkg } // LoadPaths returns a map of paths to Depsets. func LoadPaths(paths ...struct{ Root, Prefix string }) (map[string]*Depset, error) { m := make(map[string]*Depset) for _, p := range paths { set, err := LoadTree(p.Root, p.Prefix) if err != nil { return nil, err } m[set.Root] = set } return m, nil } // LoadTree parses a tree of source files into a map of *pkgs. func LoadTree(root string, prefix string) (*Depset, error) { d := Depset{ Root: root, Prefix: prefix, Pkgs: make(map[string]*Pkg), } fn := func(dir string, fi os.FileInfo) error { importpath := filepath.Join(prefix, dir[len(root)+1:]) // if we're at the root of a tree, skip it if importpath == "" { return nil } p, err := loadPackage(&d, dir) if err != nil { if _, ok := err.(*build.NoGoError); ok { return nil } return fmt.Errorf("loadPackage(%q, %q): %v", dir, importpath, err) } p.ImportPath = filepath.ToSlash(importpath) if p != nil { d.Pkgs[p.ImportPath] = p } return nil } // handle root of the tree fi, err := os.Stat(root) if err != nil { return nil, err } if err := fn(root+string(filepath.Separator), fi); err != nil { return nil, err } // walk sub directories err = eachDir(root, fn) return &d, err } func loadPackage(d *Depset, dir string) (*Pkg, error) { p := Pkg{ Depset: d, } var err error // expolit local import logic p.Package, err = build.ImportDir(dir, build.ImportComment) return &p, err } func eachDir(dir string, fn func(string, os.FileInfo) error) error { f, err := os.Open(dir) if err != nil { return err } defer f.Close() files, err := f.Readdir(-1) for _, fi := range files { if !fi.IsDir() { continue } if strings.HasPrefix(fi.Name(), "_") || strings.HasPrefix(fi.Name(), ".") || fi.Name() == "testdata" { continue } path := filepath.Join(dir, fi.Name()) if err := fn(path, fi); err != nil { return err } if err := eachDir(path, fn); err != nil { return err } } return nil } gb-0.3.2/vendor/discovery.go000066400000000000000000000041751262556506200157570ustar00rootroot00000000000000// Copyright 2012 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 vendor import ( "encoding/xml" "fmt" "io" "strings" ) // charsetReader returns a reader for the given charset. Currently // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful // error which is printed by go get, so the user can find why the package // wasn't downloaded if the encoding is not supported. Note that, in // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters // greater than 0x7f are not rejected). func charsetReader(charset string, input io.Reader) (io.Reader, error) { switch strings.ToLower(charset) { case "ascii": return input, nil default: return nil, fmt.Errorf("can't decode XML document using charset %q", charset) } } type metaImport struct { Prefix, VCS, RepoRoot string } // parseMetaGoImports returns meta imports from the HTML in r. // Parsing ends at the end of the section or the beginning of the . func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { d := xml.NewDecoder(r) d.CharsetReader = charsetReader d.Strict = false var t xml.Token for { t, err = d.Token() if err != nil { if err == io.EOF { err = nil } return } if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { return } if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { return } e, ok := t.(xml.StartElement) if !ok || !strings.EqualFold(e.Name.Local, "meta") { continue } if attrValue(e.Attr, "name") != "go-import" { continue } if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { imports = append(imports, metaImport{ Prefix: f[0], VCS: f[1], RepoRoot: f[2], }) } } } // attrValue returns the attribute value for the case-insensitive key // `name', or the empty string if nothing is found. func attrValue(attrs []xml.Attr, name string) string { for _, a := range attrs { if strings.EqualFold(a.Name.Local, name) { return a.Value } } return "" } gb-0.3.2/vendor/imports.go000066400000000000000000000047171262556506200154470ustar00rootroot00000000000000package vendor import ( "fmt" "go/parser" "go/token" "io" "net/http" "os" "path/filepath" "strings" ) // ParseImports parses Go packages from a specific root returning a set of import paths. func ParseImports(root string) (map[string]bool, error) { pkgs := make(map[string]bool) var walkFn = func(path string, info os.FileInfo, err error) error { if info.IsDir() { name := info.Name() if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" { return filepath.SkipDir } return nil } if filepath.Ext(path) != ".go" { // Parse only go source files return nil } fs := token.NewFileSet() f, err := parser.ParseFile(fs, path, nil, parser.ImportsOnly) if err != nil { return err } for _, s := range f.Imports { p := strings.Replace(s.Path.Value, "\"", "", -1) if !contains(stdlib, p) { pkgs[p] = true } } return nil } err := filepath.Walk(root, walkFn) return pkgs, err } // FetchMetadata fetchs the remote metadata for path. func FetchMetadata(path string, insecure bool) (rc io.ReadCloser, err error) { defer func() { if err != nil { err = fmt.Errorf("unable to determine remote metadata protocol: %s", err) } }() // try https first rc, err = fetchMetadata("https", path) if err == nil { return } // try http if supported if insecure { rc, err = fetchMetadata("http", path) } return } func fetchMetadata(scheme, path string) (io.ReadCloser, error) { url := fmt.Sprintf("%s://%s?go-get=1", scheme, path) switch scheme { case "https", "http": resp, err := http.Get(url) if err != nil { return nil, fmt.Errorf("failed to access url %q", url) } return resp.Body, nil default: return nil, fmt.Errorf("unknown remote protocol scheme: %q", scheme) } } // ParseMetadata fetchs and decodes remote metadata for path. func ParseMetadata(path string, insecure bool) (string, string, string, error) { rc, err := FetchMetadata(path, insecure) if err != nil { return "", "", "", err } defer rc.Close() imports, err := parseMetaGoImports(rc) if err != nil { return "", "", "", err } match := -1 for i, im := range imports { if !strings.HasPrefix(path, im.Prefix) { continue } if match != -1 { return "", "", "", fmt.Errorf("multiple meta tags match import path %q", path) } match = i } if match == -1 { return "", "", "", fmt.Errorf("go-import metadata not found") } return imports[match].Prefix, imports[match].VCS, imports[match].RepoRoot, nil } gb-0.3.2/vendor/imports_test.go000066400000000000000000000102251262556506200164750ustar00rootroot00000000000000package vendor import ( "bytes" "fmt" "io" "os" "path/filepath" "reflect" "testing" ) func TestParseImports(t *testing.T) { root := filepath.Join(getwd(t), "_testdata", "src") got, err := ParseImports(root) if err != nil { t.Fatalf("ParseImports(%q): %v", root, err) } want := set("github.com/quux/flobble", "github.com/lypo/moopo", "github.com/hoo/wuu") if !reflect.DeepEqual(got, want) { t.Fatalf("ParseImports(%q): want: %v, got %v", root, want, got) } } func TestFetchMetadata(t *testing.T) { if testing.Short() { t.Skipf("skipping network tests in -short mode") } type testParams struct { path string want string insecure bool } tests := []testParams{{ path: "golang.org/x/tools/cmd/godoc", want: ` Nothing to see here; move along. `, }, { path: "gopkg.in/check.v1", want: ` go get gopkg.in/check.v1 `, }} for _, tt := range tests { r, err := FetchMetadata(tt.path, tt.insecure) if err != nil { t.Error(err) continue } var buf bytes.Buffer if _, err := io.Copy(&buf, r); err != nil { t.Error(err) r.Close() continue } r.Close() got := buf.String() if got != tt.want { t.Errorf("FetchMetadata(%q): want %q, got %q", tt.path, tt.want, got) } } // Test for error catch. errTests := []testParams{{ path: "any.inaccessible.server/the.project", want: `unable to determine remote metadata protocol: failed to access url "http://any.inaccessible.server/the.project?go-get=1"`, insecure: true, }, { path: "any.inaccessible.server/the.project", want: `unable to determine remote metadata protocol: failed to access url "https://any.inaccessible.server/the.project?go-get=1"`, insecure: false, }} for _, ett := range errTests { r, err := FetchMetadata(ett.path, ett.insecure) if err == nil { t.Errorf("Access to url %q without any error, but the error should be happen.", ett.path) if r != nil { r.Close() } continue } got := err.Error() if got != ett.want { t.Errorf("FetchMetadata(%q): want %q, got %q", ett.path, ett.want, got) } } } func TestParseMetadata(t *testing.T) { if testing.Short() { t.Skipf("skipping network tests in -short mode") } tests := []struct { path string importpath string vcs string reporoot string insecure bool err error }{{ path: "golang.org/x/tools/cmd/godoc", importpath: "golang.org/x/tools", vcs: "git", reporoot: "https://go.googlesource.com/tools", }, { path: "gopkg.in/check.v1", importpath: "gopkg.in/check.v1", vcs: "git", reporoot: "https://gopkg.in/check.v1", }, { path: "gopkg.in/mgo.v2/bson", importpath: "gopkg.in/mgo.v2", vcs: "git", reporoot: "https://gopkg.in/mgo.v2", }, { path: "speter.net/go/exp", err: fmt.Errorf("go-import metadata not found"), }} for _, tt := range tests { importpath, vcs, reporoot, err := ParseMetadata(tt.path, tt.insecure) if !reflect.DeepEqual(err, tt.err) { t.Error(err) continue } if importpath != tt.importpath || vcs != tt.vcs || reporoot != tt.reporoot { t.Errorf("ParseMetadata(%q): want %s %s %s, got %s %s %s ", tt.path, tt.importpath, tt.vcs, tt.reporoot, importpath, vcs, reporoot) } } } func getwd(t *testing.T) string { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } return cwd } gb-0.3.2/vendor/manifest.go000066400000000000000000000100171262556506200155460ustar00rootroot00000000000000package vendor import ( "bytes" "encoding/json" "fmt" "io" "os" "reflect" "sort" ) // gb-vendor manifest support // Manifest describes the layout of $PROJECT/vendor/manifest. type Manifest struct { // Manifest version. Current manifest version is 0. Version int `json:"version"` // Depenencies is a list of vendored dependencies. Dependencies []Dependency `json:"dependencies"` } // AddDependency adds a Dependency to the current Manifest. // If the dependency exists already then it returns and error. func (m *Manifest) AddDependency(dep Dependency) error { if m.HasImportpath(dep.Importpath) { return fmt.Errorf("already registered") } m.Dependencies = append(m.Dependencies, dep) return nil } // RemoveDependency removes a Dependency from the current Manifest. // If the dependency does not exist then it returns an error. func (m *Manifest) RemoveDependency(dep Dependency) error { for i, d := range m.Dependencies { if reflect.DeepEqual(d, dep) { m.Dependencies = append(m.Dependencies[:i], m.Dependencies[i+1:]...) return nil } } return fmt.Errorf("dependency does not exist") } // HasImportpath reports whether the Manifest contains the import path. func (m *Manifest) HasImportpath(path string) bool { _, err := m.GetDependencyForImportpath(path) return err == nil } // GetDependencyForRepository return a dependency for specified URL // If the dependency does not exist it returns an error func (m *Manifest) GetDependencyForImportpath(path string) (Dependency, error) { for _, d := range m.Dependencies { if d.Importpath == path { return d, nil } } return Dependency{}, fmt.Errorf("dependency for %s does not exist", path) } // Dependency describes one vendored import path of code // A Dependency is an Importpath sources from a Respository // at Revision from Path. type Dependency struct { // Importpath is name by which this dependency is known. Importpath string `json:"importpath"` // Repository is the remote DVCS location that this // dependency was fetched from. Repository string `json:"repository"` // Revision is the revision that describes the dependency's // remote revision. Revision string `json:"revision"` // Branch is the branch the Revision was located on. // Can be blank if not needed. Branch string `json:"branch"` // Path is the path inside the Repository where the // dependency was fetched from. Path string `json:"path,omitempty"` } // WriteManifest writes a Manifest to the path. If the manifest does // not exist, it is created. If it does exist, it will be overwritten. // If the manifest file is empty (0 dependencies) it will be deleted. // The dependencies will be ordered by import path to reduce churn when making // changes. // TODO(dfc) write to temporary file and move atomically to avoid // destroying a working vendorfile. func WriteManifest(path string, m *Manifest) error { if len(m.Dependencies) == 0 { err := os.Remove(path) if !os.IsNotExist(err) { return err } return nil } f, err := os.Create(path) if err != nil { return err } if err := writeManifest(f, m); err != nil { f.Close() return err } return f.Close() } func writeManifest(w io.Writer, m *Manifest) error { sort.Sort(byImportpath(m.Dependencies)) buf, err := json.MarshalIndent(m, "", "\t") if err != nil { return err } _, err = io.Copy(w, bytes.NewReader(buf)) return err } // ReadManifest reads a Manifest from path. If the Manifest is not // found, a blank Manifest will be returned. func ReadManifest(path string) (*Manifest, error) { f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return new(Manifest), nil } return nil, err } defer f.Close() return readManifest(f) } func readManifest(r io.Reader) (*Manifest, error) { var m Manifest d := json.NewDecoder(r) err := d.Decode(&m) return &m, err } type byImportpath []Dependency func (s byImportpath) Len() int { return len(s) } func (s byImportpath) Less(i, j int) bool { return s[i].Importpath < s[j].Importpath } func (s byImportpath) Swap(i, j int) { s[i], s[j] = s[j], s[i] } gb-0.3.2/vendor/manifest_test.go000066400000000000000000000041511262556506200166070ustar00rootroot00000000000000package vendor import ( "bytes" "os" "path/filepath" "testing" "github.com/constabulary/gb/fileutils" ) func mktemp(t *testing.T) string { s, err := mktmp() if err != nil { t.Fatal(err) } return s } func assertNotExists(t *testing.T, path string) { _, err := os.Stat(path) if err == nil || !os.IsNotExist(err) { t.Fatalf("expected %q to be not found, got %v", path, err) } } func assertExists(t *testing.T, path string) { _, err := os.Stat(path) if err != nil { t.Fatalf("expected %q to be found, got %v", path, err) } } func TestManifest(t *testing.T) { root := mktemp(t) defer fileutils.RemoveAll(root) mf := filepath.Join(root, "vendor") // check that reading an non existant manifest // does not return an error m, err := ReadManifest(mf) if err != nil { t.Fatalf("reading a non existant manifest should not fail: %v", err) } // check that no manifest file was created assertNotExists(t, mf) // add a dep m.Dependencies = append(m.Dependencies, Dependency{ Importpath: "github.com/foo/bar/baz", Repository: "https://github.com/foo/bar", Revision: "cafebad", Branch: "master", Path: "/baz", }) // write it back if err := WriteManifest(mf, m); err != nil { t.Fatalf("WriteManifest failed: %v", err) } // check the manifest was written assertExists(t, mf) // remove it m.Dependencies = nil if err := WriteManifest(mf, m); err != nil { t.Fatalf("WriteManifest failed: %v", err) } // check that no manifest file was removed assertNotExists(t, mf) } func TestEmptyPathIsNotWritten(t *testing.T) { m := Manifest{ Version: 0, Dependencies: []Dependency{{ Importpath: "github.com/foo/bar", Repository: "https://github.com/foo/bar", Revision: "abcdef", Branch: "master", }}, } var buf bytes.Buffer if err := writeManifest(&buf, &m); err != nil { t.Fatal(err) } want := `{ "version": 0, "dependencies": [ { "importpath": "github.com/foo/bar", "repository": "https://github.com/foo/bar", "revision": "abcdef", "branch": "master" } ] }` got := buf.String() if want != got { t.Fatalf("want: %s, got %s", want, got) } } gb-0.3.2/vendor/repo.go000066400000000000000000000320661262556506200147150ustar00rootroot00000000000000package vendor import ( "bytes" "fmt" "io" "io/ioutil" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/constabulary/gb/fileutils" ) // RemoteRepo describes a remote dvcs repository. type RemoteRepo interface { // Checkout checks out a specific branch, tag, or revision. // The interpretation of these three values is impementation // specific. Checkout(branch, tag, revision string) (WorkingCopy, error) // URL returns the URL the clone was taken from. It should // only be called after Clone. URL() string } // WorkingCopy represents a local copy of a remote dvcs repository. type WorkingCopy interface { // Dir is the root of this working copy. Dir() string // Revision returns the revision of this working copy. Revision() (string, error) // Branch returns the branch to which this working copy belongs. Branch() (string, error) // Destroy removes the working copy and cleans path to the working copy. Destroy() error } var ( ghregex = regexp.MustCompile(`^(?Pgithub\.com/([A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`) bbregex = regexp.MustCompile(`^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`) lpregex = regexp.MustCompile(`^launchpad.net/([A-Za-z0-9-._]+)(/[A-Za-z0-9-._]+)?(/.+)?`) gcregex = regexp.MustCompile(`^(?Pcode\.google\.com/[pr]/(?P[a-z0-9\-]+)(\.(?P[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`) genericre = regexp.MustCompile(`^(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?Pbzr|git|hg|svn))([/A-Za-z0-9_.\-]+)*$`) ) // DeduceRemoteRepo takes a potential import path and returns a RemoteRepo // representing the remote location of the source of an import path. // Remote repositories can be bare import paths, or urls including a checkout scheme. // If deduction would cause traversal of an insecure host, a message will be // printed and the travelsal path will be ignored. func DeduceRemoteRepo(path string, insecure bool) (RemoteRepo, string, error) { u, err := url.Parse(path) if err != nil { return nil, "", fmt.Errorf("%q is not a valid import path", path) } var schemes []string if u.Scheme != "" { schemes = append(schemes, u.Scheme) } path = u.Host + u.Path if !regexp.MustCompile(`^([A-Za-z0-9-]+)(.[A-Za-z0-9-]+)+(/[A-Za-z0-9-_.]+)+$`).MatchString(path) { return nil, "", fmt.Errorf("%q is not a valid import path", path) } switch { case ghregex.MatchString(path): v := ghregex.FindStringSubmatch(path) url := &url.URL{ Host: "github.com", Path: v[2], } repo, err := Gitrepo(url, insecure, schemes...) return repo, v[0][len(v[1]):], err case bbregex.MatchString(path): v := bbregex.FindStringSubmatch(path) url := &url.URL{ Host: "bitbucket.org", Path: v[2], } repo, err := Gitrepo(url, insecure, schemes...) if err == nil { return repo, v[0][len(v[1]):], nil } repo, err = Hgrepo(url, insecure) if err == nil { return repo, v[0][len(v[1]):], nil } return nil, "", fmt.Errorf("unknown repository type") case gcregex.MatchString(path): v := gcregex.FindStringSubmatch(path) url := &url.URL{ Host: "code.google.com", Path: "p/" + v[2], } repo, err := Hgrepo(url, insecure, schemes...) if err == nil { return repo, v[0][len(v[1]):], nil } repo, err = Gitrepo(url, insecure, schemes...) if err == nil { return repo, v[0][len(v[1]):], nil } return nil, "", fmt.Errorf("unknown repository type") case lpregex.MatchString(path): v := lpregex.FindStringSubmatch(path) v = append(v, "", "") if v[2] == "" { // launchpad.net/project" repo, err := Bzrrepo(fmt.Sprintf("https://launchpad.net/%v", v[1])) return repo, "", err } // launchpad.net/project/series" repo, err := Bzrrepo(fmt.Sprintf("https://launchpad.net/%s/%s", v[1], v[2])) return repo, v[3], err } // try the general syntax if genericre.MatchString(path) { v := genericre.FindStringSubmatch(path) switch v[5] { case "git": x := strings.SplitN(v[1], "/", 2) url := &url.URL{ Host: x[0], Path: x[1], } repo, err := Gitrepo(url, insecure, schemes...) return repo, v[6], err case "hg": x := strings.SplitN(v[1], "/", 2) url := &url.URL{ Host: x[0], Path: x[1], } repo, err := Hgrepo(url, insecure, schemes...) return repo, v[6], err case "bzr": repo, err := Bzrrepo("https://" + v[1]) return repo, v[6], err default: return nil, "", fmt.Errorf("unknown repository type: %q", v[5]) } } // no idea, try to resolve as a vanity import importpath, vcs, reporoot, err := ParseMetadata(path, insecure) if err != nil { return nil, "", err } u, err = url.Parse(reporoot) if err != nil { return nil, "", err } extra := path[len(importpath):] switch vcs { case "git": u.Path = u.Path[1:] repo, err := Gitrepo(u, insecure, u.Scheme) return repo, extra, err case "hg": u.Path = u.Path[1:] repo, err := Hgrepo(u, insecure, u.Scheme) return repo, extra, err case "bzr": repo, err := Bzrrepo(reporoot) return repo, extra, err default: return nil, "", fmt.Errorf("unknown repository type: %q", vcs) } } // Gitrepo returns a RemoteRepo representing a remote git repository. func Gitrepo(url *url.URL, insecure bool, schemes ...string) (RemoteRepo, error) { if len(schemes) == 0 { schemes = []string{"https", "git", "ssh", "http"} } u, err := probeGitUrl(url, insecure, schemes) if err != nil { return nil, err } return &gitrepo{ url: u, }, nil } func probeGitUrl(u *url.URL, insecure bool, schemes []string) (string, error) { git := func(url *url.URL) error { out, err := run("git", "ls-remote", url.String(), "HEAD") if err != nil { return err } if !bytes.Contains(out, []byte("HEAD")) { return fmt.Errorf("not a git repo") } return nil } return probe(git, u, insecure, schemes...) } func probeHgUrl(u *url.URL, insecure bool, schemes []string) (string, error) { hg := func(url *url.URL) error { _, err := run("hg", "identify", url.String()) return err } return probe(hg, u, insecure, schemes...) } func probeBzrUrl(u string) error { bzr := func(url *url.URL) error { _, err := run("bzr", "info", url.String()) return err } url, err := url.Parse(u) if err != nil { return err } _, err = probe(bzr, url, false, "https") return err } // probe calls the supplied vcs function to probe a variety of url constructions. // If vcs returns non nil, it is assumed that the url is not a valid repo. func probe(vcs func(*url.URL) error, url *url.URL, insecure bool, schemes ...string) (string, error) { var unsuccessful []string for _, scheme := range schemes { // make copy of url and apply scheme url := *url url.Scheme = scheme switch url.Scheme { case "https", "ssh": if err := vcs(&url); err == nil { return url.String(), nil } case "http", "git": if !insecure { fmt.Println("skipping insecure protocol:", url.String()) continue } if err := vcs(&url); err == nil { return url.String(), nil } default: return "", fmt.Errorf("unsupported scheme: %v", url.Scheme) } unsuccessful = append(unsuccessful, url.String()) } return "", fmt.Errorf("vcs probe failed, tried: %s", strings.Join(unsuccessful, ",")) } // gitrepo is a git RemoteRepo. type gitrepo struct { // remote repository url, see man 1 git-clone url string } func (g *gitrepo) URL() string { return g.url } // Checkout fetchs the remote branch, tag, or revision. If more than one is // supplied, an error is returned. If the branch is blank, // then the default remote branch will be used. If the branch is "HEAD", an // error will be returned. func (g *gitrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { if branch == "HEAD" { return nil, fmt.Errorf("cannot update %q as it has been previously fetched with -tag or -revision. Please use gb vendor delete then fetch again.", g.url) } if !atMostOne(branch, tag, revision) { return nil, fmt.Errorf("only one of branch, tag or revision may be supplied") } dir, err := mktmp() if err != nil { return nil, err } wc := workingcopy{ path: dir, } args := []string{ "clone", "-q", // silence progress report to stderr g.url, dir, } if branch != "" { args = append(args, "--branch", branch) } if _, err := run("git", args...); err != nil { wc.Destroy() return nil, err } if revision != "" || tag != "" { if err := runOutPath(os.Stderr, dir, "git", "checkout", "-q", oneOf(revision, tag)); err != nil { wc.Destroy() return nil, err } } return &GitClone{wc}, nil } type workingcopy struct { path string } func (w workingcopy) Dir() string { return w.path } func (w workingcopy) Destroy() error { if err := fileutils.RemoveAll(w.path); err != nil { return err } parent := filepath.Dir(w.path) return cleanPath(parent) } // GitClone is a git WorkingCopy. type GitClone struct { workingcopy } func (g *GitClone) Revision() (string, error) { rev, err := runPath(g.path, "git", "rev-parse", "HEAD") return strings.TrimSpace(string(rev)), err } func (g *GitClone) Branch() (string, error) { rev, err := runPath(g.path, "git", "rev-parse", "--abbrev-ref", "HEAD") return strings.TrimSpace(string(rev)), err } // Hgrepo returns a RemoteRepo representing a remote git repository. func Hgrepo(u *url.URL, insecure bool, schemes ...string) (RemoteRepo, error) { if len(schemes) == 0 { schemes = []string{"https", "http"} } url, err := probeHgUrl(u, insecure, schemes) if err != nil { return nil, err } return &hgrepo{ url: url, }, nil } // hgrepo is a Mercurial repo. type hgrepo struct { // remote repository url, see man 1 hg url string } func (h *hgrepo) URL() string { return h.url } func (h *hgrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { if !atMostOne(tag, revision) { return nil, fmt.Errorf("only one of tag or revision may be supplied") } dir, err := mktmp() if err != nil { return nil, err } args := []string{ "clone", h.url, dir, "--noninteractive", } if branch != "" { args = append(args, "--branch", branch) } if err := runOut(os.Stderr, "hg", args...); err != nil { fileutils.RemoveAll(dir) return nil, err } if revision != "" { if err := runOut(os.Stderr, "hg", "--cwd", dir, "update", "-r", revision); err != nil { fileutils.RemoveAll(dir) return nil, err } } return &HgClone{ workingcopy{ path: dir, }, }, nil } // HgClone is a mercurial WorkingCopy. type HgClone struct { workingcopy } func (h *HgClone) Revision() (string, error) { rev, err := run("hg", "--cwd", h.path, "id", "-i") return strings.TrimSpace(string(rev)), err } func (h *HgClone) Branch() (string, error) { rev, err := run("hg", "--cwd", h.path, "branch") return strings.TrimSpace(string(rev)), err } // Bzrrepo returns a RemoteRepo representing a remote bzr repository. func Bzrrepo(url string) (RemoteRepo, error) { if err := probeBzrUrl(url); err != nil { return nil, err } return &bzrrepo{ url: url, }, nil } // bzrrepo is a bzr RemoteRepo. type bzrrepo struct { // remote repository url url string } func (b *bzrrepo) URL() string { return b.url } func (b *bzrrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { if !atMostOne(tag, revision) { return nil, fmt.Errorf("only one of tag or revision may be supplied") } dir, err := mktmp() if err != nil { return nil, err } wc := filepath.Join(dir, "wc") if err := runOut(os.Stderr, "bzr", "branch", b.url, wc); err != nil { fileutils.RemoveAll(dir) return nil, err } return &BzrClone{ workingcopy{ path: wc, }, }, nil } // BzrClone is a bazaar WorkingCopy. type BzrClone struct { workingcopy } func (b *BzrClone) Revision() (string, error) { return "1", nil } func (b *BzrClone) Branch() (string, error) { return "master", nil } func cleanPath(path string) error { if files, _ := ioutil.ReadDir(path); len(files) > 0 || filepath.Base(path) == "src" { return nil } parent := filepath.Dir(path) if err := fileutils.RemoveAll(path); err != nil { return err } return cleanPath(parent) } func mkdir(path string) error { return os.MkdirAll(path, 0755) } func mktmp() (string, error) { return ioutil.TempDir("", "gb-vendor-") } func run(c string, args ...string) ([]byte, error) { var buf bytes.Buffer err := runOut(&buf, c, args...) return buf.Bytes(), err } func runOut(w io.Writer, c string, args ...string) error { cmd := exec.Command(c, args...) cmd.Stdin = nil cmd.Stdout = w cmd.Stderr = os.Stderr return cmd.Run() } func runPath(path string, c string, args ...string) ([]byte, error) { var buf bytes.Buffer err := runOutPath(&buf, path, c, args...) return buf.Bytes(), err } func runOutPath(w io.Writer, path string, c string, args ...string) error { cmd := exec.Command(c, args...) cmd.Dir = path cmd.Stdin = nil cmd.Stdout = w cmd.Stderr = os.Stderr return cmd.Run() } // atMostOne returns true if no more than one string supplied is not empty. func atMostOne(args ...string) bool { var c int for _, arg := range args { if arg != "" { c++ } } return c < 2 } // oneof returns the first non empty string func oneOf(args ...string) string { for _, arg := range args { if arg != "" { return arg } } return "" } gb-0.3.2/vendor/repo_test.go000066400000000000000000000062651262556506200157560ustar00rootroot00000000000000package vendor import ( "fmt" "reflect" "testing" ) func TestDeduceRemoteRepo(t *testing.T) { if testing.Short() { t.Skipf("skipping network tests in -short mode") } tests := []struct { path string want RemoteRepo extra string err error insecure bool }{{ path: "", err: fmt.Errorf(`"" is not a valid import path`), }, { path: "corporate", err: fmt.Errorf(`"corporate" is not a valid import path`), }, { path: "github.com/cznic/b", want: &gitrepo{ url: "https://github.com/cznic/b", }, }, { path: "github.com/pkg/sftp", want: &gitrepo{ url: "https://github.com/pkg/sftp", }, }, { path: "github.com/pkg/sftp/examples/gsftp", want: &gitrepo{ url: "https://github.com/pkg/sftp", }, extra: "/examples/gsftp", }, { path: "github.com/coreos/go-etcd", want: &gitrepo{ url: "https://github.com/coreos/go-etcd", }, }, { path: "bitbucket.org/davecheney/gitrepo/cmd/main", want: &gitrepo{ url: "https://bitbucket.org/davecheney/gitrepo", }, extra: "/cmd/main", }, { path: "bitbucket.org/davecheney/hgrepo/cmd/main", want: &hgrepo{ url: "https://bitbucket.org/davecheney/hgrepo", }, extra: "/cmd/main", }, { path: "code.google.com/p/goauth2/oauth", want: &hgrepo{ url: "https://code.google.com/p/goauth2", }, extra: "/oauth", }, { path: "code.google.com/p/gami", want: &gitrepo{ url: "https://code.google.com/p/gami", }, }, { path: "git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git", want: &gitrepo{ url: "https://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git", }, }, { path: "git.apache.org/thrift.git/lib/go/thrift", want: &gitrepo{ url: "https://git.apache.org/thrift.git", }, extra: "/lib/go/thrift", }, { path: "gopkg.in/check.v1", want: &gitrepo{ url: "https://gopkg.in/check.v1", }, extra: "", }, { path: "golang.org/x/tools/go/vcs", want: &gitrepo{ url: "https://go.googlesource.com/tools", }, extra: "/go/vcs", }, { path: "labix.org/v2/mgo", want: &bzrrepo{ url: "https://launchpad.net/mgo/v2", }, insecure: true, }, { path: "launchpad.net/gnuflag", want: &bzrrepo{ url: "https://launchpad.net/gnuflag", }, }, { path: "https://github.com/pkg/sftp", want: &gitrepo{ url: "https://github.com/pkg/sftp", }, }, { path: "git://github.com/pkg/sftp", want: &gitrepo{ url: "git://github.com/pkg/sftp", }, insecure: true, }, { path: "code.google.com/p/google-api-go-client/bigquery/v2", want: &hgrepo{ url: "https://code.google.com/p/google-api-go-client", }, extra: "/bigquery/v2", }, { path: "code.google.com/p/go-sqlite/go1/sqlite3", want: &hgrepo{ url: "https://code.google.com/p/go-sqlite", }, extra: "/go1/sqlite3", }} for _, tt := range tests { t.Logf("DeduceRemoteRepo(%q, %v)", tt.path, tt.insecure) got, extra, err := DeduceRemoteRepo(tt.path, tt.insecure) if !reflect.DeepEqual(err, tt.err) { t.Errorf("DeduceRemoteRepo(%q): want err: %v, got err: %v", tt.path, tt.err, err) continue } if !reflect.DeepEqual(got, tt.want) || extra != tt.extra { t.Errorf("DeduceRemoteRepo(%q): want %#v, %v, got %#v, %v", tt.path, tt.want, tt.extra, got, extra) } } } gb-0.3.2/vendor/stdlib.go000066400000000000000000000077661262556506200152420ustar00rootroot00000000000000package vendor // packages from the standard lib. They are excluded // from the package map. var stdlib = map[string]bool{ "C": true, "archive/tar": true, "archive/zip": true, "bufio": true, "bytes": true, "compress/bzip2": true, "compress/flate": true, "compress/gzip": true, "compress/lzw": true, "compress/zlib": true, "container/heap": true, "container/list": true, "container/ring": true, "crypto": true, "crypto/aes": true, "crypto/cipher": true, "crypto/des": true, "crypto/dsa": true, "crypto/ecdsa": true, "crypto/elliptic": true, "crypto/hmac": true, "crypto/md5": true, "crypto/rand": true, "crypto/rc4": true, "crypto/rsa": true, "crypto/sha1": true, "crypto/sha256": true, "crypto/sha512": true, "crypto/subtle": true, "crypto/tls": true, "crypto/x509": true, "crypto/x509/pkix": true, "database/sql": true, "database/sql/driver": true, "debug/dwarf": true, "debug/elf": true, "debug/gosym": true, "debug/macho": true, "debug/pe": true, "encoding": true, "encoding/ascii85": true, "encoding/asn1": true, "encoding/base32": true, "encoding/base64": true, "encoding/binary": true, "encoding/csv": true, "encoding/gob": true, "encoding/hex": true, "encoding/json": true, "encoding/pem": true, "encoding/xml": true, "errors": true, "expvar": true, "flag": true, "fmt": true, "go/ast": true, "go/build": true, "go/doc": true, "go/format": true, "go/parser": true, "go/printer": true, "go/scanner": true, "go/token": true, "hash": true, "hash/adler32": true, "hash/crc32": true, "hash/crc64": true, "hash/fnv": true, "html": true, "html/template": true, "image": true, "image/color": true, "image/draw": true, "image/gif": true, "image/jpeg": true, "image/png": true, "index/suffixarray": true, "io": true, "io/ioutil": true, "log": true, "log/syslog": true, "math": true, "math/big": true, "math/cmplx": true, "math/rand": true, "mime": true, "mime/multipart": true, "net": true, "net/http": true, "net/http/cgi": true, "net/http/cookiejar": true, "net/http/fcgi": true, "net/http/httptest": true, "net/http/httputil": true, "net/http/pprof": true, "net/mail": true, "net/rpc": true, "net/rpc/jsonrpc": true, "net/smtp": true, "net/textproto": true, "net/url": true, "os": true, "os/exec": true, "os/signal": true, "os/user": true, "path": true, "path/filepath": true, "reflect": true, "regexp": true, "regexp/syntax": true, "runtime": true, "runtime/cgo": true, "runtime/debug": true, "runtime/pprof": true, "sort": true, "strconv": true, "strings": true, "sync": true, "sync/atomic": true, "syscall": true, "testing": true, "testing/iotest": true, "testing/quick": true, "text/scanner": true, "text/tabwriter": true, "text/template": true, "text/template/parse": true, "time": true, "unicode": true, "unicode/utf16": true, "unicode/utf8": true, "unsafe": true, } gb-0.3.2/vendor/stringset.go000066400000000000000000000016051262556506200157650ustar00rootroot00000000000000package vendor // union returns the union of a and b. func union(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { r[k] = true } for k := range b { r[k] = true } return r } // intersection returns the intersection of a and b. func intersection(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { if b[k] { r[k] = true } } return r } // difference returns the symetric difference of a and b. func difference(a, b map[string]bool) map[string]bool { r := make(map[string]bool) for k := range a { if !b[k] { r[k] = true } } for k := range b { if !a[k] { r[k] = true } } return r } // contains returns true if a contains all the elements in s. func contains(a map[string]bool, s ...string) bool { var r bool for _, e := range s { if !a[e] { return false } r = true } return r } gb-0.3.2/vendor/stringset_test.go000066400000000000000000000052161262556506200170260ustar00rootroot00000000000000package vendor import "testing" import "reflect" func set(args ...string) map[string]bool { r := make(map[string]bool) for _, a := range args { r[a] = true } return r } func TestUnion(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set("b"), }, { a: set("a"), b: nil, want: set("a"), }, { a: set("a"), b: set("b"), want: set("b", "a"), }, { a: set("c"), b: set("c"), want: set("c"), }} for _, tt := range tests { got := union(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("union(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestIntersection(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set(), }, { a: set("a"), b: nil, want: set(), }, { a: set("a"), b: set("b"), want: set(), }, { a: set("c"), b: set("c"), want: set("c"), }, { a: set("a", "c"), b: set("b", "c"), want: set("c"), }} for _, tt := range tests { got := intersection(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("intersection(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestDifference(t *testing.T) { tests := []struct { a, b map[string]bool want map[string]bool }{{ a: nil, b: nil, want: set(), }, { a: nil, b: set("b"), want: set("b"), }, { a: set("a"), b: nil, want: set("a"), }, { a: set("a"), b: set("b"), want: set("a", "b"), }, { a: set("c"), b: set("c"), want: set(), }, { a: set("a", "c"), b: set("b", "c"), want: set("a", "b"), }} for _, tt := range tests { got := difference(tt.a, tt.b) if !reflect.DeepEqual(tt.want, got) { t.Errorf("difference(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) } } } func TestContains(t *testing.T) { tests := []struct { a map[string]bool s []string want bool }{{ a: nil, s: nil, want: false, }, { a: set("a"), s: nil, want: false, }, { a: set("a"), s: []string{"a"}, want: true, }, { a: set("a"), s: []string{"b"}, want: false, }, { a: set("a", "b"), s: []string{"b"}, want: true, }, { a: set("a"), s: []string{"a", "b"}, want: false, }, { a: set("a", "b", "c"), s: []string{"a", "b"}, want: true, }, { a: set("a", "b", "c"), s: []string{"x", "b"}, want: false, }, { a: set("a", "b", "c"), s: []string{"b", "c", "d"}, want: false, }} for _, tt := range tests { got := contains(tt.a, tt.s...) if !reflect.DeepEqual(tt.want, got) { t.Errorf("contains(%v, %v) want: %v, got %v", tt.a, tt.s, tt.want, got) } } }