Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build beats outside GOPATH #16329

Merged
merged 4 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev-tools/mage/crossbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (b GolangCrossBuilder) Build() error {
return errors.Wrap(err, "failed to determine repo root and package sub dir")
}

mountPoint := filepath.ToSlash(filepath.Join("/go", "src", repoInfo.RootImportPath))
mountPoint := filepath.ToSlash(filepath.Join("/go", "src", repoInfo.MountPoint))
// use custom dir for build if given, subdir if not:
cwd := repoInfo.SubDir
if b.InDir != "" {
Expand Down
13 changes: 13 additions & 0 deletions dev-tools/mage/gotool/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package gotool

import (
"fmt"
"os"
"strings"

Expand Down Expand Up @@ -55,6 +56,18 @@ type goTest func(opts ...ArgOpt) error
// Test runs `go test` and provides optionals for adding command line arguments.
var Test goTest = runGoTest

// GetModuleName returns the name of the module.
func GetModuleName() (string, error) {
lines, err := getLines(callGo(nil, "list", "-m"))
if err != nil {
return "", err
}
if len(lines) != 1 {
return "", fmt.Errorf("unexpected number of lines")
}
return lines[0], nil
}

// ListProjectPackages lists all packages in the current project
func ListProjectPackages() ([]string, error) {
return ListPackages("./...")
Expand Down
165 changes: 139 additions & 26 deletions dev-tools/mage/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
"github.com/magefile/mage/sh"
"github.com/pkg/errors"
"golang.org/x/tools/go/vcs"

"github.com/elastic/beats/dev-tools/mage/gotool"
)

const (
Expand Down Expand Up @@ -196,6 +198,7 @@ date = {{ date }}
elastic_beats_dir = {{ elastic_beats_dir }}
go_version = {{ go_version }}
repo.RootImportPath = {{ repo.RootImportPath }}
repo.MountPoint = {{ repo.MountPoint }}
repo.RootDir = {{ repo.RootDir }}
repo.ImportPath = {{ repo.ImportPath }}
repo.SubDir = {{ repo.SubDir }}
Expand Down Expand Up @@ -540,6 +543,7 @@ func parseDocBranch(data []byte) (string, error) {
// ProjectRepoInfo contains information about the project's repo.
type ProjectRepoInfo struct {
RootImportPath string // Import path at the project root.
MountPoint string // Mount point for the crossBuilder.
kvch marked this conversation as resolved.
Show resolved Hide resolved
RootDir string // Root directory of the project.
ImportPath string // Import path of the current directory.
SubDir string // Relative path from the root dir to the current dir.
Expand All @@ -548,7 +552,7 @@ type ProjectRepoInfo struct {
// IsElasticBeats returns true if the current project is
// github.com/elastic/beats.
func (r *ProjectRepoInfo) IsElasticBeats() bool {
return r.RootImportPath == elasticBeatsImportPath
return strings.HasPrefix(r.RootImportPath, elasticBeatsImportPath)
}

var (
Expand All @@ -561,61 +565,170 @@ var (
// import path and the current directory's import path.
func GetProjectRepoInfo() (*ProjectRepoInfo, error) {
repoInfoOnce.Do(func() {
repoInfoValue, repoInfoErr = getProjectRepoInfo()
if isUnderGOPATH() {
repoInfoValue, repoInfoErr = getProjectRepoInfoUnderGopath()
} else {
repoInfoValue, repoInfoErr = getProjectRepoInfoWithModules()
}
})

return repoInfoValue, repoInfoErr
}

func getProjectRepoInfo() (*ProjectRepoInfo, error) {
func isUnderGOPATH() bool {
underGOPATH := false
srcDirs, err := listSrcGOPATHs()
if err != nil {
return false
}
for _, srcDir := range srcDirs {
rel, err := filepath.Rel(srcDir, CWD())
if err != nil {
continue
}

if !strings.Contains(rel, "..") {
underGOPATH = true
}
}

return underGOPATH
}

func getProjectRepoInfoWithModules() (*ProjectRepoInfo, error) {
var (
cwd = CWD()
rootImportPath string
srcDir string
cwd = CWD()
rootDir string
subDir string
)

// Search upward from the CWD to determine the project root based on VCS.
possibleRoot := cwd
var errs []string
for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
gopath = filepath.Clean(gopath)
for {
isRoot, err := isGoModRoot(possibleRoot)
if err != nil {
errs = append(errs, err.Error())
}

if !strings.HasPrefix(cwd, gopath) {
// Fixes an issue on macOS when /var is actually /private/var.
var err error
gopath, err = filepath.EvalSymlinks(gopath)
if err != nil {
errs = append(errs, err.Error())
continue
}
if isRoot {
rootDir = possibleRoot
break
}

srcDir = filepath.Join(gopath, "src")
subDir, err = filepath.Rel(possibleRoot, cwd)
if err != nil {
errs = append(errs, err.Error())
}
possibleRoot = filepath.Dir(possibleRoot)
}

if rootDir == "" {
return nil, errors.Errorf("failed to find root dir of module file: %v", errs)
}

rootImportPath, err := gotool.GetModuleName()
if err != nil {
return nil, err
}

return &ProjectRepoInfo{
RootImportPath: rootImportPath,
MountPoint: extractMountPoint(rootImportPath),
RootDir: rootDir,
SubDir: subDir,
ImportPath: filepath.ToSlash(filepath.Join(rootImportPath, subDir)),
}, nil
}

func isGoModRoot(path string) (bool, error) {
gomodPath := filepath.Join(path, "go.mod")
_, err := os.Stat(gomodPath)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}

return true, nil
}

func getProjectRepoInfoUnderGopath() (*ProjectRepoInfo, error) {
var (
cwd = CWD()
errs []string
rootDir string
)

srcDirs, err := listSrcGOPATHs()
if err != nil {
return nil, err
}

for _, srcDir := range srcDirs {
_, root, err := vcs.FromDir(cwd, srcDir)
if err != nil {
// Try the next gopath.
errs = append(errs, err.Error())
continue
}
rootImportPath = root
rootDir = filepath.Join(srcDir, root)
break
}
if rootImportPath == "" {
return nil, errors.Errorf("failed to determine root import path (Did "+
"you git init?, Is the project in the GOPATH? GOPATH=%v, CWD=%v?): %v",
build.Default.GOPATH, cwd, errs)

if rootDir == "" {
return nil, errors.Errorf("error while determining root directory: %v", errs)
}

rootDir := filepath.Join(srcDir, rootImportPath)
subDir, err := filepath.Rel(rootDir, cwd)
if err != nil {
return nil, errors.Wrap(err, "failed to get relative path to repo root")
}
importPath := filepath.ToSlash(filepath.Join(rootImportPath, subDir))

rootImportPath, err := gotool.GetModuleName()
if err != nil {
return nil, err
}

return &ProjectRepoInfo{
RootImportPath: rootImportPath,
MountPoint: extractMountPoint(rootImportPath),
RootDir: rootDir,
SubDir: subDir,
ImportPath: importPath,
ImportPath: filepath.ToSlash(filepath.Join(rootImportPath, subDir)),
}, nil
}

func extractMountPoint(rootImportPath string) string {
re := regexp.MustCompile(`/v[2-9][0-9]*$`) // ends with major version
kvch marked this conversation as resolved.
Show resolved Hide resolved
return re.ReplaceAllString(rootImportPath, "")
}

func listSrcGOPATHs() ([]string, error) {
var (
cwd = CWD()
errs []string
srcDirs []string
)
for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
gopath = filepath.Clean(gopath)

if !strings.HasPrefix(cwd, gopath) {
// Fixes an issue on macOS when /var is actually /private/var.
var err error
gopath, err = filepath.EvalSymlinks(gopath)
if err != nil {
errs = append(errs, err.Error())
continue
}
}

srcDirs = append(srcDirs, filepath.Join(gopath, "src"))
}

if len(srcDirs) == 0 {
return srcDirs, errors.Errorf("failed to find any GOPATH %v", errs)
}

return srcDirs, nil
}