Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Embedding Rust into a C++ CMake project #156

Closed
wants to merge 6 commits into from

Conversation

VelorumS
Copy link

@VelorumS VelorumS commented Apr 26, 2020

It's not a real PR, more like a question about the demo code in demo-cxx/ and demo-rs/.

The setup is an existing C++ CMake project, the goal is to be able to add Rust code to it. The C++ project is internally structured as a main application and shared libraries. In this demo we will have a main C++ application and a shared library. The shared library will be entirely in Rust except for the interface, the FFI will provide the C++ interface so the main application could link to it as to a regular C++ shared library.

I haven't found any main.cpp in the demo-cxx/ or demo-rs/, so there is probably a space for this kind of demo.

The first step is creating the demo-cmake/ dir, dumping the cmake-cargo into it, fixing workspace errors. Then testing their build:

mkdir ../build
cd ../build
cmake ../cxx/demo-cmake
make -j

The application of interest is demo-cmake/test/rust2cpp, it does the C-like variation of what we want. So the second step is adding parts of demo-cxx/ and demo-rs/ to that application (main.rs turns into lib.rs, demo.h turns into a pure-C++ interface for the library).

There are minor things such as: hardcoded include paths in demo.cc and build.rs, hardcoded cxx dependency path, cxxbridge complaining about the "target path" that has to end with "target" but instead ends with "out". I'm currently looking into this error that occurs while building the library:

src/lib.rs:1:1: error[E0658]: custom attributes cannot be applied to modules

So it doesn't compile yet. I suppose most of the work that is left is related to the Rust/cxx build system side.

Suggestions welcome. Maybe I've missed an obvious solution or maybe somebody has already done such an integration.

@dtolnay dtolnay added the wip work in progress PR label Apr 26, 2020
@dtolnay
Copy link
Owner

dtolnay commented Apr 26, 2020

E0658 means your compiler is too old. This project requires rustc 1.42.0 or newer.

@VelorumS
Copy link
Author

Ok, totally forgot about the compiler version.

Should be working now (if changed to staticlib in Cargo.toml). Last non-cosmetic thing is that it's building only a static lib, not a shared one. Looks like a limitation of cc-rs.

@dtolnay
Copy link
Owner

dtolnay commented Apr 26, 2020

How would you like to proceed with this? Do you want to put together a Travis build to test with CMake in CI? We already test Bazel and Buck and Cargo so it would be an appropriate way to demonstrate how a CMake setup involving this crate is intended to work.

@VelorumS
Copy link
Author

If you want it in this repository then we can have it. Although it doesn't really depend directly on the cxx code of the repository, it should load everything from crates.io.

About CMake: it has different generators. I can deal with "Unix Makefiles" generator on Linux, but I haven't planned to test others like Xcode, MSVC, ninja, etc.

@dtolnay
Copy link
Owner

dtolnay commented Apr 27, 2020

Do you want to maintain a separate repo as a sort of example project using CMake + CXX? We can add a link from the readme here. That keeps it isolated from unpublished changes in this repo and more representative of how a downstream project would use the two.

@VelorumS
Copy link
Author

VelorumS commented May 2, 2020

Yes, I'll make it as a fork of the cmake-cargo repo.

I've partially worked around the problem of the shared library. It happens because the glue code is compiled into a static library and then linked into a shared one without "--whole-archive" flag (the shared library itself doesn't need any symbols from it so it drops it). It can be probably worked around with options and env vars for cargo, but I did some relinking in CMake for now.

Other part of the problem with shared libraries is that cargo doesn't like building rust parts into shared libraries, especially for cdylib. With dylib and RUSTFLAGS="-C prefer-dynamic" it's possible to separate the std, but there is still a 10MiB shared library left. There is probably some work to do in cargo for making a shared library setup possible.

The two actual FFI questions are:

  • in src/lib.rs I had to build.include() the path to the generated header, otherwise the cc can't find *.rs.h during the build. Can we push it to master as a fix or I'm missing some option in the demo?
  • the src/paths.rs wants "target_dir" to end with "target", but in my build it's called "out". I've grepped the whole source and haven't seen "target" or "out" anywhere. What is it and why is that?

@dtolnay
Copy link
Owner

dtolnay commented May 2, 2020

I don't know anything about CMake, but based on your questions it sounds like it would be easier for you to follow the non-Cargo workflow instead (https://github.com/dtolnay/cxx/tree/0.3.0#non-cargo-setup). Both of those things are specific to how Cargo lays out its build directory.

@VelorumS
Copy link
Author

VelorumS commented May 2, 2020

Thank you. When I read "Non-Cargo setup" I thought that the whole thing including rust code is to be built without cargo.

FFI integrates fine. The only thing left is to convince cargo to build shared libraries for everything because currently every plugin of this demo (currently one) is getting a separate copy of the whole rust runtime.

@Nehliin
Copy link
Contributor

Nehliin commented Jul 28, 2020

Hello @ChipmunkV! I have been experimenting with Cmake and Cxx but I have some issues testing and running your example. I am on a mac running clang so I removed the -Wl, --no-whole-archive and have the -Wl, --whole-archive linker flag in the last target_link_libraries in the demo-rust-lib/CMakeLists.txt to -Wl, -all_load. I have also tested with -Wl, -force_load. I also had to specify the C++ version with set_property(TARGET ${THIS_LIB} PROPERTY CXX_STANDARD 11). However I still get linker issues:

[  0%] Built target cargo-build_demo-rust-lib-bridged
[ 28%] Generating lib.rs.h
[ 28%] Generating lib.rs.cc
Scanning dependencies of target demo-rust-lib
[ 57%] Building CXX object test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/lib.rs.cc.o
[ 57%] Building CXX object test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/src/demo.cc.o
[ 71%] Linking CXX shared library libdemo-rust-lib.dylib
Undefined symbols for architecture x86_64:
  "_org$example$cxxbridge03$print_str", referenced from:
      org::example::print_str(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) in lib.rs.cc.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [test/rust2cpp/demo-rust-lib/libdemo-rust-lib.dylib] Error 1
make[1]: *** [test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/all] Error 2
make: *** [all] Error 2

I am a bit of a noob in Cmake so sorry if I am just missing something obvious. But I'd really appreciate any help if you have any ideas of why this fails?

@VelorumS
Copy link
Author

@Nehliin , run the build in verbose mode (make VERBOSE=1) to see what commands are executed. Copy-paste individual commands and run/debug them manually. Maybe CMake rearranges the arguments differently, maybe such usage of the linker arguments is too gcc specific.

Scanning dependencies of target demo-rust-lib
make[2]: Leaving directory '/home/user/projects/rust-cxx/build'
make -f test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/build.make test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/build
make[2]: Entering directory '/home/user/projects/rust-cxx/build'
[ 42%] Building CXX object test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/src/demo.cc.o
cd /home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib && /usr/bin/c++  -Ddemo_rust_lib_EXPORTS -I/home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/demo-rust-lib/src -I/home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib  -fPIC   -o CMakeFiles/demo-rust-lib.dir/src/demo.cc.o -c /home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/demo-rust-lib/src/demo.cc
[ 57%] Building CXX object test/rust2cpp/demo-rust-lib/CMakeFiles/demo-rust-lib.dir/lib.rs.cc.o
cd /home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib && /usr/bin/c++  -Ddemo_rust_lib_EXPORTS -I/home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/demo-rust-lib/src -I/home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib  -fPIC   -o CMakeFiles/demo-rust-lib.dir/lib.rs.cc.o -c /home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib/lib.rs.cc
[ 71%] Linking CXX shared library libdemo-rust-lib.so
cd /home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib && /usr/bin/cmake -E cmake_link_script CMakeFiles/demo-rust-lib.dir/link.txt --verbose=1
/usr/bin/c++ -fPIC   -shared -Wl,-soname,libdemo-rust-lib.so -o libdemo-rust-lib.so CMakeFiles/demo-rust-lib.dir/src/demo.cc.o CMakeFiles/demo-rust-lib.dir/lib.rs.cc.o  -Wl,--whole-archive ../../../cargo/build/x86_64-unknown-linux-gnu/debug/libdemo_rust_lib_bridged.a -Wl,--no-whole-archive ../../../cargo/build/x86_64-unknown-linux-gnu/debug/libdemo_rust_lib_bridged.a -ldl -lrt -lpthread -lgcc_s -lc -lm -lutil 
make[2]: Leaving directory '/home/user/projects/rust-cxx/build'
[ 71%] Built target demo-rust-lib
make -f test/rust2cpp/CMakeFiles/cpp-exe.dir/build.make test/rust2cpp/CMakeFiles/cpp-exe.dir/depend
make[2]: Entering directory '/home/user/projects/rust-cxx/build'
cd /home/user/projects/rust-cxx/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/user/projects/rust-cxx/cxx/demo-cmake /home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp /home/user/projects/rust-cxx/build /home/user/projects/rust-cxx/build/test/rust2cpp /home/user/projects/rust-cxx/build/test/rust2cpp/CMakeFiles/cpp-exe.dir/DependInfo.cmake --color=
make[2]: Leaving directory '/home/user/projects/rust-cxx/build'
make -f test/rust2cpp/CMakeFiles/cpp-exe.dir/build.make test/rust2cpp/CMakeFiles/cpp-exe.dir/build
make[2]: Entering directory '/home/user/projects/rust-cxx/build'
[ 85%] Building CXX object test/rust2cpp/CMakeFiles/cpp-exe.dir/main.cpp.o
cd /home/user/projects/rust-cxx/build/test/rust2cpp && /usr/bin/c++   -I/home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/rust/demo-cxx -I/home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/demo-rust-lib/src -I/home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib   -o CMakeFiles/cpp-exe.dir/main.cpp.o -c /home/user/projects/rust-cxx/cxx/demo-cmake/test/rust2cpp/main.cpp
[100%] Linking CXX executable cpp-exe
cd /home/user/projects/rust-cxx/build/test/rust2cpp && /usr/bin/cmake -E cmake_link_script CMakeFiles/cpp-exe.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/cpp-exe.dir/main.cpp.o  -o cpp-exe  -Wl,-rpath,/home/user/projects/rust-cxx/build/test/rust2cpp/demo-rust-lib demo-rust-lib/libdemo-rust-lib.so ../../cargo/build/x86_64-unknown-linux-gnu/debug/libdemo_rust_lib_bridged.a -ldl -lrt -lpthread -lgcc_s -lc -lm -lutil 
make[2]: Leaving directory '/home/user/projects/rust-cxx/build'
[100%] Built target cpp-exe
make[1]: Leaving directory '/home/user/projects/rust-cxx/build'
/usr/bin/cmake -E cmake_progress_start /home/user/projects/rust-cxx/build/CMakeFiles 0

@Nehliin
Copy link
Contributor

Nehliin commented Jul 29, 2020

@ChipmunkV Thanks for the tip! I finally managed to get it to work by simply expanding the example in the more updated cmake-cargo repo you linked in the original post. I modified it to use cxx instead. I had to remove all -Wl, -all_load because I got duplicate symbol errors when using it.

Copy link
Owner

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go ahead and close the PR at this point since it wasn't intended to get merged to begin with, but it'll be here for anyone searching for CMake guidance (https://github.com/dtolnay/cxx/search?q=cmake&type=issues). Thanks for figuring all of this out!

@dtolnay dtolnay closed this Oct 10, 2020
@dtolnay dtolnay removed the wip work in progress PR label Oct 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants