Skip to content

Commit

Permalink
cmd/egg: Fix run and test command to accept kernel as argument and qe…
Browse files Browse the repository at this point in the history
…mu options from QEMU_OPTS env var (#73)

Thanks for @prologic 's contribution.

The following adjustments have been made on the basis of the original code:
- The binary files generated by `egg test` and `egg run` are placed in the temporary directory to keep the source code directory clean
- Modify the code of `magefile.go` to synchronize with the `egg` command
  • Loading branch information
icexin authored Aug 6, 2021
1 parent 4257451 commit 4985da0
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Install Qemu
run: |
sudo apt install -y qemu
sudo apt-get update && sudo apt-get install -y qemu-system-x86
- name: Checkout code
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ eggos has the ability to convert normal go program into an `ELF unikernel` which

First, get the `egg` binary, which can be accessed through https://github.com/icexin/eggos/releases, or directly through `go install github.com/icexin/eggos/cmd/egg`

Run `egg build -o kernel.elf` in your project directory to get the kernel file, and then run `egg run -k kernel.elf` to start the qemu virtual machine to run the kernel.
Run `egg build -o kernel.elf` in your project directory to get the kernel file, and then run `egg run kernel.elf` to start the qemu virtual machine to run the kernel.

`egg pack -o eggos.iso -k kernel.elf` can pack the kernel into an iso file, and then you can use https://github.com/ventoy/Ventoy to run the iso file on a bare metal.

Expand Down
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ $ mage qemu

首先获取egg二进制,可以通过 https://github.com/icexin/eggos/releases 下载。也可以直接运行`go install github.com/icexin/eggos/cmd/egg`获取。

在你的项目目录运行`egg build -o kernel.elf`,接着运行`egg run -k kernel.elf`启动qemu虚拟机。
在你的项目目录运行`egg build -o kernel.elf`,接着运行`egg run kernel.elf`启动qemu虚拟机。


`egg pack -o eggos.iso -k kernel.elf` 可以将内核打包成一个iso文件,通过 https://github.com/ventoy/Ventoy 即可运行在真实的机器上。
Expand Down
69 changes: 56 additions & 13 deletions cmd/egg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import (
"path/filepath"
"strings"

"github.com/google/shlex"
"github.com/icexin/eggos/cmd/egg/assets"
"github.com/icexin/eggos/cmd/egg/build"
"github.com/spf13/cobra"
)

Expand All @@ -33,27 +35,47 @@ const (
)

var (
kernelFile string
ports []string
ports []string
)

// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run",
Use: "run <kernel>",
Short: "run running a eggos kernel in qemu",
Run: func(cmd *cobra.Command, args []string) {
runKernel(args)
err := runKernel(args)
if err != nil {
log.Fatal(err)
}
},
}

func runKernel(qemuArgs []string) {
func runKernel(args []string) error {
base, err := ioutil.TempDir("", "eggos-run")
if err != nil {
log.Fatal(err)
return err
}
defer os.RemoveAll(base)
if kernelFile == "" {
log.Fatal("missing kernel file")

var kernelFile string

if len(args) == 0 || args[0] == "" {
kernelFile = filepath.Join(base, "kernel.elf")

b := build.NewBuilder(build.Config{
GoRoot: goroot,
Basedir: base,
BuildTest: false,
EggosVersion: eggosVersion,
GoArgs: []string{
"-o", kernelFile,
},
})
if err := b.Build(); err != nil {
return fmt.Errorf("error building kernel: %s", err)
}
} else {
kernelFile = args[0]
}

var runArgs []string
Expand All @@ -69,6 +91,11 @@ func runKernel(qemuArgs []string) {
runArgs = append(runArgs, "-cdrom", kernelFile)
}

var qemuArgs []string
if qemuArgs, err = shlex.Split(os.Getenv("QEMU_OPTS")); err != nil {
return fmt.Errorf("error parsing QEMU_OPTS: %s", err)
}

runArgs = append(runArgs, "-m", "256M", "-no-reboot", "-serial", "mon:stdio")
runArgs = append(runArgs, "-netdev", "user,id=eth0"+portMapingArgs())
runArgs = append(runArgs, "-device", "e1000,netdev=eth0")
Expand All @@ -79,12 +106,29 @@ func runKernel(qemuArgs []string) {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
err = cmd.Run()
if err == nil {
return
return nil
}
switch e := err.(type) {
case *exec.ExitError:
code := e.ExitCode()
if code == 0 || code == 1 {
return nil
}
return err
default:
return err
}
}

func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
exiterr := err.(*exec.ExitError)
os.Exit(exiterr.ExitCode())
return true
}

func mustLoaderFile(fname string) {
Expand Down Expand Up @@ -113,6 +157,5 @@ func portMapingArgs() string {

func init() {
rootCmd.AddCommand(runCmd)
runCmd.Flags().StringVarP(&kernelFile, "kernel", "k", "", "eggos kernel file, kernel.elf|eggos.iso")
runCmd.Flags().StringSliceVarP(&ports, "port", "p", nil, "port mapping from host to kernel, format $host_port:$kernel_port")
}
10 changes: 4 additions & 6 deletions cmd/egg/cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ var testCmd = &cobra.Command{
Use: "test",
Short: "test likes go test but running in qemu",
Run: func(cmd *cobra.Command, args []string) {
err := runTest(args)
err := runTest()
if err != nil {
log.Fatal(err)
}
},
}

func runTest(qemuArgs []string) error {
func runTest() error {
base, err := ioutil.TempDir("", "eggos-test")
if err != nil {
return err
}
defer os.RemoveAll(base)

outfile := filepath.Join(base, "eggos.test.elf")
outfile := filepath.Join(base, "kernel.test.elf")

b := build.NewBuilder(build.Config{
GoRoot: goroot,
Expand All @@ -61,9 +61,7 @@ func runTest(qemuArgs []string) error {
return err
}

kernelFile = outfile
runKernel(qemuArgs)
return nil
return runKernel([]string{outfile})
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions cmd/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/icexin/eggos/cmd
go 1.16

require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
)
2 changes: 2 additions & 0 deletions cmd/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
Expand Down
60 changes: 32 additions & 28 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func Kernel() error {
mg.Deps(Egg)

detectGoVersion()
return rundir("app", eggBin, "build", "-o", "../kernel.elf",
return rundir("app", nil, eggBin, "build", "-o", "../kernel.elf",
"-gcflags", GOGCFLAGS,
"-tags", GOTAGS,
"./kmain")
Expand Down Expand Up @@ -88,31 +88,19 @@ func Multiboot() error {
func Test() error {
mg.Deps(Egg)

var args []string
args = append(args, "test", "--")
args = append(args, QEMU_OPT...)

err := rundir("tests", eggBin, args...)
status := mg.ExitStatus(err)
if status != 0 && status != 1 {
return err
envs := map[string]string{
"QEMU_OPTS": quoteArgs(QEMU_OPT),
}
return nil
return rundir("tests", envs, eggBin, "test")
}

func TestDebug() error {
mg.Deps(Egg)

var args []string
args = append(args, "test", "--")
args = append(args, QEMU_DEBUG_OPT...)

err := rundir("tests", eggBin, args...)
status := mg.ExitStatus(err)
if status != 0 && status != 1 {
return err
envs := map[string]string{
"QEMU_OPTS": quoteArgs(QEMU_DEBUG_OPT),
}
return nil
return rundir("tests", envs, eggBin, "test")
}

// Qemu run multiboot.elf on qemu.
Expand All @@ -123,7 +111,7 @@ func Qemu() error {
mg.Deps(Kernel)

detectQemu()
return eggrun(QEMU_OPT, "-k", "kernel.elf")
return eggrun(QEMU_OPT, "kernel.elf")
}

// QemuDebug run multiboot.elf in debug mode.
Expand All @@ -133,7 +121,7 @@ func QemuDebug() error {
mg.Deps(Kernel)

detectQemu()
return eggrun(QEMU_DEBUG_OPT, "-k", "kernel.elf")
return eggrun(QEMU_DEBUG_OPT, "kernel.elf")
}

// Iso generate eggos.iso, which can be used with qemu -cdrom option.
Expand All @@ -160,7 +148,7 @@ func GraphicDebug() error {
}

func Egg() error {
err := rundir("cmd", "go", "build", "-o", "../egg", "./egg")
err := rundir("cmd", nil, "go", "build", "-o", "../egg", "./egg")
if err != nil {
return err
}
Expand Down Expand Up @@ -299,7 +287,8 @@ func initQemuDebugOpt() []string {
-s -S
`
ret := append([]string{}, initQemuOpt()...)
return append(ret, strings.Fields(opts)...)
ret = append(ret, strings.Fields(opts)...)
return ret
}

func compileCfile(file string, extFlags ...string) {
Expand All @@ -312,27 +301,42 @@ func compileCfile(file string, extFlags ...string) {
}
}

func rundir(dir string, cmd string, args ...string) error {
func rundir(dir string, envs map[string]string, cmd string, args ...string) error {
current, _ := os.Getwd()
os.Chdir(dir)
defer os.Chdir(current)
return sh.RunV(cmd, args...)
return sh.RunWithV(envs, cmd, args...)
}

func eggrun(qemuArgs []string, flags ...string) error {
qemuOpts := quoteArgs(qemuArgs)
var args []string
args = append(args, "run")
args = append(args, "-p", "8080:80")
args = append(args, flags...)
args = append(args, "--")
args = append(args, qemuArgs...)
return sh.RunV(eggBin, args...)
envs := map[string]string{
"QEMU_OPTS": qemuOpts,
}
return sh.RunWithV(envs, eggBin, args...)
}

func cmdOutput(cmd string, args ...string) ([]byte, error) {
return exec.Command(cmd, args...).CombinedOutput()
}

// quote string which has spaces with ""
func quoteArgs(args []string) string {
var ret []string
for _, s := range args {
if strings.Index(s, " ") != -1 {
ret = append(ret, strconv.Quote(s))
} else {
ret = append(ret, s)
}
}
return strings.Join(ret, " ")
}

func hasCommand(cmd string) bool {
_, err := exec.LookPath(cmd)
if err != nil {
Expand Down

0 comments on commit 4985da0

Please sign in to comment.