Cmake: Include Library Dependencies in a Static Library

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 #includes 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 more include_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



Leave a reply



Submit