diff --git a/README.md b/README.md index 866b982..e4b8a45 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,10 @@ go build ## Usage -Build a Linux x86-64 ELF executable from `examples/hello`: +Build a Linux x86-64 ELF executable from `examples/hello` and run it: ```shell -./q build examples/hello -./examples/hello/hello +./q run examples/hello ``` ## Documentation diff --git a/src/build/errors/UnknownCLIParameter.go b/src/build/errors/UnknownCLIParameter.go new file mode 100644 index 0000000..136864e --- /dev/null +++ b/src/build/errors/UnknownCLIParameter.go @@ -0,0 +1,13 @@ +package errors + +import "fmt" + +// UnknownCLIParameter error is created when a command line parameter is not recognized. +type UnknownCLIParameter struct { + Parameter string +} + +// Error generates the string representation. +func (err *UnknownCLIParameter) Error() string { + return fmt.Sprintf("Unknown parameter '%s'", err.Parameter) +} diff --git a/src/cli/Build.go b/src/cli/Build.go index ac4066c..6b74ce6 100644 --- a/src/cli/Build.go +++ b/src/cli/Build.go @@ -7,10 +7,30 @@ import ( "git.akyoto.dev/cli/q/src/build" "git.akyoto.dev/cli/q/src/build/config" + "git.akyoto.dev/cli/q/src/build/errors" ) // Build parses the arguments and creates a build. func Build(args []string) int { + _, err := buildWithArgs(args) + + if err != nil { + fmt.Fprintln(os.Stderr, err) + + switch err.(type) { + case *errors.UnknownCLIParameter: + return 2 + + default: + return 1 + } + } + + return 0 +} + +// buildWithArgs creates a new build with the given arguments. +func buildWithArgs(args []string) (*build.Build, error) { b := build.New() for i := 0; i < len(args); i++ { @@ -26,8 +46,7 @@ func Build(args []string) int { config.Comments = true default: if strings.HasPrefix(args[i], "-") { - fmt.Printf("Unknown parameter: %s\n", args[i]) - return 2 + return b, &errors.UnknownCLIParameter{Parameter: args[i]} } b.Files = append(b.Files, args[i]) @@ -38,16 +57,16 @@ func Build(args []string) int { b.Files = append(b.Files, ".") } - return run(b) + err := makeExecutable(b) + return b, err } -// run starts the build by running the compiler and then writing the result to disk. -func run(b *build.Build) int { +// makeExecutable starts the build by running the compiler and then writing the result to disk. +func makeExecutable(b *build.Build) error { result, err := b.Run() if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 + return err } if config.Assembler { @@ -55,15 +74,8 @@ func run(b *build.Build) int { } if config.Dry { - return 0 + return nil } - err = result.Write(b.Executable()) - - if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 - } - - return 0 + return result.Write(b.Executable()) } diff --git a/src/cli/Help.go b/src/cli/Help.go index b6c8f58..8d4f0ef 100644 --- a/src/cli/Help.go +++ b/src/cli/Help.go @@ -12,6 +12,7 @@ func Help(w io.Writer, code int) int { commands: build [directory | file] + run [directory | file] help system diff --git a/src/cli/Main.go b/src/cli/Main.go index 2b90bda..31b387b 100644 --- a/src/cli/Main.go +++ b/src/cli/Main.go @@ -14,6 +14,9 @@ func Main(args []string) int { case "build": return Build(args[1:]) + case "run": + return Run(args[1:]) + case "system": return System(args[1:]) diff --git a/src/cli/Main_test.go b/src/cli/Main_test.go index 6471939..4d40adb 100644 --- a/src/cli/Main_test.go +++ b/src/cli/Main_test.go @@ -25,6 +25,8 @@ func TestCLI(t *testing.T) { {[]string{"build", "../../examples/hello", "--dry", "--verbose"}, 0}, {[]string{"build", "../../examples/hello/hello.q", "--dry"}, 0}, {[]string{"build", "../../examples/hello"}, 0}, + {[]string{"run", "../../examples/hello", "--invalid"}, 2}, + {[]string{"run", "../../examples/hello"}, 0}, } for _, test := range tests { diff --git a/src/cli/Run.go b/src/cli/Run.go new file mode 100644 index 0000000..9133f36 --- /dev/null +++ b/src/cli/Run.go @@ -0,0 +1,46 @@ +package cli + +import ( + "fmt" + "os" + "os/exec" + + "git.akyoto.dev/cli/q/src/build/errors" +) + +// Run builds and runs the executable. +func Run(args []string) int { + b, err := buildWithArgs(args) + + if err != nil { + fmt.Fprintln(os.Stderr, err) + + switch err.(type) { + case *errors.UnknownCLIParameter: + return 2 + + default: + return 1 + } + } + + cmd := exec.Command(b.Executable()) + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + err = cmd.Run() + + if err != nil { + fmt.Fprintln(os.Stderr, err) + + switch err.(type) { + case *exec.ExitError: + return 0 + + default: + return 1 + } + } + + return 0 +}