2021-09-02 09:48:11 +00:00
|
|
|
|
/*
|
|
|
|
|
Copyright 2021 Qiniu Cloud (qiniu.com)
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-05-23 14:23:35 +00:00
|
|
|
|
package build
|
|
|
|
|
|
|
|
|
|
import (
|
2021-06-20 13:14:21 +00:00
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
2021-09-02 06:36:41 +00:00
|
|
|
|
"strings"
|
2021-06-20 13:14:21 +00:00
|
|
|
|
|
2021-05-23 14:23:35 +00:00
|
|
|
|
"github.com/qiniu/goc/v2/pkg/log"
|
2021-09-02 06:36:41 +00:00
|
|
|
|
"github.com/spf13/pflag"
|
2021-05-23 14:23:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Build struct a build
|
|
|
|
|
type Build struct {
|
2021-09-02 06:36:41 +00:00
|
|
|
|
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]
|
|
|
|
|
|
2022-02-21 12:15:52 +00:00
|
|
|
|
IsVendorMod bool // vendor, readonly, or mod?
|
|
|
|
|
IsModEdit bool // is mod file edited?
|
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
ImportPath string // the whole import path of the project
|
|
|
|
|
Pkgs map[string]*Package
|
|
|
|
|
GlobalCoverVarImportPath string
|
|
|
|
|
GlobalCoverVarImportPathDir string
|
2021-05-23 14:23:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewBuild creates a Build struct
|
|
|
|
|
//
|
2021-09-05 12:27:29 +00:00
|
|
|
|
func NewBuild(opts ...gocOption) *Build {
|
2021-05-23 14:23:35 +00:00
|
|
|
|
b := &Build{}
|
2021-07-21 09:06:58 +00:00
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
for _, opt := range opts {
|
|
|
|
|
opt(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1. 解析 goc 命令行和 go 命令行
|
|
|
|
|
b.buildCmdArgsParse()
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// 2. 解析 go 包位置
|
2021-09-02 06:36:41 +00:00
|
|
|
|
b.getPackagesDir()
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// 3. 读取工程元信息:go.mod, pkgs list ...
|
2021-05-23 14:23:35 +00:00
|
|
|
|
b.readProjectMetaInfo()
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// 4. 展示元信息
|
2021-05-23 14:23:35 +00:00
|
|
|
|
b.displayProjectMetaInfo()
|
|
|
|
|
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build starts go build
|
|
|
|
|
//
|
|
|
|
|
// 1. copy project to temp,
|
|
|
|
|
// 2. inject cover variables and functions into the project,
|
|
|
|
|
// 3. build the project in temp.
|
|
|
|
|
func (b *Build) Build() {
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// 1. 拷贝至临时目录
|
2021-05-23 14:23:35 +00:00
|
|
|
|
b.copyProjectToTmp()
|
2021-06-21 07:59:59 +00:00
|
|
|
|
defer b.clean()
|
|
|
|
|
|
2021-05-23 14:23:35 +00:00
|
|
|
|
log.Donef("project copied to temporary directory")
|
2021-09-02 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
// 2. update go.mod file if needed
|
|
|
|
|
b.updateGoModFile()
|
|
|
|
|
// 3. inject cover vars
|
|
|
|
|
b.Inject()
|
2022-02-21 12:15:52 +00:00
|
|
|
|
|
|
|
|
|
if b.IsVendorMod && b.IsModEdit {
|
|
|
|
|
b.reVendor()
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
// 4. build in the temp project
|
2021-06-20 13:14:21 +00:00
|
|
|
|
b.doBuildInTemp()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Build) doBuildInTemp() {
|
2021-06-21 07:59:59 +00:00
|
|
|
|
log.StartWait("building the injected project")
|
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
goflags := b.Goflags
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// 检查用户是否自定义了 -o
|
|
|
|
|
oSet := false
|
|
|
|
|
for _, flag := range goflags {
|
|
|
|
|
if flag == "-o" {
|
|
|
|
|
oSet = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没被设置就加一个至原命令执行的目录
|
|
|
|
|
if !oSet {
|
2021-09-02 06:36:41 +00:00
|
|
|
|
goflags = append(goflags, "-o", b.CurWd)
|
2021-06-20 13:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-21 12:15:52 +00:00
|
|
|
|
if b.IsVendorMod && b.IsModEdit {
|
|
|
|
|
b.reVendor()
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
pacakges := b.Packages
|
2021-06-20 13:14:21 +00:00
|
|
|
|
|
2021-06-21 07:59:59 +00:00
|
|
|
|
goflags = append(goflags, pacakges...)
|
2021-06-20 13:14:21 +00:00
|
|
|
|
|
|
|
|
|
args := []string{"build"}
|
|
|
|
|
args = append(args, goflags...)
|
|
|
|
|
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
|
|
|
|
cmd := exec.Command("go", args...)
|
2021-09-02 06:36:41 +00:00
|
|
|
|
cmd.Dir = b.TmpWd
|
2021-06-20 13:14:21 +00:00
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
2021-09-02 06:36:41 +00:00
|
|
|
|
log.Infof("go build cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
2021-06-20 13:14:21 +00:00
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
|
log.Fatalf("fail to execute go build: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
|
log.Fatalf("fail to execute go build: %v", err)
|
|
|
|
|
}
|
2021-06-20 08:25:30 +00:00
|
|
|
|
|
2021-06-20 13:14:21 +00:00
|
|
|
|
// done
|
2021-06-21 07:59:59 +00:00
|
|
|
|
log.StopWait()
|
2021-06-20 13:14:21 +00:00
|
|
|
|
log.Donef("go build done")
|
2021-05-23 14:23:35 +00:00
|
|
|
|
}
|
2021-09-02 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|
2022-02-21 12:15:52 +00:00
|
|
|
|
|
|
|
|
|
func (b *Build) reVendor() {
|
|
|
|
|
log.StartWait("re-vendoring the project")
|
|
|
|
|
cmd := exec.Command("go", "mod", "vendor")
|
|
|
|
|
cmd.Dir = b.TmpModProjectDir
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
|
log.Fatalf("fail to execute go vendor: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
|
log.Fatalf("fail to execute go vendor: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.StopWait()
|
|
|
|
|
log.Donef("re-vendor the project done")
|
|
|
|
|
}
|