I'm having issues linking a static C/C++ library into a rust library, and then that library into an executable, all inside one workspace. The strange part is that it works in debug mode (default, just cargo build
) but not in release mode (with cargo build --release
). This is on Windows 10.
I've built the static library in C/C++, and have tested it with a console application in C++. It also works directly from a Rust console application in either release or debug mode. There's something about trying to link together a Rust console application, to a rust library, to a static C library that's breaking but only in release mode.
My general workspace layout is the following:
workspace_test
--> Cargo.toml
--> console_test
--> Cargo.toml
--> build.rs
--> src
--> main.rs
|
--> link_to_c
--> Cargo.toml
--> build.rs
--> RustTestStatic - (C library, I built this)
--> x64
--> debug
--> RustTestStatic.lib
--> release
--> RustTestStatic.lib
--> src
--> lib.rs
The top-level workspace Cargo.toml is:
[workspace]
members = [
"console_test",
"link_to_c"
]
The Cargo.toml in console_test is:
[package]
name = "console_test"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
link_to_c = { path = "../link_to_c" }
The main.rs there is:
use std::error::Error;
fn main()-> Result<(), Box<dyn Error>> {
println!("Hello, world!");
let print_result = link_to_c::print_int(55);
println!("Got back {} from C/C++", print_result);
Ok(())
}
In link_to_c the Cargo.toml is:
[package]
name = "link_to_c"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2"
The build.rs there is:
use std::error::Error;
fn main()-> Result<(), Box<dyn Error>> {
// Get the proper directory for this based on the build profile
let profile = std::env::var("PROFILE").unwrap();
let subdir = match profile.as_str() {
"debug" => "Debug",
"release" => "Release",
_ => panic!("Unknown cargo profile:"),
};
let cur_path = std::env::current_dir()?;
println!("cargo:rustc-link-search={}\\RustTestStatic\\x64\\{subdir}", cur_path.display());
// didn't do anything, not needed on debug either!
//println!("cargo:rustc-link-lib=static=RustTestStatic");
Ok(())
}
In link_to_c lib.rs it is:
use libc::size_t;
#[link(name = "RustTestStatic", kind = "static")]
extern {
fn print_int_val(value: i32) -> size_t;
}
pub fn print_int(num: i32) -> usize {
unsafe {
print_int_val(num)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn print_works() {
let result = print_int(54);
assert_eq!(result, 13);
}
}
And for reference, the library's .h and .cpp files are:
#pragma once
#include <cstdint>
extern "C"
{
extern size_t print_int_val(int value);
}
.cpp
// RustTestStatic.cpp : Defines the functions for the static library.
//
#include "pch.h"
#include "framework.h"
#include "RustTestStatic.h"
#include <cstdio>
size_t print_int_val(int value)
{
auto num_printed = printf("Value is: %d\n", value);
return num_printed;
}
Again, I can take this static library, link it from a console application, works with the same build.rs in either release or debug mode. And in the workspace, I can even do cargo test
and that's fine. And just cargo build
or cargo run
and it's fine. But add --release
and it fails.
The message I get from cargo on failure is:
= note: liblink_to_c-f5eb5c4be5a6058d.rlib(link_to_c-f5eb5c4be5a6058d.link_to_c.7ca51d5a-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol print_int_val referenced in function _ZN9link_to_c9print_int17h48d39a375ccf86f1E
C:\Users\Kevin\Source\Rust\workspace_test\target\release\deps\console_test.exe : fatal error LNK1120: 1 unresolved externals
I omitted the full link.exe command, but it includes "/LIBPATH:C:\\Users\\Kevin\\Source\\Rust\\workspace_test\\link_to_c\\RustTestStatic\\x64\\Release"
so it's definitely getting to the right directory.
Why is this working in debug, but not release? And again, same build.rs works just fine for a console application, but not "via" a library. And I'd prefer everything statically linked (at least for now).