-
-
Notifications
You must be signed in to change notification settings - Fork 114
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
Generate gir without link attribute #1508
Conversation
move g_param_spec_types to gobject_sys and add a prefix on windows to avoid linker errors
glib/gobject-sys/src/manual.rs
Outdated
use glib_sys::GType; | ||
|
||
// `g_param_spec_types` extern binding generated by build.rs | ||
include!(concat!(env!("OUT_DIR"), "/param_spec.rs")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great if we could avoid such things because that makes the crate unusable with meson's cargo subproject support for the time being.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree, I'm not too happy with the current solution either. Ideally we could skip the code generation part and have the attribute read the link name from an env variable set by the build.rs, or something similar. However, it seems that derive attributes can't read constants yet.
#[cfg_attr(target_os = "windows", link(name = env!("...")))]
extern "C" {
pub static g_param_spec_types: *const GType;
}
glib/gobject-sys/build_manual.rs
Outdated
let out_dir = env::var("OUT_DIR").unwrap(); | ||
let out_path = Path::new(&out_dir).join("param_spec.rs"); | ||
|
||
// Generating a link attribute is necessary in windows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check if e.g. gtk4-rs or gstreamer-rs are also accessing some library constant? That would have the same issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I tested gstreamer-rs and I just checked gtk4-rs, and the only use seems to be on glib. At least on github no other crates seem to use this specific constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not talking about this specific constant, but generally constants from shared libraries :) I thought in gstreamer-rs I used some but I can't remember exactly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They do use constants, but they are generated by gir and are all defined as pub const ...
. The thing with this one is that it is not added to gir files and that it is defined but not initialized in the header https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gparamspecs.h#L1147. So maybe that has to do with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I found the difference between the #[link]
attribute and adding them on the command line. Here is the code that generates libraries with the attribute (https://github.com/rust-lang/rust/blob/170d6cb845c8c3f0dcec5cdd4210df9ecf990244/compiler/rustc_metadata/src/native_libs.rs#L452), which sets foreign_modules
and dll_imports
. If we rename the libraries when they have the link attribute already set, they use the existing metadata and it works. However, when adding a library that is not mentioned in #[link]
, the dll imports is not set (https://github.com/rust-lang/rust/blob/170d6cb845c8c3f0dcec5cdd4210df9ecf990244/compiler/rustc_metadata/src/native_libs.rs#L541).
I'm not sure how feasible it is to modify the behaviour of the linking there, because it needs access to the foreign items in the module to call dllimport, and it is doing that with the macro.
Did you check what would be necessary for solving this in rustc, if it's indeed a bug? Shared libraries on Windows are a bit special and it wouldn't surprise me if this is actually necessary in more cases than just library constants and we'll get another surprise failure in the future if we don't follow the recommended procedure (link attribute). It would be good to at least understand why this happens, why the attribute solves it and what the correct way forward is. |
I tried to dive into the source code to see what is happening. The code that handles the _imp creation seems to be here and here. I'm going to check where the
Yeah, I agree. Me and @thiblahute have spent all week trying to figure out what the problem is but it is a very strange issue. I'm going to dive a bit more to see if I can find more answers |
330ec5b
to
15aeddd
Compare
I think that manual |
It will, but it is possible to feature gate it. I was just trying this workaround I found here https://github.com/aldanor/hdf5-rust/blame/master/hdf5-sys/src/lib.rs#L11C1-L18C2 (they only do it for windows and not static). I have been looking at how other libraries solve this issue. Some of them use the generated link attribute for windows approach (see here). Others link the library and then rename it, and others load the dll directly using other crates. Maybe we can get around with linking gobject statically? System-deps has an option for that EDIT: Yesterday I also tried to figure out what was happening on the C library side, and I think MSVC may not consider it a bug but rather intended design to not export the extern variable (https://developercommunity.visualstudio.com/t/const-extern-static-library-does-not-export-symbol-1/816981). |
That's not really an option. That's the decision of whoever builds the software, not us. |
Does this mean that this is actually a bug in GLib then with the way how the variable is exported? |
I am not sure if I am reading this right, but running
So my guess is that this allows the other symbols to work without the For context, the variable is exported here: https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gparamspecs.h#L1147 and assigned here: https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gparamspecs.c#L1374 And the macro defintions are built here: https://gitlab.gnome.org/GNOME/gtk/-/blob/main/build-aux/meson/gen-visibility-macros.py#L117. |
bb09463
to
3e20a30
Compare
I updated the description to reflect this new approach. The only way within Rust at the moment to call dllimport is the The link attribute for this variable was not I think we should also comment on rust's issue section to discuss our use case and how better tooling would be helpful. What do you think of this @sdroege? |
An issue for Rust would be very useful, yes. Also this will have to be fixed one way or another properly, all this code is just a hack and the build.rs pieces are rather fragile and I'm sure we'll have to deal with various follow-up issues here until it doesn't break in unexpected situations. I think just checking |
Yeah, I agree that this should be fixed properly, and that these types of workarounds could be dangerous. I don't see an easy way of fixing dllimport in rust when linking on the command line since it relies on the attribute getting the symbols from the extern block. However, if some improvements are made in how the In the meantime, to avoid being blocked on If you think this is a better way forward, we can wait to merge this pr until 1.80 is there, but keep working on the linking with |
That's a good idea and this will actually work because the types are registered in
|
Hi @eerii , coming here from the GStreamer team. I'm having trouble following what you've attempted here. If I understand correctly (please point me out if mistaken), you're facing linking issues with only those imports that are Can you point me out to what's holding up the PR, so that I can help? 🙇 |
Awesome. Yes, this feels much better than attempting to hack the linker. Thank you for all of the previous input! And yes, I have commented on the relevant issue in Rust (rust-lang/rust#27438) and offered some possible alternatives, to at least get better support for working with the
Oh that's great! Should I then remove the |
Yes
I would pass the name as a NUL-terminated string to the macro (e.g. |
Hi @amyspark! Thanks for checking in. That's right, when linking to dlls in Windows they need to "hop" through a middle symbol. However, for functions it exposes both names pointing to the same place, but for data types it only exposes the one prefixed by
Yes, in that hack we had to manually dereference the
In the end, since this is a limitation of the compiler, and hacks probably bring more issues than anything, we ended up opting to remove the variable from the bindings since it was only internal and there was an alternate way of accessing the same information. I think now it should be mostly ready, specially if we don't need to wait for MSRV 1.80 for |
I passed the string (and then concat the nul at the end statically in the macro). Concating literals at compile time is easy, but doing the same with slices not so much. There is the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me otherwise, thanks :) Can you also update the gir submodule with the version that was merged now and then regenerate?
Also, I assume you'd need this backported to the latest stable release?
To be clear, I don't expect you to do the backports. I'll do that before the next bugfix release |
That would be great, thanks! |
Thanks for untangling all this :) |
You also create a PR for gtk4-rs? |
I didn't, since gstreamer-rs didn't depend on it, but I can absolutely open one :) |
The gst plugins do actually, but nonetheless, I would appreciate that :) |
Sorry, I didn't realize that! I opened gtk-rs/gtk4-rs#1840 c: |
Backport is in #1520 . I've squashed it all into a single commit to remove all the noise. Sorry for not noticing that before merging here. |
Thank you! I'm sorry for not merging the commits together here. |
Motivation
We are working on
gstreamer-full
, a big static library with gstreamer and its dependencies bundled together. We would like the user to be able to link against this library without having the dependencies installed. This is handled bysystem-deps
, since using custompkg-config
files we can specify the location and name of every library. Using a.pc
file like so:With a
glib-2.0.pc
file like this,system-deps
can adjust the linking arguments of the compiler, turning-lglib-2.0
into-lgstreamer-full-1.0
.This is great, but one problem remains. Apart from the generated
build.rs
files for each-sys
dependency,gir
is also generating#[link]
attributes for eachextern "C"
block. For example:This is not necessary, and it is causing cargo to add an extra
-lglib-2.0
(and so on) on top of thesystem-deps
generated ones. This ones are independent and are not renamed, so they cause linking errors if the library is not installed.What we tried
Using
system-deps
environment variables orrustc-link-lib
for renamingBefore changing the code generation and removing the
#[link]
attributes, we tried to find a way around them. Cargo has a way of renaming already linked variables, using therustc-link-lib
directive. It can be done like so:Additionally,
system-deps
has a wrapper around this featuring, allowing to set the env variable SYSTEM_DEPS_NAME_LIB to any value, which will in turn callrustc-link-lib
.This is handy, but the issue is that it doesn't directly allow passing down instructions to dependencies, while configuring them. Additionally, if we just set the
system-deps
env variable, it will fail for crates that don't have that specific#[link]
attribute inside them.Cargo.toml
links
keyTo help with the issue of modifying the build of dependencies from the dependent crates (in this case, have
gstreamer-rs
change the libraries thatgtk-rs-core
links against) there is thelinks
manifest key. This allows to specify what system package a crate links to. But more importantly, it allows to overwrite thebuild.rs
script by providing the linking values manually. For example:This method works great, instead of running the
build.rs
ofglib
, it will just tellrusct
to link againstgstreamer-full
. However, there is one issue that it currently has. As opposed to other manifest values, it can only be set for specific targets, so it would have to be repeated for every supported target. Furthermore, it doesn't allow anycfg(...)
directive, or toggling via features, or anything similar. This is problematic since we want to allow the user to easily choose what type of linking they want to do (or even do it automatically). We intend comment on the issue on rust to hopefully allow for further configuration here in the future, but at the moment it is not really viable.Just remove the link attributes
Removing the
#[link]
attribute works in almost all cases. It compiles perfectly on Linux, but there is one undefined symbol error on Windows,g_param_spec_types
. We did some investigation onto why this is happening. Inspecting thegobject
dll library on Windows we found that every symbol is reexported without the__imp_
prefix except for this one:We still don't fully understand why the library is being compiled this way, though the theory is that it has to do with the fact that
g_param_spec_types
is a constant and that all of the other symbols are functions. We also don't know exactly why it works with the#[link]
attribute and not when linking via rustc flags likesystem-deps
does, since they are supposed to be equally valid. Maybe this is a bug of rust compiler codegen, the original RCF for linking libraries with MSVC says this may be a flaw of the implementation. We will also ask on the relevant issues about this behaviour and if we can do something to make it work. This issue seems to be related.Add the
__imp_
prefix on WindowsA workaround that we found was that we could use the
#[link_name]
attribute to change the linking name on Windows to have the__imp_
prefix, since this seem what the#[link]
attribute does.Now it links correctly and it build again, but there are fails on the tests. We suspect that the
#[link]
attribute is doing more than just setting this prefix and that there are some pointer issues underneath.Since this was already a hacky solution and the proper way of fixing it would be to know the underlying issue on why this symbol is not being linked properly, we decided to move to another method.
Generate the link attribute on windows
Another option (the one that was first proposed on this PR) was to create a custom
build.rs
script forgobject-sys
that only added the necessary#[link]
attribute tog_param_spec_types
on Windows. It also allowed for customization of the name of the linked library with analias
varaible on thepkg-config
files. This removed the issue of passing down environment variables or rustc flags to dependencies and did not break the current behaviour.However, as noted by @sdroege, the code generation paired with
include!
would break the meson cargo subproject support at this time.Proposed solution
#[link]
attributes bygir
, since they don't seem to serve any purpose when we are already linking withsystem-deps
and they make alternate linking difficult.#[link]
attributes.g_param_spec_types
extern definition fromglib
togobject-sys
and create wrapper function. It seems more sensible and clean to have the direct calls to the C library in the-sys
package, though this is not required for this solution to work.When using the
#[link]
attribute, Cargo setsdllimport
on the library symbols. This only happens when using the attribute, and not when linking on the command line, because the code generation needs to access the symbols to emit the annotations and that can only currently be done with the first.However, even if calling
dllimport
is not feasible, underneath it is only dereferencing the__imp_
pointer to the underlying symbol. This is not a problem with functions since they export both symbols, with__imp_
and without it, but variables don't. So, when using Windows and linking dynamically, we can manually do this.link_name
to link to the__imp_
symbol on Windows when linking dynamically.system_deps
to also find out if the library is being linked statically or dynamically, and if the compiler is MSVC. If so, set a newcfg
condition that we can use to choose the linking method.#[link]
, we are not adding more overhead) when callingg_param_spec_types
on Windows.Caveats and future
This is the best compromise we could find to allow for overriding the linked libraries easily from higher up the tree. If at any point the
links
key allows for overriding the build based on specificcfg
flags, or libraries can be more easily overwritten, those methods are a better fit and they would require less specific code.Related MRs