pax_global_header 0000666 0000000 0000000 00000000064 13053027363 0014514 g ustar 00root root 0000000 0000000 52 comment=d94d4f1fbab3a54e1d5feafa545e1c9014516fcb
gb-0.4.4/ 0000775 0000000 0000000 00000000000 13053027363 0012111 5 ustar 00root root 0000000 0000000 gb-0.4.4/.gitignore 0000664 0000000 0000000 00000000021 13053027363 0014072 0 ustar 00root root 0000000 0000000 !*
testdata/bin/
gb-0.4.4/.travis.yml 0000664 0000000 0000000 00000001476 13053027363 0014232 0 ustar 00root root 0000000 0000000 language: 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/LICENSE 0000664 0000000 0000000 00000002070 13053027363 0013115 0 ustar 00root root 0000000 0000000 The 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.md 0000664 0000000 0000000 00000004350 13053027363 0013372 0 ustar 00root root 0000000 0000000 # gb
### Build status
Unix:
[](https://travis-ci.org/constabulary/gb)
Windows:
[](https://ci.appveyor.com/project/davecheney/gb/branch/master)
[](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.go 0000664 0000000 0000000 00000004331 13053027363 0014755 0 ustar 00root root 0000000 0000000 package 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.yml 0000664 0000000 0000000 00000002127 13053027363 0014503 0 ustar 00root root 0000000 0000000 version: 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/ 0000775 0000000 0000000 00000000000 13053027363 0012661 5 ustar 00root root 0000000 0000000 gb-0.4.4/bin/coverage.sh 0000775 0000000 0000000 00000000371 13053027363 0015014 0 ustar 00root root 0000000 0000000 #!/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.go 0000664 0000000 0000000 00000016612 13053027363 0013545 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000017232 13053027363 0014603 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000036030 13053027363 0013212 0 ustar 00root root 0000000 0000000 package 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/ 0000775 0000000 0000000 00000000000 13053027363 0012654 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/cmd.go 0000664 0000000 0000000 00000004447 13053027363 0013757 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000001230 13053027363 0013767 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001551 13053027363 0015034 0 ustar 00root root 0000000 0000000 package 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/ 0000775 0000000 0000000 00000000000 13053027363 0014537 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb-vendor/alldocs.go 0000664 0000000 0000000 00000006235 13053027363 0016515 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003753 13053027363 0016340 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000014637 13053027363 0016172 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000004675 13053027363 0016032 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000002532 13053027363 0016043 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000003742 13053027363 0016020 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000003265 13053027363 0016216 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000003361 13053027363 0016554 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000007327 13053027363 0016361 0 ustar 00root root 0000000 0000000 package 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/ 0000775 0000000 0000000 00000000000 13053027363 0013244 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/alldocs.go 0000664 0000000 0000000 00000013332 13053027363 0015216 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000010466 13053027363 0014701 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000006765 13053027363 0016260 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000010474 13053027363 0015065 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001456 13053027363 0014346 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001120 13053027363 0014353 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000000666 13053027363 0014373 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000002314 13053027363 0014504 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000130177 13053027363 0015233 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000001746 13053027363 0015375 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000006627 13053027363 0014536 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000004152 13053027363 0014530 0 ustar 00root root 0000000 0000000 package 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/ 0000775 0000000 0000000 00000000000 13053027363 0015060 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/ 0000775 0000000 0000000 00000000000 13053027363 0016154 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/ 0000775 0000000 0000000 00000000000 13053027363 0020124 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/ 0000775 0000000 0000000 00000000000 13053027363 0020344 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/ 0000775 0000000 0000000 00000000000 13053027363 0021133 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/.gitturd 0000664 0000000 0000000 00000000000 13053027363 0022604 0 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/ 0000775 0000000 0000000 00000000000 13053027363 0021676 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/main/ 0000775 0000000 0000000 00000000000 13053027363 0022622 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/cmd/main/main.go 0000664 0000000 0000000 00000000110 13053027363 0024065 0 ustar 00root root 0000000 0000000 package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/ 0000775 0000000 0000000 00000000000 13053027363 0023172 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/ 0000775 0000000 0000000 00000000000 13053027363 0023755 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/_testdata/ 0000775 0000000 0000000 00000000000 13053027363 0025725 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/_testdata/.gitturd 0000664 0000000 0000000 00000000000 13053027363 0027376 0 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/ 0000775 0000000 0000000 00000000000 13053027363 0024521 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/_testdata/ 0000775 0000000 0000000 00000000000 13053027363 0026471 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/_testdata/.gitturd 0000664 0000000 0000000 00000000000 13053027363 0030142 0 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/foo/bar/bar.go 0000664 0000000 0000000 00000000014 13053027363 0025607 0 ustar 00root root 0000000 0000000 package bar
gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/ 0000775 0000000 0000000 00000000000 13053027363 0024177 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/bar/ 0000775 0000000 0000000 00000000000 13053027363 0024743 5 ustar 00root root 0000000 0000000 gb-0.4.4/cmd/gb/internal/match/_testdata/a/src/github.com/quxx/bar/bar.go 0000664 0000000 0000000 00000000014 13053027363 0026031 0 ustar 00root root 0000000 0000000 package bar
gb-0.4.4/cmd/gb/internal/match/match.go 0000664 0000000 0000000 00000010177 13053027363 0017605 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000013376 13053027363 0020650 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000006274 13053027363 0014557 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000012032 13053027363 0014515 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001543 13053027363 0015074 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001174 13053027363 0015053 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000000601 13053027363 0016631 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000000522 13053027363 0016112 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000001603 13053027363 0015615 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000005214 13053027363 0016656 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000005113 13053027363 0014552 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000013147 13053027363 0015412 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000013707 13053027363 0016453 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001211 13053027363 0014132 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000002551 13053027363 0015201 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000025314 13053027363 0014131 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000013242 13053027363 0015165 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000000131 13053027363 0014365 0 ustar 00root root 0000000 0000000 // +build !darwin,!freebsd
package gb
const defaultCC = "gcc"
const defaultCXX = "g++"
gb-0.4.4/defaultcc_bsd.go 0000664 0000000 0000000 00000000135 13053027363 0015221 0 ustar 00root root 0000000 0000000 // +build darwin freebsd
package gb
const defaultCC = "clang"
const defaultCXX = "clang++"
gb-0.4.4/depfile.go 0000664 0000000 0000000 00000012147 13053027363 0014055 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000001250 13053027363 0015130 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000004166 13053027363 0014305 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000007675 13053027363 0015354 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000005031 13053027363 0013027 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000002203 13053027363 0014064 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000013543 13053027363 0013037 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000000161 13053027363 0014614 0 ustar 00root root 0000000 0000000 // +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.go 0000664 0000000 0000000 00000000152 13053027363 0014615 0 ustar 00root root 0000000 0000000 // +build go1.3
// +build !go1.4,!go1.5,!go1.6
package gb
const (
goversion = 1.3
allowVendor = 0
)
gb-0.4.4/goversion14.go 0000664 0000000 0000000 00000000143 13053027363 0014616 0 ustar 00root root 0000000 0000000 // +build go1.4
// +build !go1.5,!go1.6
package gb
const (
goversion = 1.4
allowVendor = 0
)
gb-0.4.4/goversion15.go 0000664 0000000 0000000 00000000134 13053027363 0014617 0 ustar 00root root 0000000 0000000 // +build go1.5
// +build !go1.6
package gb
const (
goversion = 1.5
allowVendor = 0
)
gb-0.4.4/goversion16.go 0000664 0000000 0000000 00000000070 13053027363 0014617 0 ustar 00root root 0000000 0000000 // +build go1.6
package gb
const (
goversion = 1.6
)
gb-0.4.4/internal/ 0000775 0000000 0000000 00000000000 13053027363 0013725 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/debug/ 0000775 0000000 0000000 00000000000 13053027363 0015013 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/debug/debug.go 0000664 0000000 0000000 00000001366 13053027363 0016436 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13053027363 0015335 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/depfile/depfile.go 0000664 0000000 0000000 00000007036 13053027363 0017302 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000011437 13053027363 0020341 0 ustar 00root root 0000000 0000000 package 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/ 0000775 0000000 0000000 00000000000 13053027363 0015725 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/fileutils/_testdata/ 0000775 0000000 0000000 00000000000 13053027363 0017675 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/fileutils/_testdata/copyfile/ 0000775 0000000 0000000 00000000000 13053027363 0021507 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/fileutils/_testdata/copyfile/a/ 0000775 0000000 0000000 00000000000 13053027363 0021727 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/fileutils/_testdata/copyfile/a/rick 0000777 0000000 0000000 00000000000 13053027363 0027635 2/never/going/to/give/you/up ustar 00root root 0000000 0000000 gb-0.4.4/internal/fileutils/fileutils.go 0000664 0000000 0000000 00000004450 13053027363 0020257 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000001020 13053027363 0021304 0 ustar 00root root 0000000 0000000 package 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.go 0000664 0000000 0000000 00000005370 13053027363 0020254 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13053027363 0015056 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/untar/_testdata/ 0000775 0000000 0000000 00000000000 13053027363 0017026 5 ustar 00root root 0000000 0000000 gb-0.4.4/internal/untar/_testdata/a.tar.gz 0000664 0000000 0000000 00000000224 13053027363 0020373 0 ustar 00root root 0000000 0000000 ‹ ¼kjW íÑ1à …aÏ=…oC0œµâ msÿ &êСª*ýßò,ñÀu‘é̬¸ë+ó‘Ó‘'
±¬1eÏ^ÔBˆ!‹úüj"ÛãYï½Ê]‡s}¬µÁùùŽwþ‰ºÔéwôÿÈ)ö?÷¿zµéÍ„ý_~Ý À·va¾| ( gb-0.4.4/internal/untar/_testdata/errors.tar.gz 0000664 0000000 0000000 00000020343 13053027363 0021473 0 ustar 00root root 0000000 0000000 ‹ ì