Closed ClayCore closed 6 months ago
How does your CMakeLists.txt look like? Did you do anything like the following:
target_link_libraries(my_cpp_bin PUBLIC my_rust_lib)
Also please note that no mangle is not sufficient. You need to specify that your Rust function should use the C-ABI, otherwise behavior when calling from C or C++ is undefined.
Also please note that no mangle is not sufficient. You need to specify that your Rust function should use the C-ABI, otherwise behavior when calling from C or C++ is undefined.
I've added extern "C"
to the function definitions, but I'm getting the same errors. Wasn't aware I needed this though
How does your CMakeLists.txt look like? Did you do anything like the following:
target_link_libraries(my_cpp_bin PUBLIC my_rust_lib)
The CMakeLists.txt
relies on the template, but the one used to build the executable is this:
add_executable(
cli
main.cpp
)
add_executable(dev::cli ALIAS cli)
corrosion_import_crate(
MANIFEST_PATH
${CMAKE_SOURCE_DIR}/rlib/Cargo.toml
)
if(NOT TARGET rlib)
message(FATAL_ERROR " !! rust library not built/imported")
endif()
target_link_libraries(cli PRIVATE dev_options dev_warnings)
target_link_libraries(cli PUBLIC rlib)
target_include_directories(
cli
${WARNING_GUARD}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/cli>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/cli>
)
target_compile_features(cli PUBLIC cxx_std_20)
set_target_properties(
cli
PROPERTIES
VERSION ${PROJECT_VERSION}
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
)
if(BUILD_TESTING)
message(TRACE "Building tests...")
add_subdirectory(test)
endif()
add_custom_command(
TARGET
cli
POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy $<TARGET_FILE:cli> ${PROJECT_SOURCE_DIR}/target
)
I've added extern "C" to the function definitions, but I'm getting the same errors. Wasn't aware I needed this though
It's not related to the linking issue. By default Rust functions use the Rust-ABI, which is neither stable nor guaranteed in any way to be compatible with C/C++ ABIs. If your function is Rust-ABI, but you call it from C/C++ the behavior is undefined. For such a simple function as in your example the ABIs will probably be the same, but you can't know that.
I believe the same also applies for C++ code - you should declare the Rust function as having C-ABI. I'm not really thatfamiliar with C++, so perhaps a simple extern
is equivalent with extern "C"
, but I would still recommend writing it out to be explicit.
extern "C" void greet();
extern "C" size_t add(size_t, size_t);
That being said, I'm not sure whats going wrong for you - We do test windows -msvc
with clang-cl and the Ninja Generator in CI.
Could you try removing the custom command that moves the cli target to another location post-build?
add_custom_command(
TARGET
cli
POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy $<TARGET_FILE:cli> ${PROJECT_SOURCE_DIR}/target
)
I've added extern "C" to the function definitions, but I'm getting the same errors. Wasn't aware I needed this though
It's not related to the linking issue. By default Rust functions use the Rust-ABI, which is neither stable nor guaranteed in any way to be compatible with C/C++ ABIs. If your function is Rust-ABI, but you call it from C/C++ the behavior is undefined. For such a simple function as in your example the ABIs will probably be the same, but you can't know that.
I believe the same also applies for C++ code - you should declare the Rust function as having C-ABI. I'm not really thatfamiliar with C++, so perhaps a simple
extern
is equivalent withextern "C"
, but I would still recommend writing it out to be explicit.extern "C" void greet(); extern "C" size_t add(size_t, size_t);
Adding extern "C"
to function definitions in C++ source fixed it. I wasn't aware that it needs to be added. Oops.
That being said, I'm not sure whats going wrong for you - We do test windows
-msvc
with clang-cl and the Ninja Generator in CI. Could you try removing the custom command that moves the cli target to another location post-build?add_custom_command( TARGET cli POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:cli> ${PROJECT_SOURCE_DIR}/target )
To be precise, I'm using clang
LLVM binaries, not clang-cl
. Removing the post-build command didn't help on its own. I had to do the above of adding extern "C"
to C++ source.
It does appear I'll have to add a custom command to copy the compiled rust shared library to the same directory that I'm copying the compiled executable to, corrosion doesn't seem to do it on its own
You should have a look at the Cmake output directory variables, which corrosion will respect and copy the rust artifacts to.
You should have a look at the Cmake output directory variables, which corrosion will respect and copy the rust artifacts to.
Yeah, I should probably stop using custom commands everywhere. Thank you! I believe this solves my issue
Great to hear, closing the issue.
Current Behavior
After adding a rust
cdylib
into a C++ project viacorrosion_import_crate
the project fails atlld-link
and throws undefined symbol errors. The rust library contains two#[no_mangle] pub
functions, one of which is the defaultadd
function created by thecargo new
with the previously mentioned attribute.My cmake setup is based on cpp-best-practices/cmake_template. I've added
corrosion
with the template'sCPM.cmake
andDependencies.cmake
as follows:My
main.cpp
file:the crate import:
rlib/src/lib.rs
file:The symbols missing according to
lld-link
are:void __cdecl greet(void)
unsigned __int64 __cdecl add(unsigned __int64, unsigned __int64)
I peeked into the built
.dll
and sure enough, both functions are exported. The generated.dll.lib
file, when dumped withdumpbin
doesn't show any/EXPORT
directives, but I'm not sure if that's a problem or not. I also checked whether I need__declspec(dllimport)
in the C++ source for the function definitions and it didn't make any difference.Apologies in advance for making some obvious mistake somewhere.
Expected Behavior
Succesful linking of the C++ executable with the Rust library.
Steps To Reproduce
No response
Environment
CMake configure log with Debug log-level
CMake Build step log