Merge pull request #42 from lyyyuna/error-improve

Enhance error handling
This commit is contained in:
qiniu-bot 2020-06-18 23:06:24 +08:00 committed by GitHub
commit a5504c17d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 334 additions and 151 deletions

View File

@ -17,7 +17,9 @@
package cmd package cmd
import ( import (
"log" "os"
log "github.com/sirupsen/logrus"
"github.com/qiniu/goc/pkg/build" "github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover" "github.com/qiniu/goc/pkg/cover"
@ -28,7 +30,7 @@ var buildCmd = &cobra.Command{
Use: "build", Use: "build",
Short: "Do cover for all go files and execute go build command", Short: "Do cover for all go files and execute go build command",
Long: ` 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. Build command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target, binaries will be generated to their original place.
`, `,
Example: ` Example: `
# Build the current binary with cover variables injected. The binary will be generated in the current folder. # Build the current binary with cover variables injected. The binary will be generated in the current folder.
@ -44,7 +46,11 @@ goc build --output /to/this/path
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'" goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
runBuild() wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
runBuild(args, wd)
}, },
} }
@ -52,14 +58,14 @@ var buildOutput string
func init() { func init() {
addBuildFlags(buildCmd.Flags()) addBuildFlags(buildCmd.Flags())
buildCmd.Flags().StringVar(&buildOutput, "output", "", "it forces build to write the resulting executable or object to the named output file or directory") buildCmd.Flags().StringVar(&buildOutput, "output", "", "it forces build to write the resulting executable to the named output file")
rootCmd.AddCommand(buildCmd) rootCmd.AddCommand(buildCmd)
} }
func runBuild() { func runBuild(args []string, wd string) {
gocBuild, err := build.NewBuild(buildFlags, packages, buildOutput) gocBuild, err := build.NewBuild(buildFlags, args, wd, buildOutput)
if err != nil { if err != nil {
log.Fatalf("Fail to NewBuild: %v", err) log.Fatalf("Fail to build: %v", err)
} }
// remove temporary directory if needed // remove temporary directory if needed
defer gocBuild.Clean() defer gocBuild.Clean()

View File

@ -17,13 +17,14 @@
package cmd package cmd
import ( import (
"github.com/stretchr/testify/assert"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
var baseDir string var baseDir string
@ -38,14 +39,14 @@ func TestGeneratedBinary(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
gopath := "" gopath := ""
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput = "", ".", "" buildFlags, buildOutput = "", ""
runBuild() args := []string{"."}
runBuild(args, workingDir)
obj := filepath.Join(".", "simple-project") obj := filepath.Join(workingDir, "simple-project")
fInfo, err := os.Lstat(obj) fInfo, err := os.Lstat(obj)
assert.Equal(t, err, nil, "the binary should be generated.") 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") assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one")

View File

@ -11,7 +11,7 @@ var (
mode string mode string
debugGoc bool debugGoc bool
buildFlags string buildFlags string
packages string // packages string
appArgs string appArgs string
goRunExecFlag string goRunExecFlag string
@ -35,14 +35,12 @@ func addCommonFlags(cmdset *pflag.FlagSet) {
func addBuildFlags(cmdset *pflag.FlagSet) { func addBuildFlags(cmdset *pflag.FlagSet) {
addCommonFlags(cmdset) addCommonFlags(cmdset)
cmdset.StringVar(&packages, "packages", ".", "specify the package name, only . and ./... are supported")
// bind to viper // bind to viper
viper.BindPFlags(cmdset) viper.BindPFlags(cmdset)
} }
func addRunFlags(cmdset *pflag.FlagSet) { func addRunFlags(cmdset *pflag.FlagSet) {
addBuildFlags(cmdset) addBuildFlags(cmdset)
cmdset.Lookup("packages").Usage = "specify the package name, only ., ./... and *.go are supported"
cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments") cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments")
cmdset.StringVar(&goRunExecFlag, "exec", "", "same as -exec flag in 'go run' command") cmdset.StringVar(&goRunExecFlag, "exec", "", "same as -exec flag in 'go run' command")
cmdset.StringVar(&goRunArguments, "arguments", "", "same as 'arguments' in 'go run' command") cmdset.StringVar(&goRunArguments, "arguments", "", "same as 'arguments' in 'go run' command")

View File

@ -17,6 +17,8 @@
package cmd package cmd
import ( import (
"os"
"github.com/qiniu/goc/pkg/build" "github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover" "github.com/qiniu/goc/pkg/cover"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -27,7 +29,7 @@ var installCmd = &cobra.Command{
Use: "install", Use: "install",
Short: "Do cover for all go files and execute go install command", Short: "Do cover for all go files and execute go install command",
Long: ` 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. Install command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target, binaries will be generated to their original place.
`, `,
Example: ` Example: `
# Install all binaries with cover variables injected. The binary will be installed in $GOPATH/bin or $HOME/go/bin if directory existed. # Install all binaries with cover variables injected. The binary will be installed in $GOPATH/bin or $HOME/go/bin if directory existed.
@ -40,7 +42,11 @@ goc install --center=http://127.0.0.1:7777
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'" goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
runInstall() wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
runInstall(args, wd)
}, },
} }
@ -49,10 +55,10 @@ func init() {
rootCmd.AddCommand(installCmd) rootCmd.AddCommand(installCmd)
} }
func runInstall() { func runInstall(args []string, wd string) {
gocBuild, err := build.NewInstall(buildFlags, packages) gocBuild, err := build.NewInstall(buildFlags, args, wd)
if err != nil { if err != nil {
log.Fatalf("Fail to NewInstall: %v", err) log.Fatalf("Fail to install: %v", err)
} }
// remove temporary directory if needed // remove temporary directory if needed
defer gocBuild.Clean() defer gocBuild.Clean()

View File

@ -17,13 +17,14 @@
package cmd package cmd
import ( import (
"github.com/stretchr/testify/assert"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
func TestInstalledBinaryForMod(t *testing.T) { func TestInstalledBinaryForMod(t *testing.T) {
@ -32,12 +33,12 @@ func TestInstalledBinaryForMod(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome") gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput = "", ".", "" buildFlags, buildOutput = "", ""
runInstall() args := []string{"."}
runInstall(args, workingDir)
obj := filepath.Join(gopath, "bin", "simple-project") obj := filepath.Join(gopath, "bin", "simple-project")
fInfo, err := os.Lstat(obj) fInfo, err := os.Lstat(obj)
@ -60,12 +61,12 @@ func TestInstalledBinaryForLegacy(t *testing.T) {
workingDir := filepath.Join(baseDir, "../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 := filepath.Join(baseDir, "../tests/samples/simple_gopath_project") gopath := filepath.Join(baseDir, "../tests/samples/simple_gopath_project")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off") os.Setenv("GO111MODULE", "off")
buildFlags, packages, buildOutput = "", ".", "" buildFlags, buildOutput = "", ""
runInstall() args := []string{"."}
runInstall(args, workingDir)
obj := filepath.Join(gopath, "bin", "simple_gopath_project") obj := filepath.Join(gopath, "bin", "simple_gopath_project")
fInfo, err := os.Lstat(obj) fInfo, err := os.Lstat(obj)

View File

@ -42,6 +42,6 @@ goc list [flags]
} }
func init() { func init() {
listCmd.Flags().StringVarP(&center, "center", "", "http://127.0.0.1:7777", "cover profile host center") addBasicFlags(listCmd.Flags())
rootCmd.AddCommand(listCmd) rootCmd.AddCommand(listCmd)
} }

View File

@ -17,7 +17,6 @@
package cmd package cmd
import ( import (
"io/ioutil"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
@ -37,6 +36,7 @@ Find more information at:
`, `,
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetReportCaller(true) log.SetReportCaller(true)
log.SetLevel(log.InfoLevel)
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
FullTimestamp: true, FullTimestamp: true,
CallerPrettyfier: func(f *runtime.Frame) (string, string) { CallerPrettyfier: func(f *runtime.Frame) (string, string) {
@ -49,7 +49,13 @@ Find more information at:
}) })
if debugGoc == false { if debugGoc == false {
// we only need log in debug mode // we only need log in debug mode
log.SetOutput(ioutil.Discard) log.SetLevel(log.FatalLevel)
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
CallerPrettyfier: func(f *runtime.Frame) (string, string) {
return "", ""
},
})
} }
}, },
} }

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os"
"github.com/qiniu/goc/pkg/build" "github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover" "github.com/qiniu/goc/pkg/cover"
@ -36,7 +37,14 @@ It is exactly behave as 'go run .' in addition of some internal goc features.`,
goc run . goc run .
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
gocBuild, _ := build.NewBuild(buildFlags, packages, buildOutput) wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
gocBuild, err := build.NewBuild(buildFlags, args, wd, buildOutput)
if err != nil {
log.Fatalf("Fail to run: %v", err)
}
gocBuild.GoRunExecFlag = goRunExecFlag gocBuild.GoRunExecFlag = goRunExecFlag
gocBuild.GoRunArguments = goRunArguments gocBuild.GoRunArguments = goRunArguments
defer gocBuild.Clean() defer gocBuild.Clean()
@ -53,7 +61,9 @@ goc run .
// execute covers for the target source with original buildFlags and new GOPATH( tmp:original ) // execute covers for the target source with original buildFlags and new GOPATH( tmp:original )
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, gocServer) cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, gocServer)
gocBuild.Run() if err := gocBuild.Run(); err != nil {
log.Fatalf("Fail to run: %v", err)
}
}, },
} }

View File

@ -32,6 +32,7 @@ type Build struct {
Pkgs map[string]*cover.Package // Pkg list parsed from "go list -json ./..." command Pkgs map[string]*cover.Package // Pkg list parsed from "go list -json ./..." command
NewGOPATH string // the new GOPATH NewGOPATH string // the new GOPATH
OriGOPATH string // the original GOPATH OriGOPATH string // the original GOPATH
WorkingDir string // the working directory
TmpDir string // the temporary directory to build the project 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 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 IsMod bool // determine whether it is a Mod project
@ -53,17 +54,23 @@ type Build struct {
// NewBuild creates a Build struct which can build from goc temporary directory, // NewBuild creates a Build struct which can build from goc temporary directory,
// and generate binary in current working directory // and generate binary in current working directory
func NewBuild(buildflags string, packages string, outputDir string) (*Build, error) { func NewBuild(buildflags string, args []string, workingDir string, outputDir string) (*Build, error) {
if err := checkParameters(args, workingDir); err != nil {
return nil, err
}
// buildflags = buildflags + " -o " + outputDir // buildflags = buildflags + " -o " + outputDir
b := &Build{ b := &Build{
BuildFlags: buildflags, BuildFlags: buildflags,
Packages: packages, Packages: strings.Join(args, " "),
WorkingDir: workingDir,
} }
if false == b.validatePackageForBuild() { if false == b.validatePackageForBuild() {
log.Errorln(ErrWrongPackageTypeForBuild) log.Errorln(ErrWrongPackageTypeForBuild)
return nil, ErrWrongPackageTypeForBuild return nil, ErrWrongPackageTypeForBuild
} }
b.MvProjectsToTmp() if err := b.MvProjectsToTmp(); err != nil {
return nil, err
}
dir, err := b.determineOutputDir(outputDir) dir, err := b.determineOutputDir(outputDir)
b.Target = dir b.Target = dir
if err != nil { if err != nil {
@ -90,13 +97,13 @@ func (b *Build) Build() error {
err := cmd.Start() err := cmd.Start()
if err != nil { if err != nil {
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err) log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err) return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
} }
if err = cmd.Wait(); err != nil { if err = cmd.Wait(); err != nil {
log.Errorf("go build failed. The error is: %v", err) log.Errorf("go build failed. The error is: %v", err)
return fmt.Errorf("go build faileds: %w", err) return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
} }
log.Println("Go build exit successful.") log.Infoln("Go build exit successful.")
return nil return nil
} }
@ -107,57 +114,42 @@ func (b *Build) determineOutputDir(outputDir string) (string, error) {
log.Errorf("Can only be called after Build.MvProjectsToTmp(): %v", ErrWrongCallSequence) log.Errorf("Can only be called after Build.MvProjectsToTmp(): %v", ErrWrongCallSequence)
return "", fmt.Errorf("can only be called after Build.MvProjectsToTmp(): %w", ErrWrongCallSequence) return "", fmt.Errorf("can only be called after Build.MvProjectsToTmp(): %w", ErrWrongCallSequence)
} }
curWorkingDir, err := os.Getwd()
if err != nil {
log.Errorf("Cannot get current working directory: %v", err)
return "", fmt.Errorf("cannot get current working directory: %w", err)
}
if outputDir == "" { if outputDir == "" {
_, last := filepath.Split(curWorkingDir) _, last := filepath.Split(b.WorkingDir)
if b.IsMod { if b.IsMod {
// in mod, special rule // in mod, special rule
// replace "_" with "-" in the import path // replace "_" with "-" in the import path
last = strings.ReplaceAll(last, "_", "-") last = strings.ReplaceAll(last, "_", "-")
} }
return filepath.Join(curWorkingDir, last), nil return filepath.Join(b.WorkingDir, last), nil
} }
abs, err := filepath.Abs(outputDir) abs, err := filepath.Abs(outputDir)
if err != nil { if err != nil {
log.Errorf("Fail to transform the path: %v to absolute path: %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 "", err
} }
return abs, nil return abs, nil
} }
// validatePackageForBuild only allow . as package name // validatePackageForBuild only allow . as package name
func (b *Build) validatePackageForBuild() bool { func (b *Build) validatePackageForBuild() bool {
if b.Packages == "." { if b.Packages == "." || b.Packages == "" {
return true return true
} }
return false return false
} }
// Run excutes the main package in addition with the internal goc features func checkParameters(args []string, workingDir string) error {
func (b *Build) Run() { if len(args) > 1 {
cmd := exec.Command("/bin/bash", "-c", "go run "+b.BuildFlags+" "+b.GoRunExecFlag+" "+b.Packages+" "+b.GoRunArguments) log.Errorln(ErrTooManyArgs)
cmd.Dir = b.TmpWorkingDir return ErrTooManyArgs
if b.NewGOPATH != "" {
// Change to temp GOPATH for go install command
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
} }
log.Printf("go build cmd is: %v", cmd.Args) if workingDir == "" {
cmd.Stdout = os.Stdout return ErrInvalidWorkingDir
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
log.Fatalf("Fail to start command: %v. The error is: %v", cmd.Args, err)
}
if err = cmd.Wait(); err != nil {
log.Fatalf("Fail to execute command: %v. The error is: %v", cmd.Args, err)
} }
log.Infof("Working directory: %v", workingDir)
return nil
} }

View File

@ -17,10 +17,13 @@
package build package build
import ( import (
"github.com/stretchr/testify/assert" "errors"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestInvalidPackage(t *testing.T) { func TestInvalidPackage(t *testing.T) {
@ -28,26 +31,62 @@ func TestInvalidPackage(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := "" gopath := ""
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
_, err := NewBuild("", "example.com/simple-project", "") _, err := NewBuild("", []string{"example.com/simple-project"}, workingDir, "")
assert.Equal(t, err, ErrWrongPackageTypeForBuild, "the package name should be invalid") if !assert.Equal(t, err, ErrWrongPackageTypeForBuild) {
assert.FailNow(t, "the package name should be invalid")
}
} }
func TestBasicBuildForModProject(t *testing.T) { func TestBasicBuildForModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := "" gopath := ""
os.Chdir(workingDir) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
fmt.Println(workingDir)
buildFlags, args, buildOutput := "", []string{"."}, ""
gocBuild, err := NewBuild(buildFlags, args, workingDir, buildOutput)
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should create temporary directory successfully")
}
err = gocBuild.Build()
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "temporary directory should build successfully")
}
}
func TestCheckParameters(t *testing.T) {
err := checkParameters([]string{"aa", "bb"}, "aa")
assert.Equal(t, err, ErrTooManyArgs, "too many arguments should failed")
err = checkParameters([]string{"aa"}, "")
assert.Equal(t, err, ErrInvalidWorkingDir, "empty working directory should failed")
}
func TestDetermineOutputDir(t *testing.T) {
b := &Build{}
_, err := b.determineOutputDir("")
assert.Equal(t, errors.Is(err, ErrWrongCallSequence), true, "called before Build.MvProjectsToTmp() should fail")
b.TmpDir = "fake"
_, err = b.determineOutputDir("xx")
assert.Equal(t, err, nil, "should return a directory")
}
func TestInvalidPackageNameForBuild(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput := "", ".", "" buildFlags, packages := "", []string{"main.go"}
gocBuild, err := NewBuild(buildFlags, packages, buildOutput) _, err := NewBuild(buildFlags, packages, workingDir, "")
assert.Equal(t, err, nil, "should create temporary directory successfully") if !assert.Equal(t, err, ErrWrongPackageTypeForBuild) {
assert.FailNow(t, "should not success with non . or ./... package")
err = gocBuild.Build() }
assert.Equal(t, err, nil, "temporary directory should build successfully")
} }

View File

@ -9,6 +9,8 @@ var (
ErrGocShouldExecInProject = errors.New("goc not executed in project directory") ErrGocShouldExecInProject = errors.New("goc not executed in project directory")
ErrWrongPackageTypeForInstall = errors.New("packages only support \".\" and \"./...\"") ErrWrongPackageTypeForInstall = errors.New("packages only support \".\" and \"./...\"")
ErrWrongPackageTypeForBuild = errors.New("packages only support \".\"") ErrWrongPackageTypeForBuild = errors.New("packages only support \".\"")
ErrTooManyArgs = errors.New("too many args")
ErrInvalidWorkingDir = errors.New("the working directory is invalid")
ErrWrongCallSequence = errors.New("function should be called in a specified sequence") ErrWrongCallSequence = errors.New("function should be called in a specified sequence")
ErrNoplaceToInstall = errors.New("no go env") ErrNoplaceToInstall = errors.New("dont know where to install")
) )

View File

@ -20,21 +20,28 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"strings"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// NewInstall creates a Build struct which can install from goc temporary directory // NewInstall creates a Build struct which can install from goc temporary directory
func NewInstall(buildflags string, packages string) (*Build, error) { func NewInstall(buildflags string, args []string, workingDir string) (*Build, error) {
if err := checkParameters(args, workingDir); err != nil {
return nil, err
}
b := &Build{ b := &Build{
BuildFlags: buildflags, BuildFlags: buildflags,
Packages: packages, Packages: strings.Join(args, " "),
WorkingDir: workingDir,
} }
if false == b.validatePackageForInstall() { if false == b.validatePackageForInstall() {
log.Errorln(ErrWrongPackageTypeForInstall) log.Errorln(ErrWrongPackageTypeForInstall)
return nil, ErrWrongPackageTypeForInstall return nil, ErrWrongPackageTypeForInstall
} }
b.MvProjectsToTmp() if err := b.MvProjectsToTmp(); err != nil {
return nil, err
}
return b, nil return b, nil
} }
@ -61,18 +68,18 @@ func (b *Build) Install() error {
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err) log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err) return err
} }
if err = cmd.Wait(); err != nil { if err = cmd.Wait(); err != nil {
log.Errorf("go install failed. The error is: %v", err) log.Errorf("go install failed. The error is: %v", err)
return fmt.Errorf("go install failed: %w", err) return err
} }
log.Infof("Go install successful. Binary installed in: %v", whereToInstall) log.Infof("Go install successful. Binary installed in: %v", whereToInstall)
return nil return nil
} }
func (b *Build) validatePackageForInstall() bool { func (b *Build) validatePackageForInstall() bool {
if b.Packages == "." || b.Packages == "./..." { if b.Packages == "." || b.Packages == "" || b.Packages == "./..." {
return true return true
} }
return false return false

View File

@ -1,24 +1,42 @@
package build package build
import ( import (
"github.com/stretchr/testify/assert"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestBasicInstallForModProject(t *testing.T) { func TestBasicInstallForModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome") gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
buildFlags, packages := "", "." buildFlags, packages := "", []string{"."}
gocBuild, err := NewInstall(buildFlags, packages) gocBuild, err := NewInstall(buildFlags, packages, workingDir)
assert.Equal(t, err, nil, "should create temporary directory successfully") if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should create temporary directory successfully")
}
err = gocBuild.Install() err = gocBuild.Install()
assert.Equal(t, err, nil, "temporary directory should build successfully") if !assert.Equal(t, err, nil) {
assert.FailNow(t, "temporary directory should build successfully")
}
}
func TestInvalidPackageNameForInstall(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages := "", []string{"main.go"}
_, err := NewInstall(buildFlags, packages, workingDir)
if !assert.Equal(t, err, ErrWrongPackageTypeForInstall) {
assert.FailNow(t, "should not success with non . or ./... package")
}
} }

View File

@ -50,13 +50,6 @@ func (b *Build) cpLegacyProject() {
// only cp dependency in root(current gopath), // only cp dependency in root(current gopath),
// skip deps in other GOPATHs // skip deps in other GOPATHs
func (b *Build) cpDepPackages(pkg *cover.Package, visited map[string]bool) { func (b *Build) cpDepPackages(pkg *cover.Package, visited map[string]bool) {
/*
oriGOPATH := os.Getenv("GOPATH")
if oriGOPATH == "" {
oriGOPATH = filepath.Join(os.Getenv("HOME"), "go")
}
gopaths := strings.Split(oriGOPATH, ":")
*/
gopath := pkg.Root gopath := pkg.Root
for _, dep := range pkg.Deps { for _, dep := range pkg.Deps {
src := filepath.Join(gopath, "src", dep) src := filepath.Join(gopath, "src", dep)

52
pkg/build/run.go Normal file
View File

@ -0,0 +1,52 @@
/*
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 (
"fmt"
"os"
"os/exec"
log "github.com/sirupsen/logrus"
)
// Run excutes the main package in addition with the internal goc features
func (b *Build) Run() error {
cmd := exec.Command("/bin/bash", "-c", "go run "+b.BuildFlags+" "+b.GoRunExecFlag+" "+b.Packages+" "+b.GoRunArguments)
cmd.Dir = b.TmpWorkingDir
if b.NewGOPATH != "" {
// Change to temp GOPATH for go install command
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
}
log.Infof("go build cmd is: %v", cmd.Args)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
log.Errorf("Fail to start command: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
if err = cmd.Wait(); err != nil {
log.Errorf("Fail to go run: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
return nil
}

View File

@ -29,15 +29,24 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
func (b *Build) MvProjectsToTmp() { func (b *Build) MvProjectsToTmp() error {
listArgs := []string{"-json"} listArgs := []string{"-json"}
if len(b.BuildFlags) != 0 { if len(b.BuildFlags) != 0 {
listArgs = append(listArgs, b.BuildFlags) listArgs = append(listArgs, b.BuildFlags)
} }
listArgs = append(listArgs, "./...") listArgs = append(listArgs, "./...")
b.Pkgs = cover.ListPackages(".", strings.Join(listArgs, " "), "") var err error
b.Pkgs, err = cover.ListPackages(b.WorkingDir, strings.Join(listArgs, " "), "")
if err != nil {
log.Errorln(err)
return err
}
b.mvProjectsToTmp() err = b.mvProjectsToTmp()
if err != nil {
log.Errorf("Fail to move the project to temporary directory")
return err
}
b.OriGOPATH = os.Getenv("GOPATH") b.OriGOPATH = os.Getenv("GOPATH")
if b.IsMod == true { if b.IsMod == true {
b.NewGOPATH = "" b.NewGOPATH = ""
@ -53,22 +62,17 @@ func (b *Build) MvProjectsToTmp() {
if b.Root == "" && b.IsMod == false { if b.Root == "" && b.IsMod == false {
b.NewGOPATH = b.OriGOPATH b.NewGOPATH = b.OriGOPATH
} }
log.Printf("New GOPATH: %v", b.NewGOPATH) log.Infof("New GOPATH: %v", b.NewGOPATH)
return return nil
} }
func (b *Build) mvProjectsToTmp() error { func (b *Build) mvProjectsToTmp() error {
path, err := os.Getwd() b.TmpDir = filepath.Join(os.TempDir(), TmpFolderName(b.WorkingDir))
if err != nil {
log.Errorf("Cannot get current working directory, the error is: %v", err)
return err
}
b.TmpDir = filepath.Join(os.TempDir(), TmpFolderName(path))
// Delete previous tmp folder and its content // Delete previous tmp folder and its content
os.RemoveAll(b.TmpDir) os.RemoveAll(b.TmpDir)
// Create a new tmp folder // Create a new tmp folder
err = os.MkdirAll(filepath.Join(b.TmpDir, "src"), os.ModePerm) err := os.MkdirAll(filepath.Join(b.TmpDir, "src"), os.ModePerm)
if err != nil { if err != nil {
log.Errorf("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 return err
@ -77,6 +81,7 @@ func (b *Build) mvProjectsToTmp() error {
// traverse pkg list to get project meta info // traverse pkg list to get project meta info
b.IsMod, b.Root, err = b.traversePkgsList() b.IsMod, b.Root, err = b.traversePkgsList()
log.Infof("mod project? %v", b.IsMod)
if errors.Is(err, ErrShouldNotReached) { if errors.Is(err, ErrShouldNotReached) {
return fmt.Errorf("mvProjectsToTmp with a empty project: %w", err) return fmt.Errorf("mvProjectsToTmp with a empty project: %w", err)
} }
@ -84,7 +89,7 @@ func (b *Build) mvProjectsToTmp() error {
b.TmpWorkingDir, err = b.getTmpwd() b.TmpWorkingDir, err = b.getTmpwd()
if err != nil { if err != nil {
log.Errorf("fail to get workding directory in temporary directory: %v", err) 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) return fmt.Errorf("getTmpwd failed with error: %w", err)
} }
// issue #14 // issue #14
// if b.Root == "", then the project is non-standard project // if b.Root == "", then the project is non-standard project
@ -109,7 +114,7 @@ func TmpFolderName(path string) string {
sum := sha256.Sum256([]byte(path)) sum := sha256.Sum256([]byte(path))
h := fmt.Sprintf("%x", sum[:6]) h := fmt.Sprintf("%x", sum[:6])
return "goc-" + h return "goc-build-" + h
} }
// traversePkgsList travse the Build.Pkgs list // traversePkgsList travse the Build.Pkgs list
@ -128,7 +133,7 @@ func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
isMod = true isMod = true
return return
} }
log.Error("should not reach here") log.Error(ErrShouldNotReached)
err = ErrShouldNotReached err = ErrShouldNotReached
return return
} }
@ -137,19 +142,13 @@ func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
// and store it in the Build.tmpWorkdingDir // and store it in the Build.tmpWorkdingDir
func (b *Build) getTmpwd() (string, error) { func (b *Build) getTmpwd() (string, error) {
for _, pkg := range b.Pkgs { for _, pkg := range b.Pkgs {
path, err := os.Getwd()
if err != nil {
log.Errorf("cannot get current working directory: %v", err)
return "", fmt.Errorf("cannot get current working directory: %w", err)
}
index := -1 index := -1
var parentPath string var parentPath string
if b.IsMod == false { if b.IsMod == false {
index = strings.Index(path, pkg.Root) index = strings.Index(b.WorkingDir, pkg.Root)
parentPath = pkg.Root parentPath = pkg.Root
} else { } else {
index = strings.Index(path, pkg.Module.Dir) index = strings.Index(b.WorkingDir, pkg.Module.Dir)
parentPath = pkg.Module.Dir parentPath = pkg.Module.Dir
} }
@ -157,7 +156,7 @@ func (b *Build) getTmpwd() (string, error) {
return "", ErrGocShouldExecInProject return "", ErrGocShouldExecInProject
} }
// b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):]) // b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
return filepath.Join(b.TmpDir, path[len(parentPath):]), nil return filepath.Join(b.TmpDir, b.WorkingDir[len(parentPath):]), nil
} }
return "", ErrShouldNotReached return "", ErrShouldNotReached
@ -168,16 +167,14 @@ func (b *Build) findWhereToInstall() (string, error) {
return GOBIN, nil return GOBIN, nil
} }
// old GOPATH dir
GOPATH := os.Getenv("GOPATH")
if false == b.IsMod { if false == b.IsMod {
if b.Root == "" { if b.Root == "" {
return "", ErrNoplaceToInstall return "", ErrNoplaceToInstall
} }
return filepath.Join(b.Root, "bin"), nil return filepath.Join(b.Root, "bin"), nil
} }
if GOPATH != "" { if b.OriGOPATH != "" {
return filepath.Join(strings.Split(GOPATH, ":")[0], "bin"), nil return filepath.Join(strings.Split(b.OriGOPATH, ":")[0], "bin"), nil
} }
return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil
} }

View File

@ -34,12 +34,10 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../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 := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project") gopath := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off") os.Setenv("GO111MODULE", "off")
b, _ := NewInstall("", ".") b, _ := NewInstall("", []string{"."}, workingDir)
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) { if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir) t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
} }
@ -48,7 +46,7 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir) t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
} }
b, _ = NewBuild("", ".", "") b, _ = NewBuild("", []string{"."}, workingDir, "")
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) { if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir) t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
} }
@ -62,12 +60,11 @@ func TestNewDirParseInModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project") workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := "" gopath := ""
os.Chdir(workingDir)
fmt.Println(gopath) fmt.Println(gopath)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on") os.Setenv("GO111MODULE", "on")
b, _ := NewInstall("", ".") b, _ := NewInstall("", []string{"."}, workingDir)
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) { if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir) t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
} }
@ -76,7 +73,7 @@ func TestNewDirParseInModProject(t *testing.T) {
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir) t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
} }
b, _ = NewBuild("", ".", "") b, _ = NewBuild("", []string{"."}, workingDir, "")
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) { if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir) t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
} }
@ -91,12 +88,11 @@ func TestLegacyProjectNotInGoPATH(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../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 := "" gopath := ""
os.Chdir(workingDir)
fmt.Println(gopath) fmt.Println(gopath)
os.Setenv("GOPATH", gopath) os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off") os.Setenv("GO111MODULE", "off")
b, _ := NewBuild("", ".", "") b, _ := NewBuild("", []string{"."}, workingDir, "")
if b.OriGOPATH != b.NewGOPATH { 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) t.Fatalf("New GOPATH should be same with old GOPATH, for this kind of project. New: %v, old: %v", b.NewGOPATH, b.OriGOPATH)
} }

View File

@ -21,6 +21,7 @@ import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -37,6 +38,11 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
var (
ErrCoverPkgFailed = errors.New("fail to inject code to project")
ErrCoverListFailed = errors.New("fail to list package dependencies")
)
// TestCover is a collection of all counters // TestCover is a collection of all counters
type TestCover struct { type TestCover struct {
Mode string Mode string
@ -114,9 +120,10 @@ type PackageError struct {
} }
//Execute execute go tool cover for all the .go files in the target folder //Execute execute go tool cover for all the .go files in the target folder
func Execute(args, newGopath, target, mode, center string) { func Execute(args, newGopath, target, mode, center string) error {
if !isDirExist(target) { if !isDirExist(target) {
log.Fatalf("target directory %s not exist", target) log.Errorf("Target directory %s not exist", target)
return ErrCoverPkgFailed
} }
listArgs := []string{"-json"} listArgs := []string{"-json"}
@ -124,7 +131,11 @@ func Execute(args, newGopath, target, mode, center string) {
listArgs = append(listArgs, args) listArgs = append(listArgs, args)
} }
listArgs = append(listArgs, "./...") listArgs = append(listArgs, "./...")
pkgs := ListPackages(target, strings.Join(listArgs, " "), newGopath) pkgs, err := ListPackages(target, strings.Join(listArgs, " "), newGopath)
if err != nil {
log.Errorf("Fail to list all packages, the error: %v", err)
return err
}
var seen = make(map[string]*PackageCover) var seen = make(map[string]*PackageCover)
var seenCache = make(map[string]*PackageCover) var seenCache = make(map[string]*PackageCover)
@ -134,7 +145,8 @@ func Execute(args, newGopath, target, mode, center string) {
// inject the main package // inject the main package
mainCover, err := AddCounters(pkg, mode, newGopath) mainCover, err := AddCounters(pkg, mode, newGopath)
if err != nil { if err != nil {
log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err) log.Errorf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
return ErrCoverPkgFailed
} }
// new a testcover for this service // new a testcover for this service
@ -158,7 +170,7 @@ func Execute(args, newGopath, target, mode, center string) {
if hasInternalPath(dep) { if hasInternalPath(dep) {
//scan exist cache cover to tc.CacheCover //scan exist cache cover to tc.CacheCover
if cache, ok := seenCache[dep]; ok { if cache, ok := seenCache[dep]; ok {
log.Printf("cache cover exist: %s", cache.Package.ImportPath) log.Infof("cache cover exist: %s", cache.Package.ImportPath)
tc.CacheCover[cache.Package.Dir] = cache tc.CacheCover[cache.Package.Dir] = cache
continue continue
} }
@ -166,7 +178,8 @@ func Execute(args, newGopath, target, mode, center string) {
// add counter for internal package // add counter for internal package
inPkgCover, err := AddCounters(depPkg, mode, newGopath) inPkgCover, err := AddCounters(depPkg, mode, newGopath)
if err != nil { if err != nil {
log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err) log.Errorf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
return ErrCoverPkgFailed
} }
parentDir := getInternalParent(depPkg.Dir) parentDir := getInternalParent(depPkg.Dir)
parentImportPath := getInternalParent(depPkg.ImportPath) parentImportPath := getInternalParent(depPkg.ImportPath)
@ -212,7 +225,8 @@ func Execute(args, newGopath, target, mode, center string) {
packageCover, err := AddCounters(depPkg, mode, newGopath) packageCover, err := AddCounters(depPkg, mode, newGopath)
if err != nil { if err != nil {
log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err) log.Errorf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
return err
} }
tc.DepsCover = append(tc.DepsCover, packageCover) tc.DepsCover = append(tc.DepsCover, packageCover)
seen[dep] = packageCover seen[dep] = packageCover
@ -220,21 +234,25 @@ func Execute(args, newGopath, target, mode, center string) {
} }
if errs := InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 { if errs := InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 {
log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs) log.Errorf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
return ErrCoverPkgFailed
} }
// inject Http Cover APIs // inject Http Cover APIs
var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir) var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir)
if err := InjectCountersHandlers(tc, httpCoverApis); err != nil { if err := InjectCountersHandlers(tc, httpCoverApis); err != nil {
log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err) log.Errorf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
} return ErrCoverPkgFailed
} }
} }
} }
return nil
}
// ListPackages list all packages under specific via go list command // ListPackages list all packages under specific via go list command
// The argument newgopath is if you need to go list in a different GOPATH // The argument newgopath is if you need to go list in a different GOPATH
func ListPackages(dir string, args string, newgopath string) map[string]*Package { func ListPackages(dir string, args string, newgopath string) (map[string]*Package, error) {
cmd := exec.Command("/bin/bash", "-c", "go list "+args) cmd := exec.Command("/bin/bash", "-c", "go list "+args)
log.Printf("go list cmd is: %v", cmd.Args) log.Printf("go list cmd is: %v", cmd.Args)
cmd.Dir = dir cmd.Dir = dir
@ -243,7 +261,8 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
} }
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
log.Fatalf("excute `go list -json ./...` command failed, err: %v, out: %v", err, string(out)) log.Errorf("excute `go list -json ./...` command failed, err: %v, out: %v", err, string(out))
return nil, ErrCoverListFailed
} }
dec := json.NewDecoder(bytes.NewReader(out)) dec := json.NewDecoder(bytes.NewReader(out))
@ -254,10 +273,12 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
if err == io.EOF { if err == io.EOF {
break break
} }
log.Fatalf("reading go list output: %v", err) log.Errorf("reading go list output: %v", err)
return nil, ErrCoverListFailed
} }
if pkg.Error != nil { if pkg.Error != nil {
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error) log.Errorf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
return nil, ErrCoverPkgFailed
} }
// for _, err := range pkg.DepsErrors { // for _, err := range pkg.DepsErrors {
@ -266,7 +287,7 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
pkgs[pkg.ImportPath] = &pkg pkgs[pkg.ImportPath] = &pkg
} }
return pkgs return pkgs, nil
} }
// AddCounters add counters for all go files under the package // AddCounters add counters for all go files under the package

View File

@ -27,6 +27,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/otiai10/copy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -304,3 +305,40 @@ func TestFindInternal(t *testing.T) {
} }
} }
} }
func TestExecuteForSimpleModProject(t *testing.T) {
workingDir := "../../tests/samples/simple_project"
gopath := ""
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
testDir := filepath.Join(os.TempDir(), "goc-build-test")
copy.Copy(workingDir, testDir)
Execute("", gopath, testDir, "count", "http://127.0.0.1:7777")
_, err := os.Lstat(filepath.Join(testDir, "http_cover_apis_auto_generated.go"))
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should generate http_cover_apis_auto_generated.go")
}
}
func TestListPackagesForSimpleModProject(t *testing.T) {
workingDir := "../../tests/samples/simple_project"
gopath := ""
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
pkgs, _ := ListPackages(workingDir, "-json ./...", "")
if !assert.Equal(t, len(pkgs), 1) {
assert.FailNow(t, "should only have one pkg")
}
if pkg, ok := pkgs["example.com/simple-project"]; ok {
assert.Equal(t, pkg.Module.Path, "example.com/simple-project")
} else {
assert.FailNow(t, "cannot get the pkg: example.com/simple-project")
}
}