CMake: include library dependencies in a static library
Okay, so I have a solution. First it's important to recognize that static libraries do not link other static libraries into the code. A combined library must be created, which on Linux can be done with ar
. See Linking static libraries to other static libraries for more info there.
Consider two source files:
test1.c:
int hi()
{
return 0;
}
test2.c:
int bye()
{
return 1;
}
The CMakeLists.txt
file is to create two libraries and then create a combined library looks like:
project(test)
add_library(lib1 STATIC test1.c)
add_library(lib2 STATIC test2.c)
add_custom_target(combined ALL
COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)
The options to the ar
command are platform-dependent in this case, although the CMAKE_AR
variable is platform-independent. I will poke around to see if there is a more general way to do this, but this approach will work on systems that use ar
.
Based on How do I set the options for CMAKE_AR?, it looks like the better way to do this would be:
add_custom_target(combined ALL
COMMAND ${CMAKE_CXX_ARCHIVE_CREATE} libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)
This should be platform-independent, because this is the command structure used to create archives internally by CMake. Provided of course the only options you want to pass to your archive command are rc
as these are hardwired into CMake for the ar
command.
CMake - including dependencies inside a static library
If you are bound to create a static library, the solution you linked in your original post is probably the best (CMake: include library dependencies in a static library). Using ar or library tools to combine the static libraries seems to be the only way to go. This is a pretty popular question on Stack Overflow and all the answers seem to come down to that.
However, if you are able, the easiest solution by far is to create a shared library and link your static libraries into it (as mentioned by jszpilewski in the comments). Yes, it does mean distributing the shared library for runtime. Whether that is practical or not depends on your project.
CMake: Including static libraries and resolving transitive dependencies
which was added to target_include_directories. How to resolve this ?
target_include_directories
adds #include
directories. target_link_directories
adds library search paths. Add the path to library search paths.
Is there a way to not specify the exact path of *.a libraries I wish to link and have CMake find it for me ?
No, CMake has to know the paths to libraries.
You can add the directory to target_link_directories
and have compiler search for your libraries in library search paths when linking.
How to make the above CMakeLists.txt more portable ?
To make your CMake more portable, lower the required minimum version, there are still machines even with CMake 2.8. CMake checks portability by itself with the specified version. Personally, I stick to CMake 3.11
, but for no reason.
Is there a way to include all (necessary) header files from a directory instead of including them one by one (which will be tedious if there were too many of them) ?
CMake does not include C header files.
In C programming language, create a header that #include
s all the files and then include that header in your C source files.
Any general advice
Calling multiple find_path
with the same variable is pointless - new calls are omitted, because the CMake cache variable already exists.
Well, re-read about CMake and some CMake introduction and an introduction to C compilation stages - about compiling and linking. You maybe want to research CMake IMPORTED
libraries and most probably ExternalProject_Add
utility.
There's no point in searching for header files or library files if you know they are there. Search for stuff if you are not sure if it is there, to be portable between environments that do not have that "stuff". Like some environments do not have unistd.h
- so you can check that in CMake and then differently compile code to handle such case - achieving portability.
with respect to above issues will be appreciated.
I in this case I would just go with one INTERFACE
library so it looks nice:
add_library(nag INTERFACE)
set(nag_path /usr/lib/NAG26/cll6i261d)
target_include_directories(nag INTERFACE "${nag_path}"/include)
target_link_directories(nag INTERFACE "${nag_path}"/lib)
target_link_libraries(nag INTERFACE ifcoremt svml imf irc)
target_link_libraries(AnyProject PRIVATE nag)
But maybe creating an IMPORTED
library for each ifcoremt svml imf irc
would be a nicer approach.
How to build static library with bundled dependencies - CMake
First of all, don't use the super old and outdated version 2.8
of CMake
. CMake 3.x
is so much more powerful and pretty straightforward to use.
Some tips for modern CMake.
- Don't use
file(GLOB)
, see here why that is. - Don't use directory wide instructions, rather use target instructions, e.g.
target_include_directories
vs.include_directories
. - Don't use string variables like
${<PACKAGE_NAME>_LIBRARIES}
, rather use targets, e.g.<Package_NAME>::lib
- When using targets instead of string variables, all the properties (including LINK_INTERFACE) of that target will be populated to the library/executable when calling
target_link_libraries
, so no moreinclude_directories
,link_directories
, etc.
myproject
cmake_minimum_required(VERSION 3.14)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
set(SOURCES ...) # list all the source files here
add_library(myproject ${SOURCES})
target_include_directories(myproject PUBLIC # give it a scope
${CMAKE_CURRENT_LIST_DIR}/include
)
target_link_libraries(myproject PUBLIC # give it a scope
opencv_core # using the target, you will get all LINK_LIBRARIES
opencv_highgui
opencv_video
opencv_imgcodecs
libmxnet.so # where is this coming from?
libncnn.a # where is this coming from?
nlohmann_json::nlohmann_json
OpenMP::OpenMP_CXX ## linking against a target, CXX_FLAGS will be populated automatically
)
myprojec-driver
cmake_minimum_required(VERSION 3.14)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver PUBLIC # give it a scope
myproject # gets all dependencies through the LINK_INTERFACE
)
Install prebuilt static library dependency with the parent library
Is this the right way of doing this? I could never find a good reference on how to package dependency static libraries alongside your project, and CMake doesn't seem to do this "on its own" too eagerly (I even had to manually copy the .lib there as you can see);
I think what you're doing is fine. If it were me, I would create and link to an IMPORTED STATIC
target named Parent::ThirdParty::dependency
for dependency.lib
in both the main build and in the package config file, and set its IMPORTED_LOCATION
property appropriately in each.
The reason being that CMake will validate that anything you link to that contains a ::
in its name is a CMake target. This tends to lead to fewer surprises on the link line and lets you attach things like include paths to the library.
How do I get CMake to use ${_IMPORT_PREFIX} there? [...]
Your issue is here:
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/lib/dependency.lib>
CMAKE_INSTALL_PREFIX
is set to E:/absolute/path/Parent/dist/Parent
immediately and CMake has no chance to replace it with something like ${_IMPORT_PREFIX}
. Fortunately, there is a generator expression for this.
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/lib/dependency.lib>
Try that instead.
CMake dependencies on a common static lib
If your "projects" are tightly coupled (e.g. live in the same repository / have the same parent directory), just write a CMakeLists.txt
above all three that calls add_subdirectory
for each project's directory. If you use project
in each, they should show up as separate entities in VS/XCode.
If not, just write your CMakeLists.txt
for the static library like you normally would, have it export itself (check out export()
and install(EXPORTS)
) and use find_package
in the consumers to find it, then just target_link_libraries
the imported target.
If these need to live in separate repositories, but you also need somewhere to create a build that builds all three, then you'll want to look at External Projects.
Related Topics
How to Use Parameters for the Table Name in SQLite3
Retrieving File Descriptor from a Std::Fstream
Adding Types to the Std Namespace
Sorting a Std::Vector<Std::Pair<Std::String,Bool>> by the String
Rand() Generating the Same Number - Even with Srand(Time(Null)) in My Main!
How to Use a Third-Party Dll File in Visual Studio C++
C/C++ Function Definitions Without Assembly
Iterative Dfs VS Recursive Dfs and Different Elements Order
How Define an Array of Function Pointers in C
Lambda Expressions as Class Template Parameters
Are C++ Templates Just MACros in Disguise
How Much Footprint Does C++ Exception Handling Add
How to Get a List of Files in a Directory in C++
How to Give Priority to Privileged Thread in Mutex Locking
How to Use Covariant Return Types with Smart Pointers