commit
4c7721b0e8
38
cmd/build.go
38
cmd/build.go
@ -17,6 +17,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/qiniu/goc/pkg/build"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
"github.com/spf13/cobra"
|
||||
@ -27,9 +29,7 @@ var buildCmd = &cobra.Command{
|
||||
Short: "Do cover for all go files and execute go build command",
|
||||
Long: `
|
||||
First of all, this build command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target in this temporary directory, finally go build command will be executed and binaries generated to their original place.
|
||||
|
||||
To pass original go build flags to goc command, place them after "--", see examples below for reference.
|
||||
`,
|
||||
`,
|
||||
Example: `
|
||||
# Build the current binary with cover variables injected. The binary will be generated in the current folder.
|
||||
goc build
|
||||
@ -38,21 +38,13 @@ goc build
|
||||
goc build --center=http://127.0.0.1:7777
|
||||
|
||||
# Build the current binary with cover variables injected, and redirect output to /to/this/path.
|
||||
goc build -- -o /to/this/path
|
||||
goc build --output /to/this/path
|
||||
|
||||
# Build the current binary with cover variables injected, and set necessary build flags: -ldflags "-extldflags -static" -tags="embed kodo".
|
||||
goc build -- -ldflags "-extldflags -static" -tags="embed kodo"
|
||||
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
gocBuild := build.NewBuild(buildFlags, packages, buildOutput)
|
||||
// remove temporary directory if needed
|
||||
defer gocBuild.Clean()
|
||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||
// in the tmp directory
|
||||
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||
// do install in the temporary directory
|
||||
gocBuild.Build()
|
||||
return
|
||||
runBuild()
|
||||
},
|
||||
}
|
||||
|
||||
@ -63,3 +55,21 @@ func init() {
|
||||
buildCmd.Flags().StringVar(&buildOutput, "output", "", "it forces build to write the resulting executable or object to the named output file or directory")
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
}
|
||||
|
||||
func runBuild() {
|
||||
gocBuild, err := build.NewBuild(buildFlags, packages, buildOutput)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to NewBuild: %v", err)
|
||||
}
|
||||
// remove temporary directory if needed
|
||||
defer gocBuild.Clean()
|
||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||
// in the tmp directory
|
||||
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||
// do install in the temporary directory
|
||||
err = gocBuild.Build()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to build: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
61
cmd/build_test.go
Normal file
61
cmd/build_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (qiniu.com)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var baseDir string
|
||||
|
||||
func init() {
|
||||
baseDir, _ = os.Getwd()
|
||||
}
|
||||
|
||||
func TestGeneratedBinary(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
|
||||
gopath := ""
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
buildFlags, packages, buildOutput = "", ".", ""
|
||||
runBuild()
|
||||
|
||||
obj := filepath.Join(".", "simple-project")
|
||||
fInfo, err := os.Lstat(obj)
|
||||
assert.Equal(t, err, nil, "the binary should be generated.")
|
||||
assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one")
|
||||
|
||||
cmd := exec.Command("go", "tool", "objdump", "simple-project")
|
||||
cmd.Dir = workingDir
|
||||
out, _ := cmd.CombinedOutput()
|
||||
cnt := strings.Count(string(out), "main.registerSelf")
|
||||
assert.Equal(t, cnt > 0, true, "main.registerSelf function should be in the binary")
|
||||
|
||||
cnt = strings.Count(string(out), "GoCover")
|
||||
assert.Equal(t, cnt > 0, true, "GoCover varibale should be in the binary")
|
||||
}
|
@ -19,6 +19,7 @@ package cmd
|
||||
import (
|
||||
"github.com/qiniu/goc/pkg/build"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -27,8 +28,6 @@ var installCmd = &cobra.Command{
|
||||
Short: "Do cover for all go files and execute go install command",
|
||||
Long: `
|
||||
First of all, this install command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target in this temporary directory, finally go install command will be executed and binaries generated to their original place.
|
||||
|
||||
To pass original go build flags to goc command, place them after "--", see examples below for reference.
|
||||
`,
|
||||
Example: `
|
||||
# Install all binaries with cover variables injected. The binary will be installed in $GOPATH/bin or $HOME/go/bin if directory existed.
|
||||
@ -41,14 +40,7 @@ goc install --center=http://127.0.0.1:7777
|
||||
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
gocBuild := build.NewInstall(buildFlags, packages)
|
||||
// remove temporary directory if needed
|
||||
defer gocBuild.Clean()
|
||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||
// in the tmp directory
|
||||
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||
// do install in the temporary directory
|
||||
gocBuild.Install()
|
||||
runInstall()
|
||||
},
|
||||
}
|
||||
|
||||
@ -56,3 +48,21 @@ func init() {
|
||||
addBuildFlags(installCmd.Flags())
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func runInstall() {
|
||||
gocBuild, err := build.NewInstall(buildFlags, packages)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to NewInstall: %v", err)
|
||||
}
|
||||
// remove temporary directory if needed
|
||||
defer gocBuild.Clean()
|
||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||
// in the tmp directory
|
||||
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||
// do install in the temporary directory
|
||||
err = gocBuild.Install()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to install: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
83
cmd/install_test.go
Normal file
83
cmd/install_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (qiniu.com)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInstalledBinaryForMod(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
|
||||
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome")
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
buildFlags, packages, buildOutput = "", ".", ""
|
||||
runInstall()
|
||||
|
||||
obj := filepath.Join(gopath, "bin", "simple-project")
|
||||
fInfo, err := os.Lstat(obj)
|
||||
assert.Equal(t, err, nil, "the binary should be generated.")
|
||||
assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one")
|
||||
|
||||
cmd := exec.Command("go", "tool", "objdump", "simple-project")
|
||||
cmd.Dir = workingDir
|
||||
out, _ := cmd.CombinedOutput()
|
||||
cnt := strings.Count(string(out), "main.registerSelf")
|
||||
assert.Equal(t, cnt > 0, true, "main.registerSelf function should be in the binary")
|
||||
|
||||
cnt = strings.Count(string(out), "GoCover")
|
||||
assert.Equal(t, cnt > 0, true, "GoCover varibale should be in the binary")
|
||||
}
|
||||
|
||||
func TestInstalledBinaryForLegacy(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
workingDir := filepath.Join(baseDir, "../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
|
||||
gopath := filepath.Join(baseDir, "../tests/samples/simple_gopath_project")
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
|
||||
buildFlags, packages, buildOutput = "", ".", ""
|
||||
runInstall()
|
||||
|
||||
obj := filepath.Join(gopath, "bin", "simple_gopath_project")
|
||||
fInfo, err := os.Lstat(obj)
|
||||
assert.Equal(t, err, nil, "the binary should be generated.")
|
||||
assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one")
|
||||
|
||||
cmd := exec.Command("go", "tool", "objdump", obj)
|
||||
cmd.Dir = workingDir
|
||||
out, _ := cmd.CombinedOutput()
|
||||
cnt := strings.Count(string(out), "main.registerSelf")
|
||||
assert.Equal(t, cnt > 0, true, "main.registerSelf function should be in the binary")
|
||||
|
||||
cnt = strings.Count(string(out), "GoCover")
|
||||
assert.Equal(t, cnt > 0, true, "GoCover varibale should be in the binary")
|
||||
}
|
@ -36,7 +36,7 @@ It is exactly behave as 'go run .' in addition of some internal goc features.`,
|
||||
goc run .
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
gocBuild := build.NewBuild(buildFlags, packages, buildOutput)
|
||||
gocBuild, _ := build.NewBuild(buildFlags, packages, buildOutput)
|
||||
gocBuild.GoRunExecFlag = goRunExecFlag
|
||||
gocBuild.GoRunArguments = goRunArguments
|
||||
defer gocBuild.Clean()
|
||||
|
@ -35,9 +35,12 @@ type Build struct {
|
||||
TmpDir string // the temporary directory to build the project
|
||||
TmpWorkingDir string // the working directory in the temporary directory, which is corresponding to the current directory in the project directory
|
||||
IsMod bool // determine whether it is a Mod project
|
||||
Root string // Project Root
|
||||
Root string
|
||||
// go 1.11, go 1.12 has no Root
|
||||
// Project Root:
|
||||
// 1. legacy, root == GOPATH
|
||||
// 2. mod, root == go.mod Dir
|
||||
Target string // the binary name that go build generate
|
||||
|
||||
// keep compatible with go commands:
|
||||
// go run [build flags] [-exec xprog] package [arguments...]
|
||||
// go build [-o output] [-i] [build flags] [packages]
|
||||
@ -50,26 +53,33 @@ type Build struct {
|
||||
|
||||
// NewBuild creates a Build struct which can build from goc temporary directory,
|
||||
// and generate binary in current working directory
|
||||
func NewBuild(buildflags string, packages string, outputDir string) *Build {
|
||||
func NewBuild(buildflags string, packages string, outputDir string) (*Build, error) {
|
||||
// buildflags = buildflags + " -o " + outputDir
|
||||
b := &Build{
|
||||
BuildFlags: buildflags,
|
||||
Packages: packages,
|
||||
}
|
||||
if false == b.validatePackageForBuild() {
|
||||
log.Fatalln("packages only support \".\"")
|
||||
log.Errorln(ErrWrongPackageTypeForBuild)
|
||||
return nil, ErrWrongPackageTypeForBuild
|
||||
}
|
||||
b.MvProjectsToTmp()
|
||||
b.Target = b.determineOutputDir(outputDir)
|
||||
return b
|
||||
dir, err := b.determineOutputDir(outputDir)
|
||||
b.Target = dir
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *Build) Build() {
|
||||
func (b *Build) Build() error {
|
||||
log.Infoln("Go building in temp...")
|
||||
// new -o will overwrite previous ones
|
||||
b.BuildFlags = b.BuildFlags + " -o " + b.Target
|
||||
cmd := exec.Command("/bin/bash", "-c", "go build "+b.BuildFlags+" "+b.Packages)
|
||||
cmd.Dir = b.TmpWorkingDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if b.NewGOPATH != "" {
|
||||
// Change to temp GOPATH for go install command
|
||||
@ -77,22 +87,30 @@ func (b *Build) Build() {
|
||||
}
|
||||
|
||||
log.Printf("go build cmd is: %v", cmd.Args)
|
||||
out, err := cmd.CombinedOutput()
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to execute: %v. The error is: %v, the stdout/stderr is: %v", cmd.Args, err, string(out))
|
||||
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
|
||||
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err)
|
||||
}
|
||||
if err = cmd.Wait(); err != nil {
|
||||
log.Errorf("go build failed. The error is: %v", err)
|
||||
return fmt.Errorf("go build faileds: %w", err)
|
||||
}
|
||||
log.Println("Go build exit successful.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// determineOutputDir, as we only allow . as package name,
|
||||
// the binary name is always same as the directory name of current directory
|
||||
func (b *Build) determineOutputDir(outputDir string) string {
|
||||
func (b *Build) determineOutputDir(outputDir string) (string, error) {
|
||||
if b.TmpDir == "" {
|
||||
log.Fatalln("Can only be called after Build.MvProjectsToTmp().")
|
||||
log.Errorf("Can only be called after Build.MvProjectsToTmp(): %v", ErrWrongCallSequence)
|
||||
return "", fmt.Errorf("can only be called after Build.MvProjectsToTmp(): %w", ErrWrongCallSequence)
|
||||
}
|
||||
curWorkingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directory, the err: %v.", err)
|
||||
log.Errorf("Cannot get current working directory: %v", err)
|
||||
return "", fmt.Errorf("cannot get current working directory: %w", err)
|
||||
}
|
||||
|
||||
if outputDir == "" {
|
||||
@ -102,13 +120,14 @@ func (b *Build) determineOutputDir(outputDir string) string {
|
||||
// replace "_" with "-" in the import path
|
||||
last = strings.ReplaceAll(last, "_", "-")
|
||||
}
|
||||
return filepath.Join(curWorkingDir, last)
|
||||
return filepath.Join(curWorkingDir, last), nil
|
||||
}
|
||||
abs, err := filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to transform the path: %v to absolute path, the error is: %v", outputDir, err)
|
||||
log.Errorf("Fail to transform the path: %v to absolute path: %v", outputDir, err)
|
||||
return "", fmt.Errorf("fail to transform the path %v to absolute path: %w", outputDir, err)
|
||||
}
|
||||
return abs
|
||||
return abs, nil
|
||||
}
|
||||
|
||||
// validatePackageForBuild only allow . as package name
|
||||
|
53
pkg/build/build_test.go
Normal file
53
pkg/build/build_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (qiniu.com)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInvalidPackage(t *testing.T) {
|
||||
|
||||
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
|
||||
gopath := ""
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
_, err := NewBuild("", "example.com/simple-project", "")
|
||||
assert.Equal(t, err, ErrWrongPackageTypeForBuild, "the package name should be invalid")
|
||||
}
|
||||
|
||||
func TestBasicBuildForModProject(t *testing.T) {
|
||||
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
|
||||
gopath := ""
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
buildFlags, packages, buildOutput := "", ".", ""
|
||||
gocBuild, err := NewBuild(buildFlags, packages, buildOutput)
|
||||
assert.Equal(t, err, nil, "should create temporary directory successfully")
|
||||
|
||||
err = gocBuild.Build()
|
||||
assert.Equal(t, err, nil, "temporary directory should build successfully")
|
||||
}
|
14
pkg/build/errors.go
Normal file
14
pkg/build/errors.go
Normal file
@ -0,0 +1,14 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrShouldNotReached = errors.New("should never be reached")
|
||||
ErrGocShouldExecInProject = errors.New("goc not executed in project directory")
|
||||
ErrWrongPackageTypeForInstall = errors.New("packages only support \".\" and \"./...\"")
|
||||
ErrWrongPackageTypeForBuild = errors.New("packages only support \".\"")
|
||||
ErrWrongCallSequence = errors.New("function should be called in a specified sequence")
|
||||
ErrNoplaceToInstall = errors.New("no go env")
|
||||
)
|
@ -28,7 +28,7 @@ func (b *Build) cpGoModulesProject() {
|
||||
src := v.Module.Dir
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
log.Errorf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
|
@ -25,36 +25,50 @@ import (
|
||||
)
|
||||
|
||||
// NewInstall creates a Build struct which can install from goc temporary directory
|
||||
func NewInstall(buildflags string, packages string) *Build {
|
||||
func NewInstall(buildflags string, packages string) (*Build, error) {
|
||||
b := &Build{
|
||||
BuildFlags: buildflags,
|
||||
Packages: packages,
|
||||
}
|
||||
if false == b.validatePackageForInstall() {
|
||||
log.Fatalln("packages only support . and ./...")
|
||||
log.Errorln(ErrWrongPackageTypeForInstall)
|
||||
return nil, ErrWrongPackageTypeForInstall
|
||||
}
|
||||
b.MvProjectsToTmp()
|
||||
return b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *Build) Install() {
|
||||
func (b *Build) Install() error {
|
||||
log.Println("Go building in temp...")
|
||||
cmd := exec.Command("/bin/bash", "-c", "go install "+b.BuildFlags+" "+b.Packages)
|
||||
cmd.Dir = b.TmpWorkingDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
whereToInstall, err := b.findWhereToInstall()
|
||||
if err != nil {
|
||||
// ignore the err
|
||||
log.Errorf("No place to install: %v", err)
|
||||
}
|
||||
// Change the temp GOBIN, to force binary install to original place
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOBIN=%v", b.findWhereToInstall()))
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOBIN=%v", whereToInstall))
|
||||
if b.NewGOPATH != "" {
|
||||
// Change to temp GOPATH for go install command
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
|
||||
}
|
||||
|
||||
log.Printf("go install cmd is: %v", cmd.Args)
|
||||
out, err := cmd.CombinedOutput()
|
||||
log.Infof("go install cmd is: %v", cmd.Args)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to execute: %v. The error is: %v, the stdout/stderr is: %v", cmd.Args, err, string(out))
|
||||
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
|
||||
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err)
|
||||
}
|
||||
log.Printf("Go install successful. Binary installed in: %v", b.findWhereToInstall())
|
||||
if err = cmd.Wait(); err != nil {
|
||||
log.Errorf("go install failed. The error is: %v", err)
|
||||
return fmt.Errorf("go install failed: %w", err)
|
||||
}
|
||||
log.Infof("Go install successful. Binary installed in: %v", whereToInstall)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Build) validatePackageForInstall() bool {
|
||||
|
24
pkg/build/install_test.go
Normal file
24
pkg/build/install_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBasicInstallForModProject(t *testing.T) {
|
||||
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
|
||||
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome")
|
||||
|
||||
os.Chdir(workingDir)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
buildFlags, packages := "", "."
|
||||
gocBuild, err := NewInstall(buildFlags, packages)
|
||||
assert.Equal(t, err, nil, "should create temporary directory successfully")
|
||||
|
||||
err = gocBuild.Install()
|
||||
assert.Equal(t, err, nil, "temporary directory should build successfully")
|
||||
}
|
@ -20,9 +20,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (b *Build) cpLegacyProject() {
|
||||
@ -37,7 +38,7 @@ func (b *Build) cpLegacyProject() {
|
||||
}
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
log.Errorf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
|
||||
visited[src] = true
|
||||
@ -73,9 +74,25 @@ func (b *Build) cpDepPackages(pkg *cover.Package, visited map[string]bool) {
|
||||
dst := filepath.Join(b.TmpDir, "src", dep)
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
log.Errorf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
|
||||
visited[src] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Build) cpNonStandardLegacy() {
|
||||
for _, v := range b.Pkgs {
|
||||
if v.Name == "main" {
|
||||
dst := b.TmpDir
|
||||
src := v.Dir
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package build
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -47,17 +48,20 @@ func (b *Build) MvProjectsToTmp() {
|
||||
}
|
||||
// fix #14: unable to build project not in GOPATH in legacy mode
|
||||
// this kind of project does not have a pkg.Root value
|
||||
if b.Root == "" {
|
||||
// go 1.11, 1.12 has no pkg.Root,
|
||||
// so add b.IsMod == false as secondary judgement
|
||||
if b.Root == "" && b.IsMod == false {
|
||||
b.NewGOPATH = b.OriGOPATH
|
||||
}
|
||||
log.Printf("New GOPATH: %v", b.NewGOPATH)
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Build) mvProjectsToTmp() {
|
||||
func (b *Build) mvProjectsToTmp() error {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directory, the error is: %v", err)
|
||||
log.Errorf("Cannot get current working directory, the error is: %v", err)
|
||||
return err
|
||||
}
|
||||
b.TmpDir = filepath.Join(os.TempDir(), TmpFolderName(path))
|
||||
|
||||
@ -66,20 +70,39 @@ func (b *Build) mvProjectsToTmp() {
|
||||
// Create a new tmp folder
|
||||
err = os.MkdirAll(filepath.Join(b.TmpDir, "src"), os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to create the temporary build directory. The err is: %v", err)
|
||||
log.Errorf("Fail to create the temporary build directory. The err is: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("Tmp project generated in: %v", b.TmpDir)
|
||||
|
||||
// set Build.IsMod flag, so we don't have to call checkIfLegacyProject another time
|
||||
if b.checkIfLegacyProject() {
|
||||
b.cpLegacyProject()
|
||||
} else {
|
||||
b.IsMod = true
|
||||
b.cpGoModulesProject()
|
||||
// traverse pkg list to get project meta info
|
||||
b.IsMod, b.Root, err = b.traversePkgsList()
|
||||
if errors.Is(err, ErrShouldNotReached) {
|
||||
return fmt.Errorf("mvProjectsToTmp with a empty project: %w", err)
|
||||
}
|
||||
// we should get corresponding working directory in temporary directory
|
||||
b.TmpWorkingDir, err = b.getTmpwd()
|
||||
if err != nil {
|
||||
log.Errorf("fail to get workding directory in temporary directory: %v", err)
|
||||
return fmt.Errorf("fail to get workding directory in temporary directory: %w", err)
|
||||
}
|
||||
// issue #14
|
||||
// if b.Root == "", then the project is non-standard project
|
||||
// known cases:
|
||||
// 1. a legacy project, but not in any GOPATH, will cause the b.Root == ""
|
||||
if b.IsMod == false && b.Root != "" {
|
||||
b.cpLegacyProject()
|
||||
} else if b.IsMod == true { // go 1.11, 1.12 has no Build.Root
|
||||
b.cpGoModulesProject()
|
||||
} else if b.IsMod == false && b.Root == "" {
|
||||
b.TmpWorkingDir = b.TmpDir
|
||||
b.cpNonStandardLegacy()
|
||||
} else {
|
||||
return fmt.Errorf("unknown project type: %w", ErrShouldNotReached)
|
||||
}
|
||||
b.getTmpwd()
|
||||
|
||||
log.Printf("New workingdir in tmp directory in: %v", b.TmpWorkingDir)
|
||||
log.Infof("New workingdir in tmp directory in: %v", b.TmpWorkingDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TmpFolderName(path string) string {
|
||||
@ -89,29 +112,35 @@ func TmpFolderName(path string) string {
|
||||
return "goc-" + h
|
||||
}
|
||||
|
||||
// checkIfLegacyProject Check if it is go module project
|
||||
// true legacy
|
||||
// false go mod
|
||||
func (b *Build) checkIfLegacyProject() bool {
|
||||
// traversePkgsList travse the Build.Pkgs list
|
||||
// return Build.IsMod, tell if the project is a mod project
|
||||
// return Build.Root:
|
||||
// 1. the project root if it is a mod project,
|
||||
// 2. current GOPATH if it is a legacy project,
|
||||
// 3. some non-standard project, which Build.IsMod == false, Build.Root == nil
|
||||
func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
|
||||
for _, v := range b.Pkgs {
|
||||
// get root
|
||||
b.Root = v.Root
|
||||
root = v.Root
|
||||
if v.Module == nil {
|
||||
return true
|
||||
return
|
||||
}
|
||||
return false
|
||||
isMod = true
|
||||
return
|
||||
}
|
||||
log.Fatalln("Should never be reached....")
|
||||
return false
|
||||
log.Error("should not reach here")
|
||||
err = ErrShouldNotReached
|
||||
return
|
||||
}
|
||||
|
||||
// getTmpwd get the corresponding working directory in the temporary working directory
|
||||
// and store it in the Build.tmpWorkdingDir
|
||||
func (b *Build) getTmpwd() {
|
||||
func (b *Build) getTmpwd() (string, error) {
|
||||
for _, pkg := range b.Pkgs {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directory, the error is: %v", err)
|
||||
log.Errorf("cannot get current working directory: %v", err)
|
||||
return "", fmt.Errorf("cannot get current working directory: %w", err)
|
||||
}
|
||||
|
||||
index := -1
|
||||
@ -125,33 +154,32 @@ func (b *Build) getTmpwd() {
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
log.Fatalf("goc install not executed in project directory.")
|
||||
return "", ErrGocShouldExecInProject
|
||||
}
|
||||
b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
|
||||
// log.Printf("New building directory in: %v", tmpwd)
|
||||
return
|
||||
// b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
|
||||
return filepath.Join(b.TmpDir, path[len(parentPath):]), nil
|
||||
}
|
||||
|
||||
log.Fatalln("Should never be reached....")
|
||||
return
|
||||
return "", ErrShouldNotReached
|
||||
}
|
||||
|
||||
func (b *Build) findWhereToInstall() string {
|
||||
func (b *Build) findWhereToInstall() (string, error) {
|
||||
if GOBIN := os.Getenv("GOBIN"); GOBIN != "" {
|
||||
return GOBIN
|
||||
return GOBIN, nil
|
||||
}
|
||||
|
||||
// old GOPATH dir
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
if false == b.IsMod {
|
||||
for _, v := range b.Pkgs {
|
||||
return filepath.Join(v.Root, "bin")
|
||||
if b.Root == "" {
|
||||
return "", ErrNoplaceToInstall
|
||||
}
|
||||
return filepath.Join(b.Root, "bin"), nil
|
||||
}
|
||||
if GOPATH != "" {
|
||||
return filepath.Join(strings.Split(GOPATH, ":")[0], "bin")
|
||||
return filepath.Join(strings.Split(GOPATH, ":")[0], "bin"), nil
|
||||
}
|
||||
return filepath.Join(os.Getenv("HOME"), "go", "bin")
|
||||
return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil
|
||||
}
|
||||
|
||||
// Clean clears up the temporary workspace
|
||||
|
@ -24,16 +24,22 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var baseDir string
|
||||
|
||||
func init() {
|
||||
baseDir, _ = os.Getwd()
|
||||
}
|
||||
|
||||
func TestNewDirParseInLegacyProject(t *testing.T) {
|
||||
workingDir := "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project"
|
||||
gopath, _ := filepath.Abs("../../tests/samples/simple_gopath_project")
|
||||
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
|
||||
gopath := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project")
|
||||
|
||||
os.Chdir(workingDir)
|
||||
fmt.Println(gopath)
|
||||
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
|
||||
b := NewInstall("", ".")
|
||||
b, _ := NewInstall("", ".")
|
||||
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
|
||||
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
|
||||
}
|
||||
@ -42,7 +48,7 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
|
||||
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
|
||||
}
|
||||
|
||||
b = NewBuild("", ".", "")
|
||||
b, _ = NewBuild("", ".", "")
|
||||
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
|
||||
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
|
||||
}
|
||||
@ -53,7 +59,7 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewDirParseInModProject(t *testing.T) {
|
||||
workingDir := "../../tests/samples/simple_project"
|
||||
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
|
||||
gopath := ""
|
||||
|
||||
os.Chdir(workingDir)
|
||||
@ -61,7 +67,7 @@ func TestNewDirParseInModProject(t *testing.T) {
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
|
||||
b := NewInstall("", ".")
|
||||
b, _ := NewInstall("", ".")
|
||||
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
|
||||
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
|
||||
}
|
||||
@ -70,7 +76,7 @@ func TestNewDirParseInModProject(t *testing.T) {
|
||||
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
|
||||
}
|
||||
|
||||
b = NewBuild("", ".", "")
|
||||
b, _ = NewBuild("", ".", "")
|
||||
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
|
||||
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
|
||||
}
|
||||
@ -82,15 +88,21 @@ func TestNewDirParseInModProject(t *testing.T) {
|
||||
|
||||
// Test #14
|
||||
func TestLegacyProjectNotInGoPATH(t *testing.T) {
|
||||
workingDir := "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project"
|
||||
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
|
||||
gopath := ""
|
||||
|
||||
os.Chdir(workingDir)
|
||||
fmt.Println(gopath)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
b := NewBuild("", ".", "")
|
||||
|
||||
b, _ := NewBuild("", ".", "")
|
||||
if b.OriGOPATH != b.NewGOPATH {
|
||||
t.Fatalf("New GOPATH should be same with old GOPATH, for this kind of project. New: %v, old: %v", b.NewGOPATH, b.OriGOPATH)
|
||||
}
|
||||
|
||||
_, err := os.Stat(filepath.Join(b.TmpDir, "main.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("There should be a main.go in temporary directory directly, the error: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ var _ = Describe("E2E", func() {
|
||||
testProjDir := filepath.Join(TESTS_ROOT, "samples/simple_project")
|
||||
cmd := exec.Command("goc", "build", "--debug")
|
||||
cmd.Dir = testProjDir
|
||||
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "goc build on this project should be successful", string(out))
|
||||
|
@ -22,4 +22,4 @@ chmod +x /home/runner/tools/e2e.test/e2e.test
|
||||
export PATH=/home/runner/tools/e2e.test:$PATH
|
||||
|
||||
cd e2e
|
||||
e2e.test ./...
|
||||
e2e.test -test.v ./...
|
Loading…
Reference in New Issue
Block a user