feat(tools/magrathea): implement run command

This command builds the desired target and runs the executable produced
by it. If a directory is produced, it looks for a single (!) executable
in the bin directory. Dot files are ignored, so wrappers should
generally work. In the future we could provide a flag to select one of
multiple executables.

All arguments following the target are passed to the executable as is.

Examples:

    mg run ops/mq_cli ls
    mg run web/bubblegum:examples

Change-Id: I6490668af68e028520973196d9daa5f1d58969ee
Reviewed-on: https://cl.tvl.fyi/c/depot/+/5277
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
sterni 2022-02-13 19:30:29 +01:00
parent 71d6a02ca1
commit 01b25ebd8e

View file

@ -11,6 +11,8 @@
(chicken format) (chicken format)
(chicken irregex) (chicken irregex)
(chicken port) (chicken port)
(chicken file)
(chicken file posix)
(chicken process) (chicken process)
(chicken process-context) (chicken process-context)
(chicken string) (chicken string)
@ -37,6 +39,7 @@ commands:
build - build a target build - build a target
shell - enter a shell with the target's build dependencies shell - enter a shell with the target's build dependencies
path - print source folder for the target path - print source folder for the target
run - build a target and execute its output
file all feedback on b.tvl.fyi file all feedback on b.tvl.fyi
USAGE USAGE
@ -243,6 +246,50 @@ USAGE
(guarantee-success (parse-target arg)))] (guarantee-success (parse-target arg)))]
[other (print "not yet implemented")])) [other (print "not yet implemented")]))
(define (execute-run t #!optional cmd-args)
(fprintf (current-error-port) "[mg] building target ~A~%" t)
(let* ((expr (nix-expr-for t))
(out (call-with-input-pipe
(apply string-append
;; TODO(sterni): temporary gc root
(intersperse `("nix-build" "-E" ,(qs expr) "--no-out-link")
" "))
(lambda (p)
(string-chomp (let ((s (read-string #f p)))
(if (eq? s #!eof) "" s)))))))
;; TODO(sterni): can we get the exit code of nix-build somehow?
(when (= (string-length out) 0)
(mg-error (string-append "Couldn't build target " (format "~A" t)))
(exit 1))
(fprintf (current-error-port) "[mg] running target ~A~%" t)
(process-execute
;; If the output is a file, we assume it's an executable à la writeExecline,
;; otherwise we look in the bin subdirectory and pick the only executable.
;; Handling multiple executables is not possible at the moment, the choice
;; could be made via a command line flag in the future.
(if (regular-file? out)
out
(let* ((dir-path (string-append out "/bin"))
(dir-contents (if (directory-exists? dir-path)
(directory dir-path #f)
'())))
(case (length dir-contents)
((0) (mg-error "no executables in build output")
(exit 1))
((1) (string-append dir-path "/" (car dir-contents)))
(else (mg-error "more than one executable in build output")
(exit 1)))))
cmd-args)))
(define (run args)
(match args
[() (execute-run (empty-target))]
;; TODO(sterni): flag for selecting binary name
[other (execute-run (guarantee-success (parse-target (car args)))
(cdr args))]))
(define (path args) (define (path args)
(match args (match args
[(arg) [(arg)
@ -262,6 +309,7 @@ USAGE
[("build" . _) (build (cdr args))] [("build" . _) (build (cdr args))]
[("shell" . _) (shell (cdr args))] [("shell" . _) (shell (cdr args))]
[("path" . _) (path (cdr args))] [("path" . _) (path (cdr args))]
[("run" . _) (run (cdr args))]
[other (begin (print "unknown command: mg " args) [other (begin (print "unknown command: mg " args)
(print usage))])) (print usage))]))