Building shared libraries with cmake with option -Wl,--no-undefined - cmake

I'm trying to create a ndk library using android-cmake.
My target here is a shared library and build fails because of option
-Wl,--no-undefined
As the targets refers symbols from another library.
Is there a way to the missing library to the command
add_library(foo SHARED ${sources})

I was able to build by changing cmake variable
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L<path to library> -lfoo")

Related

How does cmake set the file name of the dynamic library to be built?

I want to add a LuaJIT wrapper to libgit2 so that it can be used in neovim.
The cmake configuration is as follows:
cmake_minimum_required(VERSION 3.22.2)
project("git2-neovim")
message(STATUS "cmake binary directory: ${CMAKE_BINARY_DIR}")
# Compile commands are output to "compile_commands.json", so that tools such as "ccls" can provide assistance.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Set gcc compile options.
set(CMAKE_C_FLAGS_DEBUG "$ENV{CFLAGS} -Wall -g3 -ggdb")
set(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -O3 -Wall")
set(
SRC_FILES
src/libgit2.c
)
add_library(${PROJECT_NAME} SHARED ${SRC_FILES})
find_package(PkgConfig REQUIRED)
if (PKG_CONFIG_FOUND)
pkg_check_modules(LIBGIT2 REQUIRED libgit2)
include_directories(${LIBGIT2_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${LIBGIT2_LIBRARIES})
endif(PKG_CONFIG_FOUND)
This generates a "libgit2-neovim.so" file, but I want to generate a "libgit2.so" file.
Since I only use it in neovim, there is no conflict with the real libgit2.
environmental information:
operating system: Archlinux
cmake version: 3.25.1
============================== replenish ============================
When LuaJIT is looking for a shared library, it will only automatically add the file of the shared library according to the operating system, and will not add a prefix. The example is as follows:
require("demo")
The lua code above loads the "demo.so" file but not the "libdemo.so" file.
Therefore, I want to set in cmake, compile in any operating system, the output shared library must have "lib" prefix.
How does cmake set the file name of the dynamic library to be built?
The output filename is controlled by target properties https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#library-output-artifacts , and the default is composed of https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LIBRARY_PREFIX.html followed by library name followed by https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LIBRARY_SUFFIX.html .
I want to generate a "libgit2.so" file
So name your library git2 not git2-neovim.
add_library(git2
or set LIBRARY_OUTPUT_NAME target property of the target.

Building a rust library through CMake and using it as imported library target

I'm restucturing the CMake based build of a cross platform (macOS/Windows, Linux should be added soon) C++ project that has a third party rust library as dependendcy. Until now the rust lib dependency was supplied as precompiled library but I want to make its compilation part of my CMake build.
I got it working on macOS using the CMake makefile exporter by referencing the compiled library as a static imported library target and setting up a custom target with the command to build the rust library through cargo like this
add_library (rustlib STATIC IMPORTED)
add_custom_target (rustlib_cargo
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/Ext/rustlib/c-api
COMMAND cargo rustc --release -- --crate-type staticlib)
# Note: RUSTLIB_OUTPUT is set above refering to the absolute path of the produced platform specific library
set_target_properties (rustlib PROPERTIES IMPORTED_LOCATION ${RUSTLIB_OUTPUT})
add_dependencies (rustlib rustlib_cargo)
On macOS the cargo rustc command is invoked before the targets that link against my rustlib target are built and in case the rust library has been built previously this is detected by cargo and it just skips that compilation steph. But on Windows this fails with the built-in ninja exporter of Microsoft Visual Studio 2019 with an error like this:
ninja : error : '../../../Ext/rustlib/target/release/deps/rustlib.lib', needed by 'SomeTargetLinkingAgainstRustlib', missing and no known rule to make it
If I remove the line set_target_properties (rustlib PROPERTIES IMPORTED_LOCATION ${RUSTLIB_OUTPUT}) the build starts correctly, the rust build gets triggered, but as expected I end up with a linker error as the library to link against is not found. So is there any way to refer to a file that is not existent at configuration time but is guranteed to be created during compilation?

Add only headers of an imported module to a library in CMake

In CMake there are imported modules that are used to simply add external modules to local targets. For example if we want to use boost::filesystem library in our project we could have a CMakeLists.txt like this:
project(foo CXX)
find_packge(Boost REQUIRED COMPONENTS filesystem)
add_executable(foo main.cpp)
target_link_libraries(foo Boost::filesystem)
With above configuration CMake will add proper compiler options and include directories among required libraries to building process of the foo.
Now we have to build a library instead of an executable and we don't want to link boost::filesystem libraries to our library. We want only compiler options and include directories to be added to our target. Could we use imported modules concepts here? I mean that if we could use Boost::filesystem syntax for adding those options to our target?
project(foo CXX)
find_packge(Boost REQUIRED COMPONENTS filesystem)
add_library(foo STATIC foo.cpp)
# what should be wrote here to only add headers and configs to foo not the libs?
Turning my comments into an answer
add_library(STATIC) won't link the target_link_libraries() dependencies into itself.
In short, if two static libraries would include e.g. Boost::filesystem and then you link both of those libraries into an executable (where the external symbols get actually resolved) you would get duplicate symbol errors.
So CMake by default, does not add linker options like --whole-archive for gcc or LinkLibraryDependencies for VC.
target_link_libraries(foo Boost::filesystem) should work, it just describes the dependency resolved later when building a executable or shared library.
References
ld linker question: the --whole-archive option
CMake issue #9732: Cmake does not disable Link Libray Dependencies in the project settings

How can LD_LIBRARY_PATH be changed within CMake?

I have a local shared library which is not in $LD_LIBRARY_PATH.
I want to run my executable, but since it cannot find the shared library in the system folders, it outputs "error while loading shared libraries".
A possible fix for this is to export a new LD_LIBRARY_PATH containing the local library folder.
How can I export automatically this environment variable within CMake?
You can set the runtime shared library search path using the -rpath linker option:
SET(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath -Wl,/usr/local/lib")
If your shared lib is not build in the same CMake project of your executable, you can use the CMake rpath handling like this:
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
When you will run make install, CMake will automatically set the runtime path of your executable to your shared library.
If your shared library is built in the same CMake project, use this:
set(CMAKE_INSTALL_RPATH "/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
In this case you must add yourself the directory where your shared library will be installed to the runtime path.
For more information, you can read CMake rpath handling
When you use this solution:
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
Remembter to set the RPATH before defining the targets in your CMake-File. So this instruction have to be before add_executable() or add_library() is called, otherwise it has no effect.

cmake doesn't support imported libraries?

When I try to import a library using
add_library(libname SHARED IMPORTED)
set_property(TARGET libname PROPERTY IMPORTED_LOCATION /<foldername>/<sub-foldername>/lib")
The cmake shouts :
CMake Warning (dev) at /CMakeLists.txt:28
(target_link_libraries): Cannot specify link libraries for target
"libname" which is not built by this project.
CMake does not support this but it used to work accidentally and is
being allowed for compatibility.
Policy CMP0016 is not set: target_link_libraries() reports error if
only argument is not a target. Run "cmake --help-policy CMP0016"
for policy details. Use the cmake_policy command to set the policy
and suppress this warning. This warning is for project developers.
Use -Wno-dev to suppress it.
If this is true, what is the other best way to include a library somewhere in my build tree into another project.
I have a library setup and another place has executable which will be using the libraries.
Reading through the cmake documentation, it felt like this would be the best way forward but seems its a broken piece which is just being supported.
Cannot specify link libraries for target "libname" which is not built by this project
When you use target_link_libraries to some target you're specifying how to build it,
but imported library is already build. CMake told you that...
Example of linking imported target to executable:
add_library(boo SHARED IMPORTED)
set_target_properties(boo PROPERTIES IMPORTED_LOCATION "/path/to/boo/library")
add_executable(foo foo.cpp)
target_link_libraries(foo boo)
Note: using imported targets
I was getting the same error as navderm when trying to import the Poco C++ libPocoFoundation.so library into my project, and after trying different solutions unsuccessfully, I managed to find one that worked for me:
cmake_minimum_required(VERSION 3.5)
project(MyProject)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_library(PocoLib SHARED IMPORTED GLOBAL)
# It's important to specify the full path to the library you want to import
set_target_properties(PocoLib PROPERTIES IMPORTED_LOCATION "/usr/local/lib/Poco_1.7.2/lib/libPocoFoundation.so")
# create my executable
set(EXEC_SOURCE_FILES main.cpp)
add_executable(MyProject ${EXEC_SOURCE_FILES})
target_link_libraries(MyProject PocoLib)