ns_helloworld
ns_helloworld

Reputation: 125

How to link a library that was created from add_subdirectory in CMake?

I am trying to build a program using CMake that depends on a third party library. This third party library contains a CMakeLists.txt file so what I want to do is keep the source code of the third party library within my project directory, and build it using add_subdirectory(path/to/lib), and then link my target against the static library that the third party library generated.

my CMakeLists.txt:

cmake_minimum_version(VERSION 3.10)
project(my_project)

add_subdirectory("${CMAKE_SOURCE_DIR}/extern/somelib")

# my-code:
# somelib CMakeLists.txt file has a project name: SOMELIB
# which lets me access the directory where the files are built
# on windows it builds to /Release, on mac and linux it just builds
# to the binary dir
set(SOMELIB_LIBS "${SOMELIB_BINARY_DIR}/Release")
add_executable(my_program my_main.cpp)
target_link_libraries(my_program "${SOMELIB_LIBS}/SOMELIB.lib" "${SOMELIB_LIBS}/SOMELIBmain.lib")

I then make a build directory and from that directory I do:

cmake -G "Visual Studio 15 2017" ..
cmake --build .

The build command fails with a "LINK : fatal error LNK1181: cannot open input file 'extern/somelib/Release/SOMELIBmain.lib' ..."

My workaround for now has been to comment out the part that says "#my-code", build the somelib dependency first which generates the static libraries, and then uncomment out my-code and build again which then works correctly.

How can I tell CMake to build the subdirectory first, and then link against the static libraries that it generated?

Upvotes: 3

Views: 7360

Answers (1)

R2RT
R2RT

Reputation: 2146

Short answer: tell CMake that there is dependency between its targets.

target_link_libraries(my_program PRIVATE SOMELIB SOMELIBmain)

CMake will evaluate SOMELIBs locations for you and link my_program against SOMELIB and SOMELIBmain[1]. Works for both Debug and Release configurations and also for Linux.

You shouldn't have to worry where CMake places build files[2] and this is what so called "modern CMake" is trying to achieve. I will leave here just brief description but check link on the bottom of answer[3].

Anyway, the key concept is that whenever you create library/executable (target), you list its usage+build requirements and dependencies to other targets. You declare dependencies using target_link_libraries(<your_lib> KEYWORD <dependencies>). Such line will not only make <you_lib> link against listed dependencies, it will inherit their usage requirements (usually, public include directories) and order CMake to build dependent libraries before <your_lib>.

The beauty of it is that even if SOMELIB does not follow modern CMake ideas (does not declare build/usage requirements), you still should be able to do just with this single line.

[1] I assumed that CMake targets are named same as output library names, but it is not mandatory. For OP's case it turned out that static library targets are suffixed with -static, so he had to write SOMELIB-static SOMELIBmain-static

[2] Unfortunately, it is not so easy with shared libraries on Windows (DLLs)

[3] I'd start with this blog entry: https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/

Upvotes: 3

Related Questions