mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Set argv[0] to .roc file passed to 'roc run'
When we run `roc run <file>` or `roc <file>` then Roc will compile a binary and run it. Before this commit we would set the path to the compiled binary as argv[0]. This commit changes the behavior to make argv[0] in the binary correspond to the roc file being ran. This benefits the use of roc scripts that make use of a shebang: #!/usr/bin/env roc With this change such scripts will be able to read the path to themselves out of ARGV. This trick is commonly used for instance by bash scripts in order to access files relative to the script itself.
This commit is contained in:
parent
573d4337e7
commit
ad555297cf
3 changed files with 69 additions and 15 deletions
|
@ -986,7 +986,15 @@ pub fn build(
|
|||
// ManuallyDrop will leak the bytes because we don't drop manually
|
||||
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
|
||||
|
||||
roc_run(&arena, opt_level, target, args, bytes, expect_metadata)
|
||||
roc_run(
|
||||
&arena,
|
||||
path,
|
||||
opt_level,
|
||||
target,
|
||||
args,
|
||||
bytes,
|
||||
expect_metadata,
|
||||
)
|
||||
}
|
||||
BuildAndRunIfNoErrors => {
|
||||
if problems.fatally_errored {
|
||||
|
@ -1021,7 +1029,15 @@ pub fn build(
|
|||
// ManuallyDrop will leak the bytes because we don't drop manually
|
||||
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
|
||||
|
||||
roc_run(&arena, opt_level, target, args, bytes, expect_metadata)
|
||||
roc_run(
|
||||
&arena,
|
||||
path,
|
||||
opt_level,
|
||||
target,
|
||||
args,
|
||||
bytes,
|
||||
expect_metadata,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1034,6 +1050,7 @@ pub fn build(
|
|||
|
||||
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||
arena: &Bump,
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
target: Target,
|
||||
args: I,
|
||||
|
@ -1073,7 +1090,14 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
|||
|
||||
Ok(0)
|
||||
}
|
||||
_ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata),
|
||||
_ => roc_run_native(
|
||||
arena,
|
||||
script_path,
|
||||
opt_level,
|
||||
args,
|
||||
binary_bytes,
|
||||
expect_metadata,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1114,7 @@ fn os_str_as_utf8_bytes(os_str: &OsStr) -> &[u8] {
|
|||
|
||||
fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &'a Bump,
|
||||
executable: &ExecutableFile,
|
||||
script_path: &Path,
|
||||
args: I,
|
||||
) -> (
|
||||
bumpalo::collections::Vec<'a, CString>,
|
||||
|
@ -1098,8 +1122,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
) {
|
||||
use bumpalo::collections::CollectIn;
|
||||
|
||||
let path = executable.as_path();
|
||||
let path_cstring = CString::new(os_str_as_utf8_bytes(path.as_os_str())).unwrap();
|
||||
let path_cstring = CString::new(os_str_as_utf8_bytes(script_path.as_os_str())).unwrap();
|
||||
|
||||
// argv is an array of pointers to strings passed to the new program
|
||||
// as its command-line arguments. By convention, the first of these
|
||||
|
@ -1137,6 +1160,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
#[cfg(target_family = "unix")]
|
||||
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &Bump,
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
args: I,
|
||||
binary_bytes: &[u8],
|
||||
|
@ -1145,7 +1169,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
use bumpalo::collections::CollectIn;
|
||||
|
||||
let executable = roc_run_executable_file_path(binary_bytes)?;
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args);
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, script_path, args);
|
||||
|
||||
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
|
||||
.iter()
|
||||
|
@ -1400,6 +1424,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
|
|||
#[cfg(not(target_family = "unix"))]
|
||||
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
args: I,
|
||||
binary_bytes: &[u8],
|
||||
|
@ -1411,7 +1436,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
let executable = roc_run_executable_file_path(binary_bytes)?;
|
||||
|
||||
// TODO forward the arguments
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, script_path, args);
|
||||
|
||||
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
|
||||
.iter()
|
||||
|
|
12
crates/cli/tests/cli/argv0.roc
Normal file
12
crates/cli/tests/cli/argv0.roc
Normal file
|
@ -0,0 +1,12 @@
|
|||
app [main] {
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
|
||||
}
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Arg
|
||||
|
||||
main =
|
||||
args = Arg.list! {}
|
||||
when List.first args is
|
||||
Ok argv0 -> Stdout.line argv0
|
||||
Err ListWasEmpty -> Stdout.line "Failed: argv was empty"
|
|
@ -305,7 +305,7 @@ mod cli_run {
|
|||
}
|
||||
};
|
||||
|
||||
let self_path = file.display().to_string();
|
||||
let self_path = file.parent().unwrap().display().to_string();
|
||||
|
||||
let actual_cmd_stdout = ignore_test_timings(&strip_colors(&cmd_output.stdout))
|
||||
.replace(&self_path, "<ignored for tests>");
|
||||
|
@ -573,12 +573,12 @@ mod cli_run {
|
|||
words : List Str
|
||||
words = ["this", "will", "for", "sure", "be", "a", "large", "string", "so", "when", "we", "split", "it", "it", "will", "use", "seamless", "slices", "which", "affect", "printing"]
|
||||
|
||||
[<ignored for tests>:31] x = 42
|
||||
[<ignored for tests>:33] "Fjoer en ferdjer frieten oan dyn geve lea" = "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
[<ignored for tests>:35] "this is line 24" = "this is line 24"
|
||||
[<ignored for tests>:21] x = "abc"
|
||||
[<ignored for tests>:21] x = 10
|
||||
[<ignored for tests>:21] x = (A (B C))
|
||||
[<ignored for tests>/expects.roc:31] x = 42
|
||||
[<ignored for tests>/expects.roc:33] "Fjoer en ferdjer frieten oan dyn geve lea" = "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
[<ignored for tests>/expects.roc:35] "this is line 24" = "this is line 24"
|
||||
[<ignored for tests>/expects.roc:21] x = "abc"
|
||||
[<ignored for tests>/expects.roc:21] x = 10
|
||||
[<ignored for tests>/expects.roc:21] x = (A (B C))
|
||||
Program finished!
|
||||
"#
|
||||
),
|
||||
|
@ -1203,6 +1203,23 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
#[ignore = "broken because of a bug in basic-cli: https://github.com/roc-lang/basic-cli/issues/82"]
|
||||
fn argv0() {
|
||||
test_roc_app(
|
||||
"crates/cli/tests/cli",
|
||||
"argv0.roc",
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"<ignored for tests>/argv0.roc\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue