Statically linking screen_capture_lite in Rust - rust

I am attempting to write a Rust wrapper for screen_capture_lite, my code for which is located here. I've gotten the cmake crate compiling screen_capture_lite from source, but I get 'unresolved external symbol' linker errors when trying to run my window_count example script. These errors appear to be for Windows API symbols or C++ standard library symbols. Here is a pastebin with the errors.
From what I've figured out about linking, I assume this is because I'm trying to statically link to screen_capture_lite, which gets rid of screen_capture_lite's dynamic dependencies. This seems true, because when I link to all of the system libraries manually, my window_count example script works and correctly reports the number of open windows. This is what my build script looks like when I do that:
extern crate cmake;
use cmake::Config;
fn main() {
let dest = Config::new(".").profile("Release").build();
println!("cargo:rustc-link-search={}", dest.join("lib").display());
println!("cargo:rustc-link-lib=static=screen_capture_lite");
println!("cargo:rustc-link-lib=kernel32");
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=gdi32");
println!("cargo:rustc-link-lib=winspool");
println!("cargo:rustc-link-lib=shell32");
println!("cargo:rustc-link-lib=ole32");
println!("cargo:rustc-link-lib=oleaut32");
println!("cargo:rustc-link-lib=uuid");
println!("cargo:rustc-link-lib=comdlg32");
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=dwmapi");
}
Manually linking the system libraries doesn't seem like a good solution to me, so is there a way to export a list of paths from cmake to a file for my build script to consume?

Related

rust libraries with cargo (rlib)

I am trying to create a library in rust to be used with rust executables. In C you can just create your .a or .so (or .lib or .dll on windows) and use tools like CMake to link everything, however rust does not seem to have this kind of infrastructure?
It is possible to make an executable with cargo (cargo new ) and create a library by adding the --lib flag (cargo new --lib), but then how would you use the resulting .rlib file (from the library cargo project)? I managed to link the .rlib file as follows:
rustc main.rs --extern foo=libfoo.rlib
and that works beautifully, though, I am not interested in writing a thousand rustc commands to build the final executable (which depends on the .rlib) if there is cargo that can do that for you. I tried working with a build script (which works perfectly for any C library, static or dynamic), but if I try it with the .rlib file, cargo says that it cannot find "foo" (-lfoo), the build script:
fn main() {
println!("cargo:rustc-link-search=.");
println!("cargo:rustc-link-lib=foo");
}
I tried replacing the path (search) to different directories (whilst also moving the .rlib file to the correct directory), also tried different combinations of libfoo, libfoo.rlib, ... (note that for the C libaries, foo is sufficient).
So my question really is: How can you create a rust library for private use, and how do you use it with a rust executable in a proper way, avoiding manual rustc commands? Are there tools that do this? Am I missing something in the build script? Perhaps there exists something like CMake for rust?
I suppose it is possible to just create a C interface over the rust code and compile another C project as that does work with cargo.
I do NOT want to publish the code to crates.io as I want this library strictly for private use.
Cargo does not support using pre-compiled .rlibs. Cargo is designed to compile programs fully from source (not counting native libraries).
How can you create a rust library for private use … I do NOT want to publish the code to crates.io as I want this library strictly for private use.
To use a private library, you write a dependency using a path or git dependency (or use a private package registry, but that's more work to set up).
[dependencies]
my-lib-a = { path = "../my-lib-a/" }
my-lib-b = { git = "https://my-git-host.example/my-lib-b", branch = "stable" }
Your private library is now compiled exactly like a “public” one.

Why Rust build.rs is unable to find C headers?

Basically I am trying to cargo build a crate which has a build.rs file.
This crate is located inside a bigger project and it's supposed to be a lib crate.
And inside this build.rs file, I am trying to compile a .C file which includes a couple of headers.
Fun fact: I got this build.rs file and crate structure from another little demo crate, and in that demo crate I had no problem to compile this exact C file with these headers.
FULL ERROR HERE:
Here is a link to github: https://github.com/mihaidogaru2537/FirecrackerPlayground/tree/dpdk_component/firecracker/src/dpdk_component
There is the crate I am talking about and you can also see the bigger project in which it resides. In the README file you can see the full error.
Either I do cargo build from the root of the big project or from the root of this problematic crate, the error is the same.
"cargo:warning=/usr/include/asm-generic/errno.h:5:10: fatal error: asm-generic/errno-base.h: No such file or directory
cargo:warning= 5 | #include <asm-generic/errno-base.h>"
The missing file might change depending on the .flag("-I/path/..") calls I am doing inside the build.rs
As you can see, right now it's unable to find errno-base.h, but I am including the path to asm-generic.
Here is the code of the build.rs file from the crate where the compilation of this C file works, as you can see, I did not have to add any include flags before calling compile.
fn main() {
// Tell cargo to tell rustc to link the system bzip2
// shared library.
// println!("cargo:rustc-link-lib=rte_ring");
// println!("cargo:rustc-link-lib=rte_mempool");
// Tell cargo to invalidate the built crate whenever the wrapper changes
// println!("cargo:rerun-if-changed=wrapper.h");
let _src = ["src/static-functions.c"];
println!("cargo:rerun-if-changed=build.rs");
let mut builder = cc::Build::new();
let build = builder
.file("src/static-functions.c")
.flag("-Wno-unused-parameter");
build.compile("foo");
}
Additional info:
The problematic crate is pretty small, see the link above. There is the build.rs file, C file and header file is inside the include directory.
One thing that I suspect, is that the target of the bigger project:
TARGET = Some("x86_64-unknown-linux-musl")
might affect the way the C file is compiled.
In the project where the compilation is working, I am not using that linux-musl stuff.
I am a total noob when it comes to Rust, but I do have a decent understanding of how C/C++ works.
I am running the project on Ubuntu 20.04
Those missing headers are a result of importing DPDK headers, I have DPDK libraries installed on the machine in question.
Let me know if you have any questions, sorry for the long read and thank you.
I somehow managed to fix it by adjusting the cargo build command to use x86_64-unknown-linux-gnu as target instead of x86_64-unknown-linux-musl (By default cargo build was doing the musl target somehow)
So if you are trying to build a rust app which is using DPDK libraries and you are getting missing headers, make sure to try the following:
cargo build --target=x86_64-unknown-linux-gnu
Well if you have to use musl and there is no alternative, I don't have an answer. But to me this was enough.
If someone has an explanation why musl is not working in this scenario, please let us know.
Reddit Rust community helped me as well, check this link out if you are interested:
https://www.reddit.com/r/rust/comments/mo3i08/unable_to_compile_c_file_inside_buildrs_headers/
So Why build.rs was unable to find .C headers?
ANSWER
Because I was using x86_64-unknown-linux-musl as target.

How do I pass the linker the -rlink argument?

I'm trying to cross compile a rust project with cross. My project depends on libcurl. The linker can find the libcurl .so but not the libraries which it depends on:
I'm running:
cross build -vv --target=arm-unknown-linux-gnueabihf
which gives me:
= note: /usr/arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/8.3.0/../../../../arm-linux-gnueabihf/bin/ld: warning: libnghttp2.so.14, needed by /usr/lib/arm-linux-gnueabihf/libcurl.so, not found (try using -rpath or -rpath-link)
I've already confirmed that the library its looking for exists:
root#80a13d68ad84:/# find /usr/lib -name "libnghttp2.so.14"
/usr/lib/x86_64-linux-gnu/libnghttp2.so.14
/usr/lib/arm-linux-gnueabihf/libnghttp2.so.14
I've read about rust build scripts here, where I can add paths to the library search path, but that has not resolved the issue, this is what my build.rs looks like:
fn main() {
println!("cargo:rustc-link-search=native=/usr/lib/arm-linux-gnueabihf");
}
The error suggests using -rpath or -rpath-link, so how do I configure the rpath in rust? Or is there a different way to resolve this issue?
You might be able to specify linker arguments by using an environment variable like
RUSTFLAGS="-C link-arg=--rpath=..."
Cargo allows placing the same kind of flag in the configuration file (see here) but I'm not sure if cross has the same support for something like that.

Linking failed in rust bindgen

So I have a header library (a directory of .h files) that I need to make use of in my rust code. To get this working I have been looking into various crates to help me achieve the this and it seems like bindgen is the most promising option.
i followed the documentation for bindgen and have created a wrapper.h and a build.rs file.
in the build.rs file i have whitelisted all the needed functions, and the link parameter mentioned in the documentation specified as println! ("cargo:rustc-link-lib=C/complete path to my header files");.
when doing a cargo build this however fails with this error:
error: linking with link.exe failed: exit code: 1181
which I have not been able to find a solution for.
Any help or suggestions are greatly appreciated.
rustc-link-lib directive is only for .a/.so/.dll and cannot work with either .h or .rs files. It's completely inappropriate here.
If you make your build.rs script write to a directory specified by std::env::var("OUT_DIR") path, then your main library code can include it dynamically with:
include!(concat!(env!("OUT_DIR"), "/temp_file_built_by_build_dot_rs.rs"));
Alternatively, don't bother with build.rs at all. Use command-line bindgen and include generated .rs file as a module in your project (just as a regular source code file).

export shared library path via several level of crates denedicies

In build.rs of level1-sys crate I have:
let dst = cmake::Config::new(Path::new("somelib"))
.build()
.join("build");
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=dylib=somelib");
then there is level2 create that deped on level1-sys,
and then there is level3binary crate.
If I run this level3 crates via cargo run all works fine,
but if I run it by hands it reports:
error while loading shared libraries: libsomelib.so: cannot open shared object file: No such file or directory
Is there way to find path to libsomelib.so from cargo?
I need this info for external script that should pack binary.
Obviously I can search in target subdirectory,
but I use debug/release/cross compilation and so on thing.
Plus even for concrete variant like target/release after several rebuild of level1-sys there are several libsomelib.so libraries, like:
target/release/build/level1-sys-46422ddc8585ba79/libsomelib.so
target/release/build/level1-sys-9daa760ee41fe7b8/libsomelib.so

Resources