bbgo_origin/pkg/bbgo/builder.go

145 lines
3.3 KiB
Go

package bbgo
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"text/template"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var wrapperTemplate = template.Must(template.New("main").Parse(`// Code generated by bbgo; DO NOT EDIT.
package main
import (
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/cmd"
{{- range .Imports }}
_ "{{ . }}"
{{- end }}
)
func init() {
bbgo.SetWrapperBinary()
}
func main() {
cmd.Execute()
}
`))
// generateRunFile renders the wrapper main.go template
func generateRunFile(filepath string, config *Config, imports []string) error {
var buf = bytes.NewBuffer(nil)
if err := wrapperTemplate.Execute(buf, struct {
Config *Config
Imports []string
}{
Config: config,
Imports: imports,
}); err != nil {
return err
}
return os.WriteFile(filepath, buf.Bytes(), 0644)
}
// compilePackage generates the main.go file of the wrapper package
func compilePackage(packageDir string, userConfig *Config, imports []string) error {
if _, err := os.Stat(packageDir); os.IsNotExist(err) {
if err := os.MkdirAll(packageDir, 0777); err != nil {
return errors.Wrapf(err, "can not create wrapper package directory: %s", packageDir)
}
}
mainFile := filepath.Join(packageDir, "main.go")
if err := generateRunFile(mainFile, userConfig, imports); err != nil {
return errors.Wrap(err, "compile error")
}
return nil
}
// Build builds the bbgo wrapper binary with the given build target config
func Build(ctx context.Context, userConfig *Config, targetConfig BuildTargetConfig) (string, error) {
// combine global imports and target imports
imports := append(userConfig.Build.Imports, targetConfig.Imports...)
buildDir := userConfig.Build.BuildDir
if len(buildDir) == 0 {
buildDir = "build"
}
packageDir, err := os.MkdirTemp(buildDir, "bbgow-") // with prefix bbgow
if err != nil {
return "", err
}
if err := compilePackage(packageDir, userConfig, imports); err != nil {
return "", err
}
cwd, err := os.Getwd()
if err != nil {
return "", err
}
var buildEnvs []string
if targetConfig.OS != runtime.GOOS {
buildEnvs = append(buildEnvs, "GOOS="+targetConfig.OS)
}
if targetConfig.Arch != runtime.GOARCH {
buildEnvs = append(buildEnvs, "GOARCH="+targetConfig.Arch)
}
buildTarget := filepath.Join(cwd, packageDir)
binary := targetConfig.Name
if len(binary) == 0 {
binary = fmt.Sprintf("bbgow-%s-%s", targetConfig.OS, targetConfig.Arch)
}
output := filepath.Join(buildDir, binary)
args := []string{"build", "-tags", "wrapper", "-o", output, buildTarget}
logrus.Debugf("building binary %s from %s: go %v", output, buildTarget, args)
buildCmd := exec.CommandContext(ctx, "go", args...)
buildCmd.Env = append(os.Environ(), buildEnvs...)
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
if err := buildCmd.Run(); err != nil {
return output, err
}
return output, os.RemoveAll(packageDir)
}
// BuildTarget builds the one of the targets.
func BuildTarget(ctx context.Context, userConfig *Config, target BuildTargetConfig) (string, error) {
buildDir := userConfig.Build.BuildDir
if len(buildDir) == 0 {
buildDir = "build"
}
if _, err := os.Stat(buildDir); os.IsNotExist(err) {
err = os.Mkdir(buildDir, 0777)
if err != nil {
return "", err
}
}
buildDir = filepath.Join(userConfig.Build.BuildDir, target.Name)
return Build(ctx, userConfig, target)
}