diff --git a/builder_go110.go b/builder_go110.go index 5857c7c..c8dee05 100644 --- a/builder_go110.go +++ b/builder_go110.go @@ -24,6 +24,7 @@ var compiler = filepath.Join(tooldir, "compile") var linker = filepath.Join(tooldir, "link") var gopaths = filepath.SplitList(build.Default.GOPATH) var goarch = build.Default.GOARCH +var goroot = build.Default.GOROOT var goos = build.Default.GOOS var godogImportPath = "github.com/DATA-DOG/godog" @@ -121,11 +122,7 @@ func Build(bin string) error { // but we need it for our testmain package. // So we look it up in available source paths // including vendor directory, supported since 1.5. - try := maybeVendorPaths(abs) - for _, d := range build.Default.SrcDirs() { - try = append(try, filepath.Join(d, godogImportPath)) - } - godogPkg, err := locatePackage(try) + godogPkg, err := locatePackage(godogImportPath) if err != nil { return err } @@ -139,32 +136,48 @@ func Build(bin string) error { return fmt.Errorf("failed to install godog package: %s, reason: %v", string(out), err) } - // collect all possible package dirs, will be - // used for includes and linker - pkgDirs := []string{workdir, testdir} - for _, gopath := range gopaths { - pkgDirs = append(pkgDirs, filepath.Join(gopath, "pkg", goos+"_"+goarch)) - } - pkgDirs = uniqStringList(pkgDirs) - // compile godog testmain package archive // we do not depend on CGO so a lot of checks are not necessary testMainPkgOut := filepath.Join(testdir, "main.a") args := []string{ "-o", testMainPkgOut, - // "-trimpath", workdir, - "-importcfg", filepath.Join(testdir, "importcfg.link"), "-p", "main", "-complete", } - // if godog library is in vendor directory - // link it with import map - if i := strings.LastIndex(godogPkg.ImportPath, "vendor/"); i != -1 { - args = append(args, "-importmap", godogImportPath+"="+godogPkg.ImportPath) - } - for _, inc := range pkgDirs { - args = append(args, "-I", inc) + + var in *os.File + cfg := filepath.Join(testdir, "importcfg.link") + args = append(args, "-importcfg", cfg) + if _, err := os.Stat(cfg); err == nil { + // there were go sources + in, err = os.OpenFile(cfg, os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + return err + } + } else { + // there were no go sources in the directory + // so we need to build all dependency tree ourselves + in, err = os.Create(cfg) + if err != nil { + return err + } + fmt.Fprintln(in, "# import config") + + deps := make(map[string]string) + if err := dependencies(godogPkg, deps); err != nil { + return err + } + + for pkgName, pkgObj := range deps { + if i := strings.LastIndex(pkgName, "vendor/"); i != -1 { + fmt.Fprintf(in, "importmap %s=%s\n", pkgName, pkgObj) + } else { + fmt.Fprintf(in, "packagefile %s=%s\n", pkgName, pkgObj) + } + } } + in.Close() + args = append(args, "-pack", testmain) cmd = exec.Command(compiler, args...) cmd.Env = os.Environ() @@ -176,12 +189,9 @@ func Build(bin string) error { // link test suite executable args = []string{ "-o", bin, - "-importcfg", filepath.Join(testdir, "importcfg.link"), + "-importcfg", cfg, "-buildmode=exe", } - for _, link := range pkgDirs { - args = append(args, "-L", link) - } args = append(args, testMainPkgOut) cmd = exec.Command(linker, args...) cmd.Env = os.Environ() @@ -197,9 +207,9 @@ func Build(bin string) error { return nil } -func locatePackage(try []string) (*build.Package, error) { - for _, p := range try { - abs, err := filepath.Abs(p) +func locatePackage(name string) (*build.Package, error) { + for _, p := range build.Default.SrcDirs() { + abs, err := filepath.Abs(filepath.Join(p, name)) if err != nil { continue } @@ -209,7 +219,26 @@ func locatePackage(try []string) (*build.Package, error) { } return pkg, nil } - return nil, fmt.Errorf("failed to find godog package in any of:\n%s", strings.Join(try, "\n")) + + // search vendor paths + dir, err := filepath.Abs(".") + if err != nil { + return nil, err + } + + for _, gopath := range gopaths { + gopath = filepath.Join(gopath, "src") + for strings.HasPrefix(dir, gopath) && dir != gopath { + pkg, err := build.ImportDir(filepath.Join(dir, "vendor", name), 0) + if err != nil { + dir = filepath.Dir(dir) + continue + } + return pkg, nil + } + } + + return nil, fmt.Errorf("failed to find %s package in any of:\n%s", name, strings.Join(build.Default.SrcDirs(), "\n")) } func importPackage(dir string) *build.Package { @@ -279,20 +308,6 @@ func buildTestMain(pkg *build.Package) ([]byte, bool, error) { return buf.Bytes(), len(contexts) > 0, nil } -// maybeVendorPaths determines possible vendor paths -// which goes levels down from given directory -// until it reaches GOPATH source dir -func maybeVendorPaths(dir string) (paths []string) { - for _, gopath := range gopaths { - gopath = filepath.Join(gopath, "src") - for strings.HasPrefix(dir, gopath) && dir != gopath { - paths = append(paths, filepath.Join(dir, "vendor", godogImportPath)) - dir = filepath.Dir(dir) - } - } - return -} - // processPackageTestFiles runs through ast of each test // file pack and looks for godog suite contexts to register // on run @@ -329,3 +344,23 @@ func findToolDir() string { } return filepath.Clean(build.ToolDir) } + +func dependencies(pkg *build.Package, visited map[string]string) error { + visited[pkg.ImportPath] = pkg.PkgObj + for _, name := range pkg.Imports { + if _, ok := visited[name]; ok { + continue + } + + next, err := locatePackage(name) + if err != nil { + return err + } + + visited[name] = pkg.PkgObj + if err := dependencies(next, visited); err != nil { + return err + } + } + return nil +} diff --git a/builder_test.go b/builder_test.go index a5bc8b6..83d95de 100644 --- a/builder_test.go +++ b/builder_test.go @@ -1,40 +1,28 @@ package godog import ( - "go/build" "os" "path/filepath" - "reflect" "testing" ) -func TestVendorPaths(t *testing.T) { - gopaths = []string{"/go"} +func TestDeps(t *testing.T) { + t.Log("hh") - type Case struct { - dir string - expect []string + abs, err := filepath.Abs(".") + if err != nil { + t.Fatal(err) } - cases := []Case{ - {"/go", []string{}}, - {"/go/src", []string{}}, - {"/go/src/project", []string{"/go/src/project/vendor"}}, - {"/go/src/party/project", []string{"/go/src/party/project/vendor", "/go/src/party/vendor"}}, + // we allow package to be nil, if godog is run only when + // there is a feature file in empty directory + pkg := importPackage(abs) + deps := make(map[string]string) + err = dependencies(pkg, deps) + if err != nil { + t.Fatal(err) } - - for i, c := range cases { - actual := maybeVendorPaths(c.dir) - var expect []string - for _, s := range c.expect { - expect = append(expect, filepath.Join(s, godogImportPath)) - } - if !reflect.DeepEqual(expect, actual) { - t.Fatalf("case %d expected %+v, got %+v", i, expect, actual) - } - } - - gopaths = filepath.SplitList(build.Default.GOPATH) + t.Log(deps) } func TestBuildTestRunner(t *testing.T) {