Basic setup
A simple C function that is called from Rust via FFI, and a static library (Windows) that is linked in build.rs.
// api.c
int increment(int value) {
return value + 1;
}
// main.rs
extern {
fn increment(value: i32) -> i32;
}
fn main() {
let num = unsafe { increment(4) };
println!("The incremented number is {}", num);
}
// build.rs
fn main() {
println!("cargo:rustc-link-search=src/ffi");
println!("cargo:rustc-link-lib=static=api");
}
With this directory structure:
Cargo.toml
Cargo.lock
build.rs
src
+-- main.rs
+-- ffi
+-- api.c
+-- api.lib
Cargo.toml has nothing but a crate name, version, author and Rust edition.
So far, this works perfectly, and "The incremented number is 5" is printed as expected.
The problem
Now I add an empty lib.rs
file, so I can use my crate as both a library and a binary:
Cargo.toml
Cargo.lock
build.rs
src
+-- lib.rs <-- NEW
+-- main.rs
+-- ffi
+-- api.c
+-- api.lib
I do not change Cargo.toml.
This alone makes the link step fail (MSVC linker):
error LNK2019: Unresolved external symbol "increment" referenced in function "_ZN16my_crate4main17h887bd80180495b7eE"
The error persists even if I explicitly specify the presence of both a library and binary in Cargo.toml and run using cargo run --bin my_main
:
[lib]
name = "my_lib"
path = "src/lib.rs"
[[bin]]
name = "my_main"
path = "src/main.rs"
I also made sure that build.rs
is still executed in the binary case, by letting it panic to abort the build.
I'm aware that I could solve it by splitting the crate, but I'd like to understand what exactly happens. So:
Why does the presence of an empty lib.rs
cause the linker to fail?
And is there a way to make it succeed, in a single crate?