pax_global_header00006660000000000000000000000064130530273630014514gustar00rootroot0000000000000052 comment=d94d4f1fbab3a54e1d5feafa545e1c9014516fcb gb-0.4.4/000077500000000000000000000000001305302736300121115ustar00rootroot00000000000000gb-0.4.4/.gitignore000066400000000000000000000000211305302736300140720ustar00rootroot00000000000000!* testdata/bin/ gb-0.4.4/.travis.yml000066400000000000000000000014761305302736300142320ustar00rootroot00000000000000language: go go_import_path: github.com/constabulary/gb go: - 1.4.x - 1.5.x - 1.6.x - 1.7.x - 1.8.x - 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 - if [[ $TRAVIS_GO_VERSION == 1.4* ]]; then go get -u golang.org/x/tools/cmd/cover; fi - echo '#!/bin/bash' > "$GOPATH/bin/sudo" && echo 'echo >&2 attempted sudo "$@"' >> "$GOPATH/bin/sudo" && chmod +x "$GOPATH/bin/sudo" script: - go install -v ./... && bin/coverage.sh && go build && gb test && ../integration-tests/run-all.bash after_success: - bash <(curl -s https://codecov.io/bash) gb-0.4.4/LICENSE000066400000000000000000000020701305302736300131150ustar00rootroot00000000000000The 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.4.4/README.md000066400000000000000000000043501305302736300133720ustar00rootroot00000000000000# 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) [![codecov.io](https://codecov.io/github/constabulary/gb/branch/master/graph/badge.svg)](https://codecov.io/github/constabulary/gb) `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 ### Contribution guidelines We welcome pull requests, bug fixes and issue reports. Before proposing a large change, please discuss your change by raising an issue. ### 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 - gb test improvements, test output, test flag handling - [Race detector support](https://github.com/constabulary/gb/issues/96) #### Todo - 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. gb-0.4.4/action_test.go000066400000000000000000000043311305302736300147550ustar00rootroot00000000000000package gb import ( "reflect" "sort" "testing" ) func TestBuildAction(t *testing.T) { var tests = []struct { pkg string action *Action err error }{{ pkg: "a", action: &Action{ Name: "build: a", Deps: []*Action{{Name: "compile: a"}}, }, }, { pkg: "b", action: &Action{ Name: "build: b", Deps: []*Action{ { Name: "link: b", Deps: []*Action{ { Name: "compile: b", Deps: []*Action{ { Name: "compile: a", }}, }, }}, }, }, }, { pkg: "c", action: &Action{ Name: "build: c", Deps: []*Action{ { Name: "compile: c", Deps: []*Action{ { Name: "compile: a", }, { Name: "compile: d.v1", }}, }}, }, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !reflect.DeepEqual(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 !reflect.DeepEqual(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.4.4/appveyor.yml000066400000000000000000000021271305302736300145030ustar00rootroot00000000000000version: 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 -y --force 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 get github.com/pkg/errors - 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.4.4/bin/000077500000000000000000000000001305302736300126615ustar00rootroot00000000000000gb-0.4.4/bin/coverage.sh000077500000000000000000000003711305302736300150140ustar00rootroot00000000000000#!/bin/bash set -e rm -f coverage.txt for d in $(go list github.com/constabulary/gb/...); do go test -coverprofile=profile.out -covermode=atomic $d if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done gb-0.4.4/build.go000066400000000000000000000166121305302736300135450ustar00rootroot00000000000000package gb import ( "errors" "fmt" "path/filepath" "runtime" "strings" "time" "github.com/constabulary/gb/internal/debug" "github.com/constabulary/gb/internal/fileutils" ) // 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, errors.New("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.NotStale { 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.Context.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, 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) } // step 4. add system object files. for _, syso := range pkg.SysoFiles { ofiles = append(ofiles, filepath.Join(pkg.Dir, syso)) } 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{pkg.objfile()}, 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 if pkg.Install && !pkg.TestScope { build = &Action{ Name: fmt.Sprintf("install: %s", pkg.ImportPath), Deps: []*Action{build}, Run: func() error { return fileutils.Copyfile(pkg.installpath(), pkg.objfile()) }, } } // if this is a main package, add a link stage if pkg.Main { build = &Action{ Name: fmt.Sprintf("link: %s", pkg.ImportPath), Deps: []*Action{build}, Run: func() error { return pkg.link() }, } } if !pkg.TestScope { // 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 var extra []string switch { case pkg.Main: // all binaries depend on runtime, even if they do not // explicitly import it. extra = append(extra, "runtime") if pkg.race { // race binaries have extra implicit depdendenceis. extra = append(extra, "runtime/race") } case len(pkg.CgoFiles) > 0 && pkg.ImportPath != "runtime/cgo": // anything that uses cgo has a dependency on runtime/cgo which is // only visible after cgo file generation. extra = append(extra, "runtime/cgo") } for _, i := range extra { 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() 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, gofiles) pkg.Record("gc", time.Since(t0)) return err } func (pkg *Package) link() error { t0 := time.Now() err := pkg.tc.Ld(pkg) pkg.Record("link", time.Since(t0)) return err } gb-0.4.4/build_test.go000066400000000000000000000172321305302736300146030ustar00rootroot00000000000000package gb import ( "errors" "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: &build.NoGoError{Dir: filepath.Join(getwd(t), "testdata", "src", "blank")}, }, { pkg: "cppmain", }, { pkg: "tags1", opts: opts(Tags("x")), // excludes the test file in package err: nogoerr(filepath.Join(getwd(t), "testdata", "src", "tags1")), }, { pkg: "tags2", err: nogoerr(filepath.Join(getwd(t), "testdata", "src", "tags2")), }, { pkg: "tags2", opts: opts(Tags("x")), }, { pkg: "nosource", err: &build.NoGoError{Dir: filepath.Join(getwd(t), "testdata", "src", "nosource")}, }} proj := testProject(t) for _, tt := range tests { ctx, err := NewContext(proj, tt.opts...) ctx.Force = true defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !reflect.DeepEqual(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); !reflect.DeepEqual(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); !reflect.DeepEqual(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 options []func(*Context) error // set of options to apply to the test context 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"}, }, { pkgs: []string{"a", "b", "c"}, options: []func(*Context) error{WithRace}, actions: []string{"compile: a", "compile: c", "link: b"}, }} for _, tt := range tests { ctx := testContext(t, tt.options...) 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 !reflect.DeepEqual(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.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 := pkg.objfile() 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 := pkg.Workdir() want := filepath.Join(ctx.Workdir(), tt.want) if want != got { t.Errorf("Workdir(Package{Name: %v, ImportPath: %v, TestScope: %v}): want %s, got %s", pkg.Name, pkg.ImportPath, pkg.TestScope, 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: "main", }, { 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", }, TestScope: true, }, want: "main", }, { pkg: &Package{ Package: &build.Package{ Name: "a", ImportPath: "main", }, TestScope: true, }, want: "main", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a", }, TestScope: true, }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "a/a", }, TestScope: true, }, want: "a", }, { pkg: &Package{ Package: &build.Package{ Name: "main", ImportPath: "testmain", }, TestScope: true, }, want: "testmain", }} for _, tt := range tests { got := tt.pkg.pkgname() if got != tt.want { t.Errorf("pkgname(Package{Name:%q, ImportPath: %q, TestScope:%v}): got %v, want %v", tt.pkg.Name, tt.pkg.ImportPath, tt.pkg.TestScope, got, tt.want) } } } 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-") } func nogoerr(dir string) error { return &build.NoGoError{Dir: dir} } gb-0.4.4/cgo.go000066400000000000000000000360301305302736300132120ustar00rootroot00000000000000package gb import ( "bytes" "fmt" "io" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "github.com/pkg/errors" ) func cgo(pkg *Package) (*Action, []string, []string, error) { switch { case goversion == 1.4: return cgo14(pkg) case goversion > 1.4: return cgo15(pkg) default: return nil, nil, nil, errors.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...) ofiles = append(ofiles, pkg.SysoFiles...) 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...) ofiles = append(ofiles, pkg.SysoFiles...) 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") if gccSupportsNoPie(pkg) { args = append(args, "-no-pie") } 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 } // gccSupportsNoPie check if the -no-pie option is supported. On systems with // PIE (position independent executables) enabled by default, -no-pie must be // passed when doing a partial link with -Wl,-r. func gccSupportsNoPie(pkg *Package) bool { cmd := gccCmd(pkg, "") args := []string{ "-no-pie", "-fsyntax-only", "-xc", "-", } return exec.Command(cmd[0], append(cmd[1:], args...)...).Run() == nil } func cgotool(ctx *Context) string { return filepath.Join(runtime.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 goversion == 1.4: args = append(args, "--", "-I", pkg.Dir, ) case goversion > 1.4: args = append(args, "-importpath", pkg.ImportPath, "--", "-I", workdir, "-I", pkg.Dir, ) default: return errors.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 goversion == 1.4: args = append(args, "-dynimport", ofile, "-dynout", dynout, ) case goversion > 1.4: args = append(args, "-dynpackage", pkg.Name, "-dynimport", ofile, "-dynout", dynout, ) default: return errors.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(pkg.Workdir(), pkg.pkgname(), "_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 } // linkCmd returns the name of the binary to use for linking for the given // environment variable and using the default command when the variable is // empty. func linkCmd(pkg *Package, envvar, defcmd string) string { compiler := envList(envvar, defcmd) return compiler[0] } // 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.4.4/cmd/000077500000000000000000000000001305302736300126545ustar00rootroot00000000000000gb-0.4.4/cmd/cmd.go000066400000000000000000000044471305302736300137570ustar00rootroot00000000000000// Package command holds support functions and types for writing gb and gb plugins package cmd import ( "flag" "os" "github.com/constabulary/gb" "github.com/constabulary/gb/internal/debug" "github.com/pkg/errors" ) // 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 // SkipParseArgs avoids parsing arguments as import paths. SkipParseArgs bool } // 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 errors.Wrap(err, "unable to construct context") } 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, errors.New("project root is blank") } root, err := FindProjectroot(projectroot) if err != nil { return nil, errors.Wrap(err, "could not locate project root") } proj := gb.NewProject(root) debug.Debugf("project root %q", proj.Projectdir()) return gb.NewContext(proj, options...) } gb-0.4.4/cmd/env.go000066400000000000000000000012301305302736300137670ustar00rootroot00000000000000package cmd import ( "fmt" "log" "os" "strings" ) // MustGetwd returns current working directory and fails otherwise 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.4.4/cmd/env_test.go000066400000000000000000000015511305302736300150340ustar00rootroot00000000000000package 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.4.4/cmd/gb-vendor/000077500000000000000000000000001305302736300145375ustar00rootroot00000000000000gb-0.4.4/cmd/gb-vendor/alldocs.go000066400000000000000000000062351305302736300165150ustar00rootroot00000000000000// 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 dependencies. Flags: -precaire allow the use of insecure protocols. */ package main gb-0.4.4/cmd/gb-vendor/delete.go000066400000000000000000000037531305302736300163400ustar00rootroot00000000000000package main import ( "flag" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/fileutils" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 errors.New("delete: import path or --all flag is missing") } else if len(args) >= 1 && deleteAll { return errors.New("delete: you cannot specify path and --all flag at once") } m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return errors.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 errors.Wrap(err, "could not get dependency") } dependencies = append(dependencies, dependency) } for _, d := range dependencies { path := d.Importpath if err := m.RemoveDependency(d); err != nil { return errors.Wrap(err, "dependency could not be deleted") } 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 intermediate directories. return errors.Wrap(err, "dependency could not be deleted") } } return vendor.WriteManifest(manifestFile(ctx), m) }, AddFlags: addDeleteFlags, } gb-0.4.4/cmd/gb-vendor/fetch.go000066400000000000000000000146371305302736300161720ustar00rootroot00000000000000package 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/internal/fileutils" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 errors.New("fetch: import path missing") case 1: path := args[0] recurse = !noRecurse return fetch(ctx, path, recurse) default: return errors.New("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 errors.Wrap(err, "could not load manifest") } 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 errors.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 } b, err := wc.Branch() if err != nil { return err } dep := vendor.Dependency{ Importpath: path, Repository: repo.URL(), Revision: rev, Branch: b, 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 := fileutils.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(runtime.GOROOT(), "src", "vendor"), ""}, // include vendored pkgs from the std library {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 errors.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.4.4/cmd/gb-vendor/help.go000066400000000000000000000046751305302736300160320ustar00rootroot00000000000000package 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.4.4/cmd/gb-vendor/list.go000066400000000000000000000025321305302736300160430ustar00rootroot00000000000000package main import ( "flag" "fmt" "html/template" "os" "text/tabwriter" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 errors.Wrap(err, "could not load manifest") } tmpl, err := template.New("list").Parse(format) if err != nil { return errors.Wrapf(err, "unable to parse template %q", format) } w := tabwriter.NewWriter(os.Stdout, 1, 2, 1, ' ', 0) for _, dep := range m.Dependencies { if err := tmpl.Execute(w, dep); err != nil { return errors.Wrap(err, "unable to execute template") } 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.4.4/cmd/gb-vendor/main.go000066400000000000000000000037421305302736300160200ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/debug" ) var ( fs = flag.NewFlagSet(os.Args[0], flag.ExitOnError) projectroot = os.Getenv("GB_PROJECT_DIR") ) 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) 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 := gb.NewContext(project, 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.4.4/cmd/gb-vendor/purge.go000066400000000000000000000032651305302736300162160ustar00rootroot00000000000000package main import ( "path/filepath" "strings" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/fileutils" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 errors.Wrap(err, "could not load manifest") } imports, err := vendor.ParseImports(ctx.Projectdir()) if err != nil { return errors.Wrap(err, "import could not be parsed") } 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 errors.Wrap(err, "could not get get dependency") } if err := m.RemoveDependency(dep); err != nil { return errors.Wrap(err, "dependency could not be removed") } 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 intermediate directories. return errors.Wrap(err, "dependency could not be deleted") } } } return vendor.WriteManifest(manifestFile(ctx), m) }, } gb-0.4.4/cmd/gb-vendor/restore.go000066400000000000000000000033611305302736300165540ustar00rootroot00000000000000package main import ( "flag" "fmt" "path/filepath" "sync" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/fileutils" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 dependencies. 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 errors.Wrap(err, "could not load manifest") } errChan := make(chan error, 1) var wg sync.WaitGroup wg.Add(len(m.Dependencies)) for _, dep := range m.Dependencies { go func(dep vendor.Dependency) { defer wg.Done() fmt.Printf("Getting %s\n", dep.Importpath) repo, _, err := vendor.DeduceRemoteRepo(dep.Importpath, insecure) if err != nil { errChan <- errors.Wrap(err, "could not process dependency") return } wc, err := repo.Checkout("", "", dep.Revision) if err != nil { errChan <- errors.Wrap(err, "could not retrieve dependency") return } dst := filepath.Join(ctx.Projectdir(), "vendor", "src", dep.Importpath) src := filepath.Join(wc.Dir(), dep.Path) if err := fileutils.Copypath(dst, src); err != nil { errChan <- err return } if err := wc.Destroy(); err != nil { errChan <- err return } }(dep) } wg.Wait() close(errChan) return <-errChan } gb-0.4.4/cmd/gb-vendor/update.go000066400000000000000000000073271305302736300163610ustar00rootroot00000000000000package main import ( "flag" "path/filepath" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/fileutils" "github.com/constabulary/gb/internal/vendor" "github.com/pkg/errors" ) 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 errors.New("update: import path or --all flag is missing") } else if len(args) == 1 && updateAll { return errors.New("update: you cannot specify path and --all flag at once") } m, err := vendor.ReadManifest(manifestFile(ctx)) if err != nil { return errors.Wrap(err, "could not load manifest") } 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 errors.Wrap(err, "could not get dependency") } dependencies = append(dependencies, dependency) } for _, d := range dependencies { if err := m.RemoveDependency(d); err != nil { return errors.Wrap(err, "dependency could not be deleted from manifest") } repo, extra, err := vendor.DeduceRemoteRepo(d.Importpath, insecure) if err != nil { return errors.Wrapf(err, "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 intermediate directories. return errors.Wrap(err, "dependency could not be deleted") } dst := filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(dep.Importpath)) src := filepath.Join(wc.Dir(), dep.Path) if err := fileutils.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.4.4/cmd/gb/000077500000000000000000000000001305302736300132445ustar00rootroot00000000000000gb-0.4.4/cmd/gb/alldocs.go000066400000000000000000000133321305302736300152160ustar00rootroot00000000000000// 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. -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. -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. -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 [var ...] Env prints project environment variables. If one or more variable names is given as arguments, env prints the value of each named variable on its own line. 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 [var ...] info prints gb environment information. Values: GB_PROJECT_DIR The root of the gb project. GB_SRC_PATH The list of gb project source directories. GB_PKG_DIR The path of the gb project's package cache. GB_BIN_SUFFIX The suffix applied any binary written to $GB_PROJECT_DIR/bin GB_GOROOT The value of runtime.GOROOT for the Go version that built this copy of gb. info returns 0 if the project is well formed, and non zero otherwise. If one or more variable names is given as arguments, info prints the value of each named variable on its own line. 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] -n -v [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". Flags: -v print output from test subprocess. -n do not execute test binaries, compile only */ package main gb-0.4.4/cmd/gb/build.go000066400000000000000000000104661305302736300147010ustar00rootroot00000000000000package main import ( "flag" "go/build" "os" "path/filepath" "runtime" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/pkg/errors" ) func init() { registerCommand(buildCmd) } var ( // build flags // 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 // enable race runtime race 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(&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.BoolVar(&race, "race", false, "enable race detector") 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. -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. -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. -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.Install = !FF pkgs, err := resolveRootPackages(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, } // Resolver resolves packages. type Resolver interface { ResolvePackage(path string) (*gb.Package, error) } // resolveRootPackages resolves import paths into packages. // Only packages which exist inside $PROJECT/src are elegable to be // roots to build or test. Other import paths are discarded. func resolveRootPackages(r Resolver, paths ...string) ([]*gb.Package, error) { var pkgs []*gb.Package for _, path := range paths { pkg, err := r.ResolvePackage(path) if _, nogo := errors.Cause(err).(*build.NoGoError); nogo { // if the package is empty, to no Go files are in scope // ignore it. // TODO(dfc) ResolvePackage should return an empty *Package // and build/test should ignore them. continue } if err != nil { return pkgs, errors.Wrapf(err, "failed to resolve import path %q", path) } if pkg.SrcRoot == filepath.Join(runtime.GOROOT(), "src") { // skip package roots that are not part of this project. // TODO(dfc) should gb return an error here? continue } pkgs = append(pkgs, pkg) } return pkgs, nil } gb-0.4.4/cmd/gb/depfile_test.go000066400000000000000000000067651305302736300162600ustar00rootroot00000000000000package main_test import ( "path/filepath" "runtime" "testing" ) func TestMissingDepfile(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/github.com/user/proj") gb.tempFile("src/github.com/user/proj/main.go", `package main import "fmt" import "github.com/a/b" // would be in depfile func main() { fmt.Println(b.B) } `) gb.cd(gb.tempdir) gb.runFail("build") gb.grepStderr(`FATAL: command "build" failed:.+import "github.com/a/b": not found`, `import "github.com/a/b": not found`) } func TestDepfileVersionPresent(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/github.com/user/proj/a/") gb.tempFile("src/github.com/user/proj/a/main.go", `package main import "github.com/a/b" func main() { println(b.B) } `) gb.tempFile("depfile", ` github.com/a/b version=2.0.0 `) gbhome := gb.tempDir(".gb") gb.tempFile(".gb/cache/f51babb8d8973d3796013755348c5a072f1a2e47/src/github.com/a/b/b.go", `package b; const B=1`) gb.setenv("GB_HOME", gbhome) gb.cd(gb.tempdir) gb.run("build") name := "a" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/"+name) } func TestDepfileFetchMissingByVersion(t *testing.T) { if testing.Short() { t.Skip("skipping test during -short") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/github.com/user/proj/a/") gb.tempFile("src/github.com/user/proj/a/main.go", `package main import "github.com/pkg/profile" func main() { defer profile.Start().Stop() } `) gb.tempFile("depfile", ` github.com/pkg/profile version=1.1.0 `) gb.cd(gb.tempdir) gbhome := gb.tempDir(".gb") gb.setenv("GB_HOME", gbhome) gb.run("build") name := "a" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/"+name) gb.grepStdout("^fetching github.com/pkg/profile", "fetching pkg/profile not found") gb.mustExist(filepath.Join(gbhome, "cache", "8fd41ea4fa48cd8435005bad56faeefdc57a25d6", "src", "github.com", "pkg", "profile", "profile.go")) } func TestDepfileTagPresent(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/github.com/user/proj/a/") gb.tempFile("src/github.com/user/proj/a/main.go", `package main import "github.com/a/b" func main() { println(b.B) } `) gb.tempFile("depfile", ` github.com/a/b tag=2.0.0 `) gbhome := gb.tempDir(".gb") gb.tempFile(".gb/cache/f51babb8d8973d3796013755348c5a072f1a2e47/src/github.com/a/b/b.go", `package b; const B=1`) gb.setenv("GB_HOME", gbhome) gb.cd(gb.tempdir) gb.run("build") name := "a" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/"+name) } func TestDepfileFetchMissingByTag(t *testing.T) { if testing.Short() { t.Skip("skipping test during -short") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/github.com/user/proj/a/") gb.tempFile("src/github.com/user/proj/a/main.go", `package main import "github.com/pkg/profile" func main() { defer profile.Start().Stop() } `) gb.tempFile("depfile", ` github.com/pkg/profile tag=v1.1.0 `) gb.cd(gb.tempdir) gbhome := gb.tempDir(".gb") gb.setenv("GB_HOME", gbhome) gb.run("build") name := "a" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/"+name) gb.grepStdout("^fetching github.com/pkg/profile", "fetching pkg/profile not found") gb.mustExist(filepath.Join(gbhome, "cache", "e693c641ace92b5910c4a64d3241128094f74f19", "src", "github.com", "pkg", "profile", "profile.go")) } gb-0.4.4/cmd/gb/depset.go000066400000000000000000000104741305302736300150650ustar00rootroot00000000000000package main import ( "fmt" "go/build" "path/filepath" "runtime" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/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 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.4.4/cmd/gb/doc.go000066400000000000000000000014561305302736300143460ustar00rootroot00000000000000package 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, ".") } cmd := exec.Command("godoc", args...) cmd.Env = env cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }, SkipParseArgs: true, }) } gb-0.4.4/cmd/gb/dot.go000066400000000000000000000011201305302736300143530ustar00rootroot00000000000000package 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.4.4/cmd/gb/env.go000066400000000000000000000006661305302736300143730ustar00rootroot00000000000000package main import "github.com/constabulary/gb/cmd" func init() { registerCommand(envCmd) } var envCmd = &cmd.Command{ Name: "env", UsageLine: `env [var ...]`, Short: "print project environment variables", Long: ` Env prints project environment variables. If one or more variable names is given as arguments, env prints the value of each named variable on its own line. `, Run: info, SkipParseArgs: true, } gb-0.4.4/cmd/gb/flag.go000066400000000000000000000023141305302736300145040ustar00rootroot00000000000000// 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.4.4/cmd/gb/gb_test.go000066400000000000000000001301771305302736300152330ustar00rootroot00000000000000// Copyright 2015 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_test // gb functional tests adapted from src/cmd/go/go_test.go import ( "bytes" "flag" "fmt" "go/format" "io" "io/ioutil" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "testing" ) var ( canRun = true // whether we can run go or ./testgb canRace = false // whether we can run the race detector exeSuffix string // ".exe" on Windows testgb string = "testgb" ) func init() { switch runtime.GOOS { case "android", "nacl": canRun = false case "darwin": switch runtime.GOARCH { case "arm", "arm64": canRun = false } } switch runtime.GOOS { case "windows": exeSuffix = ".exe" } testgb += exeSuffix } // The TestMain function creates a gb command for testing purposes and // deletes it after the tests have been run. func TestMain(m *testing.M) { flag.Parse() if canRun { dir, err := ioutil.TempDir("", "testgb") if err != nil { fmt.Fprintf(os.Stderr, "cannot create temporary directory: %v", err) os.Exit(2) } testgb = filepath.Join(dir, testgb) locations := [][]string{ {runtime.GOROOT(), "bin", "go"}, {runtime.GOROOT(), "..", "bin", "go"}, {runtime.GOROOT(), "..", "..", "bin", "go"}, } ok := false for _, loc := range locations { out, err := exec.Command(filepath.Join(loc...), "build", "-o", testgb).CombinedOutput() if err == nil { ok = true break } log.Printf("building testgb failed: %v\n%s", err, out) } if !ok { os.Exit(2) } _, err = os.Stat(filepath.Join(runtime.GOROOT(), "pkg", fmt.Sprintf("%s_%s_race", runtime.GOOS, runtime.GOARCH), "runtime.a")) switch { case os.IsNotExist(err): log.Printf("go installation at %s is missing race support", runtime.GOROOT()) case runtime.GOARCH == "amd64": canRace = runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "windows" || runtime.GOOS == "darwin" } } // Don't let these environment variables confuse the test. os.Unsetenv("GOBIN") os.Unsetenv("GOPATH") os.Unsetenv("DEBUG") r := m.Run() os.Exit(r) } // T manage a single run of the testgb binary. type T struct { *testing.T temps []string wd string env []string tempdir string ran bool stdin io.Reader stdout, stderr bytes.Buffer } // must gives a fatal error if err is not nil. func (t *T) must(err error) { if err != nil { t.Fatal(err) } } // check gives a test non-fatal error if err is not nil. func (t *T) check(err error) { if err != nil { t.Error(err) } } // pwd returns the current directory. func (t *T) pwd() string { wd, err := os.Getwd() if err != nil { t.Fatalf("could not get working directory: %v", err) } return wd } // cd changes the current directory to the named directory. extra args // are passed through filepath.Join before cd. func (t *T) cd(dir string, extra ...string) { if t.wd == "" { t.wd = t.pwd() } v := append([]string{dir}, extra...) dir = filepath.Join(v...) abs, err := filepath.Abs(dir) t.must(os.Chdir(dir)) if err == nil { t.setenv("PWD", abs) } } // setenv sets an environment variable to use when running the test go // command. func (t *T) setenv(name, val string) { t.unsetenv(name) t.env = append(t.env, name+"="+val) } // unsetenv removes an environment variable. func (t *T) unsetenv(name string) { if t.env == nil { t.env = append([]string(nil), os.Environ()...) } for i, v := range t.env { if strings.HasPrefix(v, name+"=") { t.env = append(t.env[:i], t.env[i+1:]...) break } } } // doRun runs the test go command, recording stdout and stderr and // returning exit status. func (t *T) doRun(args []string) error { if !canRun { t.Fatal("T.doRun called but canRun false") } t.Logf("running %v %v", testgb, args) cmd := exec.Command(testgb, args...) t.stdout.Reset() t.stderr.Reset() cmd.Stdin = t.stdin cmd.Stdout = &t.stdout cmd.Stderr = &t.stderr cmd.Env = t.env status := cmd.Run() if t.stdout.Len() > 0 { t.Log("standard output:") t.Log(t.stdout.String()) } if t.stderr.Len() > 0 { t.Log("standard error:") t.Log(t.stderr.String()) } t.ran = true return status } // run runs the test go command, and expects it to succeed. func (t *T) run(args ...string) { if status := t.doRun(args); status != nil { t.Logf("gb %v failed unexpectedly: %v", args, status) t.FailNow() } } // runFail runs the test go command, and expects it to fail. func (t *T) runFail(args ...string) { if status := t.doRun(args); status == nil { t.Fatal(testgb, "succeeded unexpectedly") } else { t.Log(testgb, "failed as expected:", status) } } // getStdout returns standard output of the testgb run as a string. func (t *T) getStdout() string { if !t.ran { t.Fatal("internal testsuite error: stdout called before run") } return t.stdout.String() } // getStderr returns standard error of the testgb run as a string. func (t *T) getStderr() string { if !t.ran { t.Fatal("internal testsuite error: stdout called before run") } return t.stderr.String() } // doGrepMatch looks for a regular expression in a buffer, and returns // whether it is found. The regular expression is matched against // each line separately, as with the grep command. func (t *T) doGrepMatch(match string, b *bytes.Buffer) bool { if !t.ran { t.Fatal("internal testsuite error: grep called before run") } re := regexp.MustCompile(match) for _, ln := range bytes.Split(b.Bytes(), []byte{'\n'}) { if re.Match(ln) { return true } } return false } // doGrep looks for a regular expression in a buffer and fails if it // is not found. The name argument is the name of the output we are // searching, "output" or "error". The msg argument is logged on // failure. func (t *T) doGrep(match string, b *bytes.Buffer, name, msg string) { if !t.doGrepMatch(match, b) { t.Log(msg) t.Logf("pattern %v not found in standard %s", match, name) t.FailNow() } } // grepStdout looks for a regular expression in the test run's // standard output and fails, logging msg, if it is not found. func (t *T) grepStdout(match, msg string) { t.doGrep(match, &t.stdout, "output", msg) } // grepStderr looks for a regular expression in the test run's // standard error and fails, logging msg, if it is not found. func (t *T) grepStderr(match, msg string) { t.doGrep(match, &t.stderr, "error", msg) } // grepBoth looks for a regular expression in the test run's standard // output or stand error and fails, logging msg, if it is not found. func (t *T) grepBoth(match, msg string) { if !t.doGrepMatch(match, &t.stdout) && !t.doGrepMatch(match, &t.stderr) { t.Log(msg) t.Logf("pattern %v not found in standard output or standard error", match) t.FailNow() } } // doGrepNot looks for a regular expression in a buffer and fails if // it is found. The name and msg arguments are as for doGrep. func (t *T) doGrepNot(match string, b *bytes.Buffer, name, msg string) { if t.doGrepMatch(match, b) { t.Log(msg) t.Logf("pattern %v found unexpectedly in standard %s", match, name) t.FailNow() } } // grepStdoutNot looks for a regular expression in the test run's // standard output and fails, logging msg, if it is found. func (t *T) grepStdoutNot(match, msg string) { t.doGrepNot(match, &t.stdout, "output", msg) } // grepStderrNot looks for a regular expression in the test run's // standard error and fails, logging msg, if it is found. func (t *T) grepStderrNot(match, msg string) { t.doGrepNot(match, &t.stderr, "error", msg) } // grepBothNot looks for a regular expression in the test run's // standard output or stand error and fails, logging msg, if it is // found. func (t *T) grepBothNot(match, msg string) { if t.doGrepMatch(match, &t.stdout) || t.doGrepMatch(match, &t.stderr) { t.Log(msg) t.Fatalf("pattern %v found unexpectedly in standard output or standard error", match) } } // doGrepCount counts the number of times a regexp is seen in a buffer. func (t *T) doGrepCount(match string, b *bytes.Buffer) int { if !t.ran { t.Fatal("internal testsuite error: doGrepCount called before run") } re := regexp.MustCompile(match) c := 0 for _, ln := range bytes.Split(b.Bytes(), []byte{'\n'}) { if re.Match(ln) { c++ } } return c } // grepCountStdout returns the number of times a regexp is seen in // standard output. func (t *T) grepCountStdout(match string) int { return t.doGrepCount(match, &t.stdout) } // grepCountStderr returns the number of times a regexp is seen in // standard error. func (t *T) grepCountStderr(match string) int { return t.doGrepCount(match, &t.stderr) } // grepCountBoth returns the number of times a regexp is seen in both // standard output and standard error. func (t *T) grepCountBoth(match string) int { return t.doGrepCount(match, &t.stdout) + t.doGrepCount(match, &t.stderr) } // creatingTemp records that the test plans to create a temporary file // or directory. If the file or directory exists already, it will be // removed. When the test completes, the file or directory will be // removed if it exists. func (t *T) creatingTemp(path string) { if filepath.IsAbs(path) && !strings.HasPrefix(path, t.tempdir) { t.Fatal("internal testsuite error: creatingTemp(%q) with absolute path not in temporary directory", path) } // If we have changed the working directory, make sure we have // an absolute path, because we are going to change directory // back before we remove the temporary. if t.wd != "" && !filepath.IsAbs(path) { path = filepath.Join(t.pwd(), path) } t.must(os.RemoveAll(path)) t.temps = append(t.temps, path) } // makeTempdir makes a temporary directory for a run of testgb. If // the temporary directory was already created, this does nothing. func (t *T) makeTempdir() { if t.tempdir == "" { var err error t.tempdir, err = ioutil.TempDir("", "testgb") t.must(err) t.tempdir, err = filepath.EvalSymlinks(t.tempdir) // resolve OSX's stupid symlinked /tmp t.must(err) } } // tempFile adds a temporary file for a run of testgb. func (t *T) tempFile(path, contents string) { t.makeTempdir() t.must(os.MkdirAll(filepath.Join(t.tempdir, filepath.Dir(path)), 0755)) bytes := []byte(contents) if strings.HasSuffix(path, ".go") { formatted, err := format.Source(bytes) if err == nil { bytes = formatted } } t.must(ioutil.WriteFile(filepath.Join(t.tempdir, path), bytes, 0644)) } // tempDir adds a temporary directory for a run of testgb. func (t *T) tempDir(path string) string { t.makeTempdir() path = filepath.Join(t.tempdir, path) if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { t.Fatal(err) } return path } // symlink adds a symlink from src to dst. func (t *T) symlink(src, dst string) string { t.makeTempdir() src = filepath.Join(t.tempdir, src) dst = filepath.Join(t.tempdir, dst) t.must(os.Symlink(src, dst)) return dst } // path returns the absolute pathname to file with the temporary // directory. func (t *T) path(names ...string) string { if t.tempdir == "" { t.Fatalf("internal testsuite error: path(%q) with no tempdir", filepath.Join(names...)) } if len(names) == 0 || names[0] == "." { return t.tempdir } return filepath.Join(append([]string{t.tempdir}, names...)...) } // mustExist fails if path does not exists. func (t *T) mustExist(path string) { if _, err := os.Stat(path); err != nil { t.Fatalf("%s does not exist (%v)", path, err) } } // mustNotExist fails if path exists. func (t *T) mustNotExist(path string) { if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) { t.Fatalf("%s exists but should not (%v)", path, err) } } // mustBeEmpty fails if root is not a directory or is not empty. func (t *T) mustBeEmpty(root string) { fi, err := os.Stat(root) if err != nil { t.Fatalf("failed to stat: %s: %v", root, err) } if !fi.IsDir() { t.Fatalf("%s exists but is not a directory", root) } var found []string fn := func(path string, info os.FileInfo, err error) error { if path == root { return nil } if err != nil { t.Fatalf("error during walk at %s: %v", path, err) } found = append(found, path) return nil } filepath.Walk(root, fn) if len(found) > 0 { t.Fatalf("expected %s to be empty, found %s", root, found) } } // wantExecutable fails with msg if path is not executable. func (t *T) wantExecutable(path, msg string) { if st, err := os.Stat(path); err != nil { if !os.IsNotExist(err) { t.Log(err) } t.Fatal(msg) } else { if runtime.GOOS != "windows" && st.Mode()&0111 == 0 { t.Fatalf("binary %s exists but is not executable", path) } } } // wantArchive fails if path is not an archive. func (t *T) wantArchive(path string) { f, err := os.Open(path) if err != nil { t.Fatal(err) } buf := make([]byte, 100) io.ReadFull(f, buf) f.Close() if !bytes.HasPrefix(buf, []byte("!\n")) { t.Fatalf("file %s exists but is not an archive", path) } } // isStale returns whether pkg is stale. func (t *T) isStale(pkg string) bool { t.run("list", "-f", "{{.Stale}}", pkg) switch v := strings.TrimSpace(t.getStdout()); v { case "true": return true case "false": return false default: t.Fatalf("unexpected output checking staleness of package %v: %v", pkg, v) panic("unreachable") } } // wantStale fails with msg if pkg is not stale. func (t *T) wantStale(pkg, msg string) { if !t.isStale(pkg) { t.Fatal(msg) } } // wantNotStale fails with msg if pkg is stale. func (t *T) wantNotStale(pkg, msg string) { if t.isStale(pkg) { t.Fatal(msg) } } // cleanup cleans up a test that runs testgb. func (t *T) cleanup() { if t.wd != "" { if err := os.Chdir(t.wd); err != nil { // We are unlikely to be able to continue. fmt.Fprintln(os.Stderr, "could not restore working directory, crashing:", err) os.Exit(2) } } for _, path := range t.temps { t.check(os.RemoveAll(path)) } if t.tempdir != "" { t.check(os.RemoveAll(t.tempdir)) } } // resetReadOnlyFlagAll resets windows read-only flag // set on path and any children it contains. // The flag is set by git and has to be removed. // os.Remove refuses to remove files with read-only flag set. func (t *T) resetReadOnlyFlagAll(path string) { fi, err := os.Stat(path) if err != nil { t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) } if !fi.IsDir() { err := os.Chmod(path, 0666) if err != nil { t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) } } fd, err := os.Open(path) if err != nil { t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) } defer fd.Close() names, _ := fd.Readdirnames(-1) for _, name := range names { t.resetReadOnlyFlagAll(path + string(filepath.Separator) + name) } } // Invoking plain "gb" should print usage to stderr and exit with 2. func TestNoArguments(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.cd(gb.tempdir) gb.runFail() gb.grepStderr("^Usage:", `expected "Usage: ..."`) } // Invoking plain "gb" outside a project should print to stderr and exit with 2. func TestOutsideProject(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("x") gb.cd(gb.tempdir, "x") gb.runFail() gb.grepStderr("^Usage:", `expected "Usage: ..."`) } // Invoking gb outside a project should print to stderr and exit with 2. func TestInfoOutsideProject(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("x") gb.cd(gb.tempdir, "x") gb.runFail("info") regex := `FATAL: unable to construct context: could not locate project root: could not find project root in "` + regexp.QuoteMeta(filepath.Join(gb.tempdir, "x")) + `" or its parents` gb.grepStderr(regex, "expected FATAL") } // Invoking gb outside a project with -R should succeed. func TestInfoWithMinusR(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("x") gb.tempDir("y") gb.tempDir("y/src") gb.cd(gb.tempdir, "x") gb.run("info", "-R", filepath.Join(gb.tempdir, "y")) gb.grepStdout(`^GB_PROJECT_DIR="`+regexp.QuoteMeta(filepath.Join(gb.tempdir, "y"))+`"$`, "missing GB_PROJECT_DIR") } func TestInfoCmd(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.cd(gb.tempdir) gb.run("info") gb.grepStdout(`^GB_PROJECT_DIR="`+regexp.QuoteMeta(gb.tempdir)+`"$`, "missing GB_PROJECT_DIR") gb.grepStdout(`^GB_SRC_PATH="`+regexp.QuoteMeta(filepath.Join(gb.tempdir, "src")+string(filepath.ListSeparator)+filepath.Join(gb.tempdir, "vendor", "src"))+`"$`, "missing GB_SRC_PATH") gb.grepStdout(`^GB_PKG_DIR="`+regexp.QuoteMeta(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH))+`"$`, "missing GB_PKG_DIR") gb.grepStdout(`^GB_BIN_SUFFIX="-`+runtime.GOOS+"-"+runtime.GOARCH+`"$`, "missing GB_BIN_SUFFIX") gb.grepStdout(`^GB_GOROOT="`+regexp.QuoteMeta(runtime.GOROOT())+`"$`, "missing GB_GOROOT") } func TestInfoWithArgs(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.cd(gb.tempdir) gb.run("info", "GB_PROJECT_DIR", "GB_MISSING", "GB_GOROOT") gb.grepStdout(`^`+regexp.QuoteMeta(gb.tempdir), "missing "+regexp.QuoteMeta(gb.tempdir)) gb.grepStdout(`^`+regexp.QuoteMeta(runtime.GOROOT()), "missing "+regexp.QuoteMeta(runtime.GOROOT())) // second line should be empty lines := bytes.Split(gb.stdout.Bytes(), []byte{'\n'}) if len(lines[1]) != 0 { t.Fatal("want 0, got", len(lines[1])) } } // Only succeeds if source order is preserved. func TestSourceFileNameOrderPreserved(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/testorder") gb.tempFile("src/testorder/example1_test.go", `// Copyright 2013 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. // Make sure that go test runs Example_Z before Example_A, preserving source order. package p import "fmt" var n int func Example_Z() { n++ fmt.Println(n) // Output: 1 } func Example_A() { n++ fmt.Println(n) // Output: 2 } `) gb.tempFile("src/testorder/example2_test.go", `// Copyright 2013 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. // Make sure that go test runs Example_Y before Example_B, preserving source order. package p import "fmt" func Example_Y() { n++ fmt.Println(n) // Output: 3 } func Example_B() { n++ fmt.Println(n) // Output: 4 } `) gb.cd(gb.tempdir) gb.run("test", "testorder") } func TestBuildPackage(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg.go", `package pkg1 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH, "pkg1.a")) } func TestBuildOnlyOnePackage(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg.go", `package pkg1 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.tempDir("src/pkg2") gb.tempFile("src/pkg2/pkg.go", `package pkg2 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build", "pkg1") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.grepStdoutNot("^pkg2$", `did not expect "pkg2"`) gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH, "pkg1.a")) } func TestBuildOnlyOnePackageFromWorkingDir(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg.go", `package pkg1 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.tempDir("src/pkg2") gb.tempFile("src/pkg2/pkg.go", `package pkg2 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.cd(filepath.Join(gb.tempdir, "src", "pkg1")) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.grepStdoutNot("^pkg2$", `did not expect "pkg2"`) gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH, "pkg1.a")) } func TestBuildPackageWrongPackage(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg.go", `package pkg1 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.cd(gb.tempdir) gb.runFail("build", "pkg2") gb.grepStderr(`^FATAL: command "build" failed: failed to resolve import path "pkg2": import "pkg2": not found`, "expected FATAL") } func TestBuildPackageNoSource(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.cd(gb.tempdir) gb.runFail("build", "pkg1") gb.grepStderr(`^FATAL: command "build" failed: no packages supplied`, "expected FATAL") } func TestTestPackageNoTests(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg.go", `package pkg1 import "fmt" func helloworld() { fmt.Println("hello world!") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "pkg1") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } // test that compiling A in test scope compiles B in regular scope func TestTestDependantPackage(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/A") gb.tempDir("src/B") gb.tempFile("src/B/B.go", `package B const X = 1 `) gb.tempFile("src/A/A_test.go", `package A import "testing" import "B" func TestX(t *testing.T) { if B.X != 1 { t.Fatal("expected 1, got %d", B.X) } } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "A") gb.grepStdout("^B$", `expected "B"`) // output from build action gb.grepStdout("^A$", `expected "A"`) // output from test action gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH, "B.a")) } func TestTestPackageOnlyTests(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg_test.go", `package pkg1 import "testing" func TestTest(t *testing.T) { t.Log("hello") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "pkg1") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestTestPackageNopeMode(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg_test.go", `package pkg1 import "testing" func TestTest(t *testing.T) { t.Log("hello") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-n", "pkg1") gb.grepStdout("^pkg1$", `expected "pkg1"`) gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestTestPackageFailedToBuild(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg_test.go", `package pkg1 import "testing" func TestTest(t *testing.T) { t.Log("hello" // missing parens } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("test") gb.grepStderr(`FATAL: command "test" failed:`, "expected FATAL") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestTestPackageTestFailed(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg_test.go", `package pkg1 import "testing" func TestTest(t *testing.T) { t.Error("failed") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("test") gb.grepStderr("^# pkg1$", "expected # pkg1") gb.grepStdout("pkg_test.go:6: failed", "expected message from test") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestTestPackageMinusV(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/pkg1") gb.tempFile("src/pkg1/pkg_test.go", `package pkg1 import "testing" func TestTest(t *testing.T) { t.Logf("hello") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-v") gb.grepStdout("^pkg1$", "expected pkg1") gb.grepStdout("pkg_test.go:6: hello", "expected output from test binary") gb.grepStdout("PASS", "expected PASS") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } const issue349 = `package main import ( "flag" "testing" ) var name = flag.String("name", "nsf", "what is your name") func TestX(t *testing.T) { if *name != "jardin" { t.Fatalf("got: '%s', expected: 'jardin'", *name) } } ` // https://github.com/constabulary/gb/issues/349 func TestTestGbTestPassesUnknownFlags(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/projectx") gb.tempFile("src/projectx/main_test.go", issue349) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-name=jardin") gb.grepStdout("^projectx$", "expected projectx") // output from gb test gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } const issue473 = `package main import ( "flag" "testing" ) var name = flag.String("name", "nsf", "what is your name") func TestX(t *testing.T) { } func TestY(t *testing.T) { } ` // https://github.com/constabulary/gb/issues/473 func TestGbTestIssue473a(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/projectx") gb.tempFile("src/projectx/main_test.go", issue473) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-v", "projectx", "-run", "TestX") gb.grepStdout("^projectx$", "expected projectx") // output from gb test gb.grepStdout("TestX", "expected TestX") gb.grepStdoutNot("TestY", "expected TestY") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestGbTestIssue473b(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/projectx") gb.tempFile("src/projectx/main_test.go", issue473) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-v", "-run", "TestX", "projectx") gb.grepStdout("^projectx$", "expected projectx") // output from gb test gb.grepStdout("TestX", "expected TestX") gb.grepStdoutNot("TestY", "expected TestY") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestGbTestIssue473c(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/projectx") gb.tempFile("src/projectx/main_test.go", issue473) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-v", "projectx") gb.grepStdout("^projectx$", "expected projectx") // output from gb test gb.grepStdout("TestX", "expected TestX") gb.grepStdout("TestY", "expected TestY") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } func TestGbTestIssue473d(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/projectx") gb.tempFile("src/projectx/main_test.go", issue473) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "projectx", "-v") gb.grepStdout("^projectx$", "expected projectx") // output from gb test gb.grepStdout("TestX", "expected TestX") gb.grepStdout("TestY", "expected TestY") gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } // gb list with an empty project succeeds and returns nothing. func TestGbListEmpty(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("list") gb.grepStdoutNot(".", "expected no output") gb.grepStderrNot(".", "expected no output") gb.mustBeEmpty(tmpdir) } // gb list with a project with source at the top level should return nothing. func TestGbListSrcTopLevel(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempFile("src/main.go", "package main; func main() { println() }") gb.cd(gb.tempdir) gb.run("list") gb.grepStdoutNot(".", "expected no output") gb.grepStderrNot(".", "expected no output") } func TestGbListSrcCmd(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("src/cmd") gb.tempFile("src/cmd/main.go", "package main; func main() { println() }") gb.cd(gb.tempdir) gb.run("list") gb.grepStdout("cmd", "expected cmd") } func mklistfixture(gb *T) { gb.tempDir("src/p") gb.tempDir("src/q") gb.tempDir("src/r/s") gb.tempFile("src/p/p.go", "package p; const P = 'p'") gb.tempFile("src/q/q.go", "package p; const Q = 'q'") // package name differs from import path gb.tempFile("src/r/r.go", "package r; const R = 'r'") gb.tempFile("src/r/s/s.go", "package s; const S = 's'") } // gb list with a few projects should show them all. func TestGbList(t *testing.T) { gb := T{T: t} defer gb.cleanup() mklistfixture(&gb) gb.cd(gb.tempdir) gb.run("list") gb.grepStdout("^p$", "expected 'p'") gb.grepStdout("^q$", "expected 'q'") gb.grepStdout("^r$", "expected 'r'") gb.grepStdout("^r/s$", "expected 'r/s'") } func TestGbListPart(t *testing.T) { gb := T{T: t} defer gb.cleanup() mklistfixture(&gb) gb.cd(gb.tempdir) gb.run("list", "r/...", "q") gb.grepStdoutNot("^p$", "unexpected 'p'") gb.grepStdout("^q$", "expected 'q'") gb.grepStdout("^r$", "expected 'r'") gb.grepStdout("^r/s$", "expected 'r/s'") } func TestGbListPackageNames(t *testing.T) { gb := T{T: t} defer gb.cleanup() mklistfixture(&gb) gb.cd(gb.tempdir) gb.run("list", "-f", "{{ .Name }}") gb.grepStdout("^p$", "expected 'p'") gb.grepStdoutNot("^q$", "unexpected 'q'") gb.grepStdout("^r$", "expected 'r'") gb.grepStdout("^s$", "expected 's'") } func TestGbListFormatFromStdin(t *testing.T) { gb := T{T: t} defer gb.cleanup() mklistfixture(&gb) gb.cd(gb.tempdir) gb.stdin = strings.NewReader("{{ .Name }}") gb.run("list", "-s") gb.grepStdout("^p$", "expected 'p'") gb.grepStdoutNot("^q$", "unexpected 'q'") gb.grepStdout("^r$", "expected 'r'") gb.grepStdout("^s$", "expected 's'") } // TODO(dfc) add tests for -json func skipWindows(t *testing.T, msg string) { if runtime.GOOS == "windows" { t.Skip("test skipped on windows:", msg) } } // issue 481: check that project detection works correctly // in the presence of symlinks above the project root. func TestProjectRootDetectionWorksWithParentSymlink(t *testing.T) { skipWindows(t, "no symlinks, lol") gb := T{T: t} defer gb.cleanup() gb.tempDir("code/project") gb.tempDir("code/project/src/a") gb.tempFile("code/project/src/a/a.go", "package a; const A = 'a'") root := gb.symlink("code", "code1") gb.cd(filepath.Join(root, "project")) gb.run("list") gb.grepStdout("^a$", "expected 'a'") } func TestProjectRootDetectionWorksWithDirectSymlink(t *testing.T) { skipWindows(t, "no symlinks, lol") gb := T{T: t} defer gb.cleanup() gb.tempDir("code/project") gb.tempDir("code/project/src/a") gb.tempFile("code/project/src/a/a.go", "package a; const A = 'a'") root := gb.symlink("code/project", "code/symlink") gb.cd(root) gb.run("list") gb.grepStdout("^a$", "expected 'a'") } // issue 157 func TestTestWorksWithProjectSymlink(t *testing.T) { skipWindows(t, "no symlinks, lol") gb := T{T: t} defer gb.cleanup() gb.tempDir("code/project") gb.tempDir("code/project/src/a") gb.tempFile("code/project/src/a/a.go", "package a; const A = 'a'") gb.tempFile("code/project/src/a/a_test.go", `package a import "testing" func TestA(t *testing.T) { if A != 'a' { t.Fatal("expected a, got", A) } } `) root := gb.symlink("code/project", "code/symlink") gb.cd(root) gb.run("test") gb.grepStdout("^a$", "expected 'a'") } func TestTestWorksInsideProjectSymlink(t *testing.T) { skipWindows(t, "no symlinks, lol") gb := T{T: t} defer gb.cleanup() gb.tempDir("code/project") gb.tempDir("code/project/src/a") gb.tempFile("code/project/src/a/a.go", "package a; const A = 'a'") gb.tempFile("code/project/src/a/a_test.go", `package a import "testing" func TestA(t *testing.T) { if A != 'a' { t.Fatal("expected a, got", A) } } `) root := gb.symlink("code/project", "code/symlink") gb.cd(filepath.Join(root, "src", "a")) gb.run("test") gb.grepStdout("^a$", "expected 'a'") } // test -race flag is wired up correctly func TestBuildRaceFlag(t *testing.T) { if !canRace { t.Skip("skipping because race detector not supported") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/x") gb.tempFile("src/x/x_race.go", "package x\nconst A = 1\n") gb.tempFile("src/x/y.go", "// +build race\n\npackage x\nconst B = 2\n") gb.tempFile("src/x/z.go", "package x\n const C = A +B\n") gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build", "-race", "x") gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH+"-race", "x.a")) } func TestTestRaceFlag(t *testing.T) { if !canRace { t.Skip("skipping because race detector not supported") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/x") gb.tempFile("src/x/x_race.go", "package x\nconst A = 1\n") gb.tempFile("src/x/y.go", "// +build race\n\npackage x\nconst B = 2\n") gb.tempFile("src/x/q.go", "// +build !race\n\npackage x\nconst B = 7\n") gb.tempFile("src/x/x_test.go", `package x import "testing" func TestRaceFlag(t *testing.T) { if A != 1 || B != 2 { t.Fatal("expected", 1, 2,"got", A, B) } } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("test", "-race", "x") gb.grepStdout("^x$", "expected x") // output from gb test gb.mustBeEmpty(tmpdir) gb.mustNotExist(filepath.Join(gb.tempdir, "pkg")) // ensure no pkg directory is created } // check that go test -race builds and runs a racy binary, and that it finds the race. func TestTestRace(t *testing.T) { if !canRace { t.Skip("skipping because race detector not supported") } if strings.HasPrefix(runtime.Version(), "go1.4") { t.Skipf("skipping race test as Go version %v incorrectly marks race failures as success", runtime.Version()) } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/race") gb.tempFile("src/race/map_test.go", `package race import "testing" func TestRaceMapRW(t *testing.T) { m := make(map[int]int) ch := make(chan bool, 1) go func() { _ = m[1] ch <- true }() m[1] = 1 <-ch } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("test", "-race") gb.mustBeEmpty(tmpdir) } // check that missing -race support generates error message. func TestRaceMissing(t *testing.T) { if canRace { t.Skip("skipping because race detector is available") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/race") gb.tempFile("src/race/map_test.go", `package race import "testing" func TestRaceMapRW(t *testing.T) { m := make(map[int]int) ch := make(chan bool, 1) go func() { _ = m[1] ch <- true }() m[1] = 1 <-ch } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("test", "-race") raceError1 := fmt.Sprintf("FATAL: go installation at %s is missing race support", runtime.GOROOT()) raceError2 := fmt.Sprintf("FATAL: race detector not supported on %s/%s", runtime.GOOS, runtime.GOARCH) gb.grepStderr(regexp.QuoteMeta(raceError1)+"|"+regexp.QuoteMeta(raceError2), "expected missing race support message") gb.mustBeEmpty(tmpdir) } // test that gb will no build the stdlib directly, only as transitive deps. func TestNoBuildStdlib(t *testing.T) { t.Skip("constabulary/gb#505") gb := T{T: t} defer gb.cleanup() gb.tempDir("src/") gb.cd(gb.tempdir) defer gb.cleanup() gb.runFail("build", "-f", "-F", "net/http") } func TestCrossCompile(t *testing.T) { if strings.HasPrefix(runtime.Version(), "go1.4") { t.Skip("skipping cross compile test, not supported on", runtime.Version()) } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/p") gb.tempFile("src/p/main.go", `package main func main() { println("hello world") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") goos := "windows" if runtime.GOOS == goos { goos = "linux" } goarch := "386" if runtime.GOARCH == goarch { goarch = "amd64" } gb.setenv("TMP", tmpdir) gb.setenv("GOOS", goos) gb.setenv("GOARCH", goarch) gb.run("build") gb.mustBeEmpty(tmpdir) name := fmt.Sprintf("p-%s-%s", goos, goarch) if goos == "windows" { name += ".exe" } gb.mustExist(gb.path("bin", name)) gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/p-$GOOS-$GOARCH") } // https://github.com/constabulary/gb/issues/416 func TestGbBuildBuildsPackgeCalledCmd(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/cmd") gb.tempFile("src/cmd/main.go", `package main func main() { println("hello world") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build") gb.mustBeEmpty(tmpdir) gb.grepStdout("^cmd$", "expected cmd") name := "cmd" if runtime.GOOS == "windows" { name += ".exe" } gb.mustExist(gb.path("bin", name)) gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/"+name) } // https://github.com/constabulary/gb/issues/492 func TestGbBuildSubPackageOfCmd(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/cmd/hello") gb.tempFile("src/cmd/hello/main.go", `package main func main() { println("hello world") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build") gb.mustBeEmpty(tmpdir) name := "hello" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/hello") } // https://github.com/constabulary/gb/issues/515 func TestIssue515(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/main") gb.tempFile("src/main/main.go", `package main import ( "log" "net/http" ) func main() { err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } else { log.Print("Server started!") } } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.run("build") gb.mustBeEmpty(tmpdir) name := "main" if runtime.GOOS == "windows" { name += ".exe" } gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/main") } func TestGbGenerate(t *testing.T) { if runtime.GOOS == "windows" { t.Skipf("windows doesn't have echo, lol") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/gentest") gb.tempFile("src/gentest/generate.go", `package gentest //go:generate echo $GOPACKAGE $GOFILE `) gb.cd(gb.tempdir) gb.run("generate") gb.grepStdout("^gentest generate.go$", "expected $GOPACKAGE $GOFILE") } func TestGbDoc(t *testing.T) { if runtime.GOOS == "windows" { t.Skipf("windows doesn't have echo, lol") } gb := T{T: t} defer gb.cleanup() gb.tempDir("src/doctest") gb.tempFile("src/doctest/doc.go", `// Package doctest tests gb doc package doctest `) gb.cd(gb.tempdir) gb.run("doc", "doctest") gb.grepStdout("Package doctest tests gb doc$", "expected Package doctest tests gb doc") } func TestIssue346(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/p") gb.tempFile("src/p/main.go", `package main func main() { println("hello world") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) goos := runtime.GOOS // scenario 1: GOOS/GOARCH not set name := "p" if goos == "windows" { name += ".exe" } gb.unsetenv("GOOS") gb.unsetenv("GOARCH") gb.run("build") gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/p") // scenario 2: GOOS/GOARCH are both set name = fmt.Sprintf("p-%s-%s", goos, runtime.GOARCH) if goos == "windows" { name += ".exe" } gb.setenv("GOOS", goos) gb.setenv("GOARCH", runtime.GOARCH) gb.run("build") gb.wantExecutable(gb.path("bin", name), "expected $PROJECT/bin/p-$GOOS-$GOARCH") // scenario 3: just GOOS is set os.Remove(gb.path("bin", name)) gb.unsetenv("GOARCH") gb.run("build") gb.mustNotExist(gb.path("bin", name)) } func TestGbBuildCannotReferencePackagesInGoroot(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("build", "net/http") // net/http will be excluded by resolveRootPackages gb.mustBeEmpty(tmpdir) gb.mustNotExist(gb.path("pkg")) gb.grepStderr(`FATAL: command "build" failed: no packages supplied`, "expected FATAL") } func TestGbBuildWillResolvePackagesInVendorAsRoots(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src") gb.tempDir("vendor/src/test/test1") gb.tempFile("vendor/src/test/test1/test1.go", `package http func init() { println("Hello, world!") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("build") // should fail, nothing supplied gb.mustBeEmpty(tmpdir) gb.mustNotExist(gb.path("pkg")) gb.grepStderr(`FATAL: command "build" failed: no packages supplied`, "expected FATAL") gb.runFail("build", "test/...") // should fail, globbing does not match $PROJECT/vendor/src gb.mustBeEmpty(tmpdir) gb.mustNotExist(gb.path("pkg")) gb.grepStderr(`FATAL: command "build" failed: no packages supplied`, "expected FATAL") gb.run("build", "test/test1") // should resolve to vendor/src/test/test1 gb.mustBeEmpty(tmpdir) gb.wantArchive(filepath.Join(gb.tempdir, "pkg", runtime.GOOS+"-"+runtime.GOARCH, "test", "test1.a")) gb.grepStdout(`^test/test`, "expected test/test1") } func TestIssue550(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/x") gb.tempFile("src/x/x.go", `package main import ( "log" "this/is/a/bad/path" ) func main() { log.Println("Hello World.") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.setenv("TMP", tmpdir) gb.runFail("build") gb.mustBeEmpty(tmpdir) gb.grepStderr(`FATAL: command "build" failed: failed to resolve import path "x": import "this/is/a/bad/path": not found`, "expected FATAL") } func TestIssue530(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/main") gb.tempFile("src/main/main.go", `package main import "fmt" func main() { fmt.Println("hello gb") } `) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.run("build") gb.mustBeEmpty(tmpdir) } // goconvey (and probably others) do not parse flags passed to the // test binary, they just expect them to be present in their raw form // in os.Args. As such -test.v and -test.v=true are not the same. // Assert that gb is passing the latter form. func TestIssue605(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempDir("src/issue605") gb.tempFile("src/issue605/issue_test.go", `package issue605 import ( "os" "testing" ) func TestFlags(t *testing.T) { for _, f := range os.Args { if f == "-test.v=true" { return } } t.Fatalf("could not find test flag: %q", os.Args) }`) gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.run("test", "-v") // should translate into -test.v=true gb.mustBeEmpty(tmpdir) } // assert the gb build all alias works. func TestBuildAll(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempFile("src/pkg1/main.go", `package main import "fmt" func main() { fmt.Println("hello") }`) gb.tempFile("src/pkg2/main.go", `package main import "fmt" func main() { fmt.Println("hello") }`) gb.cd(filepath.Join(gb.tempdir, "src/pkg2")) tmpdir := gb.tempDir("tmp") gb.run("build", "all") gb.grepStdout("^pkg1$", "expected pkg1") gb.grepStdout("^pkg2$", "expected pkg2") gb.mustBeEmpty(tmpdir) } func TestIssue680(t *testing.T) { gb := T{T: t} defer gb.cleanup() gb.tempFile("src/issue680/issue_test.go", `// +build go1.7 package main import ( "net/http/httptest" "testing" ) func TestFoo(t *testing.T) { _ = httptest.NewRequest("", "https://example.com", nil) }`) gb.tempFile("src/issue680/test_test.go", "package main") gb.cd(gb.tempdir) tmpdir := gb.tempDir("tmp") gb.run("test") gb.mustBeEmpty(tmpdir) } gb-0.4.4/cmd/gb/generate.go000066400000000000000000000017461305302736300153750ustar00rootroot00000000000000package 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%c%s", ctx.Projectdir(), filepath.ListSeparator, filepath.Join(ctx.Projectdir(), "vendor")), }) cmd := exec.Command("go", append([]string{"generate"}, args...)...) cmd.Env = env cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }, } gb-0.4.4/cmd/gb/help.go000066400000000000000000000066271305302736300145360ustar00rootroot00000000000000package 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") 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) 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) exit(2) } gb-0.4.4/cmd/gb/info.go000066400000000000000000000041521305302736300145300ustar00rootroot00000000000000package main import ( "fmt" "path/filepath" "runtime" "strings" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" ) func init() { registerCommand(&cmd.Command{ Name: "info", UsageLine: `info [var ...]`, Short: "info returns information about this project", Long: ` info prints gb environment information. Values: GB_PROJECT_DIR The root of the gb project. GB_SRC_PATH The list of gb project source directories. GB_PKG_DIR The path of the gb project's package cache. GB_BIN_SUFFIX The suffix applied any binary written to $GB_PROJECT_DIR/bin GB_GOROOT The value of runtime.GOROOT for the Go version that built this copy of gb. info returns 0 if the project is well formed, and non zero otherwise. If one or more variable names is given as arguments, info prints the value of each named variable on its own line. `, Run: info, SkipParseArgs: true, AddFlags: addBuildFlags, }) } func info(ctx *gb.Context, args []string) error { env := makeenv(ctx) // print values for env variables when args are provided if len(args) > 0 { for _, arg := range args { // print each var on its own line, blank line for each invalid variables fmt.Println(findenv(env, arg)) } return nil } // print all variable when no args are provided for _, v := range env { fmt.Printf("%s=\"%s\"\n", v.name, v.val) } 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)) } 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_SRC_PATH", joinlist( filepath.Join(ctx.Projectdir(), "src"), filepath.Join(ctx.Projectdir(), "vendor", "src"), )}, {"GB_PKG_DIR", ctx.Pkgdir()}, {"GB_BIN_SUFFIX", ctx.Suffix()}, {"GB_GOROOT", runtime.GOROOT()}, } } gb-0.4.4/cmd/gb/internal/000077500000000000000000000000001305302736300150605ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/000077500000000000000000000000001305302736300161545ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/000077500000000000000000000000001305302736300201245ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/000077500000000000000000000000001305302736300203445ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/000077500000000000000000000000001305302736300211335ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/.gitturd000066400000000000000000000000001305302736300226040ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/000077500000000000000000000000001305302736300216765ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/main/000077500000000000000000000000001305302736300226225ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/main/main.go000066400000000000000000000001101305302736300240650ustar00rootroot00000000000000package main import "fmt" func main() { fmt.Println("Hello world") } gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/000077500000000000000000000000001305302736300231725ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/000077500000000000000000000000001305302736300237555ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/_testdata/000077500000000000000000000000001305302736300257255ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/_testdata/.gitturd000066400000000000000000000000001305302736300273760ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/000077500000000000000000000000001305302736300245215ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/_testdata/000077500000000000000000000000001305302736300264715ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/_testdata/.gitturd000066400000000000000000000000001305302736300301420ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/bar.go000066400000000000000000000000141305302736300256070ustar00rootroot00000000000000package bar gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/000077500000000000000000000000001305302736300241775ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/bar/000077500000000000000000000000001305302736300247435ustar00rootroot00000000000000gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/bar/bar.go000066400000000000000000000000141305302736300260310ustar00rootroot00000000000000package bar gb-0.4.4/cmd/gb/internal/match/match.go000066400000000000000000000101771305302736300176050ustar00rootroot00000000000000package match import ( "fmt" "os" "path" "path/filepath" "regexp" "strings" "github.com/constabulary/gb/internal/debug" ) // importPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func importPathsNoDotExpansion(srcdir string, cwd string, args []string) []string { srcdir, _ = filepath.Rel(srcdir, cwd) debug.Debugf("%s %s", cwd, srcdir) 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) } 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(srcdir, cwd string, args []string) []string { args = importPathsNoDotExpansion(srcdir, cwd, args) var out []string for _, a := range args { if strings.Contains(a, "...") { pkgs, err := matchPackages(srcdir, a) if err != nil { fmt.Printf("could not load all packages: %v\n", err) } out = append(out, pkgs...) continue } out = append(out, a) } return out } // 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) } } // matchPackages returns all the packages that can be found under the srcdir directory. // The pattern is a path including "...". func matchPackages(srcdir, pattern string) ([]string, error) { debug.Debugf("matchPackages: %v", pattern) match := matchPattern(pattern) treeCanMatch := treeCanMatchPattern(pattern) var pkgs []string src := srcdir + string(filepath.Separator) err := 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. if skipElem(fi.Name()) { 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) { pkgs = append(pkgs, name) } return nil }) return pkgs, err } // IsLocalImport reports whether the import path is // a local import path, like ".", "..", "./foo", or "../foo". func isLocalImport(path string) bool { return path == "." || path == ".." || strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") } // skipElem returns true of the path element should be ignored.thub.com/foo/bar" "github.com/quxx/bar"] func skipElem(elem string) bool { return strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" } gb-0.4.4/cmd/gb/internal/match/match_test.go000066400000000000000000000133761305302736300206500ustar00rootroot00000000000000package match import ( "path/filepath" "reflect" "testing" ) func TestImportPaths(t *testing.T) { tests := []struct { cwd string args []string want []string }{{ "_testdata/a", nil, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a", []string{}, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a", []string{"."}, []string{"."}, }, { "_testdata/a", []string{".."}, []string{".."}, }, { "_testdata/a", []string{"./."}, []string{"."}, }, { "_testdata/a", []string{"..."}, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a", []string{".../bar"}, []string{"github.com/foo/bar", "github.com/quxx/bar"}, }, { "_testdata/a", []string{"cmd"}, []string{"cmd"}, }, { "_testdata/a", []string{"cmd/go"}, []string{"cmd/go"}, }, { "_testdata/a", []string{"cmd/main"}, []string{"cmd/main"}, }, { "_testdata/a", []string{"cmd/..."}, []string{"cmd", "cmd/main"}, }, { "_testdata/a", []string{"nonexist"}, []string{"nonexist"}, // passed through because there is no wildcard }, { "_testdata/a/src", nil, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src", []string{}, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src", []string{"."}, []string{"."}, }, { "_testdata/a/src", []string{".."}, []string{".."}, }, { "_testdata/a/src", []string{"./."}, []string{"."}, }, { "_testdata/a/src", []string{"..."}, []string{"cmd", "cmd/main", "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src", []string{".../bar"}, []string{"github.com/foo/bar", "github.com/quxx/bar"}, }, { "_testdata/a/src", []string{"cmd"}, []string{"cmd"}, }, { "_testdata/a/src", []string{"cmd/go"}, []string{"cmd/go"}, }, { "_testdata/a/src", []string{"cmd/main"}, []string{"cmd/main"}, }, { "_testdata/a/src", []string{"cmd/..."}, []string{"cmd", "cmd/main"}, }, { "_testdata/a/src", []string{"nonexist"}, []string{"nonexist"}, // passed through because there is no wildcard }, { "_testdata/a/src/github.com/", nil, []string{"github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src/github.com/", []string{}, []string{"github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src/github.com/", []string{"."}, []string{"github.com"}, }, { "_testdata/a/src/github.com/", []string{".."}, []string{"."}, }, { "_testdata/a/src/github.com/", []string{"./."}, []string{"github.com"}, }, { "_testdata/a/src/github.com/", []string{"..."}, []string{"github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar"}, }, { "_testdata/a/src/github.com/", []string{".../bar"}, []string{"github.com/foo/bar", "github.com/quxx/bar"}, }, { "_testdata/a/src/github.com/", []string{"cmd"}, []string{"github.com/cmd"}, }, { "_testdata/a/src/github.com/", []string{"cmd/go"}, []string{"github.com/cmd/go"}, }, { "_testdata/a/src/github.com/", []string{"cmd/main"}, []string{"github.com/cmd/main"}, }, { "_testdata/a/src/github.com/", []string{"cmd/..."}, nil, }} for _, tt := range tests { const srcdir = "_testdata/a/src" got := ImportPaths(srcdir, tt.cwd, tt.args) if !reflect.DeepEqual(tt.want, got) { t.Errorf("ImportPaths(%q, %q): got %q, want %q", tt.cwd, tt.args, got, tt.want) } } } func TestMatchPackages(t *testing.T) { tests := []struct { pattern string want []string }{{ "", nil, }, { "github.com/foo", []string{ "github.com/foo", }, }, { "github.com/foo/...", []string{ "github.com/foo", "github.com/foo/bar", }, }, { "github.com", []string{ "github.com", }, }, { "github.com/...", []string{ "github.com", "github.com/foo", "github.com/foo/bar", "github.com/quxx", "github.com/quxx/bar", }, }} for _, tt := range tests { srcdir := "_testdata/a/src" got, err := matchPackages(srcdir, tt.pattern) if err != nil { t.Error(err) continue } if !reflect.DeepEqual(tt.want, got) { t.Errorf("matchPackagesInSrcDir(%q, ..., %q): got %q, want %q", srcdir, tt.pattern, got, tt.want) } } } func TestIsLocalImport(t *testing.T) { tests := []struct { path string want bool }{ {".", true}, {"", false}, {"..", true}, {"a/..", false}, {"../a", true}, {"./a", true}, } for _, tt := range tests { got := isLocalImport(tt.path) if got != tt.want { t.Errorf("isLocalImportPath(%q): got: %v, want: %v", tt.path, got, tt.want) } } } func TestSkipElem(t *testing.T) { tests := []struct { elem string want bool }{ {"", false}, {".", true}, {"..", true}, {"a", false}, {".a", true}, {"a.", false}, {"_", true}, {"_a", true}, {"a_", false}, {"a", false}, {"testdata", true}, {"_testdata", true}, {".testdata", true}, {"testdata_", false}, } for _, tt := range tests { got := skipElem(tt.elem) if got != tt.want { t.Errorf("skipElem(%q): got: %v, want: %v", tt.elem, got, tt.want) } } } func abs(t *testing.T, path string) string { path, err := filepath.Abs(path) if err != nil { t.Fatal(err) } return path } func absv(t *testing.T, paths ...string) []string { for i := range paths { paths[i] = abs(t, paths[i]) } return paths } gb-0.4.4/cmd/gb/list.go000066400000000000000000000062741305302736300145570ustar00rootroot00000000000000package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "os" "strings" "text/template" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/pkg/errors" ) var ( 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 := resolveRootPackages(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 errors.Wrap(err, "json encoding failed") } } else { fm := template.FuncMap{ "join": strings.Join, } tmpl, err := template.New("list").Funcs(fm).Parse(format) if err != nil { return errors.Wrapf(err, "unable to parse template %q", format) } for _, pkg := range pkgs { if err := tmpl.Execute(os.Stdout, pkg); err != nil { return errors.Wrap(err, "unable to execute template") } 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.4.4/cmd/gb/main.go000066400000000000000000000120321305302736300145150ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "os/exec" "path/filepath" "runtime" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/cmd/gb/internal/match" "github.com/constabulary/gb/internal/debug" ) var ( fs = flag.NewFlagSet(os.Args[0], flag.ExitOnError) cwd string ) const ( // disable to keep working directory destroyContext = true ) 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) { setCommandDefaults(command) commands[command.Name] = command } // atExit functions are called in sequence at the exit of the program. var atExit []func() error // exit runs all atExit functions, then calls os.Exit(code). func exit(code int) { for _, fn := range atExit { if err := fn(); err != nil { fmt.Fprintln(os.Stderr, "atExit:", err) } } os.Exit(code) } func fatalf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...) exit(1) } func main() { args := os.Args if len(args) < 2 || args[1] == "-h" { fs.Usage() // usage calles exit(2) } name := args[1] if name == "help" { help(args[2:]) exit(0) } command := lookupCommand(name) // add extra flags if necessary command.AddFlags(fs) // parse 'em err := command.FlagParse(fs, args) if err != nil { fatalf("could not parse flags: %v", err) } // reset args to the leftovers from fs.Parse args = fs.Args() // if this is the plugin command, ensure the name of the // plugin is first in the list of arguments. if command == commands["plugin"] { args = append([]string{name}, args...) } // if cwd was passed in via -R, make sure it is absolute cwd, err := filepath.Abs(cwd) if err != nil { fatalf("could not make project root absolute: %v", err) } // construct a project context at the current working directory. ctx, err := newContext(cwd) if err != nil { fatalf("unable to construct context: %v", err) } // unless the command wants to handle its own arguments, process // arguments into import paths. if !command.SkipParseArgs { srcdir := filepath.Join(ctx.Projectdir(), "src") for _, a := range args { // support the "all" build alias. This used to be handled // in match.ImportPaths, but that's too messy, so if "all" // is present in the args, replace it with "..." and set cwd // to srcdir. if a == "all" { args = []string{"..."} cwd = srcdir break } } args = match.ImportPaths(srcdir, cwd, args) } debug.Debugf("args: %v", args) if destroyContext { atExit = append(atExit, ctx.Destroy) } if err := command.Run(ctx, args); err != nil { fatalf("command %q failed: %v", name, err) } exit(0) } func lookupCommand(name string) *cmd.Command { 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() // usage calles exit(2) } 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 SkipParseArgs: true, } } setCommandDefaults(command) return command } func setCommandDefaults(command *cmd.Command) { // add a dummy default AddFlags field if none provided. if command.AddFlags == nil { command.AddFlags = func(*flag.FlagSet) {} } // add the default flag parsing if not overrriden. if command.FlagParse == nil { command.FlagParse = func(fs *flag.FlagSet, args []string) error { return fs.Parse(args[2:]) } } } func newContext(cwd string) (*gb.Context, error) { return cmd.NewContext( cwd, // project root gb.GcToolchain(), gb.Gcflags(gcflags...), gb.Ldflags(ldflags...), gb.Tags(buildtags...), func(c *gb.Context) error { if !race { return nil } // check this is a supported platform if runtime.GOARCH != "amd64" { fatalf("race detector not supported on %s/%s", runtime.GOOS, runtime.GOARCH) } switch runtime.GOOS { case "linux", "windows", "darwin", "freebsd": // supported default: fatalf("race detector not supported on %s/%s", runtime.GOOS, runtime.GOARCH) } // check the race runtime is built _, err := os.Stat(filepath.Join(runtime.GOROOT(), "pkg", fmt.Sprintf("%s_%s_race", runtime.GOOS, runtime.GOARCH), "runtime.a")) if os.IsNotExist(err) || err != nil { fatalf("go installation at %s is missing race support. See https://getgb.io/faq/#missing-race-support", runtime.GOROOT()) } return gb.WithRace(c) }, ) } gb-0.4.4/cmd/gb/plugin.go000066400000000000000000000015431305302736300150740ustar00rootroot00000000000000package main import ( "os/exec" "github.com/constabulary/gb/cmd" "github.com/pkg/errors" ) 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) return path, errors.Wrapf(err, "plugin: unable to locate %q", plugin) } gb-0.4.4/cmd/gb/signal.go000066400000000000000000000011741305302736300150530ustar00rootroot00000000000000// 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.4.4/cmd/gb/signal_notunix.go000066400000000000000000000006011305302736300166310ustar00rootroot00000000000000// 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.4.4/cmd/gb/signal_unix.go000066400000000000000000000005221305302736300161120ustar00rootroot00000000000000// 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} gb-0.4.4/cmd/gb/stringset.go000066400000000000000000000016031305302736300156150ustar00rootroot00000000000000package 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.4.4/cmd/gb/stringset_test.go000066400000000000000000000052141305302736300166560ustar00rootroot00000000000000package 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.4.4/cmd/gb/test.go000066400000000000000000000051131305302736300145520ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "sort" "github.com/constabulary/gb" "github.com/constabulary/gb/cmd" "github.com/constabulary/gb/internal/debug" "github.com/constabulary/gb/test" ) func init() { registerCommand(testCmd) } var ( tfs []string // Arguments passed to the test binary testCover bool testCoverMode string testCoverPkg string testVerbose bool // enable verbose output of test commands testNope bool // do not execute test binaries, compile and link only ) 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") fs.BoolVar(&testVerbose, "v", false, "enable verbose output of subcommands") fs.BoolVar(&testNope, "n", false, "do not execute test binaries, compile only") } var testCmd = &cmd.Command{ Name: "test", UsageLine: "test [build flags] -n -v [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". Flags: -v print output from test subprocess. -n do not execute test binaries, compile only `, Run: func(ctx *gb.Context, args []string) error { ctx.Force = F ctx.Install = !FF ctx.Verbose = testVerbose ctx.Nope = testNope 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 := resolveRootPackages(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 debug.Debugf("%s", args) args, tfs, err = TestFlagsExtraParse(args[2:]) debug.Debugf("%s %s", args, tfs) 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") exit(2) } return flags.Parse(args) }, } gb-0.4.4/cmd/gb/testflag.go000066400000000000000000000131471305302736300154120ustar00rootroot00000000000000package main import ( "strconv" "strings" "github.com/constabulary/gb/internal/debug" "github.com/pkg/errors" ) // 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}, "n": {}, "P": {}, "ldflags": {}, "gcflags": {}, "dotfile": {}, "tags": {}, "race": {}, // Passed to the test binary "q": {boolVar: true, passToTest: true}, "v": {boolVar: true, passToAll: 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 { debug.Debugf("TestFlags: args: %s", testArgs) 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 val.boolVar { // boolean variables can be either -bool, or -bool=true // some code, see issue 605, expects the latter form, so // when present, expand boolean args to their canonical // form. nVal = "true" } if nVal != "" { fArg = fArg + "=" + nVal } } } } targs = append(targs, fArg) } debug.Debugf("testFlags: targs: %s", targs) 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 } if ok && val.passToAll { // passToAll arguments, like -v or -cover, are special. They are handled by gb test // and the test sub process. So move them to the front of the parseArgs list but // the back of the extraArgs list. parseArgs = append([]string{nArg}, parseArgs...) extraArgs = append(extraArgs, nArg) continue } parseArgs = append(parseArgs, 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 = errors.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.4.4/cmd/gb/testflag_test.go000066400000000000000000000137071305302736300164530ustar00rootroot00000000000000package 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{"-v", "package_name"}, eargs: []string{"-v", "-debug"}, }, { args: []string{"-debug", "package_name", "-v"}, pargs: []string{"-v", "package_name"}, eargs: []string{"-debug", "-v"}, }, { 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"}, }, { args: []string{"-name=jardin"}, eargs: []string{"-name=jardin"}, }, { args: []string{"-cover", "-name=jardin"}, pargs: []string{"-cover"}, eargs: []string{"-name=jardin"}, }, { args: []string{"-race", "-name=jardin"}, pargs: []string{"-race"}, eargs: []string{"-name=jardin"}, }} 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=true", "-debug"}, }, { eargs: []string{"-v", "-debug"}, targs: []string{"-test.v=true", "-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=true"}, }, { 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=true"}, }, { 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"}, }, { eargs: []string{"-tags", "a,b,c"}, targs: []string{"-tags", "a,b,c"}, }} 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.4.4/cmd/path.go000066400000000000000000000012111305302736300141320ustar00rootroot00000000000000package cmd import ( "fmt" "os" "path/filepath" "github.com/pkg/errors" ) // FindProjectroot works upwards from path seaching for the // src/ directory which identifies the project root. func FindProjectroot(path string) (string, error) { if path == "" { return "", errors.New("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 } return path, nil } return "", fmt.Errorf(`could not find project root in "%s" or its parents`, start) } gb-0.4.4/cmd/path_test.go000066400000000000000000000025511305302736300152010ustar00rootroot00000000000000package cmd import ( "fmt" "io/ioutil" "os" "path/filepath" "reflect" "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 "%s" or its parents`, join(root, ".."))}, } for _, tt := range tests { got, err := FindProjectroot(tt.path) if got != tt.want || !reflect.DeepEqual(err, tt.err) { t.Errorf("FindProjectroot(%v): want: %v, %v, got %v, %v", tt.path, tt.want, tt.err, got, err) } } } gb-0.4.4/context.go000066400000000000000000000253141305302736300141310ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "io" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "sort" "strings" "sync" "time" "github.com/constabulary/gb/internal/debug" "github.com/pkg/errors" ) // enables sh style -e output const eMode = false // Importer resolves package import paths to *importer.Packages. type Importer interface { // Import attempts to resolve the package import path, path, // to an *importer.Package. Import(path string) (*build.Package, error) } // Context represents an execution of one or more Targets inside a Project. type Context struct { Project importer Importer pkgs map[string]*Package // map of package paths to resolved packages 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 Install bool // copy packages into $PROJECT/pkg Verbose bool // verbose output Nope bool // command specific flag, under test it skips the execute action. race bool // race detector requested 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 = append(c.buildtags, tags...) return nil } } // Gcflags appends flags to the list passed to the compiler. func Gcflags(flags ...string) func(*Context) error { return func(c *Context) error { c.gcflags = append(c.gcflags, flags...) return nil } } // Ldflags appends flags to the list passed to the linker. func Ldflags(flags ...string) func(*Context) error { return func(c *Context) error { c.ldflags = append(c.ldflags, flags...) return nil } } // WithRace enables the race detector and adds the tag "race" to // the Context build tags. func WithRace(c *Context) error { c.race = true Tags("race")(c) Gcflags("-race")(c) Ldflags("-race")(c) 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 NewContext(p Project, opts ...func(*Context) error) (*Context, error) { envOr := func(key, def string) string { if v := os.Getenv(key); v != "" { return v } 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, buildmode: "exe", pkgs: make(map[string]*Package), } 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) bc := build.Default bc.GOOS = ctx.gotargetos bc.GOARCH = ctx.gotargetarch bc.CgoEnabled = cgoEnabled(ctx.gohostos, ctx.gohostarch, ctx.gotargetos, ctx.gotargetarch) bc.ReleaseTags = releaseTags bc.BuildTags = ctx.buildtags i, err := buildImporter(&bc, &ctx) if err != nil { return nil, err } ctx.importer = i // C and unsafe are fake packages synthesised by the compiler. // Insert fake packages into the package cache. for _, name := range []string{"C", "unsafe"} { pkg, err := ctx.newPackage(&build.Package{ Name: name, ImportPath: name, Dir: name, // fake, but helps diagnostics Goroot: true, }) if err != nil { return nil, err } pkg.NotStale = true ctx.pkgs[pkg.ImportPath] = pkg } return &ctx, err } // IncludePaths returns the include paths visible in this context. func (c *Context) includePaths() []string { return []string{ c.workdir, c.Pkgdir(), } } // NewPackage creates a resolved Package for p. func (c *Context) NewPackage(p *build.Package) (*Package, error) { pkg, err := c.newPackage(p) if err != nil { return nil, err } pkg.NotStale = !pkg.isStale() return pkg, nil } // 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, errors.Errorf("%q is not a package", filepath.Join(c.Projectdir(), "src")) } path, err := relImportPath(filepath.Join(c.Projectdir(), "src"), path) if err != nil { return nil, err } if path == "." || path == ".." || strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") { return nil, errors.Errorf("import %q: relative import not supported", path) } return c.loadPackage(nil, path) } // loadPackage recursively resolves path as a package. If successful loadPackage // records the package in the Context's internal package cache. func (c *Context) loadPackage(stack []string, path string) (*Package, error) { if pkg, ok := c.pkgs[path]; ok { // already loaded, just return return pkg, nil } p, err := c.importer.Import(path) if err != nil { return nil, err } stack = append(stack, p.ImportPath) var stale bool for i, im := range p.Imports { for _, p := range stack { if p == im { return nil, fmt.Errorf("import cycle detected: %s", strings.Join(append(stack, im), " -> ")) } } pkg, err := c.loadPackage(stack, im) if err != nil { return nil, err } // update the import path as the import may have been discovered via vendoring. p.Imports[i] = pkg.ImportPath stale = stale || !pkg.NotStale } pkg, err := c.newPackage(p) if err != nil { return nil, errors.Wrapf(err, "loadPackage(%q)", path) } pkg.Main = pkg.Name == "main" pkg.NotStale = !(stale || pkg.isStale()) c.pkgs[p.ImportPath] = pkg return pkg, nil } // 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)) if eMode { fmt.Fprintln(os.Stderr, "+", strings.Join(cmd.Args, " ")) } 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) } func (c *Context) isCrossCompile() bool { return c.gohostos != c.gotargetos || c.gohostarch != c.gotargetarch } // 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 } func cgoEnabled(gohostos, gohostarch, gotargetos, gotargetarch string) bool { switch os.Getenv("CGO_ENABLED") { case "1": return true case "0": return false default: // cgo must be explicitly enabled for cross compilation builds if gohostos == gotargetos && gohostarch == gotargetarch { switch gotargetos + "/" + gotargetarch { case "darwin/386", "darwin/amd64", "darwin/arm", "darwin/arm64": return true case "dragonfly/amd64": return true case "freebsd/386", "freebsd/amd64", "freebsd/arm": return true case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le": return true case "android/386", "android/amd64", "android/arm": return true case "netbsd/386", "netbsd/amd64", "netbsd/arm": return true case "openbsd/386", "openbsd/amd64": return true case "solaris/amd64": return true case "windows/386", "windows/amd64": return true default: return false } } return false } } func buildImporter(bc *build.Context, ctx *Context) (Importer, error) { i, err := addDepfileDeps(bc, ctx) if err != nil { return nil, err } // construct importer stack in reverse order, vendor at the bottom, GOROOT on the top. i = &_importer{ Importer: i, im: importer{ Context: bc, Root: filepath.Join(ctx.Projectdir(), "vendor"), }, } i = &srcImporter{ i, importer{ Context: bc, Root: ctx.Projectdir(), }, } i = &_importer{ i, importer{ Context: bc, Root: runtime.GOROOT(), }, } i = &fixupImporter{ Importer: i, } return i, nil } gb-0.4.4/context_test.go000066400000000000000000000132421305302736300151650ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "path/filepath" "reflect" "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 := NewContext(proj, 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) } } } func TestContextOptions(t *testing.T) { matches := func(want Context) func(t *testing.T, got *Context) { return func(t *testing.T, got *Context) { if !reflect.DeepEqual(got, &want) { t.Errorf("got %#v, want %#v", got, &want) } } } tests := []struct { ctx Context fn func(*Context) error err error expect func(*testing.T, *Context) }{{ // assert that an zero context is not altered by the test rig. fn: func(*Context) error { return nil }, expect: matches(Context{}), }, { // test blank GOOS is an error fn: GOOS(""), err: fmt.Errorf("GOOS cannot be blank"), }, { // test blank GOARCH is an error fn: GOARCH(""), err: fmt.Errorf("GOARCH cannot be blank"), }, { ctx: Context{ gotargetos: "bar", gotargetarch: "baz", }, fn: GOOS("foo"), expect: matches(Context{ gotargetos: "foo", gotargetarch: "baz", }), }, { ctx: Context{ gotargetos: "bar", gotargetarch: "baz", }, fn: GOARCH("foo"), expect: matches(Context{ gotargetos: "bar", gotargetarch: "foo", }), }, { fn: Tags(), expect: matches(Context{}), }, { fn: Tags("foo"), expect: matches(Context{buildtags: []string{"foo"}}), }, { ctx: Context{buildtags: []string{"foo"}}, fn: Tags("bar"), expect: matches(Context{buildtags: []string{"foo", "bar"}}), }, { fn: Gcflags("foo"), expect: matches(Context{gcflags: []string{"foo"}}), }, { ctx: Context{gcflags: []string{"foo"}}, fn: Gcflags("bar"), expect: matches(Context{gcflags: []string{"foo", "bar"}}), }, { fn: Ldflags("foo"), expect: matches(Context{ldflags: []string{"foo"}}), }, { ctx: Context{ldflags: []string{"foo"}}, fn: Ldflags("bar"), expect: matches(Context{ldflags: []string{"foo", "bar"}}), }, { fn: WithRace, expect: matches(Context{ buildtags: []string{"race"}, race: true, gcflags: []string{"-race"}, ldflags: []string{"-race"}, }), }, { ctx: Context{buildtags: []string{"zzz"}}, fn: WithRace, expect: matches(Context{ buildtags: []string{"zzz", "race"}, race: true, gcflags: []string{"-race"}, ldflags: []string{"-race"}, }), }} for i, tt := range tests { ctx := tt.ctx err := tt.fn(&ctx) switch { case !reflect.DeepEqual(err, tt.err): t.Errorf("test %d: expected err: %v, got %v", i+1, tt.err, err) case err == nil: tt.expect(t, &ctx) } } } func TestContextLoadPackage(t *testing.T) { tests := []struct { opts []func(*Context) error path string }{ {path: "errors"}, {path: "net/http"}, // triggers vendor logic on go 1.6+ } proj := testProject(t) for _, tt := range tests { ctx, err := NewContext(proj, tt.opts...) if err != nil { t.Fatal(err) } defer ctx.Destroy() _, err = ctx.loadPackage(nil, tt.path) if err != nil { t.Errorf("loadPackage(%q): %v", tt.path, err) } } } func TestCgoEnabled(t *testing.T) { tests := []struct { gohostos, gohostarch string gotargetos, gotargetarch string want bool }{{ "linux", "amd64", "linux", "amd64", true, }, { "linux", "amd64", "linux", "386", false, }} for _, tt := range tests { got := cgoEnabled(tt.gohostos, tt.gohostarch, tt.gotargetos, tt.gotargetarch) if got != tt.want { t.Errorf("cgoEnabled(%q, %q, %q, %q): got %v, want %v", tt.gohostos, tt.gohostarch, tt.gotargetos, tt.gotargetarch, got, tt.want) } } } func TestContextImportPackage(t *testing.T) { proj := testProject(t) tests := []struct { path string err error }{{ path: "a", }, { path: "cgomain", }, { path: "net/http", // loaded from GOROOT }, { path: "cmd", err: &build.NoGoError{Dir: filepath.Join(proj.Projectdir(), "src", "cmd")}, }} for _, tt := range tests { ctx, err := NewContext(proj) if err != nil { t.Fatal(err) } _, err = ctx.importer.Import(tt.path) if !reflect.DeepEqual(err, tt.err) { t.Errorf("importPackage(%q): got %v, want %v", tt.path, err, tt.err) } } } gb-0.4.4/defaultcc.go000066400000000000000000000001311305302736300143650ustar00rootroot00000000000000// +build !darwin,!freebsd package gb const defaultCC = "gcc" const defaultCXX = "g++" gb-0.4.4/defaultcc_bsd.go000066400000000000000000000001351305302736300152210ustar00rootroot00000000000000// +build darwin freebsd package gb const defaultCC = "clang" const defaultCXX = "clang++" gb-0.4.4/depfile.go000066400000000000000000000121471305302736300140550ustar00rootroot00000000000000package gb import ( "compress/gzip" "crypto/sha1" "fmt" "go/build" "io" "io/ioutil" "net/http" "os" "path/filepath" "regexp" "strings" "github.com/constabulary/gb/internal/debug" "github.com/constabulary/gb/internal/depfile" "github.com/constabulary/gb/internal/untar" "github.com/pkg/errors" ) const semverRegex = `^([0-9]+)\.([0-9]+)\.([0-9]+)(?:(\-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-\-\.]+)?$` // addDepfileDeps inserts into the Context's importer list // a set of importers for entries in the depfile. func addDepfileDeps(bc *build.Context, ctx *Context) (Importer, error) { i := Importer(new(nullImporter)) df, err := readDepfile(ctx) if err != nil { if !os.IsNotExist(errors.Cause(err)) { return nil, errors.Wrap(err, "could not parse depfile") } debug.Debugf("no depfile, nothing to do.") return i, nil } re := regexp.MustCompile(semverRegex) for prefix, kv := range df { if version, ok := kv["version"]; ok { if !re.MatchString(version) { return nil, errors.Errorf("%s: %q is not a valid SemVer 2.0.0 version", prefix, version) } root := filepath.Join(cachePath(), hash(prefix, version)) dest := filepath.Join(root, "src", filepath.FromSlash(prefix)) fi, err := os.Stat(dest) if err == nil { if !fi.IsDir() { return nil, errors.Errorf("%s is not a directory", dest) } } if err != nil { if !os.IsNotExist(err) { return nil, err } if err := fetchVersion(root, dest, prefix, version); err != nil { return nil, err } } i = &_importer{ Importer: i, im: importer{ Context: bc, Root: root, }, } debug.Debugf("Add importer for %q: %v", prefix+" "+version, root) } if tag, ok := kv["tag"]; ok { root := filepath.Join(cachePath(), hash(prefix, tag)) dest := filepath.Join(root, "src", filepath.FromSlash(prefix)) fi, err := os.Stat(dest) if err == nil { if !fi.IsDir() { return nil, errors.Errorf("%s is not a directory", dest) } } if err != nil { if !os.IsNotExist(err) { return nil, err } if err := fetchTag(root, dest, prefix, tag); err != nil { return nil, err } } i = &_importer{ Importer: i, im: importer{ Context: bc, Root: root, }, } debug.Debugf("Add importer for %q: %v", prefix+" "+tag, root) } } return i, nil } func fetchVersion(root, dest, prefix, version string) error { if !strings.HasPrefix(prefix, "github.com") { return errors.Errorf("unable to fetch %v", prefix) } fmt.Printf("fetching %v (%v)\n", prefix, version) rc, err := fetchRelease(prefix, "v"+version) if err != nil { return err } defer rc.Close() return unpackReleaseTarball(dest, rc) } func fetchTag(root, dest, prefix, tag string) error { if !strings.HasPrefix(prefix, "github.com") { return errors.Errorf("unable to fetch %v", prefix) } fmt.Printf("fetching %v (%v)\n", prefix, tag) rc, err := fetchRelease(prefix, tag) if err != nil { return err } defer rc.Close() return unpackReleaseTarball(dest, rc) } func unpackReleaseTarball(dest string, r io.Reader) error { gzr, err := gzip.NewReader(r) if err != nil { return errors.Wrap(err, "unable to construct gzip reader") } parent, pkg := filepath.Split(dest) if err := os.MkdirAll(parent, 0755); err != nil { return err } tmpdir, err := ioutil.TempDir(parent, "tmp") if err != nil { return err } defer os.RemoveAll(tmpdir) tmpdir = filepath.Join(tmpdir, pkg) if err := untar.Untar(tmpdir, gzr); err != nil { return err } dents, err := ioutil.ReadDir(tmpdir) if err != nil { os.RemoveAll(tmpdir) return errors.Wrap(err, "cannot read download directory") } re := regexp.MustCompile(`\w+-\w+-[a-z0-9]+`) for _, dent := range dents { if re.MatchString(dent.Name()) { if err := os.Rename(filepath.Join(tmpdir, dent.Name()), dest); err != nil { os.RemoveAll(tmpdir) return errors.Wrap(err, "unable to rename final cache dir") } return nil } } os.RemoveAll(tmpdir) return errors.New("release directory not found in tarball") } func fetchRelease(prefix, tag string) (io.ReadCloser, error) { const format = "https://api.github.com/repos/%s/tarball/%s" prefix = prefix[len("github.com/"):] url := fmt.Sprintf(format, prefix, tag) resp, err := http.Get(url) if err != nil { return nil, errors.Wrapf(err, "failed to fetch %q", url) } if resp.StatusCode != 200 { return nil, errors.Errorf("failed to fetch %q: expected 200, got %d", url, resp.StatusCode) } return resp.Body, nil } func readDepfile(ctx *Context) (map[string]map[string]string, error) { file := filepath.Join(ctx.Projectdir(), "depfile") debug.Debugf("loading depfile at %q", file) return depfile.ParseFile(file) } func hash(arg string, args ...string) string { h := sha1.New() io.WriteString(h, arg) for _, arg := range args { io.WriteString(h, arg) } return fmt.Sprintf("%x", string(h.Sum(nil))) } func cachePath() string { return filepath.Join(gbhome(), "cache") } func gbhome() string { return envOr("GB_HOME", filepath.Join(envOr("HOME", "/tmp"), ".gb")) } func envOr(key, def string) string { v := os.Getenv(key) if v == "" { v = def } return v } gb-0.4.4/example_test.go000066400000000000000000000012501305302736300151300ustar00rootroot00000000000000package gb_test import ( "log" "path/filepath" "github.com/constabulary/gb" ) func ExampleNewProject() { // 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) // Create a new Context from the Project. A Context holds // the state of a specific compilation or test within the Project. ctx, err := gb.NewContext(proj) if err != nil { log.Fatal("Could not create new context:", err) } // Always remember to clean up your Context ctx.Destroy() } gb-0.4.4/executor.go000066400000000000000000000041661305302736300143050ustar00rootroot00000000000000package gb import ( "sync" "github.com/pkg/errors" ) // 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.4.4/executor_test.go000066400000000000000000000076751305302736300153540ustar00rootroot00000000000000package gb import ( "errors" "fmt" "go/build" "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: &build.NoGoError{Dir: filepath.Join(getwd(t), "testdata", "src", "blank")}, }} for _, tt := range tests { ctx := testContext(t) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if !reflect.DeepEqual(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): %v", tt.pkg, err) continue } if err := Execute(action); !reflect.DeepEqual(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{{ Name: "child, error", Run: niltask, Deps: []*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.4.4/gb.go000066400000000000000000000050311305302736300130270ustar00rootroot00000000000000// 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 ( "go/build" "os" "path/filepath" ) var releaseTags = build.Default.ReleaseTags // Toolchain represents a standardised set of command line tools // used to build and test Go programs. type Toolchain interface { Gc(pkg *Package, files []string) error Asm(pkg *Package, ofile, sfile string) error Pack(pkg *Package, afiles ...string) error Ld(*Package) 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) } // 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))] } 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.4.4/gb_test.go000066400000000000000000000022031305302736300140640ustar00rootroot00000000000000package 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) } } } 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.path, tt.want, got) } } } gb-0.4.4/gc.go000066400000000000000000000135431305302736300130370ustar00rootroot00000000000000package gb import ( "bytes" "fmt" "go/build" "io" "io/ioutil" "os" "path/filepath" "runtime" "strings" "github.com/pkg/errors" ) // 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 goversion == 1.4 && (c.gohostos != c.gotargetos || c.gohostarch != c.gotargetarch) { // cross-compliation is not supported yet #31 return errors.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 goversion == 1.4: 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 goversion > 1.4: 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 errors.Errorf("unsupported Go version: %v", runtime.Version()) } return nil } } func (t *gcToolchain) Asm(pkg *Package, ofile, sfile string) error { args := []string{"-o", ofile, "-D", "GOOS_" + pkg.gotargetos, "-D", "GOARCH_" + pkg.gotargetarch} switch { case goversion == 1.4: includedir := filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch) args = append(args, "-I", includedir) case goversion > 1.4: odir := filepath.Join(filepath.Dir(ofile)) includedir := filepath.Join(runtime.GOROOT(), "pkg", "include") args = append(args, "-I", odir, "-I", includedir) default: return errors.Errorf("unsupported Go version: %v", runtime.Version()) } args = append(args, sfile) if err := mkdir(filepath.Dir(ofile)); err != nil { return errors.Errorf("gc:asm: %v", err) } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, 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) 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 := pkg.bindir() if err := mkdir(dir); err != nil { return err } tmp, err := ioutil.TempFile(dir, ".gb-link") if err != nil { return err } tmp.Close() args := append(pkg.ldflags, "-o", tmp.Name()) for _, d := range pkg.includePaths() { args = append(args, "-L", d) } args = append(args, "-extld", linkCmd(pkg, "CC", defaultCC)) if goversion > 1.4 { args = append(args, "-buildmode", pkg.buildmode) } args = append(args, pkg.objfile()) 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(), pkg.Binfile()) } func (t *gcToolchain) Cc(pkg *Package, ofile, cfile string) error { if goversion > 1.4 { return errors.Errorf("gc %f does not support cc", goversion) } args := []string{ "-F", "-V", "-w", "-trimpath", pkg.Context.Workdir(), "-I", pkg.Workdir(), "-I", filepath.Join(runtime.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, files []string) error { outfile := pkg.objfile() args := append(pkg.gcflags, "-p", pkg.ImportPath, "-pack") args = append(args, "-o", outfile) for _, d := range pkg.includePaths() { args = append(args, "-I", d) } if pkg.Goroot && pkg.ImportPath == "runtime" { // runtime compiles with a special gc flag to emit // additional reflect type data. args = append(args, "-+") } switch { case pkg.Complete(): args = append(args, "-complete") case goversion > 1.4: asmhdr := filepath.Join(filepath.Dir(outfile), pkg.Name, "go_asm.h") args = append(args, "-asmhdr", asmhdr) } // If there are vendored components, create an -importmap to map the import statement // to the vendored import path. The possibilities for abusing this flag are endless. if goversion > 1.5 && pkg.Goroot { for _, path := range pkg.Package.Imports { if i := strings.LastIndex(path, "/vendor/"); i >= 0 { args = append(args, "-importmap", path[i+len("/vendor/"):]+"="+path) } else if strings.HasPrefix(path, "vendor/") { args = append(args, "-importmap", path[len("vendor/"):]+"="+path) } } } args = append(args, files...) if err := mkdir(filepath.Join(filepath.Dir(outfile), pkg.Name)); err != nil { return errors.Wrap(err, "mkdir") } var buf bytes.Buffer err := runOut(&buf, pkg.Dir, nil, t.gc, args...) if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stderr, &buf) } return err } gb-0.4.4/goversion12.go000066400000000000000000000001611305302736300146140ustar00rootroot00000000000000// +build go1.2 // +build !go1.3,!go1.4,!go1.5,!go1.6 package gb const ( goversion = 1.2 allowVendor = 0 ) gb-0.4.4/goversion13.go000066400000000000000000000001521305302736300146150ustar00rootroot00000000000000// +build go1.3 // +build !go1.4,!go1.5,!go1.6 package gb const ( goversion = 1.3 allowVendor = 0 ) gb-0.4.4/goversion14.go000066400000000000000000000001431305302736300146160ustar00rootroot00000000000000// +build go1.4 // +build !go1.5,!go1.6 package gb const ( goversion = 1.4 allowVendor = 0 ) gb-0.4.4/goversion15.go000066400000000000000000000001341305302736300146170ustar00rootroot00000000000000// +build go1.5 // +build !go1.6 package gb const ( goversion = 1.5 allowVendor = 0 ) gb-0.4.4/goversion16.go000066400000000000000000000000701305302736300146170ustar00rootroot00000000000000// +build go1.6 package gb const ( goversion = 1.6 ) gb-0.4.4/internal/000077500000000000000000000000001305302736300137255ustar00rootroot00000000000000gb-0.4.4/internal/debug/000077500000000000000000000000001305302736300150135ustar00rootroot00000000000000gb-0.4.4/internal/debug/debug.go000066400000000000000000000013661305302736300164360ustar00rootroot00000000000000// 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.4.4/internal/depfile/000077500000000000000000000000001305302736300153355ustar00rootroot00000000000000gb-0.4.4/internal/depfile/depfile.go000066400000000000000000000070361305302736300173020ustar00rootroot00000000000000// Package depfile loads a file of tagged key value pairs. package depfile import ( "bufio" "fmt" "io" "os" "strings" "github.com/pkg/errors" ) // ParseFile parses path into a tagged key value map. // See Parse for the syntax of the file. func ParseFile(path string) (map[string]map[string]string, error) { r, err := os.Open(path) if err != nil { return nil, errors.Wrapf(err, "ParseFile") } defer r.Close() return Parse(r) } // Parse parses the contents of r into a tagged key value map. // If successful Parse returns a map[string]map[string]string. // The format of the line is // // name key=value [key=value]... // // Elements can be separated by whitespace (space and tab). // Lines that do not begin with a letter or number are ignored. This // provides a simple mechanism for commentary // // # some comment // github.com/pkg/profile version=0.1.0 // // ; some other comment // // third kind of comment // lines starting with blank lines are also ignored // github.com/pkg/sftp version=0.2.1 func Parse(r io.Reader) (map[string]map[string]string, error) { sc := bufio.NewScanner(r) m := make(map[string]map[string]string) var lineno int for sc.Scan() { line := sc.Text() lineno++ // skip blank line if line == "" { continue } // valid lines start with a letter or number everything else is ignored. // we don't need to worry about unicode because import paths are restricted // to the DNS character set, which is a subset of ASCII. if !isLetterOrNumber(line[0]) { continue } name, kv, err := parseLine(line) if err != nil { return nil, fmt.Errorf("%d: %v", lineno, err) } m[name] = kv } return m, sc.Err() } func parseLine(line string) (string, map[string]string, error) { args := splitLine(line) name, rest := args[0], args[1:] if len(rest) == 0 { return "", nil, fmt.Errorf("%s: expected key=value pair after name", name) } kv, err := parseKeyVal(rest) if err != nil { return "", nil, fmt.Errorf("%s: %v", name, err) } return name, kv, nil } func parseKeyVal(args []string) (map[string]string, error) { m := make(map[string]string) for _, kv := range args { if strings.HasPrefix(kv, "=") { return nil, fmt.Errorf("expected key=value pair, missing key %q", kv) } if strings.HasSuffix(kv, "=") { return nil, fmt.Errorf("expected key=value pair, missing value %q", kv) } args := strings.Split(kv, "=") switch len(args) { case 2: key := args[0] if v, ok := m[key]; ok { return nil, fmt.Errorf("duplicate key=value pair, have \"%s=%s\" got %q", key, v, kv) } m[key] = args[1] default: return nil, fmt.Errorf("expected key=value pair, got %q", kv) } } return m, nil } func isLetterOrNumber(r byte) bool { switch { case r > '0'-1 && r < '9'+1: return true case r > 'a'-1 && r < 'z'+1: return true case r > 'A'-1 && r < 'Z'+1: return true default: return false } } // splitLine is like strings.Split(string, " "), but splits // strings by any whitespace characters, discarding them in // the process. func splitLine(line string) []string { var s []string var start, end int for ; start < len(line); start++ { c := line[start] if !isWhitespace(c) { break } } var ws bool for end = start; end < len(line); end++ { c := line[end] if !isWhitespace(c) { ws = false continue } if ws == true { start++ continue } ws = true s = append(s, line[start:end]) start = end + 1 } if start != end { s = append(s, line[start:end]) } return s } func isWhitespace(c byte) bool { return c == ' ' || c == '\t' } gb-0.4.4/internal/depfile/depfile_test.go000066400000000000000000000114371305302736300203410ustar00rootroot00000000000000package depfile import ( "fmt" "reflect" "strings" "testing" ) func TestParseKeyVal(t *testing.T) { tests := []struct { args []string want map[string]string err error }{{ args: []string{}, // handled by Parse want: map[string]string{}, // expected }, { args: []string{"version"}, err: fmt.Errorf("expected key=value pair, got %q", "version"), }, { args: []string{"=9.9.9"}, err: fmt.Errorf("expected key=value pair, missing key %q", "=9.9.9"), }, { args: []string{"version="}, err: fmt.Errorf("expected key=value pair, missing value %q", "version="), }, { args: []string{"version=1.2.3"}, want: map[string]string{ "version": "1.2.3", }, }, { args: []string{"version=1.2.3", "version=2.4.5"}, err: fmt.Errorf("duplicate key=value pair, have \"version=1.2.3\" got %q", "version=2.4.5"), }, { args: []string{"version=1.2.3", "//", "comment"}, err: fmt.Errorf("expected key=value pair, got %q", "//"), }, { args: []string{"vcs=git", "version=1.2.3"}, want: map[string]string{ "version": "1.2.3", "vcs": "git", }, }} for _, tt := range tests { got, err := parseKeyVal(tt.args) if !reflect.DeepEqual(err, tt.err) { t.Errorf("parseKeyVal(%v): got %v, expected %v", tt.args, err, tt.err) continue } if err == nil && !reflect.DeepEqual(tt.want, got) { t.Errorf("parseKeyVal(%v): got %#v, expected %#v", tt.args, got, tt.want) } } } func TestParseLine(t *testing.T) { tests := []struct { line string name string kv map[string]string err error }{{ line: "a", // no \n, sc.Text removes it err: fmt.Errorf("a: expected key=value pair after name"), }, { line: "a\ta", err: fmt.Errorf("a: expected key=value pair, got %q", "a"), }, { line: "a\tversion=7\t ", name: "a", kv: map[string]string{ "version": "7", }, }} for _, tt := range tests { name, kv, err := parseLine(tt.line) if !reflect.DeepEqual(err, tt.err) { t.Errorf("parseLine(%q): got %v, expected %v", tt.line, err, tt.err) continue } if err == nil && !reflect.DeepEqual(tt.kv, kv) || name != tt.name { t.Errorf("parseLine(%q): got %s %#v, expected %s %#v", tt.line, name, kv, tt.name, tt.kv) } } } func TestParse(t *testing.T) { tests := []struct { c string want map[string]map[string]string err error }{{ c: "", // empty want: make(map[string]map[string]string), // empty map, not nil }, { c: "\n", // effectively empty want: make(map[string]map[string]string), // empty map, not nil }, { c: "github.com/pkg/profile", // no \n err: fmt.Errorf("1: github.com/pkg/profile: expected key=value pair after name"), }, { c: "github.com/pkg/profile\n", err: fmt.Errorf("1: github.com/pkg/profile: expected key=value pair after name"), }, { c: "github.com/pkg/profile version=1.2.3", // no \n want: map[string]map[string]string{ "github.com/pkg/profile": { "version": "1.2.3", }, }, }, { c: "github.com/pkg/profile version=1.2.3\n", want: map[string]map[string]string{ "github.com/pkg/profile": { "version": "1.2.3", }, }, }, { c: "// need to pin version\ngithub.com/pkg/profile version=1.2.3\n", want: map[string]map[string]string{ "github.com/pkg/profile": { "version": "1.2.3", }, }, }, { c: "github.com/pkg/profile version=1.2.3\ngithub.com/pkg/sftp version=0.0.0", want: map[string]map[string]string{ "github.com/pkg/profile": { "version": "1.2.3", }, "github.com/pkg/sftp": { "version": "0.0.0", }, }, }, { c: `# some comment github.com/pkg/profile version=0.1.0 ; some other comment // third kind of comment lines starting with blank lines are also ignored github.com/pkg/sftp version=0.2.1 `, want: map[string]map[string]string{ "github.com/pkg/profile": { "version": "0.1.0", }, "github.com/pkg/sftp": { "version": "0.2.1", }, }, }} for _, tt := range tests { r := strings.NewReader(tt.c) got, err := Parse(r) if !reflect.DeepEqual(err, tt.err) { t.Errorf("Parse(%q): got %v, expected %v", tt.c, err, tt.err) continue } if err == nil && !reflect.DeepEqual(tt.want, got) { t.Errorf("Parse(%q): got %#v, expected %#v", tt.c, got, tt.want) } } } func TestSplitLine(t *testing.T) { tests := []struct { s string want []string }{ {s: "", want: nil}, {s: "a", want: []string{"a"}}, {s: " a", want: []string{"a"}}, {s: "a ", want: []string{"a"}}, {s: "a b", want: []string{"a", "b"}}, {s: "a b", want: []string{"a", "b"}}, {s: "a\tb", want: []string{"a", "b"}}, {s: "a \tb", want: []string{"a", "b"}}, {s: "\ta \tb ", want: []string{"a", "b"}}, } for _, tt := range tests { got := splitLine(tt.s) if !reflect.DeepEqual(got, tt.want) { t.Errorf("splitLine(%q): got %#v, expected %#v", tt.s, got, tt.want) } } } gb-0.4.4/internal/fileutils/000077500000000000000000000000001305302736300157255ustar00rootroot00000000000000gb-0.4.4/internal/fileutils/_testdata/000077500000000000000000000000001305302736300176755ustar00rootroot00000000000000gb-0.4.4/internal/fileutils/_testdata/copyfile/000077500000000000000000000000001305302736300215075ustar00rootroot00000000000000gb-0.4.4/internal/fileutils/_testdata/copyfile/a/000077500000000000000000000000001305302736300217275ustar00rootroot00000000000000gb-0.4.4/internal/fileutils/_testdata/copyfile/a/rick000077700000000000000000000000001305302736300276352/never/going/to/give/you/upustar00rootroot00000000000000gb-0.4.4/internal/fileutils/fileutils.go000066400000000000000000000044501305302736300202570ustar00rootroot00000000000000// Package fileutils provides utililty methods to copy and move files and directories. package fileutils import ( "fmt" "io" "os" "path/filepath" "runtime" "strings" "github.com/pkg/errors" ) const debugCopypath = false 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 } // Copyfile copies file to destination creating destination directory if needed func Copyfile(dst, src string) error { err := mkdir(filepath.Dir(dst)) if err != nil { return errors.Wrap(err, "copyfile: mkdirall") } r, err := os.Open(src) if err != nil { return errors.Wrapf(err, "copyfile: open(%q)", src) } defer r.Close() w, err := os.Create(dst) if err != nil { return errors.Wrapf(err, "copyfile: create(%q)", dst) } 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.4.4/internal/fileutils/fileutils_test.go000066400000000000000000000010201305302736300213040ustar00rootroot00000000000000package 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.4.4/internal/fileutils/path_test.go000066400000000000000000000053701305302736300202540ustar00rootroot00000000000000// 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" ) 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.4.4/internal/untar/000077500000000000000000000000001305302736300150565ustar00rootroot00000000000000gb-0.4.4/internal/untar/_testdata/000077500000000000000000000000001305302736300170265ustar00rootroot00000000000000gb-0.4.4/internal/untar/_testdata/a.tar.gz000066400000000000000000000002241305302736300203730ustar00rootroot00000000000000‹¼kjWíÑ1à …aÏ=…oC0œµâmsÿ &êСª*ýßò,ñÀu‘é̬¸ë+ó‘Ó‘' ±¬1eÏ^ÔBˆ!‹úüj"ÛãYï½Ê­]‡s}¬µÁùùŽwþ‰ºÔéwôÿÈ)ö?÷¿zµéÍ„ý_~ÝÀ·va ¾|(gb-0.4.4/internal/untar/_testdata/errors.tar.gz000066400000000000000000000203431305302736300214730ustar00rootroot00000000000000‹ì |û']ÕæÙGèÓ¡û88î²ÿÊþÎŒýW1^|ô˯ŸÜþ]Ãë>¯3° °gˆs52Â49á`ÜÓ!@Ü=Ù’z½êc›¬¢×ôu·«×ÔÝž^¥»Ðiš0gÊî}Ÿ¡¹±Í Ó·t]ÿI îovͱÿãÖAó´Ó|<wصk}ÎþwÁü²ÿ'¸üÑ4púƒÍ«çËìИpv0àŸ²ÿ²àáLzÐ=þ·Öp]F# ¸àÁ„[º¦µ¹åˆ0pzã–s Æ‚3ÇcÂ&§–žãÁ”Ù~0„ã¼ óúÄ%~è[ Ì È`# 0„hVè‰cÁM80BøÃˆëú׎ׇüÕ³$hЃÃÑž³,E‚ùvDŠé[Ð !$ xFϟૈžá/Ã;G€ÿr‚H#ó¬%€Ðt gÈ}€)ň˜š5ª–ø‘ŒûÁÔÔ,ßc†OœX0f øîû€ ŽáŠ„Ç$˜"fÔ}×ê°ÎÙQ÷}£ÝdpÞ>û£uذî»&;8;ÿÐn½}×eïÎŽ›íkœBëi·ÝzsÑ=ƒ†B£# ¾hœ~`Íÿœ·›;k³ÖÉùq €ôvã´Ûjvʬuzp|qØ:}[f€žuÙqë¤Õ…nݳ2"Õf‡±³#vÒl¼ƒÇÆ›Öq«û9juO× k°óF»Û:¸8n´ÙùEûü¬Ód0-í°Õ98n´Nš‡:`Œ¬ùGó´Ë:ïÇÇsg‰´gæø¦ D6Þ75³o´ð¦ùŸ&L¦ÑþPV0;Íÿ½€NðR;lœ4ÞÂÜŠw°drpÑnž ÍÀ‡ÎÅ›N·Õ½è6ÙÛ³³Cd´Öi¶ÿßù;>ë·.:M0ûF·Aˆ° ^Ãý›‹N‹˜Ö:í6Ûí‹ónëì´3lik zHÜ=;¥©‡ÎÚ(ò€˜_fïß5¡½ %N5àØA7ÕM|ÀÀnjŽì´ùö¸õ¶yzÐDjÎÊûV§YYµ:Ø¡%ѾoÎ š2ʨÒè6¥±e’$k±Æá-$[uÙwZJOˆeï˜d7(ý_í ð5gýo7‡'M}h=Ž;ÖÿZµ6“ÿïÖvv­ÿOp­3)}öñ÷]Êû6ZŸŠƒ0‰úÖ–L7MG÷ƒ~* ÐŤ_º»[ Á6F£?øÔ’îÐÏ&ØHÉ…1r ÿ÷© °òÇb«÷êåp׃±õçîmõÃ3[CCÀ‚õoÀÿ: Ƽ´ª‚¸…Œ `UYPDã[ÿÐ7P}ÖLšËÜèß’ÆîChÚ³+f•FP3¶Ò žaõù(K†ÉÇã4íÜ0¿@¢É[­ö‚ Hù\Õ̸EŒB/gÂ.ü €ˆeÌa¸ù¾ŽåøC 6Þú ¢”À÷î”_0þðµÏŸ?CnéØ8ŒýþšyŽË¾A"/ÁƇï´[ìª]sÀ@’®±HÀÍq €8QTb‚Ì/€ŽÝ"+I“äˆbJŒ”B~b˜iñÞ¸ß'z=Œ¿(ÒÎ-b‰â¡q¨C¤Lƒ–ƒƒG º ÇÅzfË*Ør^“Á®© X- Á•?¥Ñ>Äx²sb¸cŽq]Ñ<__g ËRñm„ÔPó”"Q¦ø>0FÌ{&EÞ’Ÿ2_+® @¿ÈO &AbeGЙߨJlWe[ý5s€ŸŽ«·¹aA P J+ˆ4"¯÷eV`,1[…’7̳Í!Ê䜫”/fÀŽd¶_~NAüŒS^ŽÁeÀ\¥:ø¶BZÆÙæx‡ G Nœ^¨ ò÷¨Á—±²gP…2- æ„%OYÞÂp!0‚€Ÿ€jÈÀ݇T…*¡AÊ…úÑôøŽc"ª‚õP© oªÞH¥Æ@мy€“ lSàQG À^zÓ˜MÈÇϺ$F%kƒÔøDhÔ»XR—²ÉB£ÂÔ/e™™…þhèC~"É—„ÇzÏ€}–4|.Gsñ ù‰…¼êå¸D3zYWó`â0\)gšPT¶’^Ä —`‚¦2z~2mb—z<ß­-éÀ’j4˜ýT³¸m€KÉô{_<ÿÚ˰Fûˆ† •J¹LšE’‚‹N{›•˜’t ½2ÿòúšöÔ»ÐPŽI_ÇàN@ÙÁ±1*žS:è;yä uö^fsà„áXe"¹gHÕSV‚žBL‡=ßÒRœÄ*uó¤ë¡ ²d§@ânã ÁQŽ|²Pƒ™ÀÔ>¤Í )ä;&£c!Ø2oõU40Ùß“ÔÊéC®ë™àv7«›.JóŸ€ÿÅלø?¢kàŽø¿RÙߟ©ÿïîþªÿ=Å…KXv|ˆãZ›ßèãVÿ&Cä[M3]ßãW6mêÕÙAý²ïc¸s)ó2ñ]— G—j@ (€º¢‘u†Q:['wƒ¬!ŠË-MãÞÄ |}&úÛ·gçî»M¹FˆÞSî ÜìÕp+QÅÛ×××Ùx<ªØ‚•0ôÜÚ¾^×TÂXgÝ瀻#{ì2ˆhF$Ú™è8 ¢- ÅŸèH&0Hlƨ؉ã½}ÏÀÃéò¼æNµƒïÁ¢‚#À‹³“·NUõŠ©Ø«Á$ØÐ^ïÉ ô®È†×q—Kõú²çxÿÚÀw’Ód››ú^¼Hµh4‘«ÜŒ":Ù…ÑpS&Ó+M‚vu~+G[Á:Q„¢‚.réÙ†êB¸¤]×g°ÄŽ\Z‡HégÝtý]sü¿Š¥úþcáXîÿ+ÕêþÌþÏÞvõ—ÿŠ ÂÛ‡¶¶àû¾ê€„W>TÍs êÍ­ü‚óýEó(uôÀÒdÊ’âA"¶ï®  eE„DºË ÷U€¥e…œrÜY\Ýd…!ÍÔ¤ô©Ä@ð(U„ô…†” ©p0[HÈI+[OÈ e^Ù"Âë™§Å…i%+×déaQå!¶´øðaå„꟫C䨵 ¡zÅU‰ÙQÙâDŽÏGä²ä:ÈŠ”0ÒœÈO¸nZ„23bà·³µ€˜“ö0Ô#àU@¤®Ç™#íIo#Òu&›Õ Û=¹m/Æ#tÄ$,Þø—HO98ÖJ[ІÔ6´"R)Ëä"Ù¥!F1!YrÈâ¶ØHgq;\ÎGgMÄ}ž=k»Ïž•4¿t´ÄŸEÞã1~pîž8p#'sNÒË„(å¢è˜9X¿wʯˌÉ.3ôe’ÞÙ8K?°b§%Á©S#_ñrŠLGPŽ7ñ¿p C¯r‘‘F¶–:È/vzÞC¤x²ÄK$½"W!ô¤-¯Îï<à³ÝÐ’ÁBTZC,!äã'_j”â¤~xƒ ò}†ŠœGg(ˆ—ÄJ] R +"·‘5‰dδt*^‚’’>%æ˜f¸ÑÃc0 \‚PjÌqv}Mû#¹z–™ÿEù#½˜L½ô/lŸa?’ ³#*ÎḌPfÆÄca¾ç8 »XØx!êV •òÝoóBìp>{*…(!¾*&õSi1mŽ2qª¦É£Š¬¨­€–|8~A+iˆäJ-«"1¢Øy©(G:)0'²iQqè•Z ø^6#d]Õ.,¿Ö†¢OX´]{NrÔn5 ã+VŒº—$2`°ìɾÅñŽãç x‹‚ˆÀãÙ¼Lþ“cš5µ$Q#<ÒŠólò¬®­­výÈ5úÅg/žQghó!zïEˆjw ßÑDGR¼ð$<)àû´nPGI5ÜÝÂÌ&Pˆ¡ýBT·ÈéÒ’È3’YìqÐØ(ºbpëˆä *ªÅGåEp‚ŠŸ’}8aÕ³ ·hßVKå„)}åîÝ(¹fˆUs´:«E/"X,IÑŠ$Ã*Uwc" –´KtÅ.Æco}Át]ýÇ·ÛeS"UPb’$€Pš;Ïd(¥°òYF¤½,šM¬†¦´XmÓ”6DVVP¥Œ*,bçfE–4uI–ŽZÿVµhŠŠã%ñ¾!ËqhÖš¯£®?À:çXà¥,½Žy¹ °^P¿´­~Üþ„M‘y?Ôj¯u%ÿ”å¯gL×ð ö6B•IKŽFµd²ò„d¯œâa„D޲< ´Ì»Ê_G¹c4E|DR£G%¯odÀƺTCâ¸jÀUWu°„”׸Å?¤u6σDñ× ¬aTÖ¥,⋽€1vš3÷ò#?Ž[w9¤»X(ù´_C¸Sʶ£ä=¾ËöŒ|!œž+õªq]:`™ÈÐÕ ³9n*–•Á°âV=EÞ™ð.Íw3˜Ĕ—çˆò‰ý[.Š4%©²ŸÄF%-{ài_ðs:}YÊ’Ú§­Lű¦,:6°¶–0P[Ø-SÁQZ’ J%°’ôz¿ST êÓ ¸ñEù#RM•}+,i¥ätTè¯.œþC®…õúÂÜãlܱÿ»¿]ÙË×ÿ÷·}ÿãI®%I•l*dÓ«µBÀm—›Ô€*ž“’.ò#]hÀ˜ t?%Azœ)­]^{|ý­€%,(*T.J´||+ؾŸ{…-Ù·HG¦YE¬´üæ—Þ(^Uµ{w‚9>È£Ã0I¤åŒq}Ÿš‰/¡ŽµAéû =ЦÐcÂ;ä@ÜDn1™ ^áwõB à+Äcȳ¯@94•#%éHoã@å‚éãÎÊFÑG¡­$Ï—Œ(”4EhÚ¥'4%C.ã1—IÛXŸ?oTzI"Kó躗Â`mNjÉZ6Œt(I_”AÛ<;Šv äÔ2Ouï ROˆžy#JðhBii!÷\g󮪒 ¤$G5µR$ôXgRº’ׂ±lßø+É$RTïYø‹‡V~[3UÝøg>ыŠaõE2Š¥å-WôßD6RˆUo‰D õø"œ±Šä ˆNQUÕ ºz¸-35 TÕuL/Êàç^Z ùFú>3üyÌ´ˆ9oxÀ7ƒê†1Mcϰm2«¶Ò×büœÀÉ@‰l•×*(YÕ—¿aJ‹ÁÊa7e܈ˆê¸k¸Ñxè ŽŠŒÁ¹Ã”ޤ½åïjÝÑ95¿Ž ·˜Ñܼ-Е +勤¬£8/*+zLû.—i?ÀgÚâ4í¿›×´ç¹Í$É/¡÷r¨‹Á¬N¨ÃÌpÀY¹7¬2çaß~ o?šËÕK¿~eç~M))ü4¥Î ¿"ïË3§¢È]|$0<ŽO¸•øàŽÎè0~É~q8’@¾RÛ ÷@@GT]G¡QðÔÙÿ:µ)”x;wžÒf(¡ѵ5ð‡|˲Í-˜ó¿Õ•·¬zeŸ@D W0öŽû>Wý#p©RË€jǠĽaí¼Ì€*>?)!¼ÕàD£Î~MÒD¿º†²泈x³•þµ¶ze{F{BgÈç€Ê£zâ7ÀM‚ðr'¡ïó'\ †!†WÆÐÚ«é¢^ÝÞ}•WLZ–KÉ.úB¥Ìj, S›#a_Á6TÇ#±½bjφWòTÄ™)¯æiàLõ=Ë÷;3ï«ð~èX–˱üC¸;8…qHr܉6‰‹ERçL3ß’=L™g"«3IUõõ( ‹Âãy‚ºŠõ-#I: Úk_î6„K¼†½HWWwµý]1챜ÓÞÎ/ç´ŠsªýœÓjʸö@=|©lí~:ÿ T¯"󾮇`ÚÝVþdž¿°èÙí¼kgët<²ºª‡gëÕE‘Lç{2EB”IJê<Ü {hpTg0|E7$Ñt´]ùåVñ@Õ¿“ÊiëUrŽYjê²Ãákkwœ§¢ÏœrN\ÆK™CÏZúˆ •9ŠTfùU¤9çp’±…’D&ÂèàJš´yV$ÂÛõê§–VBÄÂkŸÙx¢Y,¶­;ýÚÚRÂy02HÜç}K²5Šq@:h"Ë™ö\Èù¶w]_z¶ÌÑÓk?p­޼¥sCE¬ùÆßËI.ëþ"',{×™ÄAÿ©Å±9õY ºó?µý½™ßÿßßþõûïOr­ö¥ <õÓç7#¼“{;báùyú~§€hç(s‚5Ù•Lö’°s:Åy=~ª»xkz¶ãdÕŽ/Ò=!d/Ð Í/é™)'/CýÅšµ¯zu·Þ_XÈcDm’E1Œ¶Š|¹½·M¶Œ²›zrð½·öV—Mäše¸¹Š–ŒX ­%#(6 ñ!’Û­<©äîyPju¹àŒ(ÎV牯Zgy¡Ü `ò½(\üÑîïç©‹wÞêµ<ðÔñcLYBž'•Ç®®ó7™Ë¸;‡Ej¯î§2ðÊöþüI­Àš4~(ß¡ö›qeÿ 4xvpJ…éû",õ½•è»-êŒDôÕŸäÖ‚ïœJ×ñ8Y‚ Z ›ë„§òäXJ¼›•R|&í:™inbN sÿª… ôlTß—º.3"à£óI áÙ/G¬…ú‘ñí]YoÛF~v~« h+ ¥À—R¿Ô€Ûib@bY¢l¦2åš’l õïê{YçÚ‹\RG,ŠÅÝÝî53ßÎ9§àܱå¼×ˆÝJtÁ29ÿ‘;¨> Væ¼G ë¿âúŸ6…ÿ³ ÿ'jíì¿»{­§õÿc|V]ÿ#T íøÜÇÙpÈ5GÅ;Qô‚„ §ó”ƒâ³dkd:‘K.=í¾žÝ½ë¡–§5&Tš$c*oŸ‘CËd–à »¢ ù"Qx.ÒÌPâFTs½!TVUíÆ…YÔÇ!^m#‘¨x?–ÃÙÁ8 ÊGÁû`ûŸ¦xÁD¦Àa4ÆŽæžê™žŒåW £TJœÒ@nDß×ÇMdŸO» O1ª¯& !5vºà[¾š'ðëŒR†Š:ˆq7ÃQÌi2=HçWç ˆÜ½·vc‘"46ao¹[Q ?ñÀ»¬…˜GZh@(ö›¤HéDWÀfV¬ã‹ätÙˆU~2@Ô/Õ C¾5.@ r£¦±(2Møy™o2ͱɒÄ1†Ínâ§1y †?‰—©Ôõ’ å‹~Öï¹âIKæ’—J¨ô³^Ú›ñéeÚü º)‘“†/ùãeeÔüq€ÇÞM(B™å¹“FÔI…j ££É/[¨ò¦”õ®ò­6ŸùØAÉ":~ÊÝ>n*I7Pz~ µõª“wôÒ 3wñ^4 × õv@ÆI‚ª§ñ-,(CT>²îñSD1…§2çxaDE3Ûð…±ý˵ˆÁ’Í”RåÄ×KÙÔ0ÖJâP|®(æ¥N1ÒDhiŠ­^…žü÷îëWTèB%8¼g- ´ôr³z&’áôn_¯D,0„.n%Gœ;°#¯Äua&ƒ8­og¡Zèc†è-üÿ¥ŒCø{g‡ú| kõàˆ•ªŽexín‚Çz££×…)’;bÆ#/Ê›ö³­Å঻,èÒCÝ -g4:Ro˜·ÛÁ*±`6“ž8¢çQ'íkF¾ÏfÒ¿ê݃.¾š.bY.áÎwjÒX=Mq(FP)=ÓÀD,Åx ÝÃ1MÞs™¢7[o¾Ì¢$±·WïÙì—tßQnP‹×øRSÉ#ª¬›ì´:},e 9š–“‘U@ïo’+žm¤MNE® [~F¬¨Û˜70 \ÄìðnÏjÔKUSQâ Ñý;ð§)Ýrx‡yoh,J§Á(¹Á»Sé‡i€“„Mjd‘A$.‘{ƒœðoÅ?IÇ2c›u±—Å‚XBHTGÉß_›z"É­N­uE@îòã„„ÍfìK‚uÃ5?ÖÖ(€ç¥ÃÉœ£t­.ç™bB„4›çñ ȲôÐëbj¶âi¬P€ƒF€FÃò™p² ïaEGˆPÐÚÔßSº ˜À à gÁÄž’)VY»EHÆæ`*‘äÉVŠ'*Ùüü5æÀfU-’@r4?ÀøÖü5ž]NGºÞFKå68‡'qJ:Ñ}"Œx%„Ð_ЄÁÀþ›ÄY–ë,é((Žý0?&*ÂVjÒ0›íÎÕŽÓ‘-[¢dÉ7ÁË„14MáFr‚¨æ*¦Üc—8„!dwx ue»w;¸qñµƒ¬»i2KçUÑAC"4‰´€×-hªžÀ8ƒ%k$;ÆFËÙÂ`'hË`Gs¯Ôp:Ie:Ia:A*ð—L&ö gFF,Úí$}¦Ë'> ®<_µxÚÇV âIÇéwàiÔ |î†1sb6I`D‹ï°gû›©œÇ°¼Çæã¤@º;Où’EŠ%þ^ak„„ãž] ªsœÂâëÎ;À|§ò¾(Rû’E·ÓÏï(¿õ‘ÇÓÇú”ÿ= ù)þ÷~»ˆÿ½÷tÿã|–ŸÿY'~¶½×§8ø^¸}ÁA~E[Ç\TÓ®¸`±Bbl[È­xW&F*X;+û/й~2•ÓŠxñ‰"¨¼¯æÓ81mÞ¹ß8ÕV;ú"ò­eä yNŽBŠ©OÒ¬×FÁûcH‰V2̘¨T†Óä½¾êÂŽJ則~wD«9{3BAªb[X4T€1ìõÔÙ¨ª(dSƒ Wwa$N øwÁiˆg±vO®$¦B/Z$·&òô`4µÚÊ ÀúŠ»’µµ ÍÚ²êŒÅµ¼–8%v¤ˆß6ˆYW2V×¹ì©ó°²l;Kò9 qÞÃRj’!ª&“r.|V[öÖ‚Zoæ]“”Ǽ—ŠØY¯ÞÛ>í…˜~¶.MRò"ÍÚ)¦”¶U2U7uáQ’N±ÿ\5Y<¼šxêŒ\E:ÑY—å8gÿuj±¢s/ô=ØAU¬šíbÏéIó÷§:=!ÿ{õ·‚©ªHK¦M¼üÇùx3…ù%YÈÿ»¾ý{È'©˜Éúµ‚>âÞ¤óÛ²ÙB` |˜šáP(ctƒAMÕÀtra–Ђû`x6\%,ZWö–ö[ôD+††j;C¥™@Mݦ <¶u§ñ‹ŽÂ–p¥!/Sµ8¬sÖÁ@(9ïÂ9^¢Ë‹J#¦+ÜZ ztZcxjí“%<΄“Úàò19:€‘"u_!HË©8#úã!#(Y|s¨C ÷Èþnû¦]Ÿ~e]7üä¤ï°~ægÂþý'Švÿv[A#m+<ÓMIWoÌa•,¸1_±+—Õþ2SƒOJ£%Š *e Ž ÀïuųçO»¹©€+ºHE›-gjßËÔF,µ£*©1K¶à¾žþ´£ÖêœtizÌŠ´”£º®åÆkÙZ[÷y„_3%ix¢Ïx²è‹¿rÈiP$RJtªIÒyÌó•DhÝå⳨YŸeIgæ2y™Ù¬û¹¯¯;ðâc9m®éÑ5ÿ¬iýk­aýËfn{ýsvÙ®Õ?s;â[iß*xŠj;xÖëöúÖþH'/ª“Õ–¤æI{n••w†.=„Ðë…92ùš64w¯š¯½4»ŸÙÒìæKó–5æ¯{«&[¿ºv?×çíœ_Ã~2 /™N»}äžìï®@N5~™g²)½Áæ˜[Åó9¯ö›Ô´oodýM.•aéF¾KöYΛnm•*Ýëå§ÏÓçéó¿ùü_iº gb-0.4.4/internal/untar/_testdata/symlink.tar.gz000066400000000000000000000002711305302736300216430ustar00rootroot00000000000000‹çÁkWíÔM †á®=7pJ8OÕVÞ^j⊅ iš¾Ïf0ð…éÞ·ëù~ÙW‰HpÎ|«ªXêÈÔ646¨„FÔVVÆ•ljòêží#µrŠÇì¾´-ÆÌúx¹®D7æß<#½‡WÍåoò¯ûü½¤ü¥`O³çßî–îKšþÿ¡àÌÿÐÏ[r(M6þÿÀú}–ÕÖ‰(gb-0.4.4/internal/untar/untar.go000066400000000000000000000037161305302736300165450ustar00rootroot00000000000000package untar import ( "archive/tar" "io" "io/ioutil" "os" "path/filepath" "github.com/pkg/errors" ) // Untar extracts the contents of r to the destination dest. // dest must not aleady exist. func Untar(dest string, r io.Reader) error { if exists(dest) { return errors.Errorf("%q must not exist", dest) } parent, _ := filepath.Split(dest) tmpdir, err := ioutil.TempDir(parent, ".untar") if err != nil { return err } if err := untar(tmpdir, r); err != nil { os.RemoveAll(tmpdir) return err } if err := os.Rename(tmpdir, dest); err != nil { os.RemoveAll(tmpdir) return err } return nil } func untar(dest string, r io.Reader) error { tr := tar.NewReader(r) for { h, err := tr.Next() if err == io.EOF { return nil } if err != nil { return err } if err := untarfile(dest, h, tr); err != nil { return err } } } func untarfile(dest string, h *tar.Header, r io.Reader) error { path := filepath.Join(dest, h.Name) switch h.Typeflag { case tar.TypeDir: return os.Mkdir(path, os.FileMode(h.Mode)) case tar.TypeReg: return writefile(path, r, os.FileMode(h.Mode)) case tar.TypeXGlobalHeader: // ignore PAX headers return nil case tar.TypeSymlink: // symlinks are not supported by the go tool or windows so // cannot be part of a valie package. Any symlinks in the tarball // will be in parts of the release that we can safely ignore. return nil default: return errors.Errorf("unsupported header type: %c", rune(h.Typeflag)) } } func writefile(path string, r io.Reader, mode os.FileMode) error { dir, _ := filepath.Split(path) if err := os.MkdirAll(dir, mode); err != nil { return errors.Wrap(err, "mkdirall failed") } w, err := os.Create(path) if err != nil { return errors.Wrap(err, "could not create destination") } if _, err := io.Copy(w, r); err != nil { w.Close() return err } return w.Close() } func exists(path string) bool { _, err := os.Stat(path) return err == nil || !os.IsNotExist(err) } gb-0.4.4/internal/untar/untar_test.go000066400000000000000000000042321305302736300175760ustar00rootroot00000000000000package untar import ( "compress/gzip" "io" "io/ioutil" "os" "path/filepath" "reflect" "testing" ) func TestUntar(t *testing.T) { tests := []struct { desc string r func(*testing.T) io.ReadCloser dest string want []string }{{ desc: "a.tar.gz", r: tgz("_testdata/a.tar.gz"), want: []string{ ".", "a", "a/a", }, }, { desc: "errors.tar.gz", r: tgz("_testdata/errors.tar.gz"), want: []string{ ".", "pkg-errors-805fb19", "pkg-errors-805fb19/.gitignore", "pkg-errors-805fb19/.travis.yml", "pkg-errors-805fb19/LICENSE", "pkg-errors-805fb19/README.md", "pkg-errors-805fb19/appveyor.yml", "pkg-errors-805fb19/errors.go", "pkg-errors-805fb19/errors_test.go", "pkg-errors-805fb19/example_test.go", "pkg-errors-805fb19/format_test.go", "pkg-errors-805fb19/stack.go", "pkg-errors-805fb19/stack_test.go", }, }, { desc: "symlink.tar.gz", r: tgz("_testdata/symlink.tar.gz"), want: []string{ ".", "symlink", "symlink/a", // no symlink/b }, }} for _, tt := range tests { dest := tmpdir(t) defer os.RemoveAll(dest) r := tt.r(t) defer r.Close() err := Untar(dest, r) if err != nil { t.Error(err) continue } got := walkdir(t, dest) want := make([]string, len(tt.want)) for i := range tt.want { want[i] = filepath.FromSlash(tt.want[i]) } if !reflect.DeepEqual(got, want) { t.Errorf("%s: untar: expected %s, got %s", tt.desc, want, got) } } } func tmpdir(t *testing.T) string { dir, err := ioutil.TempDir("", ".test") if err != nil { t.Fatal(err) } return filepath.Join(dir, "dest") } func tgz(path string) func(t *testing.T) io.ReadCloser { return func(t *testing.T) io.ReadCloser { f, err := os.Open(path) if err != nil { t.Fatal(err) } r, err := gzip.NewReader(f) if err != nil { t.Fatal(err) } return r } } func walkdir(t *testing.T, root string) []string { var paths []string err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } path, err = filepath.Rel(root, path) paths = append(paths, path) return err }) if err != nil { t.Fatal(err) } return paths } gb-0.4.4/internal/vendor/000077500000000000000000000000001305302736300152225ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/000077500000000000000000000000001305302736300171725ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/src/000077500000000000000000000000001305302736300177615ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/src/github.com/000077500000000000000000000000001305302736300220205ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/src/github.com/foo/000077500000000000000000000000001305302736300226035ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/src/github.com/foo/bar/000077500000000000000000000000001305302736300233475ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/src/github.com/foo/bar/main.go000066400000000000000000000004131305302736300246200ustar00rootroot00000000000000package 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.4.4/internal/vendor/_testdata/vendor/000077500000000000000000000000001305302736300204675ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/000077500000000000000000000000001305302736300212565ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/bitbucket.org/000077500000000000000000000000001305302736300240205ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/bitbucket.org/fwoop/000077500000000000000000000000001305302736300251525ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/bitbucket.org/fwoop/ftang/000077500000000000000000000000001305302736300262515ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/bitbucket.org/fwoop/ftang/kthulu.go000066400000000000000000000000421305302736300301100ustar00rootroot00000000000000package ftang const CAT = "ack!" gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/000077500000000000000000000000001305302736300233155ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/hoo/000077500000000000000000000000001305302736300241025ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/hoo/wuu/000077500000000000000000000000001305302736300247225ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/hoo/wuu/goo.go000066400000000000000000000000351305302736300260330ustar00rootroot00000000000000package wuu const Q = "hey" gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/lypo/000077500000000000000000000000001305302736300243005ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/lypo/moopo/000077500000000000000000000000001305302736300254315ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/lypo/moopo/tropo.go000066400000000000000000000000361305302736300271220ustar00rootroot00000000000000package moopo const Q = "hi" gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/quux/000077500000000000000000000000001305302736300243175ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/quux/flobble/000077500000000000000000000000001305302736300257245ustar00rootroot00000000000000gb-0.4.4/internal/vendor/_testdata/vendor/src/github.com/quux/flobble/wobble.go000066400000000000000000000000431305302736300275220ustar00rootroot00000000000000package flobble const Q = "hello" gb-0.4.4/internal/vendor/depset.go000066400000000000000000000045761305302736300170510ustar00rootroot00000000000000package vendor import ( "go/build" "os" "path/filepath" "strings" "github.com/pkg/errors" ) // 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 errors.Wrapf(err, "loadPackage(%q, %q)", dir, importpath) } 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 { // some paths may not exist, for example $GOROOT/src/vendor on Go < 1.6 if os.IsNotExist(err) { return &d, 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.4.4/internal/vendor/discovery.go000066400000000000000000000042521305302736300175630ustar00rootroot00000000000000// 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" "io" "strings" "github.com/pkg/errors" ) // 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, errors.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.RawToken() if err != nil { if err == io.EOF || len(imports) > 0 { 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.4.4/internal/vendor/imports.go000066400000000000000000000047031305302736300172520ustar00rootroot00000000000000package vendor import ( "fmt" "go/parser" "go/token" "io" "net/http" "os" "path/filepath" "strings" "github.com/pkg/errors" ) // 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) 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 = errors.Wrap(err, "unable to determine remote metadata protocol") } }() // 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.4.4/internal/vendor/imports_test.go000066400000000000000000000125231305302736300203100ustar00rootroot00000000000000package vendor import ( "bytes" "io" "os" "path/filepath" "reflect" "strings" "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("fmt", "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 TestParseMetaRemoteImportPaths(t *testing.T) { tests := []struct { input string want []metaImport }{ // The "meta" element has a start tag, but no end tag. {``, []metaImport{{"golang.org/x/tools", "git", "https://go.googlesource.com/tools"}}}, // The parser tolerates unquoted XML attribute values, but note that the CDATA section is not terminated properly. {`Page Not Found 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.4.4/internal/vendor/manifest.go000066400000000000000000000105141305302736300173600ustar00rootroot00000000000000package vendor import ( "bytes" "encoding/json" "io" "os" "reflect" "sort" "github.com/pkg/errors" ) // 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 errors.New("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 errors.New("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{}, errors.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 } // MarshalIndent does not append a newline after the final closing // curly brace, however many editors will helpfully do this which causes // lots of pointless diffs if you edit the manifest file manually. To work // around this, add a newline after MarshalIndent. buf = append(buf, '\n') _, 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.4.4/internal/vendor/manifest_test.go000066400000000000000000000040721305302736300204210ustar00rootroot00000000000000package vendor import ( "bytes" "os" "path/filepath" "testing" ) 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 os.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.4.4/internal/vendor/repo.go000066400000000000000000000317771305302736300165350ustar00rootroot00000000000000package vendor import ( "bytes" "fmt" "io" "io/ioutil" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/constabulary/gb/internal/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 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.4.4/internal/vendor/repo_test.go000066400000000000000000000060331305302736300175570ustar00rootroot00000000000000package 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", }, /* bitbucket cannot maintain a stable ssh key across their app servers and this mucks up ci testing because mercurial does not have any way of unconditionally accepting new ssh keys for the host. Great work TEAM. }, { 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 has either changed or is now 403 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: "goji.io", want: &gitrepo{ url: "https://github.com/goji/goji", }, 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, }} 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.4.4/internal/vendor/stringset.go000066400000000000000000000016051305302736300175750ustar00rootroot00000000000000package 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.4.4/internal/vendor/stringset_test.go000066400000000000000000000052161305302736300206360ustar00rootroot00000000000000package 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) } } } gb-0.4.4/package.go000066400000000000000000000174601305302736300140430ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "os" "path/filepath" "runtime" "strings" "time" "github.com/constabulary/gb/internal/debug" "github.com/pkg/errors" ) // Package represents a resolved package from the Project with respect to the Context. type Package struct { *Context *build.Package TestScope bool NotStale bool // this package _and_ all its dependencies are not stale Main bool // is this a command Imports []*Package } // newPackage creates a resolved Package without setting pkg.Stale. func (ctx *Context) newPackage(p *build.Package) (*Package, error) { pkg := &Package{ Context: ctx, Package: p, } for _, i := range p.Imports { dep, ok := ctx.pkgs[i] if !ok { return nil, errors.Errorf("newPackage(%q): could not locate dependant package %q ", p.Name, i) } pkg.Imports = append(pkg.Imports, dep) } return pkg, nil } func (p *Package) String() string { return fmt.Sprintf("%s {Name:%s, Dir:%s}", p.ImportPath, p.Name, p.Dir) } func (p *Package) includePaths() []string { includes := p.Context.includePaths() switch { case p.TestScope && p.Main: ip := filepath.Dir(filepath.FromSlash(p.ImportPath)) return append([]string{filepath.Join(p.Context.Workdir(), ip, "_test")}, includes...) case p.TestScope: ip := strings.TrimSuffix(filepath.FromSlash(p.ImportPath), "_test") return append([]string{filepath.Join(p.Context.Workdir(), ip, "_test")}, includes...) default: return includes } } // 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.Goroot { 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 { target := filepath.Join(pkg.bindir(), pkg.binname()) // if this is a cross compile or GOOS/GOARCH are both defined or there are build tags, add ctxString. if pkg.isCrossCompile() || (os.Getenv("GOOS") != "" && os.Getenv("GOARCH") != "") { target += "-" + pkg.ctxString() } else if len(pkg.buildtags) > 0 { target += "-" + strings.Join(pkg.buildtags, "-") } if pkg.gotargetos == "windows" { target += ".exe" } return target } func (pkg *Package) bindir() string { switch { case pkg.TestScope: return filepath.Join(pkg.Context.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") default: return pkg.Context.bindir() } } func (pkg *Package) Workdir() string { path := filepath.FromSlash(pkg.ImportPath) dir := filepath.Dir(path) switch { case pkg.TestScope: ip := strings.TrimSuffix(path, "_test") return filepath.Join(pkg.Context.Workdir(), ip, "_test", dir) default: return filepath.Join(pkg.Context.Workdir(), dir) } } // objfile returns the name of the object file for this package func (pkg *Package) objfile() string { return filepath.Join(pkg.Workdir(), pkg.objname()) } func (pkg *Package) objname() string { return pkg.pkgname() + ".a" } func (pkg *Package) pkgname() string { // TODO(dfc) use pkg path instead? return filepath.Base(filepath.FromSlash(pkg.ImportPath)) } func (pkg *Package) binname() string { if !pkg.Main { panic("binname called with non main package: " + pkg.ImportPath) } // TODO(dfc) use pkg path instead? return filepath.Base(filepath.FromSlash(pkg.ImportPath)) } // installpath returns the distination to cache this package's compiled .a file. // pkgpath and installpath differ in that the former returns the location where you will find // a previously cached .a file, the latter returns the location where an installed file // will be placed. // // The difference is subtle. pkgpath must deal with the possibility that the file is from the // standard library and is previously compiled. installpath will always return a path for the // project's pkg/ directory in the case that the stdlib is out of date, or not compiled for // a specific architecture. func (pkg *Package) installpath() string { if pkg.TestScope { panic("installpath called with test scope") } return filepath.Join(pkg.Pkgdir(), filepath.FromSlash(pkg.ImportPath)+".a") } // pkgpath returns the destination for object cached for this Package. func (pkg *Package) pkgpath() string { importpath := filepath.FromSlash(pkg.ImportPath) + ".a" switch { case pkg.isCrossCompile(): return filepath.Join(pkg.Pkgdir(), importpath) case pkg.Goroot && pkg.race: // race enabled standard lib return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch+"_race", importpath) case pkg.Goroot: // standard lib return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch, importpath) default: return filepath.Join(pkg.Pkgdir(), importpath) } } // isStale returns true if the source pkg is considered to be stale with // respect to its installed version. func (pkg *Package) isStale() bool { switch pkg.ImportPath { case "C", "unsafe": // synthetic packages are never stale return false } if !pkg.Goroot && pkg.Force { return true } // tests are always stale, they are never installed if pkg.TestScope { return true } // Package is stale if completely unbuilt. var built time.Time if fi, err := os.Stat(pkg.pkgpath()); err == nil { built = fi.ModTime() } if built.IsZero() { debug.Debugf("%s is missing", pkg.pkgpath()) 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 !pkg.Goroot { if olderThan(pkg.tc.compiler()) { debug.Debugf("%s is older than %s", pkg.pkgpath(), pkg.tc.compiler()) return true } if pkg.Main && olderThan(pkg.tc.linker()) { debug.Debugf("%s is older than %s", pkg.pkgpath(), pkg.tc.compiler()) return true } } if pkg.Goroot && !pkg.isCrossCompile() { // if this is a standard lib package, and we are not cross compiling // then assume the package is up to date. This also works around // golang/go#13769. return false } // Package is stale if a dependency is newer. for _, p := range pkg.Imports { if p.ImportPath == "C" || p.ImportPath == "unsafe" { continue // ignore stale imports of synthetic packages } if olderThan(p.pkgpath()) { debug.Debugf("%s is older than %s", pkg.pkgpath(), p.pkgpath()) 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.Main && newerThan(pkg.Binfile()) { debug.Debugf("%s is newer than %s", pkg.pkgpath(), 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)) { debug.Debugf("%s is older than %s", pkg.pkgpath(), 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.4.4/package_test.go000066400000000000000000000227021305302736300150750ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "os" "path/filepath" "reflect" "runtime" "testing" "github.com/pkg/errors" ) func testContext(t *testing.T, opts ...func(*Context) error) *Context { ctx, err := NewContext(testProject(t), opts...) if err != nil { t.Fatal(err) } ctx.Force = true return ctx } func TestResolvePackage(t *testing.T) { var tests = []struct { pkg string // package name opts []func(*Context) error err error }{{ pkg: "a", }, { pkg: "localimport", err: &importErr{path: "../localimport", msg: "relative import not supported"}, }} proj := testProject(t) for _, tt := range tests { ctx, err := NewContext(proj, tt.opts...) defer ctx.Destroy() _, err = ctx.ResolvePackage(tt.pkg) err = errors.Cause(err) if !reflect.DeepEqual(err, tt.err) { t.Errorf("ResolvePackage(%q): want: %v, got %v", tt.pkg, tt.err, 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, _ := NewContext(proj, 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 TestPackageBindir(t *testing.T) { ctx := testContext(t) defer ctx.Destroy() tests := []struct { pkg *Package want string }{{ pkg: &Package{ Context: ctx, }, want: ctx.bindir(), }, { pkg: &Package{ Package: &build.Package{ Name: "testpkg", ImportPath: "github.com/constabulary/gb/testpkg", }, Context: ctx, TestScope: true, }, want: filepath.Join(ctx.Workdir(), "github.com", "constabulary", "gb", "testpkg", "_test"), }} for i, tt := range tests { got := tt.pkg.bindir() want := tt.want if got != want { t.Errorf("test %v: Bindir: got %v want %v", i+1, got, want) } } } func TestNewPackage(t *testing.T) { tests := []struct { pkg build.Package want Package }{{ pkg: build.Package{ Name: "C", ImportPath: "C", Goroot: true, }, want: Package{ NotStale: true, }, }} proj := testProject(t) for i, tt := range tests { ctx, _ := NewContext(proj) defer ctx.Destroy() got, err := ctx.NewPackage(&tt.pkg) if err != nil { t.Error(err) continue } want := tt.want // deep copy want.Package = &tt.pkg want.Context = ctx if !reflect.DeepEqual(got, &want) { t.Errorf("%d: pkg: %s: expected %#v, got %#v", i+1, tt.pkg.ImportPath, &want, got) } } } func TestStale(t *testing.T) { var tests = []struct { pkgs []string stale map[string]bool }{{ pkgs: []string{"a"}, stale: map[string]bool{ "a": false, }, }, { pkgs: []string{"a", "b"}, stale: map[string]bool{ "a": true, "b": false, }, }, { pkgs: []string{"a", "b"}, stale: map[string]bool{ "a": true, "b": true, }, }} proj := tempProject(t) defer os.RemoveAll(proj.rootdir) proj.tempfile("src/a/a.go", `package a const A = "A" `) proj.tempfile("src/b/b.go", `package main import "a" func main() { println(a.A) } `) newctx := func() *Context { ctx, err := NewContext(proj, 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() ctx.Install = true defer ctx.Destroy() for _, pkg := range tt.pkgs { resolve(ctx, pkg) } for p, s := range tt.stale { pkg := resolve(ctx, p) if pkg.NotStale != s { t.Errorf("%q.NotStale: got %v, want %v", pkg.Name, pkg.NotStale, s) } } for _, pkg := range tt.pkgs { if err := Build(resolve(ctx, pkg)); err != nil { t.Fatal(err) } } } } func TestInstallpath(t *testing.T) { ctx := testContext(t) defer ctx.Destroy() tests := []struct { pkg string installpath string }{{ pkg: "a", // from testdata installpath: filepath.Join(ctx.Pkgdir(), "a.a"), }, { pkg: "runtime", // from stdlib installpath: filepath.Join(ctx.Pkgdir(), "runtime.a"), }, { pkg: "unsafe", // synthetic installpath: filepath.Join(ctx.Pkgdir(), "unsafe.a"), }} resolve := func(pkg string) *Package { p, err := ctx.ResolvePackage(pkg) if err != nil { t.Fatal(err) } return p } for _, tt := range tests { pkg := resolve(tt.pkg) got := pkg.installpath() if got != tt.installpath { t.Errorf("installpath(%q): expected: %v, got %v", tt.pkg, tt.installpath, got) } } } func TestPkgpath(t *testing.T) { opts := func(o ...func(*Context) error) []func(*Context) error { return o } gotargetos := "windows" if runtime.GOOS == gotargetos { gotargetos = "linux" } gotargetarch := "arm64" if runtime.GOARCH == "arm64" { gotargetarch = "amd64" } tests := []struct { opts []func(*Context) error pkg string pkgpath func(*Context) string }{{ pkg: "a", // from testdata pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "a.a") }, }, { opts: opts(GOOS(gotargetos), GOARCH(gotargetarch)), pkg: "a", // from testdata pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "a.a") }, }, { opts: opts(WithRace), pkg: "a", // from testdata pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "a.a") }, }, { opts: opts(Tags("foo", "bar")), pkg: "a", // from testdata pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "a.a") }, }, { pkg: "runtime", // from stdlib pkgpath: func(ctx *Context) string { return filepath.Join(runtime.GOROOT(), "pkg", ctx.gohostos+"_"+ctx.gohostarch, "runtime.a") }, }, { opts: opts(Tags("foo", "bar")), pkg: "runtime", // from stdlib pkgpath: func(ctx *Context) string { return filepath.Join(runtime.GOROOT(), "pkg", ctx.gohostos+"_"+ctx.gohostarch, "runtime.a") }, }, { opts: opts(WithRace), pkg: "runtime", // from stdlib pkgpath: func(ctx *Context) string { return filepath.Join(runtime.GOROOT(), "pkg", ctx.gohostos+"_"+ctx.gohostarch+"_race", "runtime.a") }, }, { opts: opts(WithRace, Tags("foo", "bar")), pkg: "runtime", // from stdlib pkgpath: func(ctx *Context) string { return filepath.Join(runtime.GOROOT(), "pkg", ctx.gohostos+"_"+ctx.gohostarch+"_race", "runtime.a") }, }, { opts: opts(GOOS(gotargetos), GOARCH(gotargetarch)), pkg: "runtime", // from stdlib pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "runtime.a") }, }, { pkg: "unsafe", // synthetic pkgpath: func(ctx *Context) string { return filepath.Join(runtime.GOROOT(), "pkg", ctx.gohostos+"_"+ctx.gohostarch, "unsafe.a") }, }, { pkg: "unsafe", // synthetic opts: opts(GOOS(gotargetos), GOARCH(gotargetarch), WithRace), pkgpath: func(ctx *Context) string { return filepath.Join(ctx.Pkgdir(), "unsafe.a") }, }} for _, tt := range tests { ctx := testContext(t, tt.opts...) defer ctx.Destroy() pkg, err := ctx.ResolvePackage(tt.pkg) if err != nil { t.Fatal(err) } got := pkg.pkgpath() want := tt.pkgpath(ctx) if got != want { t.Errorf("pkgpath(%q): expected: %v, got %v", tt.pkg, want, got) } } } func TestPackageIncludePaths(t *testing.T) { ctx := testContext(t) tests := []struct { pkg *Package want []string }{{ pkg: &Package{ Context: ctx, Package: &build.Package{ ImportPath: "github.com/foo/bar", }, }, want: []string{ ctx.Workdir(), ctx.Pkgdir(), }, }, { pkg: &Package{ Context: ctx, Package: &build.Package{ ImportPath: "github.com/foo/bar", }, Main: true, }, want: []string{ ctx.Workdir(), ctx.Pkgdir(), }, }, { pkg: &Package{ Context: ctx, Package: &build.Package{ ImportPath: "github.com/foo/bar", }, TestScope: true, }, want: []string{ filepath.Join(ctx.Workdir(), "github.com", "foo", "bar", "_test"), ctx.Workdir(), ctx.Pkgdir(), }, }, { pkg: &Package{ Context: ctx, Package: &build.Package{ ImportPath: "github.com/foo/bar", }, TestScope: true, Main: true, }, want: []string{ filepath.Join(ctx.Workdir(), "github.com", "foo", "_test"), // TODO(dfc) WTF ctx.Workdir(), ctx.Pkgdir(), }, }} for i, tt := range tests { got := tt.pkg.includePaths() if !reflect.DeepEqual(got, tt.want) { t.Errorf("%d: Package: ImportPath: %v, TestScope: %v, Main: %v: got %v, want %v", i, tt.pkg.ImportPath, tt.pkg.TestScope, tt.pkg.Main, got, tt.want) } } } gb-0.4.4/project.go000066400000000000000000000023171305302736300141110ustar00rootroot00000000000000package 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 interface { // Projectdir returns the path root of this project. Projectdir() string // Pkgdir returns the path to precompiled packages. Pkgdir() string // Bindir returns the path for compiled programs. bindir() string } type project struct { rootdir string } func NewProject(root string) Project { proj := project{ rootdir: root, } 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 } // Bindir returns the path for compiled programs. func (p *project) bindir() string { return filepath.Join(p.rootdir, "bin") } gb-0.4.4/project_test.go000066400000000000000000000015171305302736300151510ustar00rootroot00000000000000package gb import ( "io" "os" "path/filepath" "testing" ) type testproject struct { *testing.T project } func testProject(t *testing.T) Project { cwd := getwd(t) root := filepath.Join(cwd, "testdata") return &testproject{ t, project{ rootdir: root, }, } } func tempProject(t *testing.T) *testproject { return &testproject{ t, project{ rootdir: mktemp(t), }, } } func (t *testproject) tempfile(path, contents string) string { dir, file := filepath.Split(path) dir = filepath.Join(t.rootdir, dir) if err := os.MkdirAll(dir, 0755); err != nil { t.Fatal(err) } path = filepath.Join(dir, file) f, err := os.Create(path) if err != nil { t.Fatal(err) } if _, err := io.WriteString(f, contents); err != nil { f.Close() t.Fatal(err) } if err := f.Close(); err != nil { t.Fatal(err) } return path } gb-0.4.4/resolver.go000066400000000000000000000053361305302736300143100ustar00rootroot00000000000000package gb import ( "fmt" "go/build" "os" pathpkg "path" "path/filepath" "runtime" "strings" "github.com/pkg/errors" ) type nullImporter struct{} func (i *nullImporter) Import(path string) (*build.Package, error) { return nil, errors.Errorf("import %q not found", path) } type srcImporter struct { Importer im importer } func (i *srcImporter) Import(path string) (*build.Package, error) { pkg, err := i.im.Import(path) if err == nil { return pkg, nil } // gb expects, when there is a failure to resolve packages that // live in $PROJECT/src that the importer for that directory // will report them. pkg, err2 := i.Importer.Import(path) if err2 == nil { return pkg, nil } return nil, err } type _importer struct { Importer im importer } func (i *_importer) Import(path string) (*build.Package, error) { pkg, err := i.im.Import(path) if err != nil { return i.Importer.Import(path) } return pkg, nil } type fixupImporter struct { Importer } func (i *fixupImporter) Import(path string) (*build.Package, error) { pkg, err := i.Importer.Import(path) switch err.(type) { case *os.PathError: return nil, errors.Wrapf(err, "import %q: not found", path) default: return pkg, err } } type importer struct { *build.Context Root string // root directory } type importErr struct { path string msg string } func (e *importErr) Error() string { return fmt.Sprintf("import %q: %v", e.path, e.msg) } func (i *importer) Import(path string) (*build.Package, error) { if path == "" { return nil, errors.WithStack(&importErr{path: path, msg: "invalid import path"}) } if path == "." || path == ".." || strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") { return nil, errors.WithStack(&importErr{path: path, msg: "relative import not supported"}) } if strings.HasPrefix(path, "/") { return nil, errors.WithStack(&importErr{path: path, msg: "cannot import absolute path"}) } var p *build.Package loadPackage := func(importpath, dir string) error { pkg, err := i.ImportDir(dir, 0) if err != nil { return err } p = pkg p.ImportPath = importpath return nil } // if this is the stdlib, then search vendor first. // this isn't real vendor support, just enough to make net/http compile. if i.Root == runtime.GOROOT() { path := pathpkg.Join("vendor", path) dir := filepath.Join(i.Root, "src", filepath.FromSlash(path)) fi, err := os.Stat(dir) if err == nil && fi.IsDir() { err := loadPackage(path, dir) return p, err } } dir := filepath.Join(i.Root, "src", filepath.FromSlash(path)) fi, err := os.Stat(dir) if err != nil { return nil, err } if !fi.IsDir() { return nil, errors.Errorf("import %q: not a directory", path) } err = loadPackage(path, dir) return p, err } gb-0.4.4/test/000077500000000000000000000000001305302736300130705ustar00rootroot00000000000000gb-0.4.4/test/gotest.go000066400000000000000000000144021305302736300147250ustar00rootroot00000000000000// 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" "unicode" "unicode/utf8" "github.com/constabulary/gb" "github.com/constabulary/gb/internal/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 Unordered bool // TODO(dfc) used for tested output of 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{ Package: pkg, Name: name, Output: "", } *doImport, *seen = true, true case isTest(name, "Test"): t.Tests = append(t.Tests, testFunc{Package: pkg, Name: name}) *doImport, *seen = true, true case isTest(name, "Benchmark"): t.Benchmarks = append(t.Benchmarks, testFunc{Package: pkg, Name: 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{Package: pkg, Name: "Example" + e.Name, Output: 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 } gb-0.4.4/test/goversion12_test.go000066400000000000000000000001471305302736300166360ustar00rootroot00000000000000// +build go1.2 // +build !go1.3,!go1.4,!go1.5,!go1.6,!go1.7 package test const ( goversion = 1.2 ) gb-0.4.4/test/goversion13_test.go000066400000000000000000000001401305302736300166300ustar00rootroot00000000000000// +build go1.3 // +build !go1.4,!go1.5,!go1.6,!go1.7 package test const ( goversion = 1.3 ) gb-0.4.4/test/goversion14_test.go000066400000000000000000000001311305302736300166310ustar00rootroot00000000000000// +build go1.4 // +build !go1.5,!go1.6,!go1.7 package test const ( goversion = 1.4 ) gb-0.4.4/test/goversion15_test.go000066400000000000000000000001131305302736300166320ustar00rootroot00000000000000// +build go1.5 // +build !go1.6 package test const ( goversion = 1.5 ) gb-0.4.4/test/goversion16_test.go000066400000000000000000000001131305302736300166330ustar00rootroot00000000000000// +build go1.6 // +build !go1.7 package test const ( goversion = 1.6 ) gb-0.4.4/test/goversion17_test.go000066400000000000000000000000721305302736300166400ustar00rootroot00000000000000// +build go1.7 package test const ( goversion = 1.7 ) gb-0.4.4/test/resolve.go000066400000000000000000000014521305302736300151000ustar00rootroot00000000000000package test import ( "github.com/constabulary/gb" ) type Resolver interface { Projectdir() string ResolvePackage(string) (*gb.Package, error) } // TestResolver returns a Resolver that resolves packages, their // dependencies including any internal or external test dependencies. func TestResolver(r Resolver) Resolver { return &testResolver{r} } type testResolver struct { 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.4.4/test/test.go000066400000000000000000000153711305302736300144050ustar00rootroot00000000000000package test import ( "bytes" "fmt" "go/build" "io" "os" "os/exec" "path" "path/filepath" "strings" "time" "github.com/constabulary/gb" "github.com/constabulary/gb/internal/debug" "github.com/pkg/errors" ) // 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, errors.New("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) { debug.Debugf("TestPackage: %s, flags: %s", pkg.ImportPath, flags) 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, err := pkg.NewPackage(&build.Package{ Name: name, ImportPath: pkg.ImportPath, 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, Dir: pkg.Dir, }) if err != nil { return nil, err } testpkg.TestScope = 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, err := pkg.NewPackage(&build.Package{ Name: name, ImportPath: pkg.ImportPath + "_test", GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, Dir: pkg.Dir, }) if err != nil { return nil, err } // build external test dependencies deps, err := gb.BuildDependencies(targets, xtestpkg) if err != nil { return nil, err } xtestpkg.TestScope = true // 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 { // nope mode means we stop at the compile and link phase. if !pkg.Nope { cmd := exec.Command(testmainpkg.Binfile(), flags...) cmd.Dir = pkg.Dir // tests run in the original source directory cmd.Stdout = &output cmd.Stderr = &output debug.Debugf("%s", cmd.Args) err = cmd.Run() // run test err = errors.Wrapf(err, "%s", cmd.Args) // wrap error if failed } // 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) } else { fmt.Println(pkg.ImportPath) } if err != nil || pkg.Verbose { io.Copy(os.Stdout, &output) } return err }, }, nil } func buildTestMain(pkg *gb.Package) (*gb.Package, error) { if !pkg.TestScope { return nil, errors.Errorf("package %q is not test scoped", pkg.Name) } dir := pkg.Workdir() if err := mkdir(dir); err != nil { return nil, 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, err := pkg.NewPackage(&build.Package{ Name: pkg.Name, ImportPath: path.Join(pkg.ImportPath, "testmain"), SrcRoot: pkg.SrcRoot, GoFiles: []string{"_testmain.go"}, Imports: pkg.Package.Imports, Dir: dir, }) if err != nil { return nil, err } if testmain.NotStale { panic("testmain not marked stale") } testmain.TestScope = true testmain.Main = true return testmain, nil } func mkdir(path string) error { return errors.Wrap(os.MkdirAll(path, 0755), "mkdir") } gb-0.4.4/test/test_test.go000066400000000000000000000125241305302736300154410ustar00rootroot00000000000000package test import ( "os" "path/filepath" "reflect" "runtime" "sort" "strings" "testing" "time" "github.com/constabulary/gb" ) func TestTest(t *testing.T) { tests := []struct { pkg string testArgs []string ldflags []string err error minversion float64 // minimum go version that supports this feature maxversion float64 // maximum go version that supports this feature }{ { 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"}, minversion: 1.5, }, { pkg: "ldflags", ldflags: []string{"-X", "ldflags.gitTagInfo", "banana", "-X", "ldflags.gitRevision", "f7926af2"}, maxversion: 1.5, }, { pkg: "cgotest", }, { pkg: "testflags", testArgs: []string{"-debug"}, }, { pkg: "main", // issue 375, a package called main }} for _, tt := range tests { if tt.minversion != 0 && goversion < tt.minversion { t.Logf("skipping test, goversion %f is below mingoversion %f", goversion, tt.minversion) continue } if tt.maxversion != 0 && goversion > tt.maxversion { t.Logf("skipping test, goversion %f is above maxgoversion %f", goversion, tt.maxversion) continue } 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() r := TestResolver(ctx) pkg, err := r.ResolvePackage(tt.pkg) if err != nil { t.Errorf("r.ResolvePackage(%v): %v", tt.pkg, err) continue } targets := make(map[string]*gb.Action) if _, err := TestPackage(targets, pkg, nil); !reflect.DeepEqual(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/testmain$EXE", "run: $WORKDIR/b/testmain/_test/testmain$EXE", "run: $WORKDIR/c/testmain/_test/testmain$EXE", }, }, { pkgs: []string{"cgotest", "cgomain", "notestfiles", "cgoonlynotest", "testonly", "extestonly"}, actions: []string{ "run: $WORKDIR/cgomain/testmain/_test/testmain$EXE", "run: $WORKDIR/cgoonlynotest/testmain/_test/testmain$EXE", "run: $WORKDIR/cgotest/testmain/_test/testmain$EXE", "run: $WORKDIR/extestonly/testmain/_test/testmain$EXE", "run: $WORKDIR/notestfiles/testmain/_test/testmain$EXE", "run: $WORKDIR/testonly/testmain/_test/testmain$EXE", }, }} for i, tt := range tests { ctx := testContext(t) defer ctx.Destroy() var pkgs []*gb.Package t.Logf("testing: %v: pkgs: %v", i+1, tt.pkgs) r := TestResolver(ctx) for _, pkg := range tt.pkgs { pkg, err := r.ResolvePackage(pkg) if err != nil { t.Errorf("r.ResolvePackage(%v): %v", pkg, err) continue } pkgs = append(pkgs, pkg) } a, err := TestPackages(nil, pkgs...) if !reflect.DeepEqual(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 runtime.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) } func testContext(t *testing.T, opts ...func(*gb.Context) error) *gb.Context { opts = append([]func(*gb.Context) error{gb.GcToolchain()}, opts...) ctx, err := gb.NewContext(testProject(t), opts...) if err != nil { t.Fatal(err) } ctx.Force = true return ctx } gb-0.4.4/test/testmain17.go000066400000000000000000000054401305302736300154160ustar00rootroot00000000000000// +build !go1.8 // 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 import "text/template" // imported from $GOROOT/src/cmd/go/test.go 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: "{{.Name}}", F: {{.Package}}.{{.Name}}, Output: {{.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.4.4/test/testmain18.go000066400000000000000000000050351305302736300154170ustar00rootroot00000000000000// +build go1.8 // 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 import "text/template" // imported from $GOROOT/src/cmd/go/test.go var testmainTmpl = template.Must(template.New("main").Parse(` package main import ( {{if not .TestMain}} "os" {{end}} "testing" "testing/internal/testdeps" {{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"}}, {{.Unordered}}}, {{end}} } {{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(testdeps.TestDeps{}, tests, benchmarks, examples) {{with .TestMain}} {{.Package}}.{{.Name}}(m) {{else}} os.Exit(m.Run()) {{end}} } `)) gb-0.4.4/testdata/000077500000000000000000000000001305302736300137225ustar00rootroot00000000000000gb-0.4.4/testdata/src/000077500000000000000000000000001305302736300145115ustar00rootroot00000000000000gb-0.4.4/testdata/src/a/000077500000000000000000000000001305302736300147315ustar00rootroot00000000000000gb-0.4.4/testdata/src/a/a.go000066400000000000000000000000311305302736300154720ustar00rootroot00000000000000package a const A = "A" gb-0.4.4/testdata/src/a/a_test.go000066400000000000000000000001061305302736300165340ustar00rootroot00000000000000package a import "testing" func TestA(t *testing.T) { t.Log("A") } gb-0.4.4/testdata/src/aprime/000077500000000000000000000000001305302736300157665ustar00rootroot00000000000000gb-0.4.4/testdata/src/aprime/a.go000066400000000000000000000000311305302736300165270ustar00rootroot00000000000000package a const A = "A" gb-0.4.4/testdata/src/b/000077500000000000000000000000001305302736300147325ustar00rootroot00000000000000gb-0.4.4/testdata/src/b/b.go000066400000000000000000000000701305302736300154770ustar00rootroot00000000000000package main import "a" func main() { println(a.A) } gb-0.4.4/testdata/src/blank/000077500000000000000000000000001305302736300156005ustar00rootroot00000000000000gb-0.4.4/testdata/src/blank/.turd000066400000000000000000000000001305302736300165450ustar00rootroot00000000000000gb-0.4.4/testdata/src/c/000077500000000000000000000000001305302736300147335ustar00rootroot00000000000000gb-0.4.4/testdata/src/c/c.go000066400000000000000000000001001305302736300154730ustar00rootroot00000000000000package c import "a" import "d.v1" const C = a.A var D = d.D gb-0.4.4/testdata/src/c/c_test.go000066400000000000000000000001041305302736300165360ustar00rootroot00000000000000package c import "testing" func TestC(t *testing.T) { t.Log(C) } gb-0.4.4/testdata/src/cgomain/000077500000000000000000000000001305302736300161265ustar00rootroot00000000000000gb-0.4.4/testdata/src/cgomain/main.go000066400000000000000000000004331305302736300174010ustar00rootroot00000000000000package 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.4.4/testdata/src/cgoonlynotest/000077500000000000000000000000001305302736300174205ustar00rootroot00000000000000gb-0.4.4/testdata/src/cgoonlynotest/cgoonly.go000066400000000000000000000002371305302736300214230ustar00rootroot00000000000000package 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.4.4/testdata/src/cgotest/000077500000000000000000000000001305302736300161615ustar00rootroot00000000000000gb-0.4.4/testdata/src/cgotest/basic.go000066400000000000000000000056001305302736300175720ustar00rootroot00000000000000// 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.4.4/testdata/src/cgotest/cgo_test.go000066400000000000000000000073361305302736300203300ustar00rootroot00000000000000// 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.4.4/testdata/src/cmd/000077500000000000000000000000001305302736300152545ustar00rootroot00000000000000gb-0.4.4/testdata/src/cmd/f/000077500000000000000000000000001305302736300155015ustar00rootroot00000000000000gb-0.4.4/testdata/src/cmd/f/main.go000066400000000000000000000000321305302736300167470ustar00rootroot00000000000000package main const X = 7 gb-0.4.4/testdata/src/cmd/f/main_test.go000066400000000000000000000001411305302736300200070ustar00rootroot00000000000000package main import "testing" func TestX(t *testing.T) { if X != 7 { t.Fatal("X != 7") } } gb-0.4.4/testdata/src/cppmain/000077500000000000000000000000001305302736300161405ustar00rootroot00000000000000gb-0.4.4/testdata/src/cppmain/main.cpp000066400000000000000000000002671305302736300175750ustar00rootroot00000000000000typedef 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.4.4/testdata/src/cppmain/main.go000066400000000000000000000003351305302736300174140ustar00rootroot00000000000000package 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.4.4/testdata/src/cycle0/000077500000000000000000000000001305302736300156705ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle0/cycle.go000066400000000000000000000000371305302736300173160ustar00rootroot00000000000000package cycle import "cycle0" gb-0.4.4/testdata/src/cycle1/000077500000000000000000000000001305302736300156715ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle1/a/000077500000000000000000000000001305302736300161115ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle1/a/a.go000066400000000000000000000000421305302736300166540ustar00rootroot00000000000000package cyclea import "cycle1/b" gb-0.4.4/testdata/src/cycle1/b/000077500000000000000000000000001305302736300161125ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle1/b/b.go000066400000000000000000000000421305302736300166560ustar00rootroot00000000000000package cycleb import "cycle1/b" gb-0.4.4/testdata/src/cycle2/000077500000000000000000000000001305302736300156725ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle2/a/000077500000000000000000000000001305302736300161125ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle2/a/a.go000066400000000000000000000000431305302736300166560ustar00rootroot00000000000000package cycle2a import "cycle2/b" gb-0.4.4/testdata/src/cycle2/b/000077500000000000000000000000001305302736300161135ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle2/b/b.go000066400000000000000000000000431305302736300166600ustar00rootroot00000000000000package cycle2b import "cycle2/c" gb-0.4.4/testdata/src/cycle2/c/000077500000000000000000000000001305302736300161145ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle2/c/c.go000066400000000000000000000000431305302736300166620ustar00rootroot00000000000000package cycle2c import "cycle2/d" gb-0.4.4/testdata/src/cycle2/d/000077500000000000000000000000001305302736300161155ustar00rootroot00000000000000gb-0.4.4/testdata/src/cycle2/d/d.go000066400000000000000000000000431305302736300166640ustar00rootroot00000000000000package cycle2d import "cycle2/a" gb-0.4.4/testdata/src/d.v1/000077500000000000000000000000001305302736300152615ustar00rootroot00000000000000gb-0.4.4/testdata/src/d.v1/d.go000066400000000000000000000000271305302736300160320ustar00rootroot00000000000000package d var D = "d" gb-0.4.4/testdata/src/e/000077500000000000000000000000001305302736300147355ustar00rootroot00000000000000gb-0.4.4/testdata/src/e/e.go000066400000000000000000000000311305302736300155020ustar00rootroot00000000000000package e const E = "e" gb-0.4.4/testdata/src/e/e_test.go000066400000000000000000000001771305302736300165540ustar00rootroot00000000000000package e import "testing" import "f" // only imported in internal test scope func TestE(t *testing.T) { t.Log(f.F > 0.9) } gb-0.4.4/testdata/src/external_only_test/000077500000000000000000000000001305302736300204335ustar00rootroot00000000000000gb-0.4.4/testdata/src/external_only_test/x_test.go000066400000000000000000000001261305302736300222670ustar00rootroot00000000000000package x_test import "testing" func TestT(t *testing.T) { t.Log("all good brah") } gb-0.4.4/testdata/src/extest/000077500000000000000000000000001305302736300160255ustar00rootroot00000000000000gb-0.4.4/testdata/src/extest/q.go000066400000000000000000000000321305302736300166070ustar00rootroot00000000000000package extest var V = 1 gb-0.4.4/testdata/src/extest/q_test.go000066400000000000000000000001661305302736300176560ustar00rootroot00000000000000package extest import "testing" func TestV(t *testing.T) { if V != 0 { t.Fatalf("V: got %v, expected 0", V) } } gb-0.4.4/testdata/src/extest/q_test_test.go000066400000000000000000000001041305302736300207050ustar00rootroot00000000000000package extest_test import "extest" func init() { extest.V = 0 } gb-0.4.4/testdata/src/extestonly/000077500000000000000000000000001305302736300167275ustar00rootroot00000000000000gb-0.4.4/testdata/src/extestonly/x_test.go000066400000000000000000000001241305302736300205610ustar00rootroot00000000000000package extestonly_test import "testing" func TestT(t *testing.T) { t.Log("ok") } gb-0.4.4/testdata/src/f/000077500000000000000000000000001305302736300147365ustar00rootroot00000000000000gb-0.4.4/testdata/src/f/f.go000066400000000000000000000000311305302736300155040ustar00rootroot00000000000000package f const F = 1.0 gb-0.4.4/testdata/src/g/000077500000000000000000000000001305302736300147375ustar00rootroot00000000000000gb-0.4.4/testdata/src/g/export_test.go000066400000000000000000000000451305302736300176450ustar00rootroot00000000000000package g const WHATEVER = whatever gb-0.4.4/testdata/src/g/g.go000066400000000000000000000000361305302736300155130ustar00rootroot00000000000000package g const whatever = 0 gb-0.4.4/testdata/src/g/g_test.go000066400000000000000000000001441305302736300165520ustar00rootroot00000000000000package g_test import "g" import "testing" func TestWhatever(t *testing.T) { t.Log(g.WHATEVER) } gb-0.4.4/testdata/src/h/000077500000000000000000000000001305302736300147405ustar00rootroot00000000000000gb-0.4.4/testdata/src/h/h.go000066400000000000000000000001361305302736300155160ustar00rootroot00000000000000package h import "blank" // go/build.NoGoErr const b = blank.Blank // should be build error gb-0.4.4/testdata/src/ldflags/000077500000000000000000000000001305302736300161255ustar00rootroot00000000000000gb-0.4.4/testdata/src/ldflags/ldflags.go000066400000000000000000000000721305302736300200670ustar00rootroot00000000000000package ldflags var gitRevision string // set by linker gb-0.4.4/testdata/src/ldflags/ldflags_test.go000066400000000000000000000003751305302736300211340ustar00rootroot00000000000000package 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.4.4/testdata/src/localimport/000077500000000000000000000000001305302736300170365ustar00rootroot00000000000000gb-0.4.4/testdata/src/localimport/local.go000066400000000000000000000000731305302736300204570ustar00rootroot00000000000000package localimport import "../localimport" // pure evil gb-0.4.4/testdata/src/main/000077500000000000000000000000001305302736300154355ustar00rootroot00000000000000gb-0.4.4/testdata/src/main/main.go000066400000000000000000000000151305302736300167040ustar00rootroot00000000000000package main gb-0.4.4/testdata/src/mainnoruntime/000077500000000000000000000000001305302736300173765ustar00rootroot00000000000000gb-0.4.4/testdata/src/mainnoruntime/main.go000066400000000000000000000002601305302736300206470ustar00rootroot00000000000000package 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.4.4/testdata/src/nested/000077500000000000000000000000001305302736300157735ustar00rootroot00000000000000gb-0.4.4/testdata/src/nested/a/000077500000000000000000000000001305302736300162135ustar00rootroot00000000000000gb-0.4.4/testdata/src/nested/a/a.go000066400000000000000000000000311305302736300167540ustar00rootroot00000000000000package a const A = `a` gb-0.4.4/testdata/src/nested/b/000077500000000000000000000000001305302736300162145ustar00rootroot00000000000000gb-0.4.4/testdata/src/nested/b/b.go000066400000000000000000000000311305302736300167560ustar00rootroot00000000000000package b const A = `b` gb-0.4.4/testdata/src/nosource/000077500000000000000000000000001305302736300163465ustar00rootroot00000000000000gb-0.4.4/testdata/src/nosource/x.txt000066400000000000000000000000211305302736300173470ustar00rootroot00000000000000I am a text file gb-0.4.4/testdata/src/notestfiles/000077500000000000000000000000001305302736300170505ustar00rootroot00000000000000gb-0.4.4/testdata/src/notestfiles/notest.go000066400000000000000000000000361305302736300207120ustar00rootroot00000000000000package notest const X = "y" gb-0.4.4/testdata/src/tags1/000077500000000000000000000000001305302736300155305ustar00rootroot00000000000000gb-0.4.4/testdata/src/tags1/t.go000066400000000000000000000000511305302736300163160ustar00rootroot00000000000000// +build !x package tags1 const X = 1 gb-0.4.4/testdata/src/tags2/000077500000000000000000000000001305302736300155315ustar00rootroot00000000000000gb-0.4.4/testdata/src/tags2/t.go000066400000000000000000000000501305302736300163160ustar00rootroot00000000000000// +build x package tags2 const X = 2 gb-0.4.4/testdata/src/testflags/000077500000000000000000000000001305302736300165055ustar00rootroot00000000000000gb-0.4.4/testdata/src/testflags/testflags.go000066400000000000000000000000221305302736300210220ustar00rootroot00000000000000package testflags gb-0.4.4/testdata/src/testflags/testflags_test.go000066400000000000000000000003551305302736300220720ustar00rootroot00000000000000package 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.4.4/testdata/src/testonly/000077500000000000000000000000001305302736300163725ustar00rootroot00000000000000gb-0.4.4/testdata/src/testonly/x_test.go000066400000000000000000000001151305302736300202240ustar00rootroot00000000000000package testonly import "testing" func TestT(t *testing.T) { t.Log("ok") } gb-0.4.4/testdata/src/x/000077500000000000000000000000001305302736300147605ustar00rootroot00000000000000gb-0.4.4/testdata/src/x/x.go000066400000000000000000000000261305302736300155540ustar00rootroot00000000000000package x import "y" gb-0.4.4/testdata/src/y/000077500000000000000000000000001305302736300147615ustar00rootroot00000000000000gb-0.4.4/testdata/src/y/y.go000066400000000000000000000000261305302736300155560ustar00rootroot00000000000000package y import "x"