How to call a shared library in Rust? [duplicate] - c++

This question already has answers here:
Linking Rust application with a dynamic library not in the runtime linker search path
(3 answers)
How do I specify the linker path in Rust?
(3 answers)
Where should I place a static library so I can link it with a Rust program?
(3 answers)
Closed 2 years ago.
I have written a C++ library with a C wrapper and want to call those functions from my Rust project. However, when I try to link my Rust project to the C library, I get a long error with the note
note: /usr/bin/ld: cannot find -l../cpp_library/build/libdisplay.so
I have tried passing an absolute path to the library and received a similar error.
My combined project has the following directory and contents
├── cpp_library
│   ├── CMakeLists.txt
│   ├── include
│   │   └── display.h
│   └── src
│   ├── display.cpp
│   └── main.cpp
└── rust_project
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── src
   └── main.rs
CMakeLists.txt
cmake_minimum_required(VERSION 3.1.0)
project(directory_test)
set(CMAKE_BUILD_TYPE Release)
#Bring the headers into the project
include_directories(include)
set(SOURCES "src/display.cpp")
#Generate the shared library from the sources
add_library(display SHARED ${SOURCES})
add_executable(display_test "src/main.cpp" ${SOURCES})
display.h
extern "C" {
void display();
}
display.cpp
#include <iostream>
#include "display.h"
void display(){
std::cout << "A C++ println" << std::endl;
}
main.cpp
#include "display.h"
int main() {
display();
}
build.rs
fn main() {
println!("cargo:rustc-link-search=../cpp_library/build/");
println!("cargo:rustc-link-lib=../cpp_library/build/libdisplay.so");
}
main.rs
extern {
fn display();
}
fn main() {
println!("Hello, world!");
unsafe {
display();
}
}
You can also see the project on Github.
I build the C library with
cd cpp_library
mkdir build
cd build
cmake ..
make
When I build the Rust project, I get the error shown above.
How am I supposed to link the C library to the Rust project?
Update 1
The posts How do I specify the linker path in Rust? and Where should I place a static library so I can link it with a Rust program? indicate how to add a directory to the library search path and link a library. I have tried these solutions, but still receive the error mentioned above.
Following the advice of Linking Rust application with a dynamic library not in the runtime linker search path, I compiled the rust project with
cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../cpp_library/build/'
but received the same error. I also tried passing an absolute path and received the same result.
I removed the redundant #[link(name = "display")], but this did not resolve the problem.

Related

CMake: Do I need to set POSITION_INDEPENDENT_CODE here and what exactly is its purpose?

I am currently reading "Modern CMake for C++" ([1]) and I am at page 193 where the concept of "position independent code" is explained. However it is only explained in one page and I didn't completely understand what its purpose is exactly and when I need to set it manually. I am still a noob when it comes to the details of how compiling/linking in C++ works, that's part of why I bought this book. It explains the basic concepts of how compilation/linking works in the previous chapters and I felt like I understood those concepts fairly well (how static libraries are basically archived object files and shared libraries being similar to final executables that can be loaded into runtime dynamically, for example). However, I am not sure how "position indenpendent code" fits in here, or, in other words, I am not sure if I understood it correctly.
From what I have read ([2], [3]) my current understanding is this:
Static libraries produce position dependent (machine) code by default. Their variables are
mapped to hardcoded virtual memory addresses which is more efficient.
Shared libraries produce position independent machine code. Since they are dynamically loaded into the process runtime, it can't be known to which address their variables will be mapped to. For this purpose, they have a global offset table that acts as a placeholder address that will be resolved to some spot in the virtual memory when they are executed
When a static library is linked to a shared library, it itself must also be position independent, otherwise... (I don't actually know what would happen otherwise. The book I read just says "you'll run into trouble with CMake" but I don't know what that means).
Please tell me if there is anything wrong in that explanation.
So, let's look at an example project that looks like this:
example/
└── src
├── CMakeLists.txt
├── hiprinter
│   ├── CMakeLists.txt
│   ├── hiprinter.cpp
│   ├── hiprinter.h
│   └── printer
│   ├── CMakeLists.txt
│   ├── printer.cpp
│   └── printer.h
├── prog
│   ├── CMakeLists.txt
│   └── main.cpp
└── warning_props.cmake
Content of the files (from top to bottom):
# src/CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(Prog LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include(warning_props.cmake)
add_subdirectory(prog)
add_subdirectory(hiprinter)
# Create Graphviz Tree visualizing dependencies
add_custom_target(graphviz ALL
COMMAND ${CMAKE_COMMAND} "--graphviz=dtree.dot" .
COMMAND dot -Tpng dtree.dot -o dtree.png
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
# src/hiprinter/CMakeLists.txt
add_subdirectory(printer)
add_library(hiprinter STATIC hiprinter.cpp)
target_include_directories(hiprinter PRIVATE .)
target_link_libraries(hiprinter PRIVATE printer warning_props)
// src/hiprinter/hiprinter.cpp
#include "hiprinter.h"
#include "printer/printer.h"
#include <iostream>
void printHi()
{
print("hi");
}
// src/hiprinter/hiprinter.h
#pragma once
void printHi();
# src/hiprinter/printer/CMakeLists.txt
add_library(printer SHARED printer.cpp)
target_include_directories(printer PRIVATE .)
target_link_libraries(printer PRIVATE warning_props)
// src/hiprinter/printer/printer.cpp
#include "printer.h"
#include <iostream>
#include <string>
void print(std::string s)
{
std::cout << s << std::endl;
}
// src/hiprinter/printer/printer.h
#pragma once
#include <string>
void print(std::string s);
# src/prog/CMakeLists.txt
add_executable(Prog main.cpp)
target_link_libraries(Prog PRIVATE hiprinter warning_props)
target_include_directories(Prog PRIVATE ..)
// src/prog/main.cpp
#include "hiprinter/hiprinter.h"
int main()
{
printHi();
}
# src/warning_props.cmake
add_library(warning_props INTERFACE)
# Enable ALL warnings and treat them as errors
target_compile_options(warning_props INTERFACE
-Wall
-Wextra
-Wpedantic
-Werror
)
GraphViz dependency tree:
Prog depends on hiprinter, a static library. Since hiprinter itself is static and it depends on a shared library, printer, according to the reasons stated above, I would have to configure hiprinter as position-independent right? Because it is a static library and is position-dependent by default.
So, I'd have to edit src/hiprinter/CMakeLists.txt like this:
# src/hiprinter/CMakeLists.txt
add_subdirectory(printer)
add_library(hiprinter STATIC hiprinter.cpp)
target_include_directories(hiprinter PRIVATE .)
target_link_libraries(hiprinter PRIVATE printer warning_props)
# Add this:
set_target_properties(hiprinter
PROPERTIES POSITION_INDEPENDENT_CODE
ON
)
Am I doing this right?
Also, are there any mistakes in my "explanation" above?
By the way, this project compiles fine with or without the addition for the PIC property. (I am on Ubuntu 20.04, using GCC 10.3.0)
PS: Another question: If a static library depends on another static library that is PIC, the first static library must also be configured as PIC, right?
1 https://en.wikipedia.org/wiki/Position-independent_code
2 GCC -fPIC option
3 Combining CMake object libraries with shared libraries

CMake project with flex/bison

I'm working on an hobby project in C++, using flex and bison. I'm using CMake as my build system, and I had struggle in making all work together.
I'm quite new to CMake so the documentation seems quite minimal to me. It is showed how to link everything in one executable.
But I'm trying to keep all the parts of my code separated, so I would like to have my flex/bison file in a subfolder. My structure is this:
.
├── build
| └-- ...
├── CMakeLists.txt
├── flexbison
│   ├── CMakeLists.txt
│   ├── lexer.ll
│   └── parser.yy
├── lib
│   ├── CMakeLists.txt
│   └── ...
├── main.cpp
└── run.sh
So I want to have my yyparse function separate from my main.cpp, as well as my other C++ files. I managed to get it done in this way:
project_root/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(progetto-di-prova VERSION 0.1)
add_subdirectory(flexbison)
add_executable(executable main.cpp)
target_include_directories(executable PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}/flexbison"
)
set_target_properties(executable PROPERTIES CXX_STANDARD 11)
target_link_libraries(executable parserlib)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
flexbison/CMakeLists.txt:
find_package(FLEX)
find_package(BISON)
flex_target(lexer lexer.ll "${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp")
bison_target(parser parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.h)
add_flex_bison_dependency(lexer parser)
add_library(parserlib STATIC
${FLEX_lexer_OUTPUTS}
${BISON_parser_OUTPUTS}
)
the main.cpp file:
#include <parser.h> // <-this was the line causing troule
#include <iostream>
extern int yylex(void);
int main(int argc, char *argv[])
{
yyparse();
return 0;
}
Now, the point is: is this the correct way of doing so? Is it better to put the main function in the bison code?
My main problem, as highlighted in the code, was to include the parser.h header file in the main.cpp. Is this the CMake-way to do it?
Thanks in advance!
If I were you I would have:
flexbison/CMakeLists.txt:
...
add_library(parserlib STATIC
${FLEX_lexer_OUTPUTS}
${BISON_parser_OUTPUTS}
)
target_include_directories(parserlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:include/parserlib> # <prefix>/include/parserlib
)
note: $<INSTALL_INTERFACE:include/parserlib> need to be adapted if you want and how you install parserlib...
project_root/CMakeLists.txt:
...
target_include_directories(executable PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
...
Since executable is "target_link" against parserlib which provides public include directories, you don't need to list these directories anymore in executable's include dirs.
ref: https://cmake.org/cmake/help/latest/command/target_include_directories.html

Running googletest with separated src- and test-folders

Here my minimal example (https://github.com/theCollectiv/cmake_minimal). I first got this project structure with seperate Headerplacement (-> src and include)
.
├── 1Cmake.sh
├── 2runExe.sh
├── 3cleanTarget.sh
├── 4runTest.sh
├── 7VersionOfCurrentTools.sh
├── build
│   ├── CMakeCache.txt
│   ├──...
│ ...
├── CMakeDefaults.txt
├── CMakeLists.txt
├── compile_commands.json -> ...
├── data
├── docs
│   ├── ...
├── include
│   └── addition
│   └── addition.hpp
├── src
│   ├── addition
│   │   └── addition.cpp
│   ├── main.cpp
├── tests
│   └── test01_proofOfWork
│   ├── CMakeLists.txt
│   └── tests.cpp
main.cpp
#include "addition.hpp"
#include <iostream>
int main() {
int a = 5;
int b = 3;
std::cout << "a is " << a << "\nb is " << b << "\nThe Sum of both "
<< add(a, b) << std::endl; }
addition.cpp
#include "addition.hpp"
int add(int a, int b) { return a + b; }
header of addition.cpp: addition.hpp
#pragma once
int add(int a, int b);
The CMakeList.txt in the root-folder looks like this:
cmake_minimum_required(VERSION 3.21.2)
set (This
Project_main)
project(${This}
LANGUAGES CXX
VERSION 1.000)
enable_testing()
find_package(GTest REQUIRED)
add_subdirectory(./tests/test01_proofOfWork)
# Custom Variables
set(QUELLE src/main.cpp)
set(ZIEL finalExecutable)
# integrate lib (with .cpp and its header)
set(LIB_1 addition)
add_library(
${LIB_1}
STATIC src/addition/addition.cpp
)
# add .hpp hinzufuegen and connect to lib
target_include_directories(
${LIB_1}
PUBLIC include/addition
)
# add executable
add_executable(${ZIEL} ${QUELLE})
# linking the lib
target_link_libraries(
${ZIEL}
PRIVATE ${LIB_1}
# PUBLIC ${LIB_1}
)
Now I want to add unit tests by using googletest. I'm using ubuntu and googletest is installed in my systempath (with shared libs) and working.
I'm now a little bit stuck, getting googletest well integrated in the project.
The CMakeList.txt in the tests-dir:
# Name of the Tests
set (This
runTest)
# Location of the test
set (Sources
tests.cpp)
# executable of the test
add_executable(${This} ${Sources})
# linking libs for the test
target_link_libraries(${This} PUBLIC
GTest::gmock
GTest::gtest
GTest::gmock_main
GTest::gtest_main)
# registrating the test
gtest_discover_tests(${This})
The test.cpp
// tests.cpp
// #include "./../../include/addition/addition.hpp"
#include <gtest/gtest.h>
#include <gtest/internal/gtest-internal.h>
// bool foo(){
// return true;
// }
//
//
// TEST(Simpletest, trueEqualsTrue) {
// EXPECT_TRUE(foo());
// EXPECT_FALSE(foo());
// EXPECT_EQ(true, foo());
// ASSERT_FALSE(foo());
// EXPECT_EQ(false, foo());
// }
//
TEST(SquareRootTest, PositiveNos) {
ASSERT_EQ(18.0, add(1,17));
EXPECT_EQ(6, add(1,6));
ASSERT_EQ(25.4, add(55, -1));
ASSERT_EQ(0, add(-4, 4));
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
The de-commented test code works and compiles fine, so gtest works fine (means: if I m testing code, thats in the same file the code is tested).
But... (I'm new to this stuff, so maybe its an easy (or noob) question):
The reason, why I added the googletest is to test the code in the src- or -include dir. Therefore I got the add all the headers and files in the these dirs for test cases all the time.
Is there a good way to make all of them available for the possible tests that I might run without configuring it all the time?
My problem is,
that the de-commented code works/compiles fine (of code in the same location like the test is). That means:
googletest is installed correctly
the tests-CMakeLists.txt is working correctly (and the root-CmakeLists.txt too)
If I want to compile the test-code, that is not de-commented (that means the 'code to test' is not in the same file like the test itself), it is complaining for missing headers. If I add them, the compilation of the tests quits with errors (like "cmake line XXX" ...doesnt tell me anything or "undefined reference too"). The problem I got
it doesnt compile the tests (obvious)
even if it would compile, I got to rebuild all the things (adding headers in the test-source-code) in the test-directory like I did in the root-directory (similar/same file linking in both of them). If I got a more complex project structure (using a function in a file which uses functions from another file), this is 'doing the same exact same stuff in for the normal project (src and include) and in the tests-dir'. Or am I wrong at this point?
Solution:
# linking libs for the test
target_link_libraries(${This} PUBLIC
GTest::gmock
GTest::gtest
GTest::gmock_main
GTest::gtest_main
# Solution: Got to link the lib(s) (which I want to test in the root-dir) against the test-executable
addition
)
You are not linking yout test project against your library. So it doesn't use your library. Link it.
target_link_libraries(${This} addition)

Integrate thirdpary shared libraries(*.so) into multiple projects using cmake

I have the following layout which contains multiple projects separate directories (It's a CPP project):
Demo
├── Lib
│   ├── CMakeLists.txt
│   ├── inc
│   └── lib
│   ├── crypto_ic.so
│   └── crypto.so
├── Proj1
│   ├── App1
│   └── CMakeLists.txt
└── Proj2
├── App2
└── CMakeLists.txt
I created one separate third-party-lib.bb file to install crypto_ic.so,crypto.so into /usr/lib/ in the target using do_install_append.
All applications are depending on third-party libraries. There are no parent cmake for proj1,proj2 those are independent projects built by *.bb files using yocto.
Now I need to integrate the third party library for all the applications. I created one interface library for the third party headers and using find_package I am able to find the headers and the compilation is working fine.
CMakeLists.txt(Lib)
cmake_minimum_required(VERSION 3.8)
project(crypto VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
find_package(Boost REQUIRED)
add_library(crypto INTERFACE)
target_link_libraries(crypto INTERFACE
Boost::boost
)
target_include_directories(crypto INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/inc/>
$<INSTALL_INTERFACE:include>
)
# Create and Install *-config.cmake,*-version.cmake files
# Install exported targets, i.e. *-targets.cmake
# Install header files
CMakeLists.txt(Proj1)
cmake_minimum_required(VERSION 3.8)
project(App1 VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
find_package(crypto REQUIRED)
add_executable(run Run.cpp)
target_link_libraries(run PUBLIC
crypto
)
But the problem is while linking I am getting a linker error.
Run.cpp: undefined reference to 'Intialize(std::string hashName)'
Initialize function definition is available in the shared library.
Any thoughts on how to fix the issue and how to remove the hardcode path to the library in CMake?.
I am new to CMake and not sure this is a good approach to follow, only reason I created the Interface library is to install the headers in target and find the package. I can modify it if there are some better solutions?
Edit:
I found the issue in the cmake, I didn't link the shared library files (*.so) in the application that's the reason I am getting the linker error. But I am not getting how to link that from the target?

CMake cannot detect symbol conflicts in different static libraries

I created a simple cmake project to reproduct it.
├── CMakeLists.txt
├── lib1
│   ├── CMakeLists.txt
│   ├── lib1.cpp
│   └── lib1.h
├── lib2
│   ├── CMakeLists.txt
│   ├── lib2.cpp
│   └── lib2.h
└── main.cpp
lib1/CMakeLists.txt:
add_library(lib1 "")
target_include_directories(lib1
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(lib1
PRIVATE
lib1.cpp
lib1.h
)
In lib1.cpp, there is a function "void say()":
#include <stdio.h>
void say()
{
printf("hello from lib1\n");
}
lib2/CMakeLists.txt:
add_library(lib2 "")
target_include_directories(lib2
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(lib2
PRIVATE
lib2.cpp
lib2.h
)
And in lib2/lib2.cpp, there is a function of the same signature:
#include <stdio.h>
void say()
{
printf("hello from lib2\n");
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(shell LANGUAGES CXX)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_executable(test2
main.cpp
)
target_link_libraries(test2
PRIVATE
lib1
lib2
)
Here is the main.cpp:
void say();
int main()
{
say();
return 0;
}
The output:
hello from lib1
There is no compile or link error, not even a warning.
The linker just picked one and symply ignored the other one.
I'm using cmake 3.16, and tested it with msvc 2017/2019 and g++ 7.5.
How to make the linker prompts errors when there are symbol conflicts in static libraries?
Thanks!
How to make the linker prompts errors when there are symbol conflicts in static libraries?
With gcc use the --whole-archive option to include every object file in the archive rather then search the archives for the required symbol.
As there is no cmake support that I know of, I find it's typically done when linking the executable:
target_link_libraries(test2 PRIVATE
-Wl,--whole-archive
lib1
lib2
-Wl,--no-whole-archive
)
"Symbol conflicts" is rather vague term. This will only detect multiple symbol definitions. Types of symbols are not stored anywhere after compilation.