danielpclark / rutie

“The Tie Between Ruby and Rust.”
MIT License
939 stars 62 forks source link

Windows GCC Support: Ruby 3.1 windows linker error (missing x64-ucrt-ruby310.lib?) #160

Open Speak2Erase opened 2 years ago

Speak2Erase commented 2 years ago
LINK : fatal error LNK1181: cannot open input file 'x64-ucrt-ruby310.lib'

It looks like rutie tries to link against x64-ucrt-ruby310.lib, which as far as I am aware, is nonexistent, at least in ruby builds from rubyinstaller and a fresh build off of git with msys2.

Forgive me if I'm mistaken, but I believe .lib files are produced when compiling something with MSVC, not GCC from msys2.

I went ahead and compiled ruby using msys2 straight from git, and couldn't find any .lib files anywhere. image I also checked an old MSVC ruby build I have on hand, and it did spit out a handful of .lib files. image

It looks like rutie is linking against the wrong Ruby library on Windows, and should instead be linking against something else.

MolotovCherry commented 2 years ago

+1 This is happening to me as well. I can't find any lib files to link to, so unsure how to solve this

So what I did was, I renamed libx64-ucrt-ruby310.dll.a to x64-ucrt-ruby310.lib, and the error went away. But now I get

          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.9.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.1.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.15.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.4.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.8.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.11.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.5.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
          D:\...\rust\target\release\deps\highlighter.dll : fatal error LNK1120: 1 unresolved externals

Edit 1: Been messing with the build script; replaced the entire main function with this, and it appears even this causes the problem

    println!(r"cargo:rustc-link-search=D:\Ruby31-x64\bin");
    println!(r"cargo:rustc-link-search=D:\Ruby31-x64\bin\ruby_builtin_dlls");
    println!("cargo:rustc-link-lib=dylib=x64-ucrt-ruby310");

Perhaps the problem is Rust or VC that's doing this. It refuses to link against the dynamic lib even though the search path and syntax seems right

danielpclark commented 2 years ago

Forgive me if I'm mistaken, but I believe .lib files are produced when compiling something with MSVC, not GCC from msys2.

@Speak2Erase yes the Windows implementation is not thorough at all. I don't run the Windows OS other than an occasional VM so I put in enough effort to get a MVP out for Windows. I'd be glad to accept a PR to implement GCC support for the Windows OS from someone who develops natively in that OS.

MolotovCherry commented 2 years ago

@danielpclark Is it possible to dynamically link on Windows with msvc? Are there some steps that I missed? I followed the guide on the main page as much as I could, and everything seems fine (Ruby is shared and everything). The ruby conf function in the build script outputs all the right data too (I manually checked each one).

Regardless of that however, no matter how much I try to do a dynamically linked build, it seems to fail. Support seems to be baked into the build script too. As afar as I can tell, Rust still errors out about static libs even when telling it to dynamically link and providing the right search path (leads me to believe it's a Rust error not a build script error, but I could be wrong)

I'm happy to do testing or gather info if needed so this can get fixed asap as it's a pretty big blocker for anyone using the library

Speak2Erase commented 2 years ago

yes the Windows implementation is not thorough at all. I don't run the Windows OS other than an occasional VM so I put in enough effort to get a MVP out for Windows. I'd be glad to accept a PR to implement GCC support for the Windows OS from someone who develops natively in that OS.

I'll see if I can pull together a pull request.

danielpclark commented 2 years ago

@cherryleafroad Dynamically linked builds are more supported across operating systems but statically linked works with some caveats. There are tests for both statically built and dynamically built.

The windows build support was implemented 4 years ago ... looking into the CI logs it seems my focus was to get it to link properly and half of the implemented tests passed at the time. So it seems my MVP was "half of Rutie can run on Windows without issue". I didn't investigate as to whether the rest would work with a relatively easy fix or if they're multiple adjustments to be made. It's quite possible GCC will work much better as it works fully natively on Linux. Here's the last CI build I see that focussed on Windows support https://travis-ci.org/github/danielpclark/rutie/builds/445816834

I need to update the CI to point at Travis' new URL and I need to update the tests to reflect Ruby 3+. I plan to do that soon but I have a few big tasks around the house to get a big raised garden going first.

danielpclark commented 2 years ago

I'll see if I can pull together a pull request.

@Speak2Erase thank you so much!

golirev commented 1 year ago

I think I've had some progress, so I'd like to share.

Ruby on Windows is mostly RubyInstaller2 which is the Gnu toolchain, whereas Rust is mostly the MSVC toolchain version, which complicates this issue. Assuming the use of RubyInstaller2, using the GNU toolchain version of Rust is less problematic, and I have confirmed that it actually works, but there probably isn't much demand for it.

Ruby: x64-mingw-ucrt Rust: stable-x86_64-pc-windows-msvc I'll see if it works with this combination.

@MolotovCherry

So what I did was, I renamed libx64-ucrt-ruby310.dll.a to x64-ucrt-ruby310.lib, and the error went away.

I'm not sure if the two formats were fully compatible. As far as I have found, it would be better to generate the lib file from the DLL. https://gist.github.com/snipsnipsnip/149568/10aed7ad493c0c476634dc89c974c72826d0e880

Next point,

      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.9.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.1.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.15.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.4.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.8.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.11.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      librutie-00a95149a849af5b.rlib(rutie-00a95149a849af5b.rutie.b8a77c17-cgu.5.rcgu.o) : error LNK2001: unresolved external symbol rb_cObject
      D:\...\rust\target\release\deps\highlighter.dll : fatal error LNK1120: 1 unresolved externals

rb_cObject is a DLL's public data symbol. It needs to be treated as __declspec(dllimport) in C/C++.

https://learn.microsoft.com/en-us/cpp/build/importing-into-an-application-using-declspec-dllimport?source=recommendations&view=msvc-170

However, you must use __declspec(dllimport) for the importing executable to access the DLL's public data symbols and objects.

I'm not sure how to dllimport the data symbol in Rust. However, I found the related discussion "Correctly handle dllimport on Windows". https://github.com/rust-lang/rust/issues/27438

I tried #[link(name="x64-ucrt-ruby310")]. And it worked for me.

src/rubysys/mod.rs

+//#[link(name="x64-vcruntime140-ruby310")] ... for Ruby(x64-mswin64_140) +#[link(name="x64-ucrt-ruby310")] ... for Ruby(x64-mingw-ucrt) extern "C" { pub static rb_cObject: Value; }

Without the #link direction: Ruby(x64-mingw-ucrt) +Rust(stable-x86_64-pc-windows-msvc) -> 'cargo build': GO, 'rake test': NG

With the #link direction: Ruby(x64-mingw-ucrt) +Rust(stable-x86_64-pc-windows-msvc) -> 'cargo build': GO, 'rake test': GO

FYR Without the #link direction: Ruby(x64-mingw-ucrt) +Rust(stable-x86_64-pc-windows-gnu) -> 'cargo build': GO, 'rake test': GO Ruby(x64-mswin64_140)+Rust(stable-x86_64-pc-windows-msvc) -> 'cargo build': NG(rb_cObject link error), 'rake test': -

With the #link direction: Ruby(x64-mswin64_140)+Rust(stable-x86_64-pc-windows-msvc) -> 'cargo build': GO, 'rake test': - . Unable to run due to other reason. It should work.

golirev commented 1 year ago

Could you please try this?

diff --git a/build.rs b/build.rs
index d4494cb..3308e12 100644
--- a/build.rs
+++ b/build.rs
@@ -207,7 +207,7 @@ fn ruby_lib_link_name() -> String {
 fn dynamic_linker_args() {
     let mut library = Library::new();
     library.parse_libs_cflags(rbconfig("LIBRUBYARG_SHARED").as_bytes(), false);
-    println!("cargo:rustc-link-lib=dylib={}", ruby_lib_link_name());
+    println!("cargo:rustc-link-lib=dylib=dylib:{}", ruby_lib_link_name());
     library.parse_libs_cflags(rbconfig("LIBS").as_bytes(), false);
 }

diff --git a/src/rubysys/mod.rs b/src/rubysys/mod.rs
index baee579..14072a2 100644
--- a/src/rubysys/mod.rs
+++ b/src/rubysys/mod.rs
@@ -19,6 +19,7 @@ pub mod vm;

 use rubysys::types::Value;

+#[link(name="dylib")]
 extern "C" {
     pub static rb_cObject: Value;
 }
danielpclark commented 1 year ago

I'll make an effort to give this a try soon.

PieterBlomme commented 1 year ago

Chipping in on this, I have tried @golirev 's fix and the following procedure works (for me):

cargo build --release ✔️ rake test ✔️

It's a not a solution that I'm very comfortable with though ...

golirev commented 1 year ago

I was able to get "x64-ucrt-ruby320.lib" by excluding ".weak." from exports.def.

However, when executing "cargo build --release" at $RUTIE_ROOT\example\rutie_ruby_example, the x64-ucrt-ruby320.lib will not be generated as $RUTIE_ROOT\example\rutie_ruby_example\target\release\deps\x64-ucrt-ruby320.lib

but is generated as $RUTIE_ROOT\target\release\deps\ex64-ucrt-ruby320.lib

Because the example's dependency library is set to "..\.." in $RUTIE_ROOT\example\rutie_ruby_example\Cargo.toml.

[dependencies]
rutie = { path = "../../" } 

So, you'll need a build.rs for the example to specify the x64-ucrt-ruby320.lib.

#[cfg(target_env = "msvc")]
use {
    std::path::Path,
    std::env,
};

fn main() {
    // If windows OS do windows stuff
    windows_support();
}

#[cfg(target_env = "msvc")]
fn windows_support() {
    let deps_dir = Path::new("../../target").join(env::var_os("PROFILE").unwrap()).join("deps");

    #[cfg(target_env = "msvc")]
    println!("cargo:rustc-link-search={}", deps_dir.to_string_lossy());
}

#[cfg(not(target_env = "msvc"))]
fn windows_support() {}

I haven't found a way to get the .lib output directory in a more general way.

The simplest solution is to put the generated x64-ucrt-ruby320.lib in the same directory as below. libx64-ucrt-ruby320.dll.a.

eg. C:\rubyinstaller-3.2.0-1-x64\lib\x64-ucrt-ruby320.lib ... I use 7-ZIP archive version.