-
Notifications
You must be signed in to change notification settings - Fork 32
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
Undefined reference to "caml_startup" when trying to run dune utop
#70
Comments
Thanks for the report! I will be able to take a closer look in the next few days. It looks like |
Hm, It looks like this may be an issue with ocaml-interop - I will open an issue there and keep investigating. |
Any idea of what |
I will need to look into what Do you use If I run |
@zshipko yes, it is defined there too, bytecode also uses I have to reflect more on it, but I think the correct thing would be for |
Just a thought, I do know what is really going on: both static library and dynamic library are built; maybe the library type should determine the behavior here? |
Probably not, because I use |
But my point is that when producing a |
Anybody has found a work around for this? |
There is partial one is the description of the issue which work for me. |
This is what I get when I use your trick:
|
FYI @mrmr1993 made a PR that should solve this https://github.com/tezedge/ocaml-interop/pull/36 Once all is ready I will merge and make a new |
@tizoc Cool, thanks for the update! I will make sure to follow that PR |
@zshipko I have a fix that I'm planning to roll out to MinaProtocol/mina when I get the time in the next few days (backed by the changes in the PR above). Is it useful to PR here yet, or would you prefer to wait / implement yourself once it's ready? |
@mrmr1993 A PR would definitely be appreciated! I’m not very familiar with the mina repo, but a link to your changes would also work. |
@mrmr1993 @zshipko after looking at this a bit more, it seems to me that this could be fixed with the addition of a new feature flag Then the dune file would have to include a profile for building for dune loading that will enable this feature flag when calling cargo. |
…bol. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
Quick proof of concept here: https://github.com/tezedge/ocaml-interop/pull/37 |
…bol. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
Seems to work after adding the same feature flag to ocaml-rs:
|
Here is the diff to ocaml-rust-starter (not including the overrides to use my modified versions of ocaml-rs and ocaml-interop): diff --git a/Cargo.toml b/Cargo.toml
index 5b4fa55..a99a1c5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,5 +10,8 @@ crate-type = ["staticlib", "cdylib"]
[dependencies]
ocaml = "*"
+[features]
+dune = ["ocaml/no-caml-startup"]
+
# Or use the development version:
# ocaml = {git = "git://github.com/zshipko/ocaml-rs"}
diff --git a/src/dune b/src/dune
index f9c6767..7c89616 100644
--- a/src/dune
+++ b/src/dune
@@ -3,7 +3,7 @@
(deps (glob_files *.rs))
(action
(progn
- (run cargo build --target-dir %{project_root}/../../target --release)
+ (run cargo build --target-dir %{project_root}/../../target --release --features "%{env:cargo_feature_flags=}")
(run sh -c
"mv %{project_root}/../../target/release/libocaml_rust_starter.so ./dllocaml_rust_starter.so 2> /dev/null || \
mv %{project_root}/../../target/release/libocaml_rust_starter.dylib ./dllocaml_rust_starter.so") A better version would use dune's support for profiles and define one specifically for dune. What do you think of this solution? |
Nice, this looks good and it seems to be working for the intended use case. I think a feature flag is preferable to moving that code out to a separate package. |
This dune at the root of the project + the edits I am about to make to the previous comment make
|
@zshipko yes, splitting the crate has other implications I am not sure about, and it looks like it will only work for anyone not explicitly depending on the runtime initialization, but projects that do would still need to do something extra to make it work. |
I will release ocaml-interop 0.8.5 with this new feature flag tomorrow (or maybe even later today) and open a PR with the relevant changes here and on the ocaml-rust-starter repo (cargo and dune files + changes to README mentioning that). |
…bol. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
@tizoc doesn't this make every consumer of your library need to re-expose this flag, and every downstream consumer decide to turn on all of those flags? I had considered this, but it seems like an exponentially bad nuisance; I would prefer to avoid it. |
@mrmr1993 how about controlling it through an env variable? it was my original intention but I wanted to avoid |
The issue is fundamentally the same: if the library has some configurations that expose the unlinkable symbols, then every library using it needs to support both the with and without configurations. It still makes most sense to me to have the bindings as a separate but related library. |
@mrmr1993 that is not the case, the environment variable will be picked up by ocaml-interop's build script even if crates depending on it change nothing. This is the build script (note that I removed the feature and use a plain const OCAML_INTEROP_NO_CAML_STARTUP: &'static str = "OCAML_INTEROP_NO_CAML_STARTUP";
fn main() {
println!("cargo:rerun-if-env-changed={}", OCAML_INTEROP_NO_CAML_STARTUP);
if std::env::var(OCAML_INTEROP_NO_CAML_STARTUP).is_ok() {
println!("cargo:rustc-cfg=no_caml_startup");
}
} No changes need to be made to ocaml-rs or any other crate for this to work. Then optionally this
With this change, As for splitting the crate, don't we still have the same issue as with the feature flag? it still leaks. ocaml-rs still needs to be updated to take that into account, for example. Maybe I am missing something but I don't understand why you wouldn't have the same problem as with the feature flag there. Upstream crates would still need to take it into account in the same way ocaml-rs would have to take it into account if ocaml-interop makes that change. To me splitting the crate, in principle sounds nice, but I think it requires quite a bit more work than https://github.com/tezedge/ocaml-interop/pull/36 (fix tests, update docs, then repeat for ocaml-rs, and more consideration about how it will affect code that works with ocaml-rs and ocaml-interop as they are now). Another aspect is that I don't think we fully understand the problem yet -- we know that The solutions I propose don't solve any of this, but are far less invasive. |
Btw I guess that in addition to (or instead of) |
Ok, here is why So another alternative would be to just have the necessary rules in dune (taking advantage of profiles) to also compile and link a |
Okay, makes sense. tezedge/ocaml-interop#37 seems to work for me with minimal changes.
This seems far scarier to me: code that depends on |
Code that depends on So yes, what you say about the behavior changing in that case is correct, but it is not a function that should ever be called in that situation (it is not even available from a regular |
Btw, in the case somebody misuses |
This sounds like a good compromise 👍 |
Ok, cool. This is what I have in mind for the C-stubs version, but the env-var driven change to how ocaml-interop gets compiled would do the same, but with a panic instead: #include <stdio.h>
#include <stdlib.h>
// The utop program generated by `dune utop` doesn't contain this symbol,
// causing the linking of ocaml-interop to fail.
// This file provides a dummy version of the function as a workaround.
void caml_startup(char **argv) {
fputs("ERROR: `caml_startup` cannot be called from a bytecode program\n", stderr);
fputs("Rust code that is called from an OCaml program should not try to initialize the runtime.", stderr);
exit(1);
} |
re: runtime crate split, I will open a new issue later so that the idea doesn't get lost. It is a bigger change and requires more thought and work. From the point of view of things being different when the program is Rust-driven from when the program is OCaml-driven, the split makes sense, because the dependencies and requirements are not the same. It is just that I don't think it is as simple as just splitting off |
Ah, I thought you were talking about going down the feature flag route, and replacing the body of |
Yes, sure, that works too and is actually the easier one to implement (I was experimenting with this pure-dune + stub solution, but turns out it is not that easy to make the dependency on the C stub optional). |
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
…bol. Will also be enabled if the `OCAML_INTEROP_NO_CAML_STARTUP` environment variable is set. This will make `OCamlRuntime::init_persistent()` a noop. The main motivation for this feature flag is to make code that uses ocaml-rs and ocaml-interop loadable from `dune utop`: zshipko/ocaml-rs#70
In an ocaml-rs project, for example ocaml-rust-starter, running
dune utop
gives the following error (while linking):Adding this function somewhere where the linker sees it, for example using the following code, "fixes" the issue and
utop
starts fine:OCaml version: 4.11.1
Rust version: nightly
GCC version: 10.2.0
The text was updated successfully, but these errors were encountered: