重构
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 (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/build"
|
"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"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,15 +12,29 @@ var buildCmd = &cobra.Command{
|
|||||||
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
gocmode string
|
||||||
|
gochost string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
buildCmd.Flags().StringVarP(&config.GocConfig.Mode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
buildCmd.Flags().StringVarP(&gocmode, "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(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||||
rootCmd.AddCommand(buildCmd)
|
rootCmd.AddCommand(buildCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildAction(cmd *cobra.Command, args []string) {
|
func buildAction(cmd *cobra.Command, args []string) {
|
||||||
// 1. 解析 goc 命令行和 go 命令行
|
|
||||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args, flag.GO_BUILD)
|
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||||
b := build.NewBuild(remainedArgs)
|
|
||||||
|
b := build.NewBuild(
|
||||||
|
build.WithHost(gochost),
|
||||||
|
build.WithMode(gocmode),
|
||||||
|
build.WithFlagSets(sets),
|
||||||
|
build.WithArgs(args),
|
||||||
|
build.WithBuild(),
|
||||||
|
build.WithDebug(globalDebug),
|
||||||
|
)
|
||||||
b.Build()
|
b.Build()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/build"
|
"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"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,14 +13,23 @@ var installCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
installCmd.Flags().StringVarP(&config.GocConfig.Mode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
installCmd.Flags().StringVarP(&gocmode, "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(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||||
rootCmd.AddCommand(installCmd)
|
rootCmd.AddCommand(installCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func installAction(cmd *cobra.Command, args []string) {
|
func installAction(cmd *cobra.Command, args []string) {
|
||||||
// 1. 解析 goc 命令行和 go 命令行
|
|
||||||
remainedArgs := flag.BuildCmdArgsParse(cmd, args, flag.GO_INSTALL)
|
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||||
b := build.NewInstall(remainedArgs)
|
|
||||||
|
b := build.NewInstall(
|
||||||
|
build.WithHost(gochost),
|
||||||
|
build.WithMode(gocmode),
|
||||||
|
build.WithFlagSets(sets),
|
||||||
|
build.WithArgs(args),
|
||||||
|
build.WithInstall(),
|
||||||
|
build.WithDebug(globalDebug),
|
||||||
|
)
|
||||||
b.Install()
|
b.Install()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
10
cmd/list.go
10
cmd/list.go
@ -2,7 +2,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/client"
|
"github.com/qiniu/goc/v2/pkg/client"
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,14 +16,17 @@ goc list [flags]
|
|||||||
Run: list,
|
Run: list,
|
||||||
}
|
}
|
||||||
|
|
||||||
var listWide bool
|
var (
|
||||||
|
listHost string
|
||||||
|
listWide bool
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
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)")
|
listCmd.Flags().BoolVar(&listWide, "wide", false, "list all services with more information (such as pid)")
|
||||||
rootCmd.AddCommand(listCmd)
|
rootCmd.AddCommand(listCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func list(cmd *cobra.Command, args []string) {
|
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 (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/client"
|
"github.com/qiniu/goc/v2/pkg/client"
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,14 +18,17 @@ goc profile --host=http://192.168.1.1:8080 --output=./coverage.cov
|
|||||||
Run: profile,
|
Run: profile,
|
||||||
}
|
}
|
||||||
|
|
||||||
var output string // --output flag
|
var (
|
||||||
|
profileHost string
|
||||||
|
profileoutput string // --output flag
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
profileCmd.Flags().StringVar(&config.GocConfig.Host, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
profileCmd.Flags().StringVar(&profileHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||||
profileCmd.Flags().StringVarP(&output, "output", "o", "", "download cover profile")
|
profileCmd.Flags().StringVarP(&profileoutput, "output", "o", "", "download cover profile")
|
||||||
rootCmd.AddCommand(profileCmd)
|
rootCmd.AddCommand(profileCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func profile(cmd *cobra.Command, args []string) {
|
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
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -30,7 +29,7 @@ Find more information at:
|
|||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
log.DisplayGoc()
|
log.DisplayGoc()
|
||||||
// init logger
|
// init logger
|
||||||
log.NewLogger()
|
log.NewLogger(globalDebug)
|
||||||
},
|
},
|
||||||
|
|
||||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
@ -38,8 +37,10 @@ Find more information at:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var globalDebug bool
|
||||||
|
|
||||||
func init() {
|
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
|
// Execute the goc tool
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/server"
|
"github.com/qiniu/goc/v2/pkg/server"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -15,13 +14,15 @@ var serverCmd = &cobra.Command{
|
|||||||
Run: serve,
|
Run: serve,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
serverHost string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// serverCmd.Flags().IntVarP(&config.GocConfig.Port, "port", "", 7777, "listen port to start a coverage host center")
|
serverCmd.Flags().StringVarP(&serverHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||||
// 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")
|
|
||||||
rootCmd.AddCommand(serverCmd)
|
rootCmd.AddCommand(serverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serve(cmd *cobra.Command, args []string) {
|
func serve(cmd *cobra.Command, args []string) {
|
||||||
server.RunGocServerUntilExit(config.GocConfig.Host)
|
server.RunGocServerUntilExit(serverHost)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
cli "github.com/qiniu/goc/v2/pkg/watch"
|
cli "github.com/qiniu/goc/v2/pkg/watch"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -15,11 +14,15 @@ var watchCmd = &cobra.Command{
|
|||||||
Run: watch,
|
Run: watch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
watchHost string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
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)
|
rootCmd.AddCommand(watchCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func watch(cmd *cobra.Command, args []string) {
|
func watch(cmd *cobra.Command, args []string) {
|
||||||
cli.Watch()
|
cli.Watch(watchHost)
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,52 @@ package build
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"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/qiniu/goc/v2/pkg/log"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Build struct a build
|
// Build struct a build
|
||||||
// most configurations are stored in global variables: config.GocConfig & config.GoConfig
|
|
||||||
type Build struct {
|
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
|
// NewBuild creates a Build struct
|
||||||
//
|
//
|
||||||
func NewBuild(args []string) *Build {
|
func NewBuild(opts ...GocOption) *Build {
|
||||||
b := &Build{}
|
b := &Build{}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 解析 goc 命令行和 go 命令行
|
||||||
|
b.buildCmdArgsParse()
|
||||||
// 2. 解析 go 包位置
|
// 2. 解析 go 包位置
|
||||||
flag.GetPackagesDir(args)
|
b.getPackagesDir()
|
||||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||||
b.readProjectMetaInfo()
|
b.readProjectMetaInfo()
|
||||||
// 4. 展示元信息
|
// 4. 展示元信息
|
||||||
@ -41,16 +68,19 @@ func (b *Build) Build() {
|
|||||||
defer b.clean()
|
defer b.clean()
|
||||||
|
|
||||||
log.Donef("project copied to temporary directory")
|
log.Donef("project copied to temporary directory")
|
||||||
// 2. inject cover vars
|
|
||||||
cover.Inject()
|
// 2. update go.mod file if needed
|
||||||
// 3. build in the temp project
|
b.updateGoModFile()
|
||||||
|
// 3. inject cover vars
|
||||||
|
b.Inject()
|
||||||
|
// 4. build in the temp project
|
||||||
b.doBuildInTemp()
|
b.doBuildInTemp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Build) doBuildInTemp() {
|
func (b *Build) doBuildInTemp() {
|
||||||
log.StartWait("building the injected project")
|
log.StartWait("building the injected project")
|
||||||
|
|
||||||
goflags := config.GocConfig.Goflags
|
goflags := b.Goflags
|
||||||
// 检查用户是否自定义了 -o
|
// 检查用户是否自定义了 -o
|
||||||
oSet := false
|
oSet := false
|
||||||
for _, flag := range goflags {
|
for _, flag := range goflags {
|
||||||
@ -61,10 +91,10 @@ func (b *Build) doBuildInTemp() {
|
|||||||
|
|
||||||
// 如果没被设置就加一个至原命令执行的目录
|
// 如果没被设置就加一个至原命令执行的目录
|
||||||
if !oSet {
|
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...)
|
goflags = append(goflags, pacakges...)
|
||||||
|
|
||||||
@ -72,11 +102,11 @@ func (b *Build) doBuildInTemp() {
|
|||||||
args = append(args, goflags...)
|
args = append(args, goflags...)
|
||||||
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
||||||
cmd := exec.Command("go", args...)
|
cmd := exec.Command("go", args...)
|
||||||
cmd.Dir = config.GocConfig.TmpWd
|
cmd.Dir = b.TmpWd
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
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 {
|
if err := cmd.Start(); err != nil {
|
||||||
log.Fatalf("fail to execute go build: %v", err)
|
log.Fatalf("fail to execute go build: %v", err)
|
||||||
}
|
}
|
||||||
@ -88,3 +118,21 @@ func (b *Build) doBuildInTemp() {
|
|||||||
log.StopWait()
|
log.StopWait()
|
||||||
log.Donef("go build done")
|
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 {
|
// declareCoverVars attaches the required cover variables names
|
||||||
Debug bool
|
// to the files, to be used when annotating the files.
|
||||||
Host string
|
func declareCoverVars(p *Package) map[string]*FileVar {
|
||||||
Mode string // cover mode
|
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
|
for _, file := range p.CgoFiles {
|
||||||
GOBIN string
|
// These names appear in the cmd/cover HTML interface.
|
||||||
CurWd string
|
var longFile = path.Join(p.ImportPath, file)
|
||||||
TmpWd string
|
coverVars[file] = &FileVar{
|
||||||
CurModProjectDir string
|
File: longFile,
|
||||||
TmpModProjectDir string
|
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||||
|
}
|
||||||
|
coverIndex++
|
||||||
|
}
|
||||||
|
|
||||||
Goflags []string // command line flags
|
return coverVars
|
||||||
Packages []string // command line [packages]
|
|
||||||
|
|
||||||
ImportPath string // the whole import path of the project
|
|
||||||
Pkgs map[string]*Package
|
|
||||||
GlobalCoverVarImportPath string
|
|
||||||
GlobalCoverVarImportPathDir string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// PackageCover holds all the generate coverage variables of a package
|
||||||
type PackageCover struct {
|
type PackageCover struct {
|
||||||
Package *Package
|
Package *Package
|
@ -10,17 +10,16 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// readProjectMetaInfo reads all meta informations of the corresponding project
|
// readProjectMetaInfo reads all meta informations of the corresponding project
|
||||||
func (b *Build) readProjectMetaInfo() {
|
func (b *Build) readProjectMetaInfo() {
|
||||||
// get gopath & gobin
|
// get gopath & gobin
|
||||||
config.GocConfig.GOPATH = b.readGOPATH()
|
b.GOPATH = b.readGOPATH()
|
||||||
config.GocConfig.GOBIN = b.readGOBIN()
|
b.GOBIN = b.readGOBIN()
|
||||||
// 获取 [packages] 及其依赖的 package list
|
// 获取 [packages] 及其依赖的 package list
|
||||||
pkgs := b.listPackages(config.GocConfig.CurWd)
|
pkgs := b.listPackages(b.CurWd)
|
||||||
|
|
||||||
// get mod info
|
// get mod info
|
||||||
for _, pkg := range pkgs {
|
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")
|
log.Fatalf("Go module is not enabled, please set GO111MODULE=auto or on")
|
||||||
}
|
}
|
||||||
// 工程根目录
|
// 工程根目录
|
||||||
config.GocConfig.CurModProjectDir = pkg.Module.Dir
|
b.CurModProjectDir = pkg.Module.Dir
|
||||||
config.GocConfig.ImportPath = pkg.Module.Path
|
b.ImportPath = pkg.Module.Path
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果当前目录不是工程根目录,那再次 go list 一次,获取整个工程的包信息
|
// 如果当前目录不是工程根目录,那再次 go list 一次,获取整个工程的包信息
|
||||||
if config.GocConfig.CurWd != config.GocConfig.CurModProjectDir {
|
if b.CurWd != b.CurModProjectDir {
|
||||||
config.GocConfig.Pkgs = b.listPackages(config.GocConfig.CurModProjectDir)
|
b.Pkgs = b.listPackages(b.CurModProjectDir)
|
||||||
} else {
|
} else {
|
||||||
config.GocConfig.Pkgs = pkgs
|
b.Pkgs = pkgs
|
||||||
}
|
}
|
||||||
|
|
||||||
// get tmp folder name
|
// 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
|
// 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
|
// 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")
|
log.Donef("project meta information parsed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// displayProjectMetaInfo prints basic infomation of this project to stdout
|
// displayProjectMetaInfo prints basic infomation of this project to stdout
|
||||||
func (b *Build) displayProjectMetaInfo() {
|
func (b *Build) displayProjectMetaInfo() {
|
||||||
log.Infof("GOPATH: %v", config.GocConfig.GOPATH)
|
log.Infof("GOPATH: %v", b.GOPATH)
|
||||||
log.Infof("GOBIN: %v", config.GocConfig.GOBIN)
|
log.Infof("GOBIN: %v", b.GOBIN)
|
||||||
log.Infof("Project Directory: %v", config.GocConfig.CurModProjectDir)
|
log.Infof("Project Directory: %v", b.CurModProjectDir)
|
||||||
log.Infof("Temporary Project Directory: %v", config.GocConfig.TmpModProjectDir)
|
log.Infof("Temporary Project Directory: %v", b.TmpModProjectDir)
|
||||||
log.Infof("")
|
log.Infof("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +78,7 @@ func (b *Build) readGOBIN() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// listPackages list all packages under specific via go list command.
|
// 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 := exec.Command("go", "list", "-json", "./...")
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
|
|
||||||
@ -95,10 +94,10 @@ func (b *Build) listPackages(dir string) map[string]*config.Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dec := json.NewDecoder(bytes.NewBuffer(out))
|
dec := json.NewDecoder(bytes.NewBuffer(out))
|
||||||
pkgs := make(map[string]*config.Package, 0)
|
pkgs := make(map[string]*Package)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var pkg config.Package
|
var pkg Package
|
||||||
if err := dec.Decode(&pkg); err != nil {
|
if err := dec.Decode(&pkg); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package cover
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,30 +6,28 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
"github.com/qiniu/goc/v2/pkg/build/internal/tool"
|
||||||
"github.com/qiniu/goc/v2/pkg/cover/internal/tool"
|
"github.com/qiniu/goc/v2/pkg/build/internal/websocket"
|
||||||
"github.com/qiniu/goc/v2/pkg/cover/internal/websocket"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Inject injects cover variables for all the .go files in the target directory
|
// Inject injects cover variables for all the .go files in the target directory
|
||||||
func Inject() {
|
func (b *Build) Inject() {
|
||||||
log.StartWait("injecting cover variables")
|
log.StartWait("injecting cover variables")
|
||||||
|
|
||||||
var seen = make(map[string]*config.PackageCover)
|
var seen = make(map[string]*PackageCover)
|
||||||
|
|
||||||
// 所有插桩变量定义声明
|
// 所有插桩变量定义声明
|
||||||
allDecl := ""
|
allDecl := ""
|
||||||
|
|
||||||
pkgs := config.GocConfig.Pkgs
|
pkgs := b.Pkgs
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
if pkg.Name == "main" {
|
if pkg.Name == "main" {
|
||||||
log.Infof("handle main package: %v", pkg.ImportPath)
|
|
||||||
// 该 main 二进制所关联的所有插桩变量的元信息
|
// 该 main 二进制所关联的所有插桩变量的元信息
|
||||||
// 每个 main 之间是不相关的,需要重新定义
|
// 每个 main 之间是不相关的,需要重新定义
|
||||||
allMainCovers := make([]*config.PackageCover, 0)
|
allMainCovers := make([]*PackageCover, 0)
|
||||||
// 注入 main package
|
// 注入 main package
|
||||||
mainCover, mainDecl := addCounters(pkg)
|
mainCover, mainDecl := b.addCounters(pkg)
|
||||||
// 收集插桩变量的定义和元信息
|
// 收集插桩变量的定义和元信息
|
||||||
allDecl += mainDecl
|
allDecl += mainDecl
|
||||||
allMainCovers = append(allMainCovers, mainCover)
|
allMainCovers = append(allMainCovers, mainCover)
|
||||||
@ -43,7 +41,7 @@ func Inject() {
|
|||||||
// 依赖需要忽略 Go 标准库和 go.mod 引入的第三方
|
// 依赖需要忽略 Go 标准库和 go.mod 引入的第三方
|
||||||
if depPkg, ok := pkgs[dep]; ok {
|
if depPkg, ok := pkgs[dep]; ok {
|
||||||
// 注入依赖的 package
|
// 注入依赖的 package
|
||||||
packageCover, depDecl := addCounters(depPkg)
|
packageCover, depDecl := b.addCounters(depPkg)
|
||||||
// 收集插桩变量的定义和元信息
|
// 收集插桩变量的定义和元信息
|
||||||
allDecl += depDecl
|
allDecl += depDecl
|
||||||
allMainCovers = append(allMainCovers, packageCover)
|
allMainCovers = append(allMainCovers, packageCover)
|
||||||
@ -52,20 +50,25 @@ func Inject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 为每个 main 包注入 websocket handler
|
// 为每个 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 依赖
|
// 添加自定义 websocket 依赖
|
||||||
// 用户代码可能有 gorrila/websocket 的依赖,为避免依赖冲突,以及可能的 replace/vendor,
|
// 用户代码可能有 gorrila/websocket 的依赖,为避免依赖冲突,以及可能的 replace/vendor,
|
||||||
// 这里直接注入一份完整的 gorrila/websocket 实现
|
// 这里直接注入一份完整的 gorrila/websocket 实现
|
||||||
websocket.AddCustomWebsocketDep()
|
websocket.AddCustomWebsocketDep(b.GlobalCoverVarImportPathDir)
|
||||||
log.Donef("websocket library injected")
|
log.Donef("websocket library injected")
|
||||||
|
|
||||||
log.StopWait()
|
log.StopWait()
|
||||||
log.Donef("cover variables injected")
|
log.Donef("global cover variables injected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// addCounters is different from official go tool cover
|
// addCounters is different from official go tool cover
|
||||||
@ -75,18 +78,18 @@ func Inject() {
|
|||||||
// 2. no declarartions for these covervars
|
// 2. no declarartions for these covervars
|
||||||
//
|
//
|
||||||
// 3. return the declarations as string
|
// 3. return the declarations as string
|
||||||
func addCounters(pkg *config.Package) (*config.PackageCover, string) {
|
func (b *Build) addCounters(pkg *Package) (*PackageCover, string) {
|
||||||
mode := config.GocConfig.Mode
|
mode := b.Mode
|
||||||
gobalCoverVarImportPath := config.GocConfig.GlobalCoverVarImportPath
|
gobalCoverVarImportPath := b.GlobalCoverVarImportPath
|
||||||
|
|
||||||
coverVarMap := declareCoverVars(pkg)
|
coverVarMap := declareCoverVars(pkg)
|
||||||
|
|
||||||
decl := ""
|
decl := ""
|
||||||
for file, coverVar := range coverVarMap {
|
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,
|
Package: pkg,
|
||||||
Vars: coverVarMap,
|
Vars: coverVarMap,
|
||||||
}, decl
|
}, decl
|
||||||
@ -99,13 +102,13 @@ func addCounters(pkg *config.Package) (*config.PackageCover, string) {
|
|||||||
//
|
//
|
||||||
// 在原工程目录已经做了一次 go list -json,在临时目录没有必要再做一遍,直接转换一下就能得到
|
// 在原工程目录已经做了一次 go list -json,在临时目录没有必要再做一遍,直接转换一下就能得到
|
||||||
// 临时目录中的 pkg.Dir。
|
// 临时目录中的 pkg.Dir。
|
||||||
func getPkgTmpDir(pkgDir string) string {
|
func (b *Build) getPkgTmpDir(pkgDir string) string {
|
||||||
relDir, err := filepath.Rel(config.GocConfig.CurModProjectDir, pkgDir)
|
relDir, err := filepath.Rel(b.CurModProjectDir, pkgDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("go json -list meta info wrong: %v", err)
|
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
|
// 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.
|
// 11111_22222_bridge.go 仅仅用于引用 11111_22222_package, where package contains ws agent main logic.
|
||||||
// 使用 bridge.go 文件是为了避免插桩逻辑中的变量名污染 main 包
|
// 使用 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"
|
injectPkgName := "goc-cover-agent-apis-auto-generated-11111-22222-package"
|
||||||
injectBridgeName := "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"
|
injectBridgeName := "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"
|
||||||
wherePkg := filepath.Join(where, injectPkgName)
|
wherePkg := filepath.Join(where, injectPkgName)
|
||||||
@ -159,22 +162,22 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
|||||||
defer f2.Close()
|
defer f2.Close()
|
||||||
|
|
||||||
var _coverMode string
|
var _coverMode string
|
||||||
if config.GocConfig.Mode == "watch" {
|
if b.Mode == "watch" {
|
||||||
_coverMode = "cover"
|
_coverMode = "cover"
|
||||||
} else {
|
} else {
|
||||||
_coverMode = config.GocConfig.Mode
|
_coverMode = b.Mode
|
||||||
}
|
}
|
||||||
tmplData := struct {
|
tmplData := struct {
|
||||||
Covers []*config.PackageCover
|
Covers []*PackageCover
|
||||||
GlobalCoverVarImportPath string
|
GlobalCoverVarImportPath string
|
||||||
Package string
|
Package string
|
||||||
Host string
|
Host string
|
||||||
Mode string
|
Mode string
|
||||||
}{
|
}{
|
||||||
Covers: covers,
|
Covers: covers,
|
||||||
GlobalCoverVarImportPath: config.GocConfig.GlobalCoverVarImportPath,
|
GlobalCoverVarImportPath: b.GlobalCoverVarImportPath,
|
||||||
Package: injectPkgName,
|
Package: injectPkgName,
|
||||||
Host: config.GocConfig.Host,
|
Host: b.Host,
|
||||||
Mode: _coverMode,
|
Mode: _coverMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +186,7 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 写入 watch
|
// 写入 watch
|
||||||
if config.GocConfig.Mode != "watch" {
|
if b.Mode != "watch" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f, err := os.Create(filepath.Join(wherePkg, "watchagent.go"))
|
f, err := os.Create(filepath.Join(wherePkg, "watchagent.go"))
|
||||||
@ -196,9 +199,9 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
|||||||
Host string
|
Host string
|
||||||
GlobalCoverVarImportPath string
|
GlobalCoverVarImportPath string
|
||||||
}{
|
}{
|
||||||
Random: filepath.Base(config.GocConfig.TmpModProjectDir),
|
Random: filepath.Base(b.TmpModProjectDir),
|
||||||
Host: config.GocConfig.Host,
|
Host: b.Host,
|
||||||
GlobalCoverVarImportPath: config.GocConfig.GlobalCoverVarImportPath,
|
GlobalCoverVarImportPath: b.GlobalCoverVarImportPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := coverWatchTmpl.Execute(f, tmplwatchData); err != nil {
|
if err := coverWatchTmpl.Execute(f, tmplwatchData); err != nil {
|
||||||
@ -207,10 +210,10 @@ func injectGocAgent(where string, covers []*config.PackageCover) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// injectGlobalCoverVarFile 写入所有插桩变量的全局定义至一个单独的文件
|
// injectGlobalCoverVarFile 写入所有插桩变量的全局定义至一个单独的文件
|
||||||
func injectGlobalCoverVarFile(decl string) {
|
func (b *Build) injectGlobalCoverVarFile(decl string) {
|
||||||
globalCoverVarPackage := path.Base(config.GocConfig.GlobalCoverVarImportPath)
|
globalCoverVarPackage := path.Base(b.GlobalCoverVarImportPath)
|
||||||
globalCoverDef := filepath.Join(config.GocConfig.TmpModProjectDir, globalCoverVarPackage)
|
globalCoverDef := filepath.Join(b.TmpModProjectDir, globalCoverVarPackage)
|
||||||
config.GocConfig.GlobalCoverVarImportPathDir = globalCoverDef
|
b.GlobalCoverVarImportPathDir = globalCoverDef
|
||||||
|
|
||||||
err := os.MkdirAll(globalCoverDef, os.ModePerm)
|
err := os.MkdirAll(globalCoverDef, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -225,7 +228,7 @@ func injectGlobalCoverVarFile(decl string) {
|
|||||||
|
|
||||||
packageName := "package coverdef\n\n"
|
packageName := "package coverdef\n\n"
|
||||||
|
|
||||||
random := filepath.Base(config.GocConfig.TmpModProjectDir)
|
random := filepath.Base(b.TmpModProjectDir)
|
||||||
varWatchDef := fmt.Sprintf(`
|
varWatchDef := fmt.Sprintf(`
|
||||||
var WatchChannel_%v = make(chan *blockInfo, 1024)
|
var WatchChannel_%v = make(chan *blockInfo, 1024)
|
||||||
|
|
@ -4,13 +4,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/cover"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewInstall(args []string) *Build {
|
func NewInstall(opts ...GocOption) *Build {
|
||||||
return NewBuild(args)
|
return NewBuild(opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install starts go install
|
// Install starts go install
|
||||||
@ -24,18 +22,21 @@ func (b *Build) Install() {
|
|||||||
defer b.clean()
|
defer b.clean()
|
||||||
|
|
||||||
log.Donef("project copied to temporary directory")
|
log.Donef("project copied to temporary directory")
|
||||||
// 2. inject cover vars
|
|
||||||
cover.Inject()
|
// 2. update go.mod file if needed
|
||||||
// 3. install in the temp project
|
b.updateGoModFile()
|
||||||
|
// 3. inject cover vars
|
||||||
|
b.Inject()
|
||||||
|
// 4. install in the temp project
|
||||||
b.doInstallInTemp()
|
b.doInstallInTemp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Build) doInstallInTemp() {
|
func (b *Build) doInstallInTemp() {
|
||||||
log.StartWait("installing the injected project")
|
log.StartWait("installing the injected project")
|
||||||
|
|
||||||
goflags := config.GocConfig.Goflags
|
goflags := b.Goflags
|
||||||
|
|
||||||
pacakges := config.GocConfig.Packages
|
pacakges := b.Packages
|
||||||
|
|
||||||
goflags = append(goflags, pacakges...)
|
goflags = append(goflags, pacakges...)
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ func (b *Build) doInstallInTemp() {
|
|||||||
args = append(args, goflags...)
|
args = append(args, goflags...)
|
||||||
// go 命令行由 go install [build flags] [packages] 组成
|
// go 命令行由 go install [build flags] [packages] 组成
|
||||||
cmd := exec.Command("go", args...)
|
cmd := exec.Command("go", args...)
|
||||||
cmd.Dir = config.GocConfig.TmpWd
|
cmd.Dir = b.TmpWd
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ var depTarFile embed.FS
|
|||||||
//
|
//
|
||||||
// 从 embed 文件系统中解压 websocket.tar 文件,并依次写入临时工程中,作为一个单独的包存在。
|
// 从 embed 文件系统中解压 websocket.tar 文件,并依次写入临时工程中,作为一个单独的包存在。
|
||||||
// gorrila/websocket 是一个无第三方依赖的库,因此其位置可以随处移动,而不影响自身的编译。
|
// gorrila/websocket 是一个无第三方依赖的库,因此其位置可以随处移动,而不影响自身的编译。
|
||||||
func AddCustomWebsocketDep() {
|
func AddCustomWebsocketDep(customWebsocketPath string) {
|
||||||
data, err := depTarFile.ReadFile("websocket.tar")
|
data, err := depTarFile.ReadFile("websocket.tar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("cannot find the websocket.tar in the embed file: %v", err)
|
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)
|
log.Fatalf("cannot untar the websocket.tar: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
customWebsocketPath := config.GocConfig.GlobalCoverVarImportPathDir
|
|
||||||
fpath := filepath.Join(customWebsocketPath, hdr.Name)
|
fpath := filepath.Join(customWebsocketPath, hdr.Name)
|
||||||
if hdr.FileInfo().IsDir() {
|
if hdr.FileInfo().IsDir() {
|
||||||
// 处理目录
|
// 处理目录
|
@ -1,4 +1,4 @@
|
|||||||
package cover
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
@ -8,7 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
"github.com/tongjingran/copy"
|
"github.com/tongjingran/copy"
|
||||||
"golang.org/x/mod/modfile"
|
"golang.org/x/mod/modfile"
|
||||||
@ -18,8 +17,8 @@ import (
|
|||||||
//
|
//
|
||||||
// It will ignore .git and irregular files, only copy source(text) files
|
// It will ignore .git and irregular files, only copy source(text) files
|
||||||
func (b *Build) copyProjectToTmp() {
|
func (b *Build) copyProjectToTmp() {
|
||||||
curProject := config.GocConfig.CurModProjectDir
|
curProject := b.CurModProjectDir
|
||||||
tmpProject := config.GocConfig.TmpModProjectDir
|
tmpProject := b.TmpModProjectDir
|
||||||
|
|
||||||
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
||||||
log.Infof("find previous temporary directory, delete")
|
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
|
// clean clears the temporary project
|
||||||
func (b *Build) clean() {
|
func (b *Build) clean() {
|
||||||
if config.GocConfig.Debug != true {
|
if !b.Debug {
|
||||||
if err := os.RemoveAll(config.GocConfig.TmpModProjectDir); err != nil {
|
if err := os.RemoveAll(b.TmpModProjectDir); err != nil {
|
||||||
log.Fatalf("fail to delete the temporary project: %v", err)
|
log.Fatalf("fail to delete the temporary project: %v", err)
|
||||||
}
|
}
|
||||||
log.Donef("delete the temporary project")
|
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
|
// 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'
|
// 'replace github.com/qiniu/bar => /path/to/aa/bb/home/foo/bar'
|
||||||
func (b *Build) updateGoModFile() (updateFlag bool, newModFile []byte) {
|
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)
|
buf, err := ioutil.ReadFile(tempModfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("cannot find go.mod file in temporary directory: %v", err)
|
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
|
// absolute path no need to rewrite
|
||||||
if newVersion == "" && !filepath.IsAbs(newPath) {
|
if newVersion == "" && !filepath.IsAbs(newPath) {
|
||||||
var absPath string
|
var absPath string
|
||||||
fullPath := filepath.Join(config.GocConfig.CurModProjectDir, newPath)
|
fullPath := filepath.Join(b.CurModProjectDir, newPath)
|
||||||
absPath, _ = filepath.Abs(fullPath)
|
absPath, _ = filepath.Abs(fullPath)
|
||||||
// DropReplace & AddReplace will not return error
|
// DropReplace & AddReplace will not return error
|
||||||
// so no need to check the 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
|
// return Format(f.Syntax), nil
|
||||||
// }
|
// }
|
||||||
newModFile, _ = oriGoModFile.Format()
|
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
|
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
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
var g Logger
|
var g Logger
|
||||||
|
|
||||||
func NewLogger() {
|
func NewLogger(debug bool) {
|
||||||
if config.GocConfig.Debug == true {
|
if debug == true {
|
||||||
g = newCiLogger()
|
g = newCiLogger()
|
||||||
} else {
|
} else {
|
||||||
g = &terminalLogger{
|
g = &terminalLogger{
|
||||||
|
@ -4,12 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/qiniu/goc/v2/pkg/config"
|
|
||||||
"github.com/qiniu/goc/v2/pkg/log"
|
"github.com/qiniu/goc/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Watch() {
|
func Watch(host string) {
|
||||||
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", config.GocConfig.Host)
|
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", host)
|
||||||
c, _, err := websocket.DefaultDialer.Dial(watchUrl, nil)
|
c, _, err := websocket.DefaultDialer.Dial(watchUrl, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("cannot connect to goc server: %v", err)
|
log.Fatalf("cannot connect to goc server: %v", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user