NDK project build with multiple CMakeFiles.txt - gradle

My intent is to have prebuilt libprebuilt.so and libmylib.so built into the apk. My project is structured like this
|-mylib_prebuilts
| |-libprebuilt.so
| |-include_dir
| '-CMakeFiles.txt
|-mylib
| |-src
| '-CMakeFiles.txt
'-build.gradle
In the CMakeFiles.txt for mylib_prebuilts, I have the following.
add_library(prebuiltlib SHARED IMPORTED)
target_include_directories(prebuiltlib INTERFACE include_dir)
set_property(TARGET prebuiltlib PROPERTY IMPORTED_LOCATION libprebuilt.so)
In the CMakeFiles.txt for lib, I have the following
add_subdirectory(../mylib_prebuilts mylib_prebuilts)
add_library(mylib SHARED ${srcs})
target_link_libraries(mylib PUBLIC prebuiltlib)
The root gradle.build has the following
externalNativeBuild {
cmake {
version '3.18.1'
path 'mylib/CMakeLists.txt'
}
}
I cannot reach the prebuilt headers or library from mylib. I get "headers not found" error, or if I manually add the header path using include_directories(../mylib_prebuilts/include_dir), build cannot find symbols when linking.

Answering my own question with a solution I stumbled upon.
mylib_prebuilts/CMakeLists.txt has to contain the following changes.
Add GLOBAL keyword to add_library call.
Use absolute path instead
of relative for set_property call using ${MAKE_CURRENT_SOURCE_DIR}
variable
add_library(prebuiltlib SHARED IMPORTED GLOBAL)
target_include_directories(prebuiltlib INTERFACE include_dir)
set_property(TARGET prebuiltlib PROPERTY IMPORTED_LOCATION ${MAKE_CURRENT_SOURCE_DIR}/libprebuilt.so)

Related

Installation issue with a `FetchContent`ed library

I am trying to install and export the target defined by the following CMakeLists.txt in a Windows environment. It uses FetchContent to download a library from github. I can build the library with no problem and generate nfd.lib. My goal is to use this library in my other projects.
cmake_minimum_required(VERSION 3.24)
project(mynfd VERSION 1.0.0)
include(FetchContent)
FetchContent_Declare(nfd
GIT_REPOSITORY https://github.com/btzy/nativefiledialog-extended.git
GIT_TAG c++-version)
FetchContent_MakeAvailable(nfd)
At this point, nfd.lib is successfully built in _deps/nfd-build/src/nfd.lib.
When I add the following install commands to the CMakeLists.txt above, I run into issues.
# Installation
include(GNUInstallDirs)
install(TARGETS nfd
EXPORT ${PROJECT_NAME}Targets
FILE_SET HEADERS
PUBLIC_HEADER DESTINATION ${nfd_SOURCE_DIR}/src/include
)
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION lib/cmake/${PROJECT_NAME})
I get the following error messages.
CMake Error in cmake-build-debug-visual-studio/_deps/nfd-src/src/CMakeLists.txt:
Target "nfd" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the build directory.
CMake Error in cmake-build-debug-visual-studio/_deps/nfd-src/src/CMakeLists.txt:
Target "nfd" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the build directory.Target "nfd"
INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the source directory.
How can I resolve this problem?

Executable built using cmake cannot find shared library

I have been working on a Visual Studio 2019 project with the objective of porting the application to Centos 8. I'm using a CMake project in VS2019 with remote building on my Centos machine. Basically, all the files are transferred to the remote machine and the project is built. My project directory looks as
Child
| CMakeLists.txt
| source
|__public
| headers
| libs
Parent
| CMakeLists.txt
| source
Child generates a shared library (.so) and some public headers which are to be linked in Parent. The so files and public headers are present in the public directory of Child. The CMake files of both the files are as follows
Child/CMakeLists.txt
set(PROJECT_NAME "Child")
set(Header_Files
#set of public headers and source headers
)
set(Source_Files
#set of source files
)
set(ALL_FILES
${Header_Files}
${Source_Files}
)
if (UNIX)
#Target is an SO
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
set(BOOST_INCLUDEDIR "${CMAKE_HOME_DIRECTORY}/Boost")
set(BOOST_LIBRARYDIR "${CMAKE_HOME_DIRECTORY}/Boost/libs/linux64-clang-9.0.0/static/release")
find_package(Boost REQUIRED)
if (Boost_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
endif()
set(XercesC_INCLUDE_DIR "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/include")
set(XercesC_LIBRARY "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/lib64")
find_package(XercesC REQUIRED)
if (XercesC_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${XercesC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/lib64/libxerces-c.so")
endif()
endif()
Parent/CMakeLists.txt
set(PROJECT_NAME "Parent")
set(Header_Files
#set of headers
)
set(Source_Files
#set of source files
)
set(ALL_FILES
${Header_Files}
${Source_Files}
)
if (UNIX)
set(CHILD_HEADERS "${CMAKE_HOME_DIRECTORY}/Child/public/headers")
set(CHILD_LIB_DIR "${CMAKE_BINARY_DIR}/Child/libs")
link_directories(${CHILD_LIB_DIR})
add_executable(${PROJECT_NAME} ${ALL_FILES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CHILD_HEADERS})
target_link_libraries(${PROJECT_NAME} PRIVATE Child)
#Copy libChild.so to Parent executable directory
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND
cp "${CHILD_LIB_DIR}/libChild.so"
"${CMAKE_CURRENT_BINARY_DIR}/libChild.so"
)
endif()
Problem
My objective is to have a build for my customer. Hence, I need to package all the executables and libs under a single zip folder. However, upon doing so I find the linker paths to have taken absolute path values of the sort
$ldd Parent
libChild.so => /root/.vs/Project/829a6827-2641-4552-a2fb-abec91ed47a2/out/build/Linux-Clang-Release/Child/libChild.so (0x00007f6934908000)
However, concerning my final build folder will be like this
Parent (executable)
libChild.so
libboost_regex...
libxerces... etc. etc.
I kinda want a relative path link to the same directory
libChild.so => ./libChild.so
Thus, when I export as a zip to another machine, it is unable to find the shared library. I have tried a couple of things (using other CMake functions) as well as copying the library into the executable directory first and then linking. However, it still links it as an absolute path which is likely to cause the same issue again. I have gone through the CMake documentation and perused through cmake-packages and relocatable packages but unable to fix the problem.
The issue can be resolved by placing these two statements in the CMake files for Project Child and Project Parent.
set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_BUILD_WITH_INSTALL_RPATH true)
Details about what these variables mean can be found in the CMake documentation.
Although this resolves the issue at the moment, however, this is not the correct approach for creating builds for a customer, as pointed out here.

Visual Studio Solution Style using CMakelists [duplicate]

I have a CMake project that looks like this:
project/
CMakeLists.txt
subprojectA/
CMakeLists.txt
include/
headerA.hpp
src/
libraryA.cpp
subprojectB/
CMakeLists.txt
src/
mainB.cpp
The "library" subproject, A, is compiled as a static library, becoming libsubprojectA.a. The "main" project, B, is compiled as a binary and depends on the library. mainB.cpp includes a reference to headerA.hpp.
Here is subprojectA/CMakeLists.txt:
project(SubProjectA)
include_directories(include)
add_library(subprojectA STATIC src/libraryA.cpp)
set(${PROJECT_NAME}_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/include
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
And here is subprojectB/CMakeLists.txt:
project(SubProjectB)
include_directories(${SubProjectA_INCLUDE_DIRS})
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
The main Project CMakeLists.txt looks like:
project(Project)
add_subdirectory(subprojectB)
add_subdirectory(subprojectA)
Note that subprojectB, the main project, is listed before subprojectA.
Here's the problem. When I first run "cmake" on this project, ${SubProjectA_INCLUDE_DIRS} is not set within SubProjectB.
What I think is happening is that the CMakeLists for SubProjectB loads first, when ${SubProjectA_INCLUDE_DIRS} has not yet been set. It sets its own include path to an empty string as a result. However, even though libsubprojectA.a gets built successfully before mainBinary, the include path was already set empty beforehand. As a result, I get this error when trying to make mainBinary:
subprojectB/src/mainB.cpp:1:23: fatal error: headerA.hpp: No such file or directory
#include "headerA.hpp"
^
It's a workaround to put subprojectA before subprojectB in the main Project CMakeLists in the declarative world of CMake. What I really want is to know the proper way to indicate to CMake that the include_directories(${SubProjectA_INCLUDE_DIRS}) line depends on the definitions that exist inside SubProjectA's CMakeLists. Is there a better way to do this?
If you want to express that include directory subprojectA/include is an interface of the library subprojectA, attach this property to the target with target_include_directories command:
subprojectA/CMakeLists.txt:
project(SubProjectA)
add_library(subprojectA STATIC src/libraryA.cpp)
# PUBLIC adds both:
# 1) include directories for compile library and
# 2) include directories for library's interface
target_include_directories(subprojectA PUBLIC include)
So any executable(or other library) which linked with subprojectA will have this include directory automatically:
subprojectB/CMakeLists.txt:
project(SubProjectB)
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
Of course, for use last command properly you need to process directory with library before one with executable:
CMakeLists.txt:
project(Project)
add_subdirectory(subprojectA)
add_subdirectory(subprojectB)

cmake import exist SimpleAmqp library

Now my CMakeList.txt looking like this.
cmake_minimum_required(VERSION 3.6)
project(RabbitMQClient)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)
include_directories(src)
include_directories(dependentFile)
add_executable(RabbitMQClient ${SOURCE_FILES})
find_library(SimpleAmqpClient SimpleAmqpClient.2.lib PATHS ./SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
set(IMPORTED_IMPLIB ./SimpleAmqpClientLib)
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
when Linking the RabbitMQClient it complains.
cannot find -lSimpleAmqpClient
I want to use the SimpleAmqpClient library in the project,however not quite familiar with cmake not sure the find_library,IMPORTED_IMPLIB,PUBLIC in target_link_libraries was correctly used.Any help would be appreciate.
You have messed up with variables, targets and properties.
Proper usage of IMPORTED libraries for linking would be:
# This command sets *variable* SimpleAmqpClient_LIBRARY
find_library(SimpleAmqpClient_LIBRARY SimpleAmqpClient.2.lib
PATHS ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib # Specify a *directory*, not a library *file*
)
# Next, create an IMPORTED *target*
add_library(SimpleAmqpClient SHARED IMPORTED)
# And set IMPORTED_LIB *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES IMPORTED_LIB ${SimpleAmqpClient_LIBRARY})
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
However, there some simplifications which could be done:
Normally, find_library is used when you don't know complete path to the library file. E.g., its directory may be different on different machines, or its prefix/extension may be different on different plaforms.
If you know complete path to the library, just use this path directly (e.g., assign it to the variable).
Normally, property IMPORTED_LOCATION is used for specify library to link with. Property IMPORTED_LIB is specific for Windows .dlls, when linking requires not a library file (.dll), but some other one (.lib).
However, CMake perfectly understands .lib file in IMPORTED_LOCATION property even for Windows .dlls, so your code need not distinguish SHARED Windows libraries from others: just use IMPORTED_LOCATION property in all cases.
Simplified version of the code:
# Create an IMPORTED library *target*
add_library(SimpleAmqpClient IMPORTED)
# Set IMPORTED_LOCATION *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)

CMake Hierarchical Project Management Without Abusing Libraries

I have a project where there's only a handful of logical groupings for generating static libraries. However for convenience I want to have the library's source code to be managed with more granular folders.
Currently the only way I know to do this in CMake without having a library for each folder is to just list files as you would normally in with their relative paths:
add_library(SystemAbstraction STATIC "Some/Path/File.cpp")
However I can see this getting unwieldy as the project grows in size with all the different paths.
I tried to see if I could have a CMakeLists.txt in each folder and just use a variable in the base CMakeLists.txt when adding library dependencies. But it seems that add_subdirectory doesn't also import variables?
For expanding the scope of a variable inside a subdirectory, use the PARENT_SCOPE option of set. For example, you can test that if you have
# CMakeLists.txt
set(SRCS main.c)
add_subdirectory(foo)
message(${SRCS})
in the root directory and
# foo/CMakeLists.txt
set(SRCS ${SRCS} foo.c PARENT_SCOPE)
in a subdirectory then it will print main.c foo.c, i.e., the variable is correctly imported into the base CMakeLists.txt.
An option would be to use the object library feature of CMake. You still can but doesn't need to organise your CMake script into subdirectories:
add_library(lib1 OBJECT <srcs>)
add_library(lib2 OBJECT <srcs>)
...
add_library(mainlib $<TARGET_OBJECTS:lib1> $<TARGET_OBJECTS:lib2>)
You can set different compile flags for each object library:
target_include_directories(lib1 PRIVATE incl-dir-for-lib1)
target_compile_definitions(lib2 PRIVATE def-for-lib2)
You still need to set link libraries on your main library:
target_link_libraries(mainlib PRIVATE deps-of-lib1 deps-of-lib2)
Related documentation: Object Libraries

Resources