Merge pull request #3990 from roc-lang/pass-args-to-cli

Pass args from cli platform to app
This commit is contained in:
Richard Feldman 2022-09-14 08:43:13 -07:00 committed by GitHub
commit 21f3d4b8bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 53 deletions

View file

@ -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

View file

@ -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,10 +279,20 @@ 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 {
app_args.push(example_file(dir_name, file).to_str().unwrap().to_string());
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" => {
// Since these require things the build system often doesn't have
@ -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,8 +592,15 @@ mod cli_run {
let mut ran_without_optimizations = false;
let mut app_args: Vec<String> = vec![];
for file in benchmark.input_paths {
app_args.push(examples_dir("benchmarks").join(file).to_str().unwrap().to_string());
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( || {
@ -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,
},

View file

@ -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")

View file

@ -1,3 +1,4 @@
args
countdown
echo
effects

View 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

View file

@ -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)

View file

@ -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);

View file

@ -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

View file

@ -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)

View file

@ -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?")

View file

@ -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