diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..68009d7 --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,47 @@ +/* + 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. +*/ + +package cmd + +import ( + "github.com/qiniu/goc/v2/pkg/build" + "github.com/spf13/cobra" +) + +var runCmd = &cobra.Command{ + Use: "run", + Run: runAction, + + DisableFlagParsing: true, // run 命令需要用原生 go 的方式处理 flags +} + +func init() { + runCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch") + runCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever") + rootCmd.AddCommand(runCmd) +} + +func runAction(cmd *cobra.Command, args []string) { + + sets := build.CustomParseCmdAndArgs(cmd, args) + + b := build.NewRun( + build.WithHost(gochost), + build.WithMode(gocmode), + build.WithFlagSets(sets), + build.WithArgs(args), + build.WithBuild(), + build.WithDebug(globalDebug), + ) + b.Run() +} diff --git a/cmd/server.go b/cmd/server.go index 95edcf3..4b58c33 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -14,7 +14,9 @@ package cmd import ( + "github.com/qiniu/goc/v2/pkg/log" "github.com/qiniu/goc/v2/pkg/server" + "github.com/qiniu/goc/v2/pkg/server/store" "github.com/spf13/cobra" ) @@ -40,5 +42,9 @@ func init() { } func serve(cmd *cobra.Command, args []string) { - server.RunGocServerUntilExit(serverHost, serverStore) + s, err := store.NewFileStore(serverStore) + if err != nil { + log.Fatalf("cannot create store for goc server: %v", err) + } + server.RunGocServerUntilExit(serverHost, s) } diff --git a/pkg/build/build_flags.go b/pkg/build/build_flags.go index 571590b..5500a63 100644 --- a/pkg/build/build_flags.go +++ b/pkg/build/build_flags.go @@ -43,6 +43,15 @@ The [goc flags] can be placed in anywhere in the command line. However, other flags' order are same with the go official command. ` +var runUsage string = `Usage: +goc run [build flags] [goc flags] [packages] [arguments...] + +[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 @@ -131,6 +140,40 @@ func (b *Build) buildCmdArgsParse() { return } +func (b *Build) runCmdArgsParse() { + args := b.Args + allFlagSets := b.FlagSets + + // 重写 help + helpFlag := allFlagSets.Lookup("help") + + if helpFlag.Changed { + printGoHelp(runUsage) + os.Exit(0) + } + + // 删除 help flag + args = findAndDelHelpFlag(args) + + // 必须手动调用 + // 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值 + log.NewLogger(b.Debug) + + curWd, err := os.Getwd() + if err != nil { + log.Fatalf("fail to get current working directory: %v", err) + } + b.CurWd = curWd + + // 获取除 goc flags 之外的 args + // 删除 cobra 定义的 flag + allFlagSets.Visit(func(f *pflag.Flag) { + args = findAndDelGocFlag(args, f.Name, f.Value.String()) + }) + + b.GoArgs = args +} + func findAndDelGocFlag(a []string, x string, v string) []string { new := make([]string, 0, len(a)) x = "--" + x diff --git a/pkg/build/run.go b/pkg/build/run.go new file mode 100644 index 0000000..4495c55 --- /dev/null +++ b/pkg/build/run.go @@ -0,0 +1,88 @@ +package build + +import ( + "os" + "os/exec" + "os/signal" + + "github.com/gin-gonic/gin" + "github.com/qiniu/goc/v2/pkg/log" + "github.com/qiniu/goc/v2/pkg/server" + "github.com/qiniu/goc/v2/pkg/server/store" +) + +func NewRun(opts ...gocOption) *Build { + b := &Build{} + + for _, opt := range opts { + opt(b) + } + + // 1. 解析 goc 命令行和 go 命令行 + b.runCmdArgsParse() + // 2. 解析 go 包位置 + // b.getPackagesDir() + // 3. 读取工程元信息:go.mod, pkgs list ... + b.readProjectMetaInfo() + // 4. 展示元信息 + b.displayProjectMetaInfo() + + return b +} + +// Run starts go run +// +// 1. copy project to temp, +// 2. inject cover variables and functions into the project, +// 3. run the project in temp. +func (b *Build) Run() { + // 1. 拷贝至临时目录 + b.copyProjectToTmp() + defer b.clean() + + log.Donef("project copied to temporary directory") + + // 2. update go.mod file if needed + b.updateGoModFile() + // 3. inject cover vars + b.Inject() + // 4. run in the temp project + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt) + <-ch + b.clean() + }() + b.doRunInTemp() +} + +func (b *Build) doRunInTemp() { + log.Infof("running the injected project") + + s := store.NewFakeStore() + go func() { + gin.SetMode(gin.ReleaseMode) + err := server.RunGocServerUntilExit(b.Host, s) + if err != nil { + log.Fatalf("goc server fail to run: %v", err) + } + }() + + args := []string{"run"} + args = append(args, b.GoArgs...) + cmd := exec.Command("go", args...) + cmd.Dir = b.TmpWd + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + log.Infof("go run cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir) + if err := cmd.Start(); err != nil { + log.Errorf("fail to execute go run: %v", err) + } + if err := cmd.Wait(); err != nil { + log.Errorf("fail to execute go run: %v", err) + } + + // done + log.Donef("go run done") +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 3bc319e..dae33dd 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -96,12 +96,7 @@ type gocWatchClient struct { once sync.Once } -func RunGocServerUntilExit(host string, path string) { - s, err := store.NewFileStore(path) - if err != nil { - log.Fatalf("cannot create store for goc server: %v", err) - } - +func RunGocServerUntilExit(host string, s store.Store) error { gs := gocServer{ store: s, upgrader: websocket.Upgrader{ @@ -136,7 +131,7 @@ func RunGocServerUntilExit(host string, path string) { go gs.watchLoop() - r.Run(host) + return r.Run(host) } func (gs *gocServer) register(c *gin.Context) {