I have some C code that is being rewritten in Rust. Now, the C code uses "HAVE_MMAP" macro, which is automatically defined if your system has the 'mmap' function. How could one go in replicating this behaviour in Rust? Is cfg integrated with C macro system, so that #[cfg(HAVE_MMAP)] should just work?
So the straight up answer would be to do the check in a build.rs script and set a cfg from there. See https://doc.rust-lang.org/cargo/reference/build-scripts.html (and I believe there are also some crates that can be used to help detect features from system C libraries, rather than writing the checks yourself).
But I feel like the better solution would perhaps be to use a crate that abstracts over the different ways to do memory mapping on Unix and Windows systems. One such example is memmap2 — Rust memory management library // Lib.rs
The code is actually more complicated than that, so other macros such as "HAVE_SYS_STAT_H" is also used, besides others related to audio drivers. I read the build-scripts reference page, including the examples, and while I understood how one would go to bind a system library like zlib, I still haven't figured out how to adapt to it my use case. Could you provide a minimal example code using Rust build script and some system macro like HAVE_MMAP?
Build scripts is really not my expertise (I have been happy to use existing bindings for platform features). And I would primarily look at using existing cross platform crates for the various functionalities I'm interested in. Only if that is unavailable would I consider making my own bindings.
Something like "does sys/stat.h
exists?" is not really interesting to a Rust application. It is only relevant to the bindings-crate that exposes the raw FFI signatures. The general approach taken in Rust is to have:
- A
-sys
crate (e.g.openssl-sys
). This consists of bindings to the underlying C library of interest. Often these are mostly auto-generated with bindgen (for C) or cxx/autocxx (for C++). For standard Unix C library features (whichsys/stat.h
would belong to) this is handled by the libc crate. There are similar crates for Windows APIs (but the details of that is outside my area of expertise). - A wrapper crate for the
-sys
crate (e.g.openssl
) that exposes a safe Rust-idiomatic API on top of the bindings. For exposing Unix-specific system functionality one of the more popular ones is nix. There is also rustix which is more heavily slanted to Linux specific features. There are similar crates for Windows as I understand it but that is outside my area of expertise. - There are also crates that try to abstract over Windows vs Unix differences for a particular type of task (e.g. memory map files, polling sockets, talking to the OS keyring/secrets store, ...). These may be better than using the nix crate directly (nix has some support for Windows, but it is not as good as the support for Unixes).
There is a reason for this multi-level approach: Cargo only lets a single crate link to a given external library (to ensure consistency with which version of that library is pulled in I believe, don't quote me on that). If the -sys
-crate just locates & links the library and exposes the API, then multiple higher level crates can use this sys library if needed, without creating conflicts between themselves.
So, in conclusion while you can roll these low level bindings yourself, you shouldn't have to in Rust. I'm suspecting a bit of a XY problem in your original question.
For Rust you really should go through this checklist first:
- Does the standard library expose what I want? (It too uses the libc and windows-equivalent crates internally.)
- Is there a nice, well maintained, trustworthy library in the ecosystem that does what I want already? (In a cross platform way)
- Are there a non-cross platform (well maintained, trustworthy) crates that do what I want that I can abstract over (switching as needed)?
- Does libc (or similar crates) expose the raw functions I need at least?
- Oh well, time to write my own bindings crate then (or contribute to an existing library to add the missing binding, if appropriate).
Some resources on making your own bindings:
I actually followed your checklist and found a safe crate that does exactly what I need (at least for Unix systems). Thanks for the help!
For the future if anyone do actually need to do these checks themselves, I just saw a post on reddit about a library to help write such checks in build.rs: rsconf — Cargo library // Lib.rs
It seems fairly new, and was apparently written for the currently work in progress port of the fish shell to Rust. So it probably has some amount of traction behind it thanks to that even though it is very new and not (yet?) wildly used.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.