From 7ff9aa24c90efb97b02e02401b9540ff744dfdde Mon Sep 17 00:00:00 2001 From: Taichi Sasaki Date: Mon, 17 Dec 2018 04:46:21 +0900 Subject: [PATCH] Support Go 1.11 Modules by goagen (#1933) * Support Go 1.11 Modules by goagen * Fix typo --- goagen/codegen/workspace.go | 175 ++++++- goagen/codegen/workspace_test.go | 789 +++++++++++++++++++++++++++++++ 2 files changed, 946 insertions(+), 18 deletions(-) create mode 100644 goagen/codegen/workspace_test.go diff --git a/goagen/codegen/workspace.go b/goagen/codegen/workspace.go index 7ed78e3d46..ff5a4cb12d 100644 --- a/goagen/codegen/workspace.go +++ b/goagen/codegen/workspace.go @@ -14,6 +14,7 @@ import ( "os/exec" "path/filepath" "runtime" + "strconv" "strings" "text/template" @@ -29,6 +30,8 @@ type ( Path string // gopath is the original GOPATH gopath string + // isModuleMode indicates whether the Module mode is enabled. + isModuleMode bool } // Package represents a temporary Go package @@ -104,19 +107,32 @@ func WorkspaceFor(source string) (*Workspace, error) { if err != nil { sourcePath = source } - for _, gp := range filepath.SplitList(gopaths) { - gopath, err := filepath.Abs(gp) - if err != nil { - gopath = gp + if os.Getenv("GO111MODULE") != "on" { // GOPATH mode + for _, gp := range filepath.SplitList(gopaths) { + gopath, err := filepath.Abs(gp) + if err != nil { + gopath = gp + } + if filepath.HasPrefix(sourcePath, gopath) { + return &Workspace{ + gopath: gopaths, + isModuleMode: false, + Path: gopath, + }, nil + } } - if filepath.HasPrefix(sourcePath, gopath) { + } + if os.Getenv("GO111MODULE") != "off" { // Module mode + root, _ := findModuleRoot(sourcePath, "", false) + if root != "" { return &Workspace{ - gopath: gopaths, - Path: gopath, + gopath: gopaths, + isModuleMode: true, + Path: root, }, nil } } - return nil, fmt.Errorf(`Go source file "%s" not in Go workspace, adjust GOPATH %s`, source, gopaths) + return nil, fmt.Errorf(`Go source file "%s" not in Go workspace, adjust GOPATH %s or use modules`, source, gopaths) } // Delete deletes the workspace temporary directory. @@ -164,7 +180,11 @@ func PackageFor(source string) (*Package, error) { if err != nil { return nil, err } - path, err := filepath.Rel(filepath.Join(w.Path, "src"), filepath.Dir(source)) + basepath := filepath.Join(w.Path, "src") // GOPATH mode. + if w.isModuleMode { + basepath = w.Path // Module mode. + } + path, err := filepath.Rel(basepath, filepath.Dir(source)) if err != nil { return nil, err } @@ -173,7 +193,11 @@ func PackageFor(source string) (*Package, error) { // Abs returns the absolute path to the package source directory func (p *Package) Abs() string { - return filepath.Join(p.Workspace.Path, "src", p.Path) + elem := "src" // GOPATH mode. + if p.Workspace.isModuleMode { + elem = "" // Module mode. + } + return filepath.Join(p.Workspace.Path, elem, p.Path) } // CreateSourceFile creates a Go source file in the given package. If the file @@ -316,15 +340,29 @@ func PackagePath(path string) (string, error) { if err != nil { absPath = path } - gopaths := filepath.SplitList(os.Getenv("GOPATH")) - for _, gopath := range gopaths { - if gp, err := filepath.Abs(gopath); err == nil { - gopath = gp + gopaths := filepath.SplitList(envOr("GOPATH", build.Default.GOPATH)) + if os.Getenv("GO111MODULE") != "on" { // GOPATH mode + for _, gopath := range gopaths { + if gp, err := filepath.Abs(gopath); err == nil { + gopath = gp + } + if filepath.HasPrefix(absPath, gopath) { + base := filepath.FromSlash(gopath + "/src") + rel, err := filepath.Rel(base, absPath) + return filepath.ToSlash(rel), err + } } - if filepath.HasPrefix(absPath, gopath) { - base := filepath.FromSlash(gopath + "/src") - rel, err := filepath.Rel(base, absPath) - return filepath.ToSlash(rel), err + } + if os.Getenv("GO111MODULE") != "off" { // Module mode + root, file := findModuleRoot(absPath, "", false) + if root != "" { + content, err := ioutil.ReadFile(filepath.Join(root, file)) + if err == nil { + p := modulePath(content) + base := filepath.FromSlash(root) + rel, err := filepath.Rel(base, absPath) + return filepath.ToSlash(filepath.Join(p, rel)), err + } } } return "", fmt.Errorf("%s does not contain a Go package", absPath) @@ -368,6 +406,107 @@ func PackageName(path string) (string, error) { return pkgNames[0], nil } +// Copied from cmd/go/internal/modload/init.go. +var altConfigs = []string{ + "Gopkg.lock", + + "GLOCKFILE", + "Godeps/Godeps.json", + "dependencies.tsv", + "glide.lock", + "vendor.conf", + "vendor.yml", + "vendor/manifest", + "vendor/vendor.json", + + ".git/config", +} + +// Copied from cmd/go/internal/modload/init.go. +func findModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) { + dir = filepath.Clean(dir) + dir1 := dir + limit = filepath.Clean(limit) + + // Look for enclosing go.mod. + for { + if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() { + return dir, "go.mod" + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + + // Failing that, look for enclosing alternate version config. + if legacyConfigOK { + dir = dir1 + for { + for _, name := range altConfigs { + if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() { + return dir, name + } + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + } + + return "", "" +} + +// Copied from cmd/go/internal/modfile/read.go +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// Copied from cmd/go/internal/modfile/read.go +func modulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} + const ( headerT = `{{if .Title}}// Code generated by goagen {{.ToolVersion}}, DO NOT EDIT. // diff --git a/goagen/codegen/workspace_test.go b/goagen/codegen/workspace_test.go new file mode 100644 index 0000000000..af9d34b048 --- /dev/null +++ b/goagen/codegen/workspace_test.go @@ -0,0 +1,789 @@ +package codegen_test + +import ( + "fmt" + "go/build" + "io/ioutil" + "os" + "path/filepath" + + "github.com/goadesign/goa/goagen/codegen" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Workspace", func() { + Describe("WorkspaceFor", func() { + oldGOPATH := build.Default.GOPATH + BeforeEach(func() { + os.Setenv("GOPATH", "/xx") + }) + AfterEach(func() { + os.Setenv("GOPATH", oldGOPATH) + }) + + var ( + err error + gopath string + ) + Context("with GOMOD", func() { + var ( + f *os.File + ) + BeforeEach(func() { + f, err = os.OpenFile("go.mod", os.O_CREATE, 0755) + Ω(err).ShouldNot(HaveOccurred()) + Ω(f.Close()).ShouldNot(HaveOccurred()) + }) + AfterEach(func() { + Ω(os.RemoveAll("go.mod")).ShouldNot(HaveOccurred()) + }) + + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode workspace", func() { + workspace, err := codegen.WorkspaceFor("/xx/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal("/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode workspace", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + workspace, err := codegen.WorkspaceFor(abs) + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal(abs)) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a Module mode workspace", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + workspace, err := codegen.WorkspaceFor(abs) + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal(abs)) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode workspace", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + workspace, err := codegen.WorkspaceFor(abs) + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal(abs)) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode workspace", func() { + workspace, err := codegen.WorkspaceFor("/xx/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal("/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.WorkspaceFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + }) + + Context("with no GOMOD", func() { + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode workspace", func() { + workspace, err := codegen.WorkspaceFor("/xx/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal("/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.WorkspaceFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return an error", func() { + _, err := codegen.WorkspaceFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH /xx or use modules`))) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.WorkspaceFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode workspace", func() { + workspace, err := codegen.WorkspaceFor("/xx/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(workspace.Path).To(Equal("/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.WorkspaceFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + }) + }) + + Describe("PackageFor", func() { + oldGOPATH := build.Default.GOPATH + BeforeEach(func() { + os.Setenv("GOPATH", "/xx") + }) + AfterEach(func() { + os.Setenv("GOPATH", oldGOPATH) + }) + + var ( + err error + gopath string + ) + Context("with GOMOD", func() { + var ( + f *os.File + ) + BeforeEach(func() { + f, err = os.OpenFile("go.mod", os.O_CREATE, 0755) + Ω(err).ShouldNot(HaveOccurred()) + Ω(f.Close()).ShouldNot(HaveOccurred()) + }) + AfterEach(func() { + Ω(os.RemoveAll("go.mod")).ShouldNot(HaveOccurred()) + }) + + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package", func() { + pkg, err := codegen.PackageFor("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode package", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + pkg, err := codegen.PackageFor(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a Module mode package", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + pkg, err := codegen.PackageFor(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode package", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + pkg, err := codegen.PackageFor(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package", func() { + pkg, err := codegen.PackageFor("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackageFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + }) + + Context("with no GOMOD", func() { + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package", func() { + pkg, err := codegen.PackageFor("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackageFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return an error", func() { + _, err := codegen.PackageFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH /xx or use modules`))) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackageFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package", func() { + pkg, err := codegen.PackageFor("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Path).To(Equal("bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackageFor("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf(`Go source file "/bar/xx/42" not in Go workspace, adjust GOPATH %s or use modules`, gopath))) + }) + }) + }) + }) + }) + + Describe("Package.Abs", func() { + var ( + err error + gopath string + f *os.File + oldGOPATH = build.Default.GOPATH + oldGO111MODULE = os.Getenv("GO111MODULE") + ) + BeforeEach(func() { + os.Setenv("GOPATH", "/xx") + f, err = os.OpenFile("go.mod", os.O_CREATE, 0755) + Ω(err).ShouldNot(HaveOccurred()) + Ω(f.Close()).ShouldNot(HaveOccurred()) + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GOPATH", oldGOPATH) + Ω(os.RemoveAll("go.mod")).ShouldNot(HaveOccurred()) + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return the absolute path to the GOPATH directory", func() { + pkg, err := codegen.PackageFor("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Abs()).To(Equal("/xx/src/bar/xx")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return the absolute path to the Module directory", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + pkg, err := codegen.PackageFor(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(pkg.Abs()).To(Equal(filepath.Join(abs, "bar/xx"))) + }) + }) + }) + + Describe("PackagePath", func() { + oldGOPATH := build.Default.GOPATH + BeforeEach(func() { + os.Setenv("GOPATH", "/xx") + }) + AfterEach(func() { + os.Setenv("GOPATH", oldGOPATH) + }) + + var ( + err error + gopath string + ) + Context("with GOMOD", func() { + var ( + f *os.File + ) + BeforeEach(func() { + f, err = os.OpenFile("go.mod", os.O_CREATE, 0755) + Ω(err).ShouldNot(HaveOccurred()) + Ω(f.Close()).ShouldNot(HaveOccurred()) + }) + AfterEach(func() { + Ω(os.RemoveAll("go.mod")).ShouldNot(HaveOccurred()) + }) + + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package path", func() { + p, err := codegen.PackagePath("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode package path", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + p, err := codegen.PackagePath(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a Module mode package path", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + p, err := codegen.PackagePath(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return a Module mode package path", func() { + abs, err := filepath.Abs(".") + Ω(err).ShouldNot(HaveOccurred()) + p, err := codegen.PackagePath(filepath.Join(abs, "bar/xx/42")) + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package path", func() { + p, err := codegen.PackagePath("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackagePath("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf("/bar/xx/42 does not contain a Go package"))) + }) + }) + }) + }) + + Context("with no GOMOD", func() { + Context("with GO111MODULE=auto", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Unsetenv("GO111MODULE") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package path", func() { + p, err := codegen.PackagePath("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackagePath("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf("/bar/xx/42 does not contain a Go package"))) + }) + }) + }) + + Context("with GO111MODULE=on", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "on") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return an error", func() { + _, err := codegen.PackagePath("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf("/bar/xx/42 does not contain a Go package"))) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackagePath("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf("/bar/xx/42 does not contain a Go package"))) + }) + }) + }) + + Context("with GO111MODULE=off", func() { + oldGO111MODULE := os.Getenv("GO111MODULE") + BeforeEach(func() { + os.Setenv("GO111MODULE", "off") + }) + AfterEach(func() { + os.Setenv("GO111MODULE", oldGO111MODULE) + }) + + Context("inside GOPATH", func() { + It("should return a GOPATH mode package path", func() { + p, err := codegen.PackagePath("/xx/src/bar/xx/42") + Ω(err).ShouldNot(HaveOccurred()) + Expect(p).To(Equal("bar/xx/42")) + }) + }) + + Context("outside GOPATH", func() { + BeforeEach(func() { + gopath, err = ioutil.TempDir(".", "go") + Ω(err).ShouldNot(HaveOccurred()) + os.Setenv("GOPATH", gopath) + }) + AfterEach(func() { + Ω(os.RemoveAll(gopath)).ShouldNot(HaveOccurred()) + }) + + It("should return an error", func() { + _, err := codegen.PackagePath("/bar/xx/42") + Ω(err).Should(Equal(fmt.Errorf("/bar/xx/42 does not contain a Go package"))) + }) + }) + }) + }) + }) + +})