Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dune exec --prefix #6029

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
- Do not ignore rules marked `(promote (until-clean))` when
`--ignore-promoted-rules` (or `-p`) is passed. (#6010, fixes #4401, @emillon)

- Add `dune exec --prefix` to run a command through a wrapper like `time` or
`perf`. (#...., fixes #2691, @emillon)

3.4.1 (26-07-2022)
------------------

Expand Down
11 changes: 9 additions & 2 deletions bin/exec.ml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ let term =
Arg.(
value & flag
& info [ "no-build" ] ~doc:"don't rebuild target before executing")
and+ args = Arg.(value & pos_right 0 string [] (Arg.info [] ~docv:"ARGS")) in
and+ args = Arg.(value & pos_right 0 string [] (Arg.info [] ~docv:"ARGS"))
and+ prefix =
Arg.(
value
& opt (list ~sep:' ' string) []
& info [ "prefix" ] ~doc:"run <prefix cmd> instead of <cmd>")
in
let config = Common.init common in
let prog, argv, env =
Scheduler.go ~common ~config (fun () ->
Expand Down Expand Up @@ -126,8 +132,9 @@ let term =
| None -> not_found ()))
in
let prog = Path.to_string prog in
let argv = prog :: args in
let argv = prefix @ (prog :: args) in
let env = Super_context.context_env sctx in
let prog = List.hd argv in
Fiber.return (prog, argv, env))
in
restore_cwd_and_execve common prog argv env
Expand Down
81 changes: 81 additions & 0 deletions test/blackbox-tests/test-cases/exec-prefix.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"dune exec --prefix wrap ./e.exe args" behaves like "dune exec ./e.exe args"
except that it executes "wrap" with the rest passed as arguments instead.

This is useful for "adverbial" commands like time or perf.

$ cat > dune-project << EOF
> (lang dune 1.0)
> EOF
$ cat > dune << EOF
> (executable
> (name e))
> EOF

The executable just displays "Hello" and its arguments.

$ cat > e.ml << EOF
> let () =
> print_endline "Hello";
> Array.iteri (fun i s ->
> Printf.printf "argv[%d] = %s\n" i s
> ) Sys.argv
> EOF

The wrapper parses its own arguments and executes the rest.

$ cat > wrap.sh << 'EOF'
> #!/bin/bash
> while getopts "xy" o; do
> echo "Got option: $o"
> done
> shift $((OPTIND-1))
> echo Before
> "$@"
> echo After
> EOF
$ chmod +x wrap.sh

With no wrapper, e is executed with the program name and arguments in argv.

$ dune exec ./e.exe a b c
Hello
argv[0] = _build/default/e.exe
argv[1] = a
argv[2] = b
argv[3] = c

With just a string as prefix, the wrapper sees no arguments and executes the
program with its arguments.

$ dune exec --prefix ./wrap.sh ./e.exe a b c
Before
Hello
argv[0] = _build/default/e.exe
argv[1] = a
argv[2] = b
argv[3] = c
After

--prefix is actually space-separated: the wrapper gets the ones passed in
--prefix, and the executable gets the rest (this is up to the wrapper, but most
work this way).

$ dune exec --prefix './wrap.sh -x -y' ./e.exe a b c
Got option: x
Got option: y
Before
Hello
argv[0] = _build/default/e.exe
argv[1] = a
argv[2] = b
argv[3] = c
After

When the prefix is empty, this is equivalent to when no prefix is passed.

$ dune exec --prefix '' ./e.exe a b c
Hello
argv[0] = _build/default/e.exe
argv[1] = a
argv[2] = b
argv[3] = c