重构
1. 去掉全局的配置 config 2. 合并 pkg/build, pkg/flag, pkg/cover, pkg/config 几个包(这几个包有强相关性,适合放一处。并且分开也容易造成循环依赖)
This commit is contained in:
parent
d55c54d3a1
commit
456c883987
26
cmd/build.go
26
cmd/build.go
@ -2,8 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/build"
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/flag"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -14,15 +12,29 @@ var buildCmd = &cobra.Command{
|
||||
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||
}
|
||||
|
||||
var (
|
||||
gocmode string
|
||||
gochost string
|
||||
)
|
||||
|
||||
func init() {
|
||||
buildCmd.Flags().StringVarP(&config.GocConfig.Mode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
buildCmd.Flags().StringVarP(&config.GocConfig.Host, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
buildCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
buildCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
}
|
||||
|
||||
func buildAction(cmd *cobra.Command, args []string) {
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args, flag.GO_BUILD)
|
||||
b := build.NewBuild(remainedArgs)
|
||||
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
|
||||
b := build.NewBuild(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithBuild(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Build()
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/build"
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/flag"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -15,14 +13,23 @@ var installCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().StringVarP(&config.GocConfig.Mode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
installCmd.Flags().StringVarP(&config.GocConfig.Host, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
installCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
installCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func installAction(cmd *cobra.Command, args []string) {
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args, flag.GO_INSTALL)
|
||||
b := build.NewInstall(remainedArgs)
|
||||
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
|
||||
b := build.NewInstall(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithInstall(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Install()
|
||||
|
||||
}
|
||||
|
10
cmd/list.go
10
cmd/list.go
@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/client"
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -17,14 +16,17 @@ goc list [flags]
|
||||
Run: list,
|
||||
}
|
||||
|
||||
var listWide bool
|
||||
var (
|
||||
listHost string
|
||||
listWide bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
listCmd.Flags().StringVar(&config.GocConfig.Host, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
listCmd.Flags().StringVar(&listHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
listCmd.Flags().BoolVar(&listWide, "wide", false, "list all services with more information (such as pid)")
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
|
||||
func list(cmd *cobra.Command, args []string) {
|
||||
client.NewWorker("http://" + config.GocConfig.Host).ListAgents(listWide)
|
||||
client.NewWorker("http://" + listHost).ListAgents(listWide)
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/client"
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -19,14 +18,17 @@ goc profile --host=http://192.168.1.1:8080 --output=./coverage.cov
|
||||
Run: profile,
|
||||
}
|
||||
|
||||
var output string // --output flag
|
||||
var (
|
||||
profileHost string
|
||||
profileoutput string // --output flag
|
||||
)
|
||||
|
||||
func init() {
|
||||
profileCmd.Flags().StringVar(&config.GocConfig.Host, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
profileCmd.Flags().StringVarP(&output, "output", "o", "", "download cover profile")
|
||||
profileCmd.Flags().StringVar(&profileHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
profileCmd.Flags().StringVarP(&profileoutput, "output", "o", "", "download cover profile")
|
||||
rootCmd.AddCommand(profileCmd)
|
||||
}
|
||||
|
||||
func profile(cmd *cobra.Command, args []string) {
|
||||
client.NewWorker("http://" + config.GocConfig.Host).Profile(output)
|
||||
client.NewWorker("http://" + profileHost).Profile(profileoutput)
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -30,7 +29,7 @@ Find more information at:
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.DisplayGoc()
|
||||
// init logger
|
||||
log.NewLogger()
|
||||
log.NewLogger(globalDebug)
|
||||
},
|
||||
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
@ -38,8 +37,10 @@ Find more information at:
|
||||
},
|
||||
}
|
||||
|
||||
var globalDebug bool
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().BoolVar(&config.GocConfig.Debug, "gocdebug", false, "run goc in debug mode")
|
||||
rootCmd.PersistentFlags().BoolVar(&globalDebug, "gocdebug", false, "run goc in debug mode")
|
||||
}
|
||||
|
||||
// Execute the goc tool
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -15,13 +14,15 @@ var serverCmd = &cobra.Command{
|
||||
Run: serve,
|
||||
}
|
||||
|
||||
var (
|
||||
serverHost string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// serverCmd.Flags().IntVarP(&config.GocConfig.Port, "port", "", 7777, "listen port to start a coverage host center")
|
||||
// serverCmd.Flags().StringVarP(&config.GocConfig.StorePath, "storepath", "", "goc.store", "the file to save all goc server information")
|
||||
serverCmd.Flags().StringVarP(&config.GocConfig.Host, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
serverCmd.Flags().StringVarP(&serverHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
}
|
||||
|
||||
func serve(cmd *cobra.Command, args []string) {
|
||||
server.RunGocServerUntilExit(config.GocConfig.Host)
|
||||
server.RunGocServerUntilExit(serverHost)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
cli "github.com/qiniu/goc/v2/pkg/watch"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -15,11 +14,15 @@ var watchCmd = &cobra.Command{
|
||||
Run: watch,
|
||||
}
|
||||
|
||||
var (
|
||||
watchHost string
|
||||
)
|
||||
|
||||
func init() {
|
||||
watchCmd.Flags().StringVarP(&config.GocConfig.Host, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
watchCmd.Flags().StringVarP(&watchHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
rootCmd.AddCommand(watchCmd)
|
||||
}
|
||||
|
||||
func watch(cmd *cobra.Command, args []string) {
|
||||
cli.Watch()
|
||||
cli.Watch(watchHost)
|
||||
}
|
||||
|
@ -3,25 +3,52 @@ package build
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/cover"
|
||||
"github.com/qiniu/goc/v2/pkg/flag"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Build struct a build
|
||||
// most configurations are stored in global variables: config.GocConfig & config.GoConfig
|
||||
type Build struct {
|
||||
Args []string // all goc + go command line args + flags
|
||||
FlagSets *pflag.FlagSet
|
||||
BuildType int
|
||||
|
||||
Debug bool
|
||||
Host string
|
||||
Mode string // cover mode
|
||||
|
||||
GOPATH string
|
||||
GOBIN string
|
||||
CurWd string
|
||||
TmpWd string
|
||||
CurModProjectDir string
|
||||
TmpModProjectDir string
|
||||
|
||||
Goflags []string // go command line flags
|
||||
GoArgs []string // go command line args
|
||||
Packages []string // go command line [packages]
|
||||
|
||||
ImportPath string // the whole import path of the project
|
||||
Pkgs map[string]*Package
|
||||
GlobalCoverVarImportPath string
|
||||
GlobalCoverVarImportPathDir string
|
||||
}
|
||||
|
||||
// NewBuild creates a Build struct
|
||||
//
|
||||
func NewBuild(args []string) *Build {
|
||||
func NewBuild(opts ...GocOption) *Build {
|
||||
b := &Build{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
b.buildCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
flag.GetPackagesDir(args)
|
||||
b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo()
|
||||
// 4. 展示元信息
|
||||
@ -41,16 +68,19 @@ func (b *Build) Build() {
|
||||
defer b.clean()
|
||||
|
||||
log.Donef("project copied to temporary directory")
|
||||
// 2. inject cover vars
|
||||
cover.Inject()
|
||||
// 3. build in the temp project
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
// 4. build in the temp project
|
||||
b.doBuildInTemp()
|
||||
}
|
||||
|
||||
func (b *Build) doBuildInTemp() {
|
||||
log.StartWait("building the injected project")
|
||||
|
||||
goflags := config.GocConfig.Goflags
|
||||
goflags := b.Goflags
|
||||
// 检查用户是否自定义了 -o
|
||||
oSet := false
|
||||
for _, flag := range goflags {
|
||||
@ -61,10 +91,10 @@ func (b *Build) doBuildInTemp() {
|
||||
|
||||
// 如果没被设置就加一个至原命令执行的目录
|
||||
if !oSet {
|
||||
goflags = append(goflags, "-o", config.GocConfig.CurWd)
|
||||
goflags = append(goflags, "-o", b.CurWd)
|
||||
}
|
||||
|
||||
pacakges := config.GocConfig.Packages
|
||||
pacakges := b.Packages
|
||||
|
||||
goflags = append(goflags, pacakges...)
|
||||
|
||||
@ -72,11 +102,11 @@ func (b *Build) doBuildInTemp() {
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = config.GocConfig.TmpWd
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
log.Infof("go build cmd is: %v, in path [%v]", cmd.Args, cmd.Dir)
|
||||
log.Infof("go build cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go build: %v", err)
|
||||
}
|
||||
@ -88,3 +118,21 @@ func (b *Build) doBuildInTemp() {
|
||||
log.StopWait()
|
||||
log.Donef("go build done")
|
||||
}
|
||||
|
||||
// nicePrintArgs 优化 args 打印内容
|
||||
//
|
||||
// 假如:go build -ldflags "-X my/package/config.Version=1.0.0" -o /home/lyy/gitdown/gin-test/cmd .
|
||||
//
|
||||
// 实际输出会变为:go build -ldflags -X my/package/config.Version=1.0.0 -o /home/lyy/gitdown/gin-test/cmd .
|
||||
func nicePrintArgs(args []string) []string {
|
||||
output := make([]string, 0)
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, " ") {
|
||||
output = append(output, "\""+arg+"\"")
|
||||
} else {
|
||||
output = append(output, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
425
pkg/build/build_flags.go
Normal file
425
pkg/build/build_flags.go
Normal file
@ -0,0 +1,425 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var buildUsage string = `Usage:
|
||||
goc build [-o output] [build flags] [packages] [goc flags]
|
||||
|
||||
[build flags] are same with go official command, you can copy them here directly.
|
||||
|
||||
The [goc flags] can be placed in anywhere in the command line.
|
||||
However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
var installUsage string = `Usage:
|
||||
goc install [-o output] [build flags] [packages] [goc flags]
|
||||
|
||||
[build flags] are same with go official command, you can copy them here directly.
|
||||
|
||||
The [goc flags] can be placed in anywhere in the command line.
|
||||
However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
const (
|
||||
GO_BUILD = iota
|
||||
GO_INSTALL
|
||||
)
|
||||
|
||||
// CustomParseCmdAndArgs 因为关闭了 cobra 的解析功能,需要手动构造并解析 goc flags
|
||||
func CustomParseCmdAndArgs(cmd *cobra.Command, args []string) *pflag.FlagSet {
|
||||
// 首先解析 cobra 定义的 flag
|
||||
allFlagSets := cmd.Flags()
|
||||
// 因为 args 里面含有 go 的 flag,所以需要忽略解析 go flag 的错误
|
||||
allFlagSets.Init("GOC", pflag.ContinueOnError)
|
||||
// 忽略 go flag 在 goc 中的解析错误
|
||||
allFlagSets.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{
|
||||
UnknownFlags: true,
|
||||
}
|
||||
allFlagSets.Parse(args)
|
||||
|
||||
return allFlagSets
|
||||
}
|
||||
|
||||
// buildCmdArgsParse parse both go flags and goc flags, it rewrite go flags if
|
||||
// necessary, and returns all non-flag arguments.
|
||||
//
|
||||
// 吞下 [packages] 之前所有的 flags.
|
||||
func (b *Build) buildCmdArgsParse() {
|
||||
args := b.Args
|
||||
cmdType := b.BuildType
|
||||
allFlagSets := b.FlagSets
|
||||
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
|
||||
if helpFlag.Changed {
|
||||
if cmdType == GO_BUILD {
|
||||
printGoHelp(buildUsage)
|
||||
} else if cmdType == GO_INSTALL {
|
||||
printGoHelp(installUsage)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger(b.Debug)
|
||||
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
|
||||
// 然后解析 go 的 flag
|
||||
goFlagSets := flag.NewFlagSet("GO", flag.ContinueOnError)
|
||||
addBuildFlags(goFlagSets)
|
||||
addOutputFlags(goFlagSets)
|
||||
err := goFlagSets.Parse(args)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 找出设置的 go flag
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
flags := make([]string, 0)
|
||||
goFlagSets.Visit(func(f *flag.Flag) {
|
||||
// 将用户指定 -o 改成绝对目录
|
||||
if f.Name == "o" {
|
||||
outputDir := f.Value.String()
|
||||
outputDir, err := filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalf("output flag is not valid: %v", err)
|
||||
}
|
||||
flags = append(flags, "-o", outputDir)
|
||||
} else {
|
||||
flags = append(flags, "-"+f.Name, f.Value.String())
|
||||
}
|
||||
})
|
||||
|
||||
b.Goflags = flags
|
||||
b.CurWd = curWd
|
||||
b.GoArgs = goFlagSets.Args()
|
||||
return
|
||||
}
|
||||
|
||||
func findAndDelGocFlag(a []string, x string, v string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
x = "--" + x
|
||||
x_v := x + "=" + v
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] == "--gocdebug" {
|
||||
// debug 是 bool,就一个元素
|
||||
continue
|
||||
} else if a[i] == x {
|
||||
// 有 goc flag 长这样 --mode watch
|
||||
i++
|
||||
continue
|
||||
} else if a[i] == x_v {
|
||||
// 有 goc flag 长这样 --mode=watch
|
||||
continue
|
||||
} else {
|
||||
// 剩下的是 go flag
|
||||
new = append(new, a[i])
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
func findAndDelHelpFlag(a []string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
for _, v := range a {
|
||||
if v == "--help" || v == "-h" {
|
||||
continue
|
||||
} else {
|
||||
new = append(new, v)
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
type goConfig struct {
|
||||
BuildA bool
|
||||
BuildBuildmode string // -buildmode flag
|
||||
BuildMod string // -mod flag
|
||||
BuildModReason string // reason -mod flag is set, if set by default
|
||||
BuildI bool // -i flag
|
||||
BuildLinkshared bool // -linkshared flag
|
||||
BuildMSan bool // -msan flag
|
||||
BuildN bool // -n flag
|
||||
BuildO string // -o flag
|
||||
BuildP int // -p flag
|
||||
BuildPkgdir string // -pkgdir flag
|
||||
BuildRace bool // -race flag
|
||||
BuildToolexec string // -toolexec flag
|
||||
BuildToolchainName string
|
||||
BuildToolchainCompiler func() string
|
||||
BuildToolchainLinker func() string
|
||||
BuildTrimpath bool // -trimpath flag
|
||||
BuildV bool // -v flag
|
||||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
// from buildcontext
|
||||
Installsuffix string // -installSuffix
|
||||
BuildTags string // -tags
|
||||
// from load
|
||||
BuildAsmflags string
|
||||
BuildCompiler string
|
||||
BuildGcflags string
|
||||
BuildGccgoflags string
|
||||
BuildLdflags string
|
||||
|
||||
// mod related
|
||||
ModCacheRW bool
|
||||
ModFile string
|
||||
}
|
||||
|
||||
var goflags goConfig
|
||||
|
||||
func addBuildFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.BoolVar(&goflags.BuildA, "a", false, "")
|
||||
cmdSet.BoolVar(&goflags.BuildN, "n", false, "")
|
||||
cmdSet.IntVar(&goflags.BuildP, "p", 4, "")
|
||||
cmdSet.BoolVar(&goflags.BuildV, "v", false, "")
|
||||
cmdSet.BoolVar(&goflags.BuildX, "x", false, "")
|
||||
cmdSet.StringVar(&goflags.BuildBuildmode, "buildmode", "default", "")
|
||||
cmdSet.StringVar(&goflags.BuildMod, "mod", "", "")
|
||||
cmdSet.StringVar(&goflags.Installsuffix, "installsuffix", "", "")
|
||||
|
||||
// 类型和 go 原生的不一样,这里纯粹是为了 parse 并传递给 go
|
||||
cmdSet.StringVar(&goflags.BuildAsmflags, "asmflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildCompiler, "compiler", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGcflags, "gcflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGccgoflags, "gccgoflags", "", "")
|
||||
// mod related
|
||||
cmdSet.BoolVar(&goflags.ModCacheRW, "modcacherw", false, "")
|
||||
cmdSet.StringVar(&goflags.ModFile, "modfile", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildLdflags, "ldflags", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildLinkshared, "linkshared", false, "")
|
||||
cmdSet.StringVar(&goflags.BuildPkgdir, "pkgdir", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildRace, "race", false, "")
|
||||
cmdSet.BoolVar(&goflags.BuildMSan, "msan", false, "")
|
||||
cmdSet.StringVar(&goflags.BuildTags, "tags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildToolexec, "toolexec", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildTrimpath, "trimpath", false, "")
|
||||
cmdSet.BoolVar(&goflags.BuildWork, "work", false, "")
|
||||
}
|
||||
|
||||
func addOutputFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.StringVar(&goflags.BuildO, "o", "", "")
|
||||
}
|
||||
|
||||
func printGoHelp(usage string) {
|
||||
fmt.Println(usage)
|
||||
}
|
||||
|
||||
func printGocHelp(cmd *cobra.Command) {
|
||||
flags := cmd.LocalFlags()
|
||||
globalFlags := cmd.Parent().PersistentFlags()
|
||||
|
||||
fmt.Println("Flags:")
|
||||
fmt.Println(flags.FlagUsages())
|
||||
|
||||
fmt.Println("Global Flags:")
|
||||
fmt.Println(globalFlags.FlagUsages())
|
||||
}
|
||||
|
||||
// GetPackagesDir parse [pacakges] part of args, it will fatal if error encountered
|
||||
//
|
||||
// 函数获取 1: [packages] 所在的目录位置,供后续插桩使用。
|
||||
//
|
||||
// 函数获取 2: 如果参数是 *.go,第一个 .go 文件的文件名。go build 中,二进制名字既可能是目录名也可能是文件名,和参数类型有关。
|
||||
//
|
||||
// 如果 [packages] 非法(即不符合 go 原生的定义),则返回对应错误
|
||||
// 这里只考虑 go mod 的方式
|
||||
func (b *Build) getPackagesDir() {
|
||||
patterns := b.GoArgs
|
||||
packages := make([]string, 0)
|
||||
for _, p := range patterns {
|
||||
// patterns 只支持两种格式
|
||||
// 1. 要么是直接指向某些 .go 文件的相对/绝对路径
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
|
||||
// check if valid
|
||||
if err := goFilesPackage(patterns); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 获取相对于 current working directory 对路径
|
||||
for _, p := range patterns {
|
||||
if filepath.IsAbs(p) {
|
||||
relPath, err := filepath.Rel(b.CurWd, p)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get [packages] relative path from current working directory: %v", err)
|
||||
}
|
||||
packages = append(packages, relPath)
|
||||
} else {
|
||||
packages = append(packages, p)
|
||||
}
|
||||
}
|
||||
// fix: go build ./xx/main.go 需要转换为
|
||||
// go build ./xx/main.go ./xx/goc-cover-agent-apis-auto-generated-11111-22222-bridge.go
|
||||
dir := filepath.Dir(packages[0])
|
||||
packages = append(packages, filepath.Join(dir, "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"))
|
||||
b.Packages = packages
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 要么是 import path
|
||||
b.Packages = patterns
|
||||
}
|
||||
|
||||
// goFilesPackage 对一组 go 文件解析,判断是否合法
|
||||
// go 本身还判断语法上是否是同一个 package,goc 这里不做解析
|
||||
// 1. 都是 *.go 文件?
|
||||
// 2. *.go 文件都在同一个目录?
|
||||
// 3. *.go 文件存在?
|
||||
func goFilesPackage(gofiles []string) error {
|
||||
// 1. 必须都是 *.go 结尾
|
||||
for _, f := range gofiles {
|
||||
if !strings.HasSuffix(f, ".go") {
|
||||
return fmt.Errorf("named files must be .go files: %s", f)
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
for _, file := range gofiles {
|
||||
// 3. 文件都存在?
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2.1 有可能以 *.go 结尾的目录
|
||||
if fi.IsDir() {
|
||||
return fmt.Errorf("%s is a directory, should be a Go file", file)
|
||||
}
|
||||
|
||||
// 2.2 所有 *.go 必须在同一个目录内
|
||||
dir1, _ := filepath.Split(file)
|
||||
if dir1 == "" {
|
||||
dir1 = "./"
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
dir = dir1
|
||||
} else if dir != dir1 {
|
||||
return fmt.Errorf("named files must all be in one directory: have %s and %s", dir, dir1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDirFromImportPaths return the import path's real abs directory
|
||||
//
|
||||
// 该函数接收到的只有 dir 或 import path,file 在上一步已被排除
|
||||
// 只考虑 go modules 的情况
|
||||
func getDirFromImportPaths(patterns []string) (string, error) {
|
||||
// no import path, pattern = current wd
|
||||
if len(patterns) == 0 {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fail to parse import path: %w", err)
|
||||
}
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
// 为了简化插桩的逻辑,goc 对 import path 要求必须都在同一个目录
|
||||
// 所以干脆只允许一个 pattern 得了 -_-
|
||||
// 对于 goc build/run 来说本身就是只能在一个目录内
|
||||
// 对于 goc install 来讲,这个行为就和 go install 不同,不过多 import path 较少见 >_<,先忽略
|
||||
if len(patterns) > 1 {
|
||||
return "", fmt.Errorf("goc only support one import path now")
|
||||
}
|
||||
|
||||
pattern := patterns[0]
|
||||
switch {
|
||||
// case isLocalImport(pattern) || filepath.IsAbs(pattern):
|
||||
// dir1, err := filepath.Abs(pattern)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// if _, err := os.Stat(dir1); err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// return dir1, nil
|
||||
|
||||
case strings.Contains(pattern, "..."):
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := filepath.Split(pattern[:i])
|
||||
dir, _ = filepath.Abs(dir)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
return dir, nil
|
||||
|
||||
case strings.IndexByte(pattern, '@') > 0:
|
||||
return "", fmt.Errorf("import path with @ version query is not supported in goc")
|
||||
|
||||
case isMetaPackage(pattern):
|
||||
return "", fmt.Errorf("`std`, `cmd`, `all` import path is not supported by goc")
|
||||
|
||||
default: // 到这一步认为 pattern 是相对路径或者绝对路径
|
||||
dir1, err := filepath.Abs(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
if _, err := os.Stat(dir1); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
|
||||
return dir1, nil
|
||||
}
|
||||
}
|
||||
|
||||
// isLocalImport reports whether the import path is
|
||||
// a local import path, like ".", "..", "./foo", or "../foo"
|
||||
func isLocalImport(path string) bool {
|
||||
return path == "." || path == ".." ||
|
||||
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
|
||||
}
|
||||
|
||||
// isMetaPackage checks if the name is a reserved package name
|
||||
func isMetaPackage(name string) bool {
|
||||
return name == "std" || name == "cmd" || name == "all"
|
||||
}
|
||||
|
||||
// find direct path of current project which contains go.mod
|
||||
func findModuleRoot(dir string) string {
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// look for enclosing go.mod
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
49
pkg/build/config.go
Normal file
49
pkg/build/config.go
Normal file
@ -0,0 +1,49 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type GocOption func(*Build)
|
||||
|
||||
func WithHost(host string) GocOption {
|
||||
return func(b *Build) {
|
||||
b.Host = host
|
||||
}
|
||||
}
|
||||
|
||||
func WithMode(mode string) GocOption {
|
||||
return func(b *Build) {
|
||||
b.Mode = mode
|
||||
}
|
||||
}
|
||||
|
||||
func WithArgs(args []string) GocOption {
|
||||
return func(b *Build) {
|
||||
b.Args = args
|
||||
}
|
||||
}
|
||||
|
||||
func WithFlagSets(sets *pflag.FlagSet) GocOption {
|
||||
return func(b *Build) {
|
||||
b.FlagSets = sets
|
||||
}
|
||||
}
|
||||
|
||||
func WithBuild() GocOption {
|
||||
return func(b *Build) {
|
||||
b.BuildType = 0
|
||||
}
|
||||
}
|
||||
|
||||
func WithInstall() GocOption {
|
||||
return func(b *Build) {
|
||||
b.BuildType = 1
|
||||
}
|
||||
}
|
||||
|
||||
func WithDebug(enable bool) GocOption {
|
||||
return func(b *Build) {
|
||||
b.Debug = enable
|
||||
}
|
||||
}
|
@ -1,69 +1,48 @@
|
||||
package config
|
||||
package build
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
type gocConfig struct {
|
||||
Debug bool
|
||||
Host string
|
||||
Mode string // cover mode
|
||||
// declareCoverVars attaches the required cover variables names
|
||||
// to the files, to be used when annotating the files.
|
||||
func declareCoverVars(p *Package) map[string]*FileVar {
|
||||
coverVars := make(map[string]*FileVar)
|
||||
coverIndex := 0
|
||||
// We create the cover counters as new top-level variables in the package.
|
||||
// We need to avoid collisions with user variables (GoCover_0 is unlikely but still)
|
||||
// and more importantly with dot imports of other covered packages,
|
||||
// so we append 12 hex digits from the SHA-256 of the import path.
|
||||
// The point is only to avoid accidents, not to defeat users determined to
|
||||
// break things.
|
||||
sum := sha256.Sum256([]byte(p.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
for _, file := range p.GoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
GOPATH string
|
||||
GOBIN string
|
||||
CurWd string
|
||||
TmpWd string
|
||||
CurModProjectDir string
|
||||
TmpModProjectDir string
|
||||
for _, file := range p.CgoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
Goflags []string // command line flags
|
||||
Packages []string // command line [packages]
|
||||
|
||||
ImportPath string // the whole import path of the project
|
||||
Pkgs map[string]*Package
|
||||
GlobalCoverVarImportPath string
|
||||
GlobalCoverVarImportPathDir string
|
||||
return coverVars
|
||||
}
|
||||
|
||||
// GocConfig 全局变量,存放 goc 的各种元属性
|
||||
var GocConfig gocConfig
|
||||
|
||||
type goConfig struct {
|
||||
BuildA bool
|
||||
BuildBuildmode string // -buildmode flag
|
||||
BuildMod string // -mod flag
|
||||
BuildModReason string // reason -mod flag is set, if set by default
|
||||
BuildI bool // -i flag
|
||||
BuildLinkshared bool // -linkshared flag
|
||||
BuildMSan bool // -msan flag
|
||||
BuildN bool // -n flag
|
||||
BuildO string // -o flag
|
||||
BuildP int // -p flag
|
||||
BuildPkgdir string // -pkgdir flag
|
||||
BuildRace bool // -race flag
|
||||
BuildToolexec string // -toolexec flag
|
||||
BuildToolchainName string
|
||||
BuildToolchainCompiler func() string
|
||||
BuildToolchainLinker func() string
|
||||
BuildTrimpath bool // -trimpath flag
|
||||
BuildV bool // -v flag
|
||||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
// from buildcontext
|
||||
Installsuffix string // -installSuffix
|
||||
BuildTags string // -tags
|
||||
// from load
|
||||
BuildAsmflags string
|
||||
BuildCompiler string
|
||||
BuildGcflags string
|
||||
BuildGccgoflags string
|
||||
BuildLdflags string
|
||||
|
||||
// mod related
|
||||
ModCacheRW bool
|
||||
ModFile string
|
||||
}
|
||||
|
||||
var GoConfig goConfig
|
||||
|
||||
// PackageCover holds all the generate coverage variables of a package
|
||||
type PackageCover struct {
|
||||
Package *Package
|
@ -10,17 +10,16 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"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()
|
||||
b.GOPATH = b.readGOPATH()
|
||||
b.GOBIN = b.readGOBIN()
|
||||
// 获取 [packages] 及其依赖的 package list
|
||||
pkgs := b.listPackages(config.GocConfig.CurWd)
|
||||
pkgs := b.listPackages(b.CurWd)
|
||||
|
||||
// get mod info
|
||||
for _, pkg := range pkgs {
|
||||
@ -29,34 +28,34 @@ func (b *Build) readProjectMetaInfo() {
|
||||
log.Fatalf("Go module is not enabled, please set GO111MODULE=auto or on")
|
||||
}
|
||||
// 工程根目录
|
||||
config.GocConfig.CurModProjectDir = pkg.Module.Dir
|
||||
config.GocConfig.ImportPath = pkg.Module.Path
|
||||
b.CurModProjectDir = pkg.Module.Dir
|
||||
b.ImportPath = pkg.Module.Path
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// 如果当前目录不是工程根目录,那再次 go list 一次,获取整个工程的包信息
|
||||
if config.GocConfig.CurWd != config.GocConfig.CurModProjectDir {
|
||||
config.GocConfig.Pkgs = b.listPackages(config.GocConfig.CurModProjectDir)
|
||||
if b.CurWd != b.CurModProjectDir {
|
||||
b.Pkgs = b.listPackages(b.CurModProjectDir)
|
||||
} else {
|
||||
config.GocConfig.Pkgs = pkgs
|
||||
b.Pkgs = pkgs
|
||||
}
|
||||
|
||||
// get tmp folder name
|
||||
config.GocConfig.TmpModProjectDir = filepath.Join(os.TempDir(), TmpFolderName(config.GocConfig.CurModProjectDir))
|
||||
b.TmpModProjectDir = filepath.Join(os.TempDir(), TmpFolderName(b.CurModProjectDir))
|
||||
// get working dir in the corresponding tmp dir
|
||||
config.GocConfig.TmpWd = filepath.Join(config.GocConfig.TmpModProjectDir, config.GocConfig.CurWd[len(config.GocConfig.CurModProjectDir):])
|
||||
b.TmpWd = filepath.Join(b.TmpModProjectDir, b.CurWd[len(b.CurModProjectDir):])
|
||||
// get GlobalCoverVarImportPath
|
||||
config.GocConfig.GlobalCoverVarImportPath = path.Join(config.GocConfig.ImportPath, TmpFolderName(config.GocConfig.CurModProjectDir))
|
||||
b.GlobalCoverVarImportPath = path.Join(b.ImportPath, TmpFolderName(b.CurModProjectDir))
|
||||
log.Donef("project meta information parsed")
|
||||
}
|
||||
|
||||
// displayProjectMetaInfo prints basic infomation of this project to stdout
|
||||
func (b *Build) displayProjectMetaInfo() {
|
||||
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("GOPATH: %v", b.GOPATH)
|
||||
log.Infof("GOBIN: %v", b.GOBIN)
|
||||
log.Infof("Project Directory: %v", b.CurModProjectDir)
|
||||
log.Infof("Temporary Project Directory: %v", b.TmpModProjectDir)
|
||||
log.Infof("")
|
||||
}
|
||||
|
||||
@ -79,7 +78,7 @@ func (b *Build) readGOBIN() string {
|
||||
}
|
||||
|
||||
// listPackages list all packages under specific via go list command.
|
||||
func (b *Build) listPackages(dir string) map[string]*config.Package {
|
||||
func (b *Build) listPackages(dir string) map[string]*Package {
|
||||
cmd := exec.Command("go", "list", "-json", "./...")
|
||||
cmd.Dir = dir
|
||||
|
||||
@ -95,10 +94,10 @@ func (b *Build) listPackages(dir string) map[string]*config.Package {
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewBuffer(out))
|
||||
pkgs := make(map[string]*config.Package, 0)
|
||||
pkgs := make(map[string]*Package)
|
||||
|
||||
for {
|
||||
var pkg config.Package
|
||||
var pkg Package
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cover
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -6,30 +6,28 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/cover/internal/tool"
|
||||
"github.com/qiniu/goc/v2/pkg/cover/internal/websocket"
|
||||
"github.com/qiniu/goc/v2/pkg/build/internal/tool"
|
||||
"github.com/qiniu/goc/v2/pkg/build/internal/websocket"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
// Inject injects cover variables for all the .go files in the target directory
|
||||
func Inject() {
|
||||
func (b *Build) Inject() {
|
||||
log.StartWait("injecting cover variables")
|
||||
|
||||
var seen = make(map[string]*config.PackageCover)
|
||||
var seen = make(map[string]*PackageCover)
|
||||
|
||||
// 所有插桩变量定义声明
|
||||
allDecl := ""
|
||||
|
||||
pkgs := config.GocConfig.Pkgs
|
||||
pkgs := b.Pkgs
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Name == "main" {
|
||||
log.Infof("handle main package: %v", pkg.ImportPath)
|
||||
// 该 main 二进制所关联的所有插桩变量的元信息
|
||||
// 每个 main 之间是不相关的,需要重新定义
|
||||
allMainCovers := make([]*config.PackageCover, 0)
|
||||
allMainCovers := make([]*PackageCover, 0)
|
||||
// 注入 main package
|
||||
mainCover, mainDecl := addCounters(pkg)
|
||||
mainCover, mainDecl := b.addCounters(pkg)
|
||||
// 收集插桩变量的定义和元信息
|
||||
allDecl += mainDecl
|
||||
allMainCovers = append(allMainCovers, mainCover)
|
||||
@ -43,7 +41,7 @@ func Inject() {
|
||||
// 依赖需要忽略 Go 标准库和 go.mod 引入的第三方
|
||||
if depPkg, ok := pkgs[dep]; ok {
|
||||
// 注入依赖的 package
|
||||
packageCover, depDecl := addCounters(depPkg)
|
||||
packageCover, depDecl := b.addCounters(depPkg)
|
||||
// 收集插桩变量的定义和元信息
|
||||
allDecl += depDecl
|
||||
allMainCovers = append(allMainCovers, packageCover)
|
||||
@ -52,20 +50,25 @@ func Inject() {
|
||||
}
|
||||
}
|
||||
// 为每个 main 包注入 websocket handler
|
||||
injectGocAgent(getPkgTmpDir(pkg.Dir), allMainCovers)
|
||||
b.injectGocAgent(b.getPkgTmpDir(pkg.Dir), allMainCovers)
|
||||
if b.Mode == "watch" {
|
||||
log.Donef("inject main package [%v] with rpcagent and watchagent", pkg.ImportPath)
|
||||
} else {
|
||||
log.Donef("inject main package [%v] with rpcagent", pkg.ImportPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 在工程根目录注入所有插桩变量的声明+定义
|
||||
injectGlobalCoverVarFile(allDecl)
|
||||
b.injectGlobalCoverVarFile(allDecl)
|
||||
|
||||
// 添加自定义 websocket 依赖
|
||||
// 用户代码可能有 gorrila/websocket 的依赖,为避免依赖冲突,以及可能的 replace/vendor,
|
||||
// 这里直接注入一份完整的 gorrila/websocket 实现
|
||||
websocket.AddCustomWebsocketDep()
|
||||
websocket.AddCustomWebsocketDep(b.GlobalCoverVarImportPathDir)
|
||||
log.Donef("websocket library injected")
|
||||
|
||||
log.StopWait()
|
||||
log.Donef("cover variables injected")
|
||||
log.Donef("global cover variables injected")
|
||||
}
|
||||
|
||||
// addCounters is different from official go tool cover
|
||||
@ -75,18 +78,18 @@ func Inject() {
|
||||
// 2. no declarartions for these covervars
|
||||
//
|
||||
// 3. return the declarations as string
|
||||
func addCounters(pkg *config.Package) (*config.PackageCover, string) {
|
||||
mode := config.GocConfig.Mode
|
||||
gobalCoverVarImportPath := config.GocConfig.GlobalCoverVarImportPath
|
||||
func (b *Build) addCounters(pkg *Package) (*PackageCover, string) {
|
||||
mode := b.Mode
|
||||
gobalCoverVarImportPath := b.GlobalCoverVarImportPath
|
||||
|
||||
coverVarMap := declareCoverVars(pkg)
|
||||
|
||||
decl := ""
|
||||
for file, coverVar := range coverVarMap {
|
||||
decl += "\n" + tool.Annotate(filepath.Join(getPkgTmpDir(pkg.Dir), file), mode, coverVar.Var, coverVar.File, gobalCoverVarImportPath) + "\n"
|
||||
decl += "\n" + tool.Annotate(filepath.Join(b.getPkgTmpDir(pkg.Dir), file), mode, coverVar.Var, coverVar.File, gobalCoverVarImportPath) + "\n"
|
||||
}
|
||||
|
||||
return &config.PackageCover{
|
||||
return &PackageCover{
|
||||
Package: pkg,
|
||||
Vars: coverVarMap,
|
||||
}, decl
|
||||
@ -99,13 +102,13 @@ func addCounters(pkg *config.Package) (*config.PackageCover, string) {
|
||||
//
|
||||
// 在原工程目录已经做了一次 go list -json,在临时目录没有必要再做一遍,直接转换一下就能得到
|
||||
// 临时目录中的 pkg.Dir。
|
||||
func getPkgTmpDir(pkgDir string) string {
|
||||
relDir, err := filepath.Rel(config.GocConfig.CurModProjectDir, pkgDir)
|
||||
func (b *Build) getPkgTmpDir(pkgDir string) string {
|
||||
relDir, err := filepath.Rel(b.CurModProjectDir, pkgDir)
|
||||
if err != nil {
|
||||
log.Fatalf("go json -list meta info wrong: %v", err)
|
||||
}
|
||||
|
||||
return filepath.Join(config.GocConfig.TmpModProjectDir, relDir)
|
||||
return filepath.Join(b.TmpModProjectDir, relDir)
|
||||
}
|
||||
|
||||
// injectGocAgent inject handlers like following
|
||||
@ -121,7 +124,7 @@ func getPkgTmpDir(pkgDir string) string {
|
||||
//
|
||||
// 11111_22222_bridge.go 仅仅用于引用 11111_22222_package, where package contains ws agent main logic.
|
||||
// 使用 bridge.go 文件是为了避免插桩逻辑中的变量名污染 main 包
|
||||
func injectGocAgent(where string, covers []*config.PackageCover) {
|
||||
func (b *Build) injectGocAgent(where string, covers []*PackageCover) {
|
||||
injectPkgName := "goc-cover-agent-apis-auto-generated-11111-22222-package"
|
||||
injectBridgeName := "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"
|
||||
wherePkg := filepath.Join(where, injectPkgName)
|
||||
@ -159,22 +162,22 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
||||
defer f2.Close()
|
||||
|
||||
var _coverMode string
|
||||
if config.GocConfig.Mode == "watch" {
|
||||
if b.Mode == "watch" {
|
||||
_coverMode = "cover"
|
||||
} else {
|
||||
_coverMode = config.GocConfig.Mode
|
||||
_coverMode = b.Mode
|
||||
}
|
||||
tmplData := struct {
|
||||
Covers []*config.PackageCover
|
||||
Covers []*PackageCover
|
||||
GlobalCoverVarImportPath string
|
||||
Package string
|
||||
Host string
|
||||
Mode string
|
||||
}{
|
||||
Covers: covers,
|
||||
GlobalCoverVarImportPath: config.GocConfig.GlobalCoverVarImportPath,
|
||||
GlobalCoverVarImportPath: b.GlobalCoverVarImportPath,
|
||||
Package: injectPkgName,
|
||||
Host: config.GocConfig.Host,
|
||||
Host: b.Host,
|
||||
Mode: _coverMode,
|
||||
}
|
||||
|
||||
@ -183,7 +186,7 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
||||
}
|
||||
|
||||
// 写入 watch
|
||||
if config.GocConfig.Mode != "watch" {
|
||||
if b.Mode != "watch" {
|
||||
return
|
||||
}
|
||||
f, err := os.Create(filepath.Join(wherePkg, "watchagent.go"))
|
||||
@ -196,9 +199,9 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
||||
Host string
|
||||
GlobalCoverVarImportPath string
|
||||
}{
|
||||
Random: filepath.Base(config.GocConfig.TmpModProjectDir),
|
||||
Host: config.GocConfig.Host,
|
||||
GlobalCoverVarImportPath: config.GocConfig.GlobalCoverVarImportPath,
|
||||
Random: filepath.Base(b.TmpModProjectDir),
|
||||
Host: b.Host,
|
||||
GlobalCoverVarImportPath: b.GlobalCoverVarImportPath,
|
||||
}
|
||||
|
||||
if err := coverWatchTmpl.Execute(f, tmplwatchData); err != nil {
|
||||
@ -207,10 +210,10 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
||||
}
|
||||
|
||||
// injectGlobalCoverVarFile 写入所有插桩变量的全局定义至一个单独的文件
|
||||
func injectGlobalCoverVarFile(decl string) {
|
||||
globalCoverVarPackage := path.Base(config.GocConfig.GlobalCoverVarImportPath)
|
||||
globalCoverDef := filepath.Join(config.GocConfig.TmpModProjectDir, globalCoverVarPackage)
|
||||
config.GocConfig.GlobalCoverVarImportPathDir = globalCoverDef
|
||||
func (b *Build) injectGlobalCoverVarFile(decl string) {
|
||||
globalCoverVarPackage := path.Base(b.GlobalCoverVarImportPath)
|
||||
globalCoverDef := filepath.Join(b.TmpModProjectDir, globalCoverVarPackage)
|
||||
b.GlobalCoverVarImportPathDir = globalCoverDef
|
||||
|
||||
err := os.MkdirAll(globalCoverDef, os.ModePerm)
|
||||
if err != nil {
|
||||
@ -225,7 +228,7 @@ func injectGlobalCoverVarFile(decl string) {
|
||||
|
||||
packageName := "package coverdef\n\n"
|
||||
|
||||
random := filepath.Base(config.GocConfig.TmpModProjectDir)
|
||||
random := filepath.Base(b.TmpModProjectDir)
|
||||
varWatchDef := fmt.Sprintf(`
|
||||
var WatchChannel_%v = make(chan *blockInfo, 1024)
|
||||
|
@ -4,13 +4,11 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/cover"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
func NewInstall(args []string) *Build {
|
||||
return NewBuild(args)
|
||||
func NewInstall(opts ...GocOption) *Build {
|
||||
return NewBuild(opts...)
|
||||
}
|
||||
|
||||
// Install starts go install
|
||||
@ -24,18 +22,21 @@ func (b *Build) Install() {
|
||||
defer b.clean()
|
||||
|
||||
log.Donef("project copied to temporary directory")
|
||||
// 2. inject cover vars
|
||||
cover.Inject()
|
||||
// 3. install in the temp project
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
// 4. install in the temp project
|
||||
b.doInstallInTemp()
|
||||
}
|
||||
|
||||
func (b *Build) doInstallInTemp() {
|
||||
log.StartWait("installing the injected project")
|
||||
|
||||
goflags := config.GocConfig.Goflags
|
||||
goflags := b.Goflags
|
||||
|
||||
pacakges := config.GocConfig.Packages
|
||||
pacakges := b.Packages
|
||||
|
||||
goflags = append(goflags, pacakges...)
|
||||
|
||||
@ -43,7 +44,7 @@ func (b *Build) doInstallInTemp() {
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go install [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = config.GocConfig.TmpWd
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
@ -19,7 +18,7 @@ var depTarFile embed.FS
|
||||
//
|
||||
// 从 embed 文件系统中解压 websocket.tar 文件,并依次写入临时工程中,作为一个单独的包存在。
|
||||
// gorrila/websocket 是一个无第三方依赖的库,因此其位置可以随处移动,而不影响自身的编译。
|
||||
func AddCustomWebsocketDep() {
|
||||
func AddCustomWebsocketDep(customWebsocketPath string) {
|
||||
data, err := depTarFile.ReadFile("websocket.tar")
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find the websocket.tar in the embed file: %v", err)
|
||||
@ -36,7 +35,6 @@ func AddCustomWebsocketDep() {
|
||||
log.Fatalf("cannot untar the websocket.tar: %v", err)
|
||||
}
|
||||
|
||||
customWebsocketPath := config.GocConfig.GlobalCoverVarImportPathDir
|
||||
fpath := filepath.Join(customWebsocketPath, hdr.Name)
|
||||
if hdr.FileInfo().IsDir() {
|
||||
// 处理目录
|
@ -1,4 +1,4 @@
|
||||
package cover
|
||||
package build
|
||||
|
||||
import (
|
||||
_ "embed"
|
@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/tongjingran/copy"
|
||||
"golang.org/x/mod/modfile"
|
||||
@ -18,8 +17,8 @@ import (
|
||||
//
|
||||
// It will ignore .git and irregular files, only copy source(text) files
|
||||
func (b *Build) copyProjectToTmp() {
|
||||
curProject := config.GocConfig.CurModProjectDir
|
||||
tmpProject := config.GocConfig.TmpModProjectDir
|
||||
curProject := b.CurModProjectDir
|
||||
tmpProject := b.TmpModProjectDir
|
||||
|
||||
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
||||
log.Infof("find previous temporary directory, delete")
|
||||
@ -67,8 +66,8 @@ func skipCopy(src string, info os.FileInfo) (bool, error) {
|
||||
|
||||
// clean clears the temporary project
|
||||
func (b *Build) clean() {
|
||||
if config.GocConfig.Debug != true {
|
||||
if err := os.RemoveAll(config.GocConfig.TmpModProjectDir); err != nil {
|
||||
if !b.Debug {
|
||||
if err := os.RemoveAll(b.TmpModProjectDir); err != nil {
|
||||
log.Fatalf("fail to delete the temporary project: %v", err)
|
||||
}
|
||||
log.Donef("delete the temporary project")
|
||||
@ -90,7 +89,7 @@ func (b *Build) clean() {
|
||||
// after the project is copied to temporary directory, it should be rewritten as
|
||||
// 'replace github.com/qiniu/bar => /path/to/aa/bb/home/foo/bar'
|
||||
func (b *Build) updateGoModFile() (updateFlag bool, newModFile []byte) {
|
||||
tempModfile := filepath.Join(config.GocConfig.TmpModProjectDir, "go.mod")
|
||||
tempModfile := filepath.Join(b.TmpModProjectDir, "go.mod")
|
||||
buf, err := ioutil.ReadFile(tempModfile)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find go.mod file in temporary directory: %v", err)
|
||||
@ -111,7 +110,7 @@ func (b *Build) updateGoModFile() (updateFlag bool, newModFile []byte) {
|
||||
// absolute path no need to rewrite
|
||||
if newVersion == "" && !filepath.IsAbs(newPath) {
|
||||
var absPath string
|
||||
fullPath := filepath.Join(config.GocConfig.CurModProjectDir, newPath)
|
||||
fullPath := filepath.Join(b.CurModProjectDir, newPath)
|
||||
absPath, _ = filepath.Abs(fullPath)
|
||||
// DropReplace & AddReplace will not return error
|
||||
// so no need to check the error
|
||||
@ -126,5 +125,13 @@ func (b *Build) updateGoModFile() (updateFlag bool, newModFile []byte) {
|
||||
// return Format(f.Syntax), nil
|
||||
// }
|
||||
newModFile, _ = oriGoModFile.Format()
|
||||
|
||||
if updateFlag {
|
||||
log.Infof("go.mod needs rewrite")
|
||||
err := os.WriteFile(tempModfile, newModFile, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to update go.mod: %v", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
package cover
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
)
|
||||
|
||||
// declareCoverVars attaches the required cover variables names
|
||||
// to the files, to be used when annotating the files.
|
||||
func declareCoverVars(p *config.Package) map[string]*config.FileVar {
|
||||
coverVars := make(map[string]*config.FileVar)
|
||||
coverIndex := 0
|
||||
// We create the cover counters as new top-level variables in the package.
|
||||
// We need to avoid collisions with user variables (GoCover_0 is unlikely but still)
|
||||
// and more importantly with dot imports of other covered packages,
|
||||
// so we append 12 hex digits from the SHA-256 of the import path.
|
||||
// The point is only to avoid accidents, not to defeat users determined to
|
||||
// break things.
|
||||
sum := sha256.Sum256([]byte(p.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
for _, file := range p.GoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &config.FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
for _, file := range p.CgoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &config.FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
return coverVars
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var buildUsage string = `Usage:
|
||||
goc build [-o output] [build flags] [packages] [goc flags]
|
||||
|
||||
[build flags] are same with go official command, you can copy them here directly.
|
||||
|
||||
The [goc flags] can be placed in anywhere in the command line.
|
||||
However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
var installUsage string = `Usage:
|
||||
goc install [-o output] [build flags] [packages] [goc flags]
|
||||
|
||||
[build flags] are same with go official command, you can copy them here directly.
|
||||
|
||||
The [goc flags] can be placed in anywhere in the command line.
|
||||
However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
const (
|
||||
GO_BUILD = iota
|
||||
GO_INSTALL
|
||||
)
|
||||
|
||||
// BuildCmdArgsParse parse both go flags and goc flags, it rewrite go flags if
|
||||
// necessary, and returns all non-flag arguments.
|
||||
//
|
||||
// 吞下 [packages] 之前所有的 flags.
|
||||
func BuildCmdArgsParse(cmd *cobra.Command, args []string, cmdType int) []string {
|
||||
// 首先解析 cobra 定义的 flag
|
||||
allFlagSets := cmd.Flags()
|
||||
// 因为 args 里面含有 go 的 flag,所以需要忽略解析 go flag 的错误
|
||||
allFlagSets.Init("GOC", pflag.ContinueOnError)
|
||||
// 忽略 go flag 在 goc 中的解析错误
|
||||
allFlagSets.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{
|
||||
UnknownFlags: true,
|
||||
}
|
||||
allFlagSets.Parse(args)
|
||||
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
|
||||
if helpFlag.Changed {
|
||||
if cmdType == GO_BUILD {
|
||||
printHelp(buildUsage, cmd)
|
||||
} else if cmdType == GO_INSTALL {
|
||||
printHelp(installUsage, cmd)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger()
|
||||
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
|
||||
// 然后解析 go 的 flag
|
||||
goFlagSets := flag.NewFlagSet("GO", flag.ContinueOnError)
|
||||
addBuildFlags(goFlagSets)
|
||||
addOutputFlags(goFlagSets)
|
||||
err := goFlagSets.Parse(args)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 找出设置的 go flag
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
config.GocConfig.CurWd = curWd
|
||||
flags := make([]string, 0)
|
||||
goFlagSets.Visit(func(f *flag.Flag) {
|
||||
// 将用户指定 -o 改成绝对目录
|
||||
if f.Name == "o" {
|
||||
outputDir := f.Value.String()
|
||||
outputDir, err := filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalf("output flag is not valid: %v", err)
|
||||
}
|
||||
flags = append(flags, "-o", outputDir)
|
||||
} else {
|
||||
flags = append(flags, "-"+f.Name, f.Value.String())
|
||||
}
|
||||
})
|
||||
|
||||
config.GocConfig.Goflags = flags
|
||||
|
||||
return goFlagSets.Args()
|
||||
}
|
||||
|
||||
func findAndDelGocFlag(a []string, x string, v string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
x = "--" + x
|
||||
x_v := x + "=" + v
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] == "--gocdebug" {
|
||||
// debug 是 bool,就一个元素
|
||||
continue
|
||||
} else if a[i] == x {
|
||||
// 有 goc flag 长这样 --mode watch
|
||||
i++
|
||||
continue
|
||||
} else if a[i] == x_v {
|
||||
// 有 goc flag 长这样 --mode=watch
|
||||
continue
|
||||
} else {
|
||||
// 剩下的是 go flag
|
||||
new = append(new, a[i])
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
func findAndDelHelpFlag(a []string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
for _, v := range a {
|
||||
if v == "--help" || v == "-h" {
|
||||
continue
|
||||
} else {
|
||||
new = append(new, v)
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
)
|
||||
|
||||
func addBuildFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildA, "a", false, "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildN, "n", false, "")
|
||||
cmdSet.IntVar(&config.GoConfig.BuildP, "p", 4, "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildV, "v", false, "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildX, "x", false, "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildBuildmode, "buildmode", "default", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildMod, "mod", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.Installsuffix, "installsuffix", "", "")
|
||||
|
||||
// 类型和 go 原生的不一样,这里纯粹是为了 parse 并传递给 go
|
||||
cmdSet.StringVar(&config.GoConfig.BuildAsmflags, "asmflags", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildCompiler, "compiler", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildGcflags, "gcflags", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildGccgoflags, "gccgoflags", "", "")
|
||||
// mod related
|
||||
cmdSet.BoolVar(&config.GoConfig.ModCacheRW, "modcacherw", false, "")
|
||||
cmdSet.StringVar(&config.GoConfig.ModFile, "modfile", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildLdflags, "ldflags", "", "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildLinkshared, "linkshared", false, "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildPkgdir, "pkgdir", "", "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildRace, "race", false, "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildMSan, "msan", false, "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildTags, "tags", "", "")
|
||||
cmdSet.StringVar(&config.GoConfig.BuildToolexec, "toolexec", "", "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildTrimpath, "trimpath", false, "")
|
||||
cmdSet.BoolVar(&config.GoConfig.BuildWork, "work", false, "")
|
||||
}
|
||||
|
||||
func addOutputFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.StringVar(&config.GoConfig.BuildO, "o", "", "")
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func printHelp(usage string, cmd *cobra.Command) {
|
||||
fmt.Println(usage)
|
||||
|
||||
flags := cmd.LocalFlags()
|
||||
globalFlags := cmd.Parent().PersistentFlags()
|
||||
|
||||
fmt.Println("Flags:")
|
||||
fmt.Println(flags.FlagUsages())
|
||||
|
||||
fmt.Println("Global Flags:")
|
||||
fmt.Println(globalFlags.FlagUsages())
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
// GetPackagesDir parse [pacakges] part of args, it will fatal if error encountered
|
||||
//
|
||||
// 函数获取 1: [packages] 所在的目录位置,供后续插桩使用。
|
||||
//
|
||||
// 函数获取 2: 如果参数是 *.go,第一个 .go 文件的文件名。go build 中,二进制名字既可能是目录名也可能是文件名,和参数类型有关。
|
||||
//
|
||||
// 如果 [packages] 非法(即不符合 go 原生的定义),则返回对应错误
|
||||
// 这里只考虑 go mod 的方式
|
||||
func GetPackagesDir(patterns []string) {
|
||||
packages := make([]string, 0)
|
||||
for _, p := range patterns {
|
||||
// patterns 只支持两种格式
|
||||
// 1. 要么是直接指向某些 .go 文件的相对/绝对路径
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
|
||||
// check if valid
|
||||
if err := goFilesPackage(patterns); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 获取相对于 current working directory 对路径
|
||||
for _, p := range patterns {
|
||||
if filepath.IsAbs(p) {
|
||||
relPath, err := filepath.Rel(config.GocConfig.CurWd, p)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get [packages] relative path from current working directory: %v", err)
|
||||
}
|
||||
packages = append(packages, relPath)
|
||||
} else {
|
||||
packages = append(packages, p)
|
||||
}
|
||||
}
|
||||
// fix: go build ./xx/main.go 需要转换为
|
||||
// go build ./xx/main.go ./xx/goc-cover-agent-apis-auto-generated-11111-22222-bridge.go
|
||||
dir := filepath.Dir(packages[0])
|
||||
packages = append(packages, filepath.Join(dir, "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"))
|
||||
config.GocConfig.Packages = packages
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 要么是 import path
|
||||
config.GocConfig.Packages = patterns
|
||||
}
|
||||
|
||||
// goFilesPackage 对一组 go 文件解析,判断是否合法
|
||||
// go 本身还判断语法上是否是同一个 package,goc 这里不做解析
|
||||
// 1. 都是 *.go 文件?
|
||||
// 2. *.go 文件都在同一个目录?
|
||||
// 3. *.go 文件存在?
|
||||
func goFilesPackage(gofiles []string) error {
|
||||
// 1. 必须都是 *.go 结尾
|
||||
for _, f := range gofiles {
|
||||
if !strings.HasSuffix(f, ".go") {
|
||||
return fmt.Errorf("named files must be .go files: %s", f)
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
for _, file := range gofiles {
|
||||
// 3. 文件都存在?
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2.1 有可能以 *.go 结尾的目录
|
||||
if fi.IsDir() {
|
||||
return fmt.Errorf("%s is a directory, should be a Go file", file)
|
||||
}
|
||||
|
||||
// 2.2 所有 *.go 必须在同一个目录内
|
||||
dir1, _ := filepath.Split(file)
|
||||
if dir1 == "" {
|
||||
dir1 = "./"
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
dir = dir1
|
||||
} else if dir != dir1 {
|
||||
return fmt.Errorf("named files must all be in one directory: have %s and %s", dir, dir1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDirFromImportPaths return the import path's real abs directory
|
||||
//
|
||||
// 该函数接收到的只有 dir 或 import path,file 在上一步已被排除
|
||||
// 只考虑 go modules 的情况
|
||||
func getDirFromImportPaths(patterns []string) (string, error) {
|
||||
// no import path, pattern = current wd
|
||||
if len(patterns) == 0 {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fail to parse import path: %w", err)
|
||||
}
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
// 为了简化插桩的逻辑,goc 对 import path 要求必须都在同一个目录
|
||||
// 所以干脆只允许一个 pattern 得了 -_-
|
||||
// 对于 goc build/run 来说本身就是只能在一个目录内
|
||||
// 对于 goc install 来讲,这个行为就和 go install 不同,不过多 import path 较少见 >_<,先忽略
|
||||
if len(patterns) > 1 {
|
||||
return "", fmt.Errorf("goc only support one import path now")
|
||||
}
|
||||
|
||||
pattern := patterns[0]
|
||||
switch {
|
||||
// case isLocalImport(pattern) || filepath.IsAbs(pattern):
|
||||
// dir1, err := filepath.Abs(pattern)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// if _, err := os.Stat(dir1); err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// return dir1, nil
|
||||
|
||||
case strings.Contains(pattern, "..."):
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := filepath.Split(pattern[:i])
|
||||
dir, _ = filepath.Abs(dir)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
return dir, nil
|
||||
|
||||
case strings.IndexByte(pattern, '@') > 0:
|
||||
return "", fmt.Errorf("import path with @ version query is not supported in goc")
|
||||
|
||||
case isMetaPackage(pattern):
|
||||
return "", fmt.Errorf("`std`, `cmd`, `all` import path is not supported by goc")
|
||||
|
||||
default: // 到这一步认为 pattern 是相对路径或者绝对路径
|
||||
dir1, err := filepath.Abs(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
if _, err := os.Stat(dir1); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
|
||||
return dir1, nil
|
||||
}
|
||||
}
|
||||
|
||||
// isLocalImport reports whether the import path is
|
||||
// a local import path, like ".", "..", "./foo", or "../foo"
|
||||
func isLocalImport(path string) bool {
|
||||
return path == "." || path == ".." ||
|
||||
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
|
||||
}
|
||||
|
||||
// isMetaPackage checks if the name is a reserved package name
|
||||
func isMetaPackage(name string) bool {
|
||||
return name == "std" || name == "cmd" || name == "all"
|
||||
}
|
||||
|
||||
// find direct path of current project which contains go.mod
|
||||
func findModuleRoot(dir string) string {
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// look for enclosing go.mod
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var g Logger
|
||||
|
||||
func NewLogger() {
|
||||
if config.GocConfig.Debug == true {
|
||||
func NewLogger(debug bool) {
|
||||
if debug == true {
|
||||
g = newCiLogger()
|
||||
} else {
|
||||
g = &terminalLogger{
|
||||
|
@ -4,12 +4,11 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/qiniu/goc/v2/pkg/config"
|
||||
"github.com/qiniu/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
func Watch() {
|
||||
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", config.GocConfig.Host)
|
||||
func Watch(host string) {
|
||||
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", host)
|
||||
c, _, err := websocket.DefaultDialer.Dial(watchUrl, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot connect to goc server: %v", err)
|
||||
|
Loading…
Reference in New Issue
Block a user