mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge pull request #3990 from roc-lang/pass-args-to-cli
Pass args from cli platform to app
This commit is contained in:
commit
21f3d4b8bc
11 changed files with 114 additions and 53 deletions
8
.github/workflows/nix_linux_x86_64.yml
vendored
8
.github/workflows/nix_linux_x86_64.yml
vendored
|
@ -21,3 +21,11 @@ jobs:
|
|||
|
||||
- name: execute tests with --release
|
||||
run: /home/big-ci-user/.nix-profile/bin/nix develop -c cargo test --locked --release
|
||||
|
||||
- name: upload args binary for inspection
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: failing_args_binary
|
||||
path: examples/interactive/args
|
||||
retention-days: 4
|
||||
|
|
|
@ -61,12 +61,18 @@ mod cli_run {
|
|||
#[cfg(target_os = "macos")]
|
||||
const ALLOW_VALGRIND: bool = false;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Arg<'a> {
|
||||
ExamplePath(&'a str),
|
||||
PlainText(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Example<'a> {
|
||||
filename: &'a str,
|
||||
executable_filename: &'a str,
|
||||
stdin: &'a [&'a str],
|
||||
input_paths: &'a [&'a str],
|
||||
arguments: &'a [Arg<'a>],
|
||||
expected_ending: &'a str,
|
||||
use_valgrind: bool,
|
||||
}
|
||||
|
@ -223,7 +229,7 @@ mod cli_run {
|
|||
stdin: &[&str],
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
input_paths: &[&str],
|
||||
args: &[&Arg],
|
||||
expected_ending: &str,
|
||||
) {
|
||||
assert!(input_paths.is_empty(), "Wasm does not support input files");
|
||||
|
@ -273,9 +279,19 @@ mod cli_run {
|
|||
let file_name = example_file(dir_name, example.filename);
|
||||
|
||||
let mut app_args: Vec<String> = vec![];
|
||||
for file in example.input_paths {
|
||||
for arg in example.arguments {
|
||||
match arg {
|
||||
Arg::ExamplePath(file) => {
|
||||
app_args.push(example_file(dir_name, file).to_str().unwrap().to_string());
|
||||
}
|
||||
Arg::PlainText(arg) => {
|
||||
app_args.push(arg.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// workaround for surgical linker issue, see PR #3990
|
||||
let mut custom_flags : Vec<&str> = vec![];
|
||||
|
||||
match example.executable_filename {
|
||||
"form" | "hello-gui" | "breakout" | "ruby" => {
|
||||
|
@ -296,6 +312,9 @@ mod cli_run {
|
|||
eprintln!("WARNING: skipping testing example {} because it only works in a browser!", example.filename);
|
||||
return;
|
||||
}
|
||||
"args" => {
|
||||
custom_flags = vec![LINKER_FLAG, "legacy"];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -304,12 +323,13 @@ mod cli_run {
|
|||
&file_name,
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&[],
|
||||
&custom_flags,
|
||||
&app_args,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
|
||||
custom_flags.push(OPTIMIZE_FLAG);
|
||||
// This is mostly because the false interpreter is still very slow -
|
||||
// 25s for the cli tests is just not acceptable during development!
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
@ -317,7 +337,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&[OPTIMIZE_FLAG],
|
||||
&custom_flags,
|
||||
&app_args,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
|
@ -368,7 +388,7 @@ mod cli_run {
|
|||
filename: "main.roc",
|
||||
executable_filename: "helloWorld",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -376,7 +396,7 @@ mod cli_run {
|
|||
filename: "main.roc",
|
||||
executable_filename: "rocLovesPlatforms",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Which platform am I running on now?\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -387,7 +407,7 @@ mod cli_run {
|
|||
// filename: "rocLovesC.roc",
|
||||
// executable_filename: "rocLovesC",
|
||||
// stdin: &[],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending:"Roc <3 C!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -395,7 +415,7 @@ mod cli_run {
|
|||
filename: "rocLovesRust.roc",
|
||||
executable_filename: "rocLovesRust",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Roc <3 Rust!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -403,7 +423,7 @@ mod cli_run {
|
|||
filename: "rocLovesSwift.roc",
|
||||
executable_filename: "rocLovesSwift",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Roc <3 Swift!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -411,7 +431,7 @@ mod cli_run {
|
|||
filename: "rocLovesWebAssembly.roc",
|
||||
executable_filename: "rocLovesWebAssembly",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Roc <3 Web Assembly!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -419,7 +439,7 @@ mod cli_run {
|
|||
filename: "rocLovesZig.roc",
|
||||
executable_filename: "rocLovesZig",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"Roc <3 Zig!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -427,7 +447,7 @@ mod cli_run {
|
|||
filename: "main.roc",
|
||||
executable_filename: "libhello",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -435,7 +455,7 @@ mod cli_run {
|
|||
filename: "fibonacci.roc",
|
||||
executable_filename: "fibonacci",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending:"55\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -443,7 +463,7 @@ mod cli_run {
|
|||
filename: "Hello.roc",
|
||||
executable_filename: "hello-gui",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -451,7 +471,7 @@ mod cli_run {
|
|||
filename: "breakout.roc",
|
||||
executable_filename: "breakout",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -459,7 +479,7 @@ mod cli_run {
|
|||
filename: "quicksort.roc",
|
||||
executable_filename: "quicksort",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -467,15 +487,23 @@ mod cli_run {
|
|||
// filename: "Quicksort.roc",
|
||||
// executable_filename: "quicksort",
|
||||
// stdin: &[],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending: "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
args:"interactive" => Example {
|
||||
filename: "args.roc",
|
||||
executable_filename: "args",
|
||||
stdin: &[],
|
||||
arguments: &[Arg::PlainText("hello"), Arg::PlainText("world")],
|
||||
expected_ending: "hello world\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
effects:"interactive" => Example {
|
||||
filename: "effects.roc",
|
||||
executable_filename: "effects",
|
||||
stdin: &["hi there!"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "hi there!\nIt is known\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -483,7 +511,7 @@ mod cli_run {
|
|||
// filename: "Main.roc",
|
||||
// executable_filename: "tea-example",
|
||||
// stdin: &[],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending: "",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -491,7 +519,7 @@ mod cli_run {
|
|||
filename: "form.roc",
|
||||
executable_filename: "form",
|
||||
stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "Hi, Giovanni Giorgio! 👋\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -499,7 +527,7 @@ mod cli_run {
|
|||
filename: "tui.roc",
|
||||
executable_filename: "tui",
|
||||
stdin: &["foo\n"], // NOTE: adding more lines leads to memory leaks
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "Hello Worldfoo!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -507,7 +535,7 @@ mod cli_run {
|
|||
// filename: "Main.roc",
|
||||
// executable_filename: "custom-malloc-example",
|
||||
// stdin: &[],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending: "ms!\nThe list was small!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -515,7 +543,7 @@ mod cli_run {
|
|||
// filename: "Main.roc",
|
||||
// executable_filename: "task-example",
|
||||
// stdin: &[],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending: "successfully wrote to file\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -524,7 +552,7 @@ mod cli_run {
|
|||
filename: "False.roc",
|
||||
executable_filename: "false",
|
||||
stdin: &[],
|
||||
input_paths: &["examples/hello.false"],
|
||||
arguments: &[Arg::ExamplePath("examples/hello.false")],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
|
@ -534,7 +562,7 @@ mod cli_run {
|
|||
filename: "static-site.roc",
|
||||
executable_filename: "static-site",
|
||||
stdin: &[],
|
||||
input_paths: &["input", "output"],
|
||||
arguments: &[Arg::ExamplePath("input"), Arg::ExamplePath("output")],
|
||||
expected_ending: "Processed 3 files with 3 successes and 0 errors\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
|
@ -564,9 +592,16 @@ mod cli_run {
|
|||
let mut ran_without_optimizations = false;
|
||||
|
||||
let mut app_args: Vec<String> = vec![];
|
||||
for file in benchmark.input_paths {
|
||||
for arg in benchmark.arguments {
|
||||
match arg {
|
||||
Arg::ExamplePath(file) => {
|
||||
app_args.push(examples_dir("benchmarks").join(file).to_str().unwrap().to_string());
|
||||
}
|
||||
Arg::PlainText(arg) => {
|
||||
app_args.push(arg.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARKS_BUILD_PLATFORM.call_once( || {
|
||||
// Check with and without optimizations
|
||||
|
@ -717,7 +752,7 @@ mod cli_run {
|
|||
filename: "NQueens.roc",
|
||||
executable_filename: "nqueens",
|
||||
stdin: &["6"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "4\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -725,7 +760,7 @@ mod cli_run {
|
|||
filename: "CFold.roc",
|
||||
executable_filename: "cfold",
|
||||
stdin: &["3"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "11 & 11\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -733,7 +768,7 @@ mod cli_run {
|
|||
filename: "Deriv.roc",
|
||||
executable_filename: "deriv",
|
||||
stdin: &["2"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "1 count: 6\n2 count: 22\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -741,7 +776,7 @@ mod cli_run {
|
|||
filename: "RBTreeCk.roc",
|
||||
executable_filename: "rbtree-ck",
|
||||
stdin: &["100"],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "10\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -749,7 +784,7 @@ mod cli_run {
|
|||
filename: "RBTreeInsert.roc",
|
||||
executable_filename: "rbtree-insert",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "Node Black 0 {} Empty Empty\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -757,7 +792,7 @@ mod cli_run {
|
|||
// filename: "RBTreeDel.roc",
|
||||
// executable_filename: "rbtree-del",
|
||||
// stdin: &["420"],
|
||||
// input_paths: &[],
|
||||
// arguments: &[],
|
||||
// expected_ending: "30\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -765,7 +800,7 @@ mod cli_run {
|
|||
filename: "TestAStar.roc",
|
||||
executable_filename: "test-astar",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "True\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -773,7 +808,7 @@ mod cli_run {
|
|||
filename: "TestBase64.roc",
|
||||
executable_filename: "test-base64",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -781,7 +816,7 @@ mod cli_run {
|
|||
filename: "Closure.roc",
|
||||
executable_filename: "closure",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -789,7 +824,7 @@ mod cli_run {
|
|||
filename: "Issue2279.roc",
|
||||
executable_filename: "issue2279",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "Hello, world!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -797,7 +832,7 @@ mod cli_run {
|
|||
filename: "QuicksortApp.roc",
|
||||
executable_filename: "quicksortapp",
|
||||
stdin: &[],
|
||||
input_paths: &[],
|
||||
arguments: &[],
|
||||
expected_ending: "todo put the correct quicksort answer here",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@ app "type-error"
|
|||
imports [pf.Stdout.{ line }, pf.Task.{ await }]
|
||||
provides [main] to pf
|
||||
|
||||
main =
|
||||
main = \_args ->
|
||||
_ <- await (line "a")
|
||||
_ <- await (line "b")
|
||||
_ <- await (line "c")
|
||||
|
|
1
examples/interactive/.gitignore
vendored
1
examples/interactive/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
args
|
||||
countdown
|
||||
echo
|
||||
effects
|
||||
|
|
11
examples/interactive/args.roc
Normal file
11
examples/interactive/args.roc
Normal file
|
@ -0,0 +1,11 @@
|
|||
app "args"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Task]
|
||||
provides [main] to pf
|
||||
|
||||
main : List Str -> Task.Task {} [] [Write [Stdout]]
|
||||
main = \args ->
|
||||
_ <- Task.await (Stdout.line "Here are the args I got from the command line:")
|
||||
joinedArgs = Str.joinWith args " "
|
||||
|
||||
Stdout.line joinedArgs
|
|
@ -1,9 +1,9 @@
|
|||
platform "cli"
|
||||
requires {} { main : Task {} [] * }
|
||||
requires {} { main : List Str -> Task {} [] * }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task.{ Task }, InternalTask, Effect.{ Effect }]
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Effect (Result {} []) as Fx
|
||||
mainForHost = InternalTask.toEffect main
|
||||
mainForHost : List Str -> Effect (Result {} []) as Fx
|
||||
mainForHost = \args -> InternalTask.toEffect (main args)
|
||||
|
|
|
@ -9,6 +9,7 @@ use core::mem::MaybeUninit;
|
|||
use glue::Metadata;
|
||||
use libc;
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
use std::borrow::Borrow;
|
||||
use std::ffi::{CStr, OsStr};
|
||||
use std::fs::File;
|
||||
use std::os::raw::c_char;
|
||||
|
@ -20,7 +21,7 @@ use file_glue::WriteErr;
|
|||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(output: *mut u8);
|
||||
fn roc_main(output: *mut u8, args: *const RocList<RocStr>);
|
||||
|
||||
#[link_name = "roc__mainForHost_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
@ -84,11 +85,16 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
let size = unsafe { roc_main_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
|
||||
// TODO: can we be more efficient about reusing the String's memory for RocStr?
|
||||
let args: RocList<RocStr> = std::env::args_os()
|
||||
.map(|s| RocStr::from(s.to_string_lossy().borrow()))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
// TODO allocate on the stack if it's under a certain size
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
roc_main(buffer);
|
||||
roc_main(buffer, &args);
|
||||
|
||||
let result = call_the_closure(buffer);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ app "countdown"
|
|||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed }]
|
||||
provides [main] to pf
|
||||
|
||||
main =
|
||||
main = \_args ->
|
||||
_ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press <ENTER>.")
|
||||
_ <- await Stdin.line
|
||||
loop 10 tick
|
||||
|
|
|
@ -3,8 +3,8 @@ app "echo"
|
|||
imports [pf.Stdin, pf.Stdout, pf.Task]
|
||||
provides [main] to pf
|
||||
|
||||
main : Task.Task {} [] [Read [Stdin], Write [Stdout]]
|
||||
main =
|
||||
main : List Str -> Task.Task {} [] [Read [Stdin], Write [Stdout]]
|
||||
main = \_args ->
|
||||
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")
|
||||
Task.loop {} (\_ -> Task.map tick Step)
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ app "form"
|
|||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Task {} * [Read [Stdin], Write [Stdout]]
|
||||
main =
|
||||
main : List Str -> Task {} * [Read [Stdin], Write [Stdout]]
|
||||
main = \_args ->
|
||||
_ <- await (Stdout.line "What's your first name?")
|
||||
firstName <- await Stdin.line
|
||||
_ <- await (Stdout.line "What's your last name?")
|
||||
|
|
|
@ -3,8 +3,8 @@ app "http-get"
|
|||
imports [pf.Http, pf.Task, pf.Stdin, pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
main : Task.Task {} [] [Read [Stdin], Write [Stdout], Network [Http]]
|
||||
main =
|
||||
main : List Str -> Task.Task {} [] [Read [Stdin], Write [Stdout], Network [Http]]
|
||||
main = \_args ->
|
||||
_ <- Task.await (Stdout.line "Please enter a URL to fetch")
|
||||
|
||||
url <- Task.await Stdin.line
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue