copy project to temp location
This commit is contained in:
parent
922340d8c4
commit
18c4c4a2da
12
cmd/build.go
12
cmd/build.go
@ -1,14 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/flag"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var buildCmd = &cobra.Command{
|
||||
Use: "build",
|
||||
Run: build,
|
||||
Run: buildAction,
|
||||
|
||||
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||
}
|
||||
@ -17,8 +16,7 @@ func init() {
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
}
|
||||
|
||||
func build(cmd *cobra.Command, args []string) {
|
||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args)
|
||||
where, buildName := flag.GetPackagesDir(remainedArgs)
|
||||
|
||||
func buildAction(cmd *cobra.Command, args []string) {
|
||||
b := build.NewBuild(cmd, args)
|
||||
b.Build()
|
||||
}
|
||||
|
7
go.mod
7
go.mod
@ -3,19 +3,14 @@ module github.com/qiniu/goc/v2
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/ugorji/go v1.1.4 // indirect
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
|
||||
github.com/tongjingran/copy v1.4.2 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
|
||||
k8s.io/kubectl v0.20.5 // indirect
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc // indirect
|
||||
)
|
||||
|
6
go.sum
6
go.sum
@ -299,6 +299,10 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
@ -376,6 +380,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tongjingran/copy v1.4.2 h1:faPaod07yG6Z+o1B52Vu1KTvRb8il5VDNKLprC1BmsE=
|
||||
github.com/tongjingran/copy v1.4.2/go.mod h1:Njma1OR5OuzB8pLAmQSzonHXzba+DDiPVmMSonpSpy4=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
|
36
pkg/build/build.go
Normal file
36
pkg/build/build.go
Normal file
@ -0,0 +1,36 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/flag"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Build struct a build
|
||||
// most configurations are stored in global variables: config.GocConfig & config.GoConfig
|
||||
type Build struct {
|
||||
}
|
||||
|
||||
// NewBuild creates a Build struct
|
||||
//
|
||||
// consumes args, get package dirs, read project meta info.
|
||||
func NewBuild(cmd *cobra.Command, args []string) *Build {
|
||||
b := &Build{}
|
||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args)
|
||||
flag.GetPackagesDir(remainedArgs)
|
||||
b.readProjectMetaInfo()
|
||||
b.displayProjectMetaInfo()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Build starts go build
|
||||
//
|
||||
// 1. copy project to temp,
|
||||
// 2. inject cover variables and functions into the project,
|
||||
// 3. build the project in temp.
|
||||
func (b *Build) Build() {
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
log.Donef("project copied to temporary directory")
|
||||
}
|
68
pkg/build/goenv.go
Normal file
68
pkg/build/goenv.go
Normal file
@ -0,0 +1,68 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/cover"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
// readProjectMetaInfo reads all meta informations of the corresponding project
|
||||
func (b *Build) readProjectMetaInfo() {
|
||||
// get gopath & gobin
|
||||
config.GocConfig.GOPATH = b.readGOPATH()
|
||||
config.GocConfig.GOBIN = b.readGOBIN()
|
||||
// 获取当前目录及其依赖的 package list
|
||||
config.GocConfig.Pkgs = cover.ListPackages(config.GocConfig.CurPkgDir)
|
||||
|
||||
// get mod info
|
||||
pkgs := config.GocConfig.Pkgs
|
||||
for _, pkg := range pkgs {
|
||||
// check if go modules is enabled
|
||||
if pkg.Module == nil {
|
||||
log.Fatalf("Go module is not enabled, please set GO111MODULE=auto or on")
|
||||
}
|
||||
// 工程根目录
|
||||
config.GocConfig.CurModProjectDir = pkg.Root
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// get tmp folder name
|
||||
config.GocConfig.TmpModProjectDir = filepath.Join(os.TempDir(), tmpFolderName(config.GocConfig.CurModProjectDir))
|
||||
// get cur pkg dir in the corresponding tmp dir
|
||||
config.GocConfig.TmpPkgDir = filepath.Join(config.GocConfig.TmpModProjectDir, config.GocConfig.CurPkgDir[len(config.GocConfig.CurModProjectDir):])
|
||||
log.Donef("project meta information parsed")
|
||||
}
|
||||
|
||||
// displayProjectMetaInfo prints basic infomation of this project to stdout
|
||||
func (b *Build) displayProjectMetaInfo() {
|
||||
log.Infof("Project Infomation")
|
||||
log.Infof("GOPATH: %v", config.GocConfig.GOPATH)
|
||||
log.Infof("GOBIN: %v", config.GocConfig.GOBIN)
|
||||
log.Infof("Project Directory: %v", config.GocConfig.CurModProjectDir)
|
||||
log.Infof("Temporary Project Directory: %v", config.GocConfig.TmpModProjectDir)
|
||||
log.Infof("")
|
||||
}
|
||||
|
||||
// readGOPATH reads GOPATH use go env GOPATH command
|
||||
func (b *Build) readGOPATH() string {
|
||||
out, err := exec.Command("go", "env", "GOPATH").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOPATH: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// readGOBIN reads GOBIN use go env GOBIN command
|
||||
func (b *Build) readGOBIN() string {
|
||||
out, err := exec.Command("go", "env", "GOBIN").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOBIN: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
75
pkg/build/tmpfolder.go
Normal file
75
pkg/build/tmpfolder.go
Normal file
@ -0,0 +1,75 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/tongjingran/copy"
|
||||
)
|
||||
|
||||
// copyProjectToTmp copies project files to the temporary directory
|
||||
//
|
||||
// It will ignore .git and irregular files, only copy source(text) files
|
||||
func (b *Build) copyProjectToTmp() {
|
||||
curProject := config.GocConfig.CurModProjectDir
|
||||
tmpProject := config.GocConfig.TmpModProjectDir
|
||||
|
||||
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
||||
log.Infof("find previous temporary directory, delete")
|
||||
err := os.RemoveAll(tmpProject)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to remove preivous temporary directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.StartWait("coping project")
|
||||
err := os.MkdirAll(tmpProject, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to create temporary directory: %v", err)
|
||||
}
|
||||
|
||||
// copy
|
||||
if err := copy.Copy(curProject, tmpProject, copy.Options{Skip: skipCopy}); err != nil {
|
||||
log.Fatalf("fail to copy the folder from %v to %v, the err: %v", curProject, tmpProject, err)
|
||||
}
|
||||
|
||||
log.StopWait()
|
||||
}
|
||||
|
||||
// tmpFolderName generates a directory name according to the path
|
||||
func tmpFolderName(path string) string {
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
|
||||
return "goc-build-" + h
|
||||
}
|
||||
|
||||
// skipCopy skip copy .git dir and irregular files
|
||||
func skipCopy(src string, info os.FileInfo) (bool, error) {
|
||||
irregularModeType := os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
|
||||
if strings.HasSuffix(src, "/.git") {
|
||||
log.Debugf("skip .git dir [%s]", src)
|
||||
return true, nil
|
||||
}
|
||||
if info.Mode()&irregularModeType != 0 {
|
||||
log.Debugf("skip file [%s], the file mode is [%s]", src, info.Mode().String())
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// clean clears the temporary project
|
||||
func (b *Build) clean() {
|
||||
if config.GocConfig.Debug != true {
|
||||
if err := os.RemoveAll(config.GocConfig.TmpModProjectDir); err != nil {
|
||||
log.Fatalf("fail to delete the temporary project: %v", config.GocConfig.TmpModProjectDir)
|
||||
}
|
||||
log.Donef("delete the temporary project")
|
||||
} else {
|
||||
log.Debugf("--debug is enabled, keep the temporary project")
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
type gocConfig struct {
|
||||
Debug bool
|
||||
CurPkgDir string
|
||||
@ -7,8 +9,13 @@ type gocConfig struct {
|
||||
TmpModProjectDir string
|
||||
TmpPkgDir string
|
||||
BinaryName string
|
||||
Pkgs map[string]*Package
|
||||
GOPATH string
|
||||
GOBIN string
|
||||
IsMod bool // deprecated
|
||||
}
|
||||
|
||||
// GocConfig 全局变量,存放 goc 的各种元属性
|
||||
var GocConfig gocConfig
|
||||
|
||||
type goConfig struct {
|
||||
@ -48,3 +55,72 @@ type goConfig struct {
|
||||
}
|
||||
|
||||
var GoConfig goConfig
|
||||
|
||||
// PackageCover holds all the generate coverage variables of a package
|
||||
type PackageCover struct {
|
||||
Package *Package
|
||||
Vars map[string]*FileVar
|
||||
}
|
||||
|
||||
// FileVar holds the name of the generated coverage variables targeting the named file.
|
||||
type FileVar struct {
|
||||
File string
|
||||
Var string
|
||||
}
|
||||
|
||||
// Package map a package output by go list
|
||||
// this is subset of package struct in: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L58
|
||||
type Package struct {
|
||||
Dir string `json:"Dir"` // directory containing package sources
|
||||
ImportPath string `json:"ImportPath"` // import path of package in dir
|
||||
Name string `json:"Name"` // package name
|
||||
Target string `json:",omitempty"` // installed target for this package (may be executable)
|
||||
Root string `json:",omitempty"` // Go root, Go path dir, or module root dir containing this package
|
||||
|
||||
Module *ModulePublic `json:",omitempty"` // info about package's module, if any
|
||||
Goroot bool `json:"Goroot,omitempty"` // is this package in the Go root?
|
||||
Standard bool `json:"Standard,omitempty"` // is this package part of the standard Go library?
|
||||
DepOnly bool `json:"DepOnly,omitempty"` // package is only a dependency, not explicitly listed
|
||||
|
||||
// Source files
|
||||
GoFiles []string `json:"GoFiles,omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
CgoFiles []string `json:"CgoFiles,omitempty"` // .go source files that import "C"
|
||||
|
||||
// Dependency information
|
||||
Deps []string `json:"Deps,omitempty"` // all (recursively) imported dependencies
|
||||
Imports []string `json:",omitempty"` // import paths used by this package
|
||||
ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted)
|
||||
|
||||
// Error information
|
||||
Incomplete bool `json:"Incomplete,omitempty"` // this package or a dependency has an error
|
||||
Error *PackageError `json:"Error,omitempty"` // error loading package
|
||||
DepsErrors []*PackageError `json:"DepsErrors,omitempty"` // errors loading dependencies
|
||||
}
|
||||
|
||||
// ModulePublic represents the package info of a module
|
||||
type ModulePublic struct {
|
||||
Path string `json:",omitempty"` // module path
|
||||
Version string `json:",omitempty"` // module version
|
||||
Versions []string `json:",omitempty"` // available module versions
|
||||
Replace *ModulePublic `json:",omitempty"` // replaced by this module
|
||||
Time *time.Time `json:",omitempty"` // time version was created
|
||||
Update *ModulePublic `json:",omitempty"` // available update (with -u)
|
||||
Main bool `json:",omitempty"` // is this the main module?
|
||||
Indirect bool `json:",omitempty"` // module is only indirectly needed by main module
|
||||
Dir string `json:",omitempty"` // directory holding local copy of files, if any
|
||||
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
|
||||
GoVersion string `json:",omitempty"` // go version used in module
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
}
|
||||
|
||||
// ModuleError represents the error loading module
|
||||
type ModuleError struct {
|
||||
Err string // error text
|
||||
}
|
||||
|
||||
// PackageError is the error info for a package when list failed
|
||||
type PackageError struct {
|
||||
ImportStack []string // shortest path from package named on command line to this one
|
||||
Pos string // position of error (if present, file:line:col)
|
||||
Err string // the error itself
|
||||
}
|
||||
|
56
pkg/cover/list.go
Normal file
56
pkg/cover/list.go
Normal file
@ -0,0 +1,56 @@
|
||||
package cover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCoverPkgFailed represents the error that fails to inject the package
|
||||
ErrCoverPkgFailed = errors.New("fail to inject code to project")
|
||||
// ErrCoverListFailed represents the error that fails to list package dependencies
|
||||
ErrCoverListFailed = errors.New("fail to list package dependencies")
|
||||
)
|
||||
|
||||
// ListPackages list all packages under specific via go list command.
|
||||
func ListPackages(dir string) map[string]*config.Package {
|
||||
cmd := exec.Command("go", "list", "-json", "./...")
|
||||
cmd.Dir = dir
|
||||
|
||||
var errBuf bytes.Buffer
|
||||
cmd.Stderr = &errBuf
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatalf("execute go list -json failed, err: %v, stdout: %v, stderr: %v", err, string(out), errBuf.String())
|
||||
}
|
||||
// 有些时候 go 命令会打印一些信息到 stderr,但其实命令整体是成功运行了
|
||||
if errBuf.String() != "" {
|
||||
log.Errorf("%v", errBuf.String())
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewBuffer(out))
|
||||
pkgs := make(map[string]*config.Package, 0)
|
||||
|
||||
for {
|
||||
var pkg config.Package
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatalf("reading go list output error: %v", err)
|
||||
}
|
||||
if pkg.Error != nil {
|
||||
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
|
||||
}
|
||||
|
||||
pkgs[pkg.ImportPath] = &pkg
|
||||
}
|
||||
|
||||
return pkgs
|
||||
}
|
@ -16,7 +16,8 @@ However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
// BuildCmdArgsParse parse both go flags and goc flags, it returns all non-flag arguments.
|
||||
// It will log fatal if error
|
||||
//
|
||||
// 吞下 [packages] 之前所有的 flags.
|
||||
func BuildCmdArgsParse(cmd *cobra.Command, args []string) []string {
|
||||
// 首先解析 cobra 定义的 flag
|
||||
allFlagSets := cmd.Flags()
|
||||
|
@ -6,17 +6,19 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
)
|
||||
|
||||
// GetPackagesDir parse [pacakges] part of args, it will fatal if error encountered
|
||||
//
|
||||
// Return 1: [packages] 所在的目录位置,供后续插桩使用。
|
||||
// 函数获取 1: [packages] 所在的目录位置,供后续插桩使用。
|
||||
//
|
||||
// Return 2: 如果参数是 *.go,第一个 .go 文件的文件名。go build 中,二进制名字既可能是目录名也可能是文件名,和参数类型有关。
|
||||
// 函数获取 2: 如果参数是 *.go,第一个 .go 文件的文件名。go build 中,二进制名字既可能是目录名也可能是文件名,和参数类型有关。
|
||||
//
|
||||
// 如果 [packages] 非法(即不符合 go 原生的定义),则返回对应错误
|
||||
// 这里只考虑 go mod 的方式
|
||||
func GetPackagesDir(patterns []string) (string, string) {
|
||||
func GetPackagesDir(patterns []string) {
|
||||
for _, p := range patterns {
|
||||
// patterns 只支持两种格式
|
||||
// 1. 要么是直接指向某些 .go 文件的相对/绝对路径
|
||||
@ -32,7 +34,12 @@ func GetPackagesDir(patterns []string) (string, string) {
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
return filepath.Dir(absp), filepath.Base(absp)
|
||||
|
||||
// 获取当前 [packages] 所在的目录位置,供后续插桩使用。
|
||||
config.GocConfig.CurPkgDir = filepath.Dir(absp)
|
||||
// 获取二进制名字
|
||||
config.GocConfig.BinaryName = filepath.Base(absp)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,7 +50,9 @@ func GetPackagesDir(patterns []string) (string, string) {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
return coverWd, ""
|
||||
config.GocConfig.CurPkgDir = coverWd
|
||||
config.GocConfig.BinaryName = ""
|
||||
return
|
||||
}
|
||||
|
||||
// goFilesPackage 对一组 go 文件解析,判断是否合法
|
||||
|
Loading…
Reference in New Issue
Block a user