Stream: beginners

Topic: Passing program arguments with --linker=legacy


view this post on Zulip Karakatiza (Dec 17 2023 at 21:00):

Does legacy linker not support cli arguments, e.g. with roc dev --linker=legacy -- http://localhost:8080 Arg.list returns an empty list?

view this post on Zulip Luke Boswell (Dec 17 2023 at 21:51):

It should do. Also Arg.list should at least return the name of the executable I think.

view this post on Zulip Karakatiza (Dec 17 2023 at 22:03):

It does return the name, but with either roc dev or standalone executable args don't seem to get passed.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:05):

Legacy linker should have nothing to do with the args

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:06):

Not sure what is eating the args in this case

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:06):

I have definitely gotten all args from Args.list maybe an issue specific to OS?

view this post on Zulip Karakatiza (Dec 17 2023 at 22:07):

Weird. I'm on non-WSL Ubuntu ~20. I'll do some more testing tomorrow

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:08):

Hmm....actually one idea. How does rust initialize args? I have never dug into that. Calling rust from c may not initialize the args. So could be a legacy linker issue due to that.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:25):

Could be an issue with this?

On glibc Linux systems, arguments are retrieved by placing a function in .init_array. glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. This allows std::env::args to work even in a cdylib or staticlib, as it does on macOS and Windows.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 22:25):

From the rust docs: https://doc.rust-lang.org/std/env/fn.args.html

view this post on Zulip Karakatiza (Mar 21 2024 at 06:54):

Coming back to this as the issue persists; I needed to create a CLI program that both utilizes Arg.list and roc-json library, and it looks like either way one of them gets broken whether or not you use --linker=legacy :smiling_face_with_tear:

view this post on Zulip Luke Boswell (Mar 21 2024 at 07:55):

What if you build the program first?

roc build --linker=legacy abc.roc

And then run it passing some args...

abc http://localhost:8080

view this post on Zulip Luke Boswell (Mar 21 2024 at 07:57):

Karakatiza said:

It does return the name, but with either roc dev or standalone executable args don't seem to get passed.

I see you've tried that... strange

view this post on Zulip Karakatiza (Mar 28 2024 at 00:57):

@Luke Boswell does it make sense to work on my own implementation for JSON in Roc just to get things done? Or is there a specific syntax in roc-json code that could be refactored in a fork to avoid the bug it is blocked by without legacy linker?

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:06):

Do you have an example you can share or are working on?

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:06):

I wouldn't suggest writing a new implementation

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:11):

I can try and re-produce on my linux machine later today, I'll have a look now on my mac

view this post on Zulip Karakatiza (Mar 28 2024 at 01:14):

Don't have a share-able piece of code, but I simply would like to write a program that uses Arg.list and roc-json library at the same time

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:19):

I'm working on an example now

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:25):

Curiously,

This works

main =
    args <- Arg.list |> Task.await

    Stdout.line "Foo, first: $(Inspect.toStr args)"

But this crashes with panicked at 'called Option::unwrap() on a None value', crates/compiler/mono/src/ir.rs:6143:56

main = run |> Task.onErr \err -> Stdout.line "ERROR: $(Inspect.toStr err)"

run =
    args <- Arg.list |> Task.await

    Stdout.line "Foo, first: $(Inspect.toStr args)"

view this post on Zulip Karakatiza (Mar 28 2024 at 01:26):

Does the first program work with --linker=legacy for you?

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:27):

Yes,

$ roc run --linker=legacy test-arg-json.roc -- blah
Foo, first: ["/var/folders/48/39th9k0n0wdcj18k3yhm_g5c0000gn/T/.tmpfcsKBR/roc_app_binary", "blah"]

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:28):

So I suspect there is a bug or something strange in the platform

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:28):

Specifically with Arg

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:32):

Found the issue

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:33):

Have a workaround, just making an example with json for you

view this post on Zulip Karakatiza (Mar 28 2024 at 01:33):

Oh well, it doesn't work on Ubuntu 22.04.4 LTS with --linker=legacy for me.

view this post on Zulip Karakatiza (Mar 28 2024 at 01:33):

The output without --linker=legacy is as expected, Foo, first: ["/proc/self/fd/3", "blah"]

view this post on Zulip Karakatiza (Mar 28 2024 at 01:34):

With - Foo, first: []

view this post on Zulip Karakatiza (Mar 28 2024 at 01:34):

base-cli 0.8.1

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:43):

So what I had to do was modify basic-cli and use a local copy. I changed the err type on the Arg.list in the platform as follows.

## Gives a list of the program's command-line arguments.
# list : Task (List Str) * <-- err type BROKEN
# list : Task (List Str) a <-- err type BROKEN
# list : Task (List Str) _ <-- err type BROKEN
list : Task (List Str) []
list =
    Effect.args
    |> Effect.map Ok
    |> InternalTask.fromEffect

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:43):

The I used the following program with something like $ roc dev test-arg-json.roc -- "{"field":"value"}"

app "json"
    packages {
        cli: "../basic-cli/platform/main.roc",
        # cli: "https://github.com/roc-lang/basic-cli/releases/download/0.8.1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA.tar.br",
        json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.6.3/_2Dh4Eju2v_tFtZeMq8aZ9qw2outG04NbkmKpFhXS_4.tar.br",
    }
    imports [
        cli.Stdout,
        cli.Task.{ Task },
        cli.Arg,
        json.Core.{json},
    ]
    provides [main] to cli

main = run |> Task.onErr \err -> Stdout.line "ERROR: $(Inspect.toStr err)"

run =
    firstArgStr <- getArgs |> Task.await

    result = parseJson firstArgStr

    result
    |> Inspect.toStr
    |> Stdout.line

getArgs : Task Str _
getArgs =
    args <-
        Arg.list
        |> Task.mapErr UnableToGetArgs
        |> Task.await

    when args is
        [_, first, ..] -> Task.ok first
        _ -> Task.err UnableToParseArgs

parseJson : Str -> Result { field: Str } _
parseJson = \str ->
    Decode.fromBytes (Str.toUtf8 str) json
    |> Result.mapErr \err ->
        when err is
            Leftover bytes -> Leftover (Str.fromUtf8 bytes)
            _ -> JsonErr err

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:44):

This doesn't parse the json correctly, I'm forgetting how to use roc-json, but it runs and gives an nice error (Err (Leftover (Ok "{field:value}")))

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:45):

Are you able to test this please?

view this post on Zulip Karakatiza (Mar 28 2024 at 01:45):

Could you reproduce my issue, though?

view this post on Zulip Luke Boswell (Mar 28 2024 at 01:46):

I don't see any difference with using --linker=legacy, but I'm on my mac

view this post on Zulip Karakatiza (Mar 28 2024 at 02:02):

Will have to test your example tomorrow
It seems my issue is a different one, and I do not have the expertise or capacity to debug the Rust implementation you mentioned a while ago in this thread

view this post on Zulip Luke Boswell (Mar 28 2024 at 02:05):

In case it helps here is the current implementation of Args.list effect in the platform.

#[no_mangle]
pub extern "C" fn roc_fx_args() -> RocList<RocStr> {
    // TODO: can we be more efficient about reusing the String's memory for RocStr?
    std::env::args_os()
        .map(|os_str| RocStr::from(os_str.to_string_lossy().borrow()))
        .collect()
}

view this post on Zulip Luke Boswell (Mar 28 2024 at 02:08):

I would recommend writing a simple rust program and seeing what the behaviour is just using std::env::args_os() to see if it is a roc issue or something else.

view this post on Zulip Brendan Hansknecht (Mar 28 2024 at 02:30):

Mac is always legacy linker

view this post on Zulip Brendan Hansknecht (Mar 28 2024 at 02:30):

So wouldn't be able to repro linking bugs seen on linux

view this post on Zulip Karakatiza (Mar 28 2024 at 02:32):

In my case the bug is reproduced with legacy linker, so that would mean the mac and linux legacy linker behavior is different

view this post on Zulip Brendan Hansknecht (Mar 28 2024 at 02:40):

Ah, I missed that. Intriguing

view this post on Zulip Karakatiza (Mar 29 2024 at 00:31):

I haven't yet tested the above patch to base-cli or a simple Rust program, but it seems like roc-json works as expected without --linker=legacy despite the warning in the docs. I will come back to this thread if I hit a wall


Last updated: Jul 06 2025 at 12:14 UTC