-
Notifications
You must be signed in to change notification settings - Fork 15
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
Enable ZMQ_HAVE_STRLCPY for GLIBC >= 2.38 #29
Conversation
d49008c
to
5ee5d55
Compare
src/lib.rs
Outdated
|
||
pub(crate) fn from_libc() -> Self { | ||
// SAFETY Call to libc via ffi. | ||
let ptr = unsafe { libc::gnu_get_libc_version() }; |
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.
This appears to be checking libc
on the current host that runs this "build script library", rather than testing if the headers/libraries intended for the target have a certain version?
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.
This does check the host libc version. In case of static linking this works fine. In case of dynamic linking, from what I recall, the binary will be compiled with the host's glibc version and should not run on the target unless it has a equal or newer version of glibc.
[...] rather than testing if the headers/libraries intended for the target have a certain version?
Isn't it the same? If you target gnu, your gonna need to compile with glibc.
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.
Here's few issues for reference on the equal or newer version requirement of glibc dynamic linking:
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.
If I'm wrong and the host libc can really differ from the target libc version, then i think the approach is to compile and run a program that outputs the libc::gnu_get_libc_version
.
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.
In case of static linking this works fine
In case of static linking your host has the static library of the target architecture, however;
then i think the approach is to compile and run a program that outputs the
libc::gnu_get_libc_version
.
This implies that your host can emulate the target architecture to run such a program.
The reason for the header check is because of the information about GLIBC in part comes from the sysroot.
Though afaik the sysroot/compiler is also available to set the GLIBC version and use older versions, in which case maybe the header emits the inline function as a fallback rather than the symbol? Or maybe older GLIBC header/library source is needed, I'm not too familiar with this.
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 have a lot of experience with cross-compilation and building binaries that work in multiple glibc versions. I also worked in a project that runs on ARM and cross-compiles this crate. So this is my input:
- glibc is forward-compatible. If you compile for a lower target glibc version, then the same binary works in systems with higher glibc version.
libc::gnu_get_libc_version
gets the host glibc version, so cross-compilation will fail.- The classic workaround that autotools and cmake are using, is to actually invoke gcc and the source code will be a small C code that just calls function in question. If the compilation succeeds then the function is supported, otherwise it doesn't.
Based on the above the best approach is what I describe in the 3rd point.
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.
Another option is to use __GLIBC__
and __GLIBC_MINOR__
and get their values with cc::Build::expand
.
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.
@oblique Thanks for confirming the above with additional context. Indeed, it should also be possible to extract the glibc version of te target sysroot via such environment variables.
The compilation test in C/C++ build-scripts is likely used in case a symbol is guarded by extra conditionals? Or more likely, to be interoperable across multiple libc standard library providers. Instead of hardcoding versions across platforms (for multiple symbols), this compile-test would for example run naturally on Android cross target instead of having to hardcode what we did in #20. Not saying that we should convert everything thus far to that though, but it's something to keep in mind.
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 think the best is to be used when we are not sure. For example in Android we are sure that exists, so we don't need to check via compilation test.
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.
For example in Android we are sure that exists, so we don't need to check via compilation test.
I haven't gone back very far at all though, but it seems very unlikely for anyone to go out of their way and build against even older versions of the NDK (which may still even contain the symbol).
@MarijnS95 I added a fallback for glibc dynamic compilation where we try to compile a simple c program linking against strlcpy in <string.h>. |
@MarijnS95 Alright, now the lib does a compilation check to see if strlcpy is in the std lib, like you originally proposed. At least now I understand the reasoning. Could you please tell me if this fixes your issue, since I don't have an easy way to replicate it. |
Co-authored-by: Yiannis Marangos <[email protected]>
Co-authored-by: Yiannis Marangos <[email protected]>
I tested the build on the latest glibc and works. Also a friend who had ready an environment for cross-compilation tested it and works there too, |
@oblique Cool thanks for the help. I'll wait on @MarijnS95 to merge since he opened the 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.
Works for me on Arch Linux with glibc 2.28
, many thanks!
Just have some minor code-cleanup comments.
Note that I don't currently use a GNU cross-compiling environment but thought about pointing it out regardless. I currently only cross-compile to Android which had this hardcoded for a long time (#20), though it looks like @oblique already took the task of testing cross-compiling to GNU targets on them, thanks!
src/lib.rs
Outdated
@@ -56,6 +56,24 @@ where | |||
Err(()) | |||
} | |||
|
|||
#[cfg(target_env = "gnu")] | |||
mod glibc { | |||
use std::path::Path; |
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.
Path
is already in scope in this file?
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 against super imports, i just find them less explicit. In this case, I don't see the point since this is just one line.
env, fs, | ||
fs::File, | ||
env, | ||
fs::{self, File}, |
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.
Personally I'm really against importing self
and then a few other items into scope, rather then also qualifying them with e.g. fs::File
if I really wanted to import fs
into scope.
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.
Do you have some logical reasoning behind that? I don't really care about that stuff personally.
// Attempt to compile a c program that links to strlcpy from the std | ||
// library to determine whether glibc packages it. | ||
pub(crate) fn has_strlcpy() -> bool { |
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.
If this is intended to be a (developer-only) doc comment:
// Attempt to compile a c program that links to strlcpy from the std | |
// library to determine whether glibc packages it. | |
pub(crate) fn has_strlcpy() -> bool { | |
/// Attempt to compile a c program that links to strlcpy from the [`std`] | |
/// library to determine whether glibc packages it. | |
pub(crate) fn has_strlcpy() -> bool { |
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.
What's the benefit? It won't show up in the generated docs. Is a GUI code editor thing?
src/lib.rs
Outdated
// Attempt to compile a c program that links to strlcpy from the std | ||
// library to determine whether glibc packages it. | ||
pub(crate) fn has_strlcpy() -> bool { | ||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/strlcpy.c"); |
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.
If this pattern occurs more often, the CMake macro generates this file on the fly and doesn't even make any attempt to reproduce the signature faithfully.
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. If in the future, if we need to do anything more complex, it would make sense to dynamically generate a file containing all the required links. For now I think its fine.
src/lib.rs
Outdated
pub(crate) fn has_strlcpy() -> bool { | ||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/strlcpy.c"); | ||
|
||
cc::Build::new() | ||
.cargo_metadata(false) | ||
.warnings_into_errors(true) | ||
.file(path) | ||
.try_compile("has_strlcpy") | ||
.is_ok() | ||
} |
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 just found the existence of get_compiler
. With that we can actually link and create an executable, which is more correct. So this is better:
pub(crate) fn has_strlcpy() -> bool {
let c_src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/strlcpy.c");
let bin =
PathBuf::from(env::var("OUT_DIR").unwrap()).join("has_strlcpy");
println!("cargo:rerun-if-changed={}", c_src.display());
cc::Build::new()
.warnings(false)
.get_compiler()
.to_command()
.arg(c_src)
.arg("-o")
.arg(bin)
.status()
.expect("failed to execute gcc")
.success()
}
You can also revert to the older comment.
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.
Does it need to link, given that the CMake scripts AFAIK don't do that either?
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.
No we don't need to link as long as strlcpy.c
produces only the warning we are interested in. If in the future gcc releases a new warning appears, then build will fail.
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.
Warning flags might even get picked up from cc-rs
-defined environment variables, skewing the result?
CMake does a bunch of tweaks to that:
https://github.com/Kitware/CMake/blob/master/Modules/CheckSymbolExists.cmake
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 also use the address of the symbol with a cast to support generic cases (something we don't need to do now) to not have to replicate the function signature.
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.
Warning flags might even get picked up from cc-rs-defined environment variables, skewing the result?
Yes. cc-rs
is aware of CFLAGS
and CFLAGS_<target>
environment variables.
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 think linking is preferable to reduce the risk of false positives.
@oblique @MarijnS95 Thanks again for your time. I'll do a release. |
Thanks a lot for the quick fix for this! EDIT: Disregard the following. It looks like the I noticed that the error still appears in cross compilation on Linux for Windows. Any idea why this would be? I have a vague memory of platform checks not working as expected because the build script still compiles on Linux and then runs "on Windows," or something like that. So I sometimes have to check the platform with The output of
|
@s-nie Can you create an example repo that reproduces the issues? |
I updated the comment, it was an issue with |
I'm still confused :) Is there an issue when |
So I thought it was just an issue with cache invalidation in sccache. It keeping the pre-fix configuration around would have explained this. But I just tried again with [build]
rustc-wrapper = "sccache" The sccache version is 0.5.4. Then, running |
Would you mind creating a new separate issue, because this is hard to follow since this is a pull request thread. |
#[cfg(target_env = "gnu")] | ||
if !has_strlcpy && glibc::has_strlcpy() { |
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 think this cfg
checks the target_env
of the host. Adding this snippet right above:
panic!(
"CARGO_CFG_TARGET_ENV: {:?} target_env=gnu?: {}",
env::var("CARGO_CFG_TARGET_ENV"),
cfg!(target_env = "gnu")
);
And run with:
cargo b --target x86_64-pc-windows-msvc -p testcrate
Gives the following output:
thread 'main' panicked at 'CARGO_CFG_TARGET_ENV: Ok("msvc") target_env=gnu?: true', src/lib.rs:429:9
confirms that.
Fixes #28
@MarijnS95 This approach checks
glibc
version. This results in better compilation speed than the CMAKECheckCXXSymbolExists
approach which involves compiling multiple times. Please tell me if it fixes your issue.This should work for any platform that uses
glibc
.Open questions:
target_env
== "" as being valid for historical reasons so I wonder if some GNU target might have that field empty.https://doc.rust-lang.org/reference/conditional-compilation.html#target_env.