Update Golang runner

This commit is contained in:
akp 2021-12-11 00:09:24 +00:00
parent 7b4e3d4cf8
commit 6502ef4ff4
No known key found for this signature in database
GPG key ID: AA5726202C8879B7
4 changed files with 152 additions and 121 deletions

View file

@ -0,0 +1,141 @@
package runners
import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
au "github.com/logrusorgru/aurora"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
)
const (
golangInstallation = "go"
golangWrapperFilename = "runtime-wrapper.go"
golangWrapperExecutableFilename = "runtime-wrapper"
)
type golangRunner struct {
dir string
cmd *exec.Cmd
wrapperFilepath string
executableFilepath string
stdin io.WriteCloser
}
func newGolangRunner(dir string) Runner {
return &golangRunner{
dir: dir,
}
}
//go:embed interface/go.go
var golangInterface []byte
func (g *golangRunner) Start() error {
g.wrapperFilepath = filepath.Join(g.dir, golangWrapperFilename)
g.executableFilepath = filepath.Join(g.dir, golangWrapperExecutableFilename)
// determine package import path
buildPath := fmt.Sprintf("github.com/codemicro/adventOfCode/challenges/%s/%s", filepath.Base(filepath.Dir(g.dir)), filepath.Base(g.dir))
importPath := buildPath + "/go"
// generate code
var wrapperContent []byte
{
tpl := template.Must(template.New("").Parse(string(golangInterface)))
b := new(bytes.Buffer)
err := tpl.Execute(b, struct {
ImportPath string
}{importPath})
if err != nil {
return err
}
wrapperContent = b.Bytes()
}
// save interaction code
if err := ioutil.WriteFile(g.wrapperFilepath, wrapperContent, 0644); err != nil {
return err
}
// compile executable
stderrBuffer := new(bytes.Buffer)
cmd := exec.Command(golangInstallation, "build", "-tags", "runtime", "-o", g.executableFilepath, buildPath)
cmd.Stderr = stderrBuffer
if err := cmd.Run(); err != nil {
return fmt.Errorf("compilation failed: %s: %s", err, stderrBuffer.String())
}
if !cmd.ProcessState.Success() {
return errors.New("compilation failed, hence cannot continue")
}
// now we run!
absExecPath, err := filepath.Abs(g.executableFilepath)
if err != nil {
return err
}
// run executable
g.cmd = exec.Command(absExecPath)
cmd.Dir = g.dir
if stdin, err := setupBuffers(g.cmd); err != nil {
return err
} else {
g.stdin = stdin
}
return g.cmd.Start()
}
func (g *golangRunner) Stop() error {
if g.cmd == nil || g.cmd.Process == nil {
return nil
}
return g.cmd.Process.Kill()
}
func (g *golangRunner) Cleanup() error {
if g.wrapperFilepath != "" {
_ = os.Remove(g.wrapperFilepath)
}
if g.executableFilepath != "" {
_ = os.Remove(g.executableFilepath)
}
return nil
}
func (g *golangRunner) Run(task *Task) (*Result, error) {
taskJSON, err := json.Marshal(task)
if err != nil {
return nil, err
}
_, _ = g.stdin.Write(append(taskJSON, '\n'))
res := new(Result)
for {
inp, err := checkWait(g.cmd)
if err != nil {
return nil, err
}
err = json.Unmarshal(inp, res)
if err != nil {
// echo anything that won't parse to stdout (this lets us add debug print statements)
fmt.Printf("[%s] %v\n", au.BrightRed("DBG"), strings.TrimSpace(string(inp)))
} else {
break
}
}
return res, nil
}

View file

@ -1,109 +0,0 @@
package runners
import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"text/template"
)
const golangInstallation = "go"
type golangRunner struct {
dir string
tasks []*Task
}
func newGolangRunner(dir string) Runner {
return &golangRunner{
dir: dir,
}
}
func (g *golangRunner) Queue(task *Task) {
g.tasks = append(g.tasks, task)
}
//go:embed interface/go.go
var golangInterface []byte
func (g *golangRunner) Run() (chan ResultOrError, func()) {
wrapperFilename := "runtime-wrapper.go"
wrapperExecutable := "runtime-wrapper"
wrapperFilepath := filepath.Join(g.dir, wrapperFilename)
wrapperExecutableFilepath := filepath.Join(g.dir, wrapperExecutable)
// generate interaction data
taskJSON, err := json.Marshal(g.tasks)
if err != nil {
return makeErrorChan(err), nil
}
// determine package import path
buildPath := fmt.Sprintf("github.com/codemicro/adventOfCode/challenges/%s/%s", filepath.Base(filepath.Dir(g.dir)), filepath.Base(g.dir))
importPath := buildPath + "/go"
// generate code
var wrapperContent []byte
{
tpl := template.Must(template.New("").Parse(string(golangInterface)))
b := new(bytes.Buffer)
err := tpl.Execute(b, struct {
ImportPath string
}{importPath})
if err != nil {
return makeErrorChan(err), nil
}
wrapperContent = b.Bytes()
}
// save interaction code
err = ioutil.WriteFile(wrapperFilepath, wrapperContent, 0644)
if err != nil {
return makeErrorChan(err), nil
}
fmt.Print("Compiling...\r")
defer fmt.Print("\n\n")
// compile executable
stderrBuffer := new(bytes.Buffer)
cmd := exec.Command(golangInstallation, "build", "-tags", "runtime", "-o", wrapperExecutableFilepath, buildPath)
cmd.Stderr = stderrBuffer
err = cmd.Run()
if err != nil {
return makeErrorChan(fmt.Errorf("compilation failed: %s: %s", err, stderrBuffer.String())), nil
}
if !cmd.ProcessState.Success() {
return makeErrorChan(errors.New("compilation failed, hence cannot continue")), nil
}
absExecPath, err := filepath.Abs(wrapperExecutableFilepath)
if err != nil {
return makeErrorChan(err), nil
}
fmt.Print("Running... ")
// run executable
cmd = exec.Command(absExecPath)
cmd.Dir = g.dir
cmd.Stdin = bytes.NewReader(append(taskJSON, '\n'))
return readResultsFromCommand(cmd), func() {
// remove leftover files
_ = os.Remove(wrapperFilepath)
_ = os.Remove(wrapperExecutableFilepath)
}
}

View file

@ -28,17 +28,16 @@ func sendResult(taskID string, ok bool, output string, duration float64) {
func run() error {
reader := bufio.NewReader(os.Stdin)
tasksBytes, err := reader.ReadBytes('\n')
if err != nil {
return err
}
for {
task := new(runners.Task)
taskBytes, err := reader.ReadBytes('\n')
if err != nil {
return err
}
if err := json.Unmarshal(taskBytes, task); err != nil {
return err
}
var tasks []*runners.Task
if err := json.Unmarshal(tasksBytes, &tasks); err != nil {
return err
}
for _, task := range tasks {
var run func() (interface{}, error)
switch task.Part {

View file

@ -24,12 +24,12 @@ type RunnerCreator func(dir string) Runner
var Available = map[string]RunnerCreator{
"py": newPythonRunner,
//"go": newGolangRunner,
"go": newGolangRunner,
//"nim": newNimRunner,
}
var RunnerNames = map[string]string{
"py": "Python",
//"go": "Golang",
"go": "Golang",
//"nim": "Nim",
}