Issue
I have the following directory layout:
main_folder
+ static_lib1
+ executable
- Both 'static_lib1' and 'executable' have a full CMakeLists so that they can be built independently.
- The 'executable' depends on 'static_lib1'. It uses
find_package()
to locate 'static_lib1'. - The main_folder contains a CMakeLists that includes both 'static_lib1' and 'executable' via
add_subdirectory
for conveniently building the whole project in one go.
Everything works fine if I manually build 'static_lib1' and then 'executable'. But when running the CMakeLists from the main folder, I get an error because find_package
is unable to find the library files from 'static_lib1' which have not yet been built.
How can I resolve this while keeping the CMakeLists files separate (i.e. without including the static_lib's CMakeLists from the executable's CMakeLists)?
Solution
Switch from a file-based approach to a target-based approach for handling the dependency from executable
to static_lib1
.
The original problem occurred because executable
called find_package
for locating static_lib1
, which then attempted to fill a variable like STATIC_LIB1_LIBRARY
with the paths to the library files by calling find_library
. executable
then consumes the content of that variable in a target_link_libraries(executable ${STATIC_LIB1_LIBRARY})
call. The problem here is, since those library files only get generated as part of the build, that call to find_library
will not be able to find anything.
Building executable
needs to support two scenarios here:
- Building standalone, where a pre-compiled version of
static_lib1
is located somewhere on the disc. - Building from
main_folder
, where bothexecutable
andstatic_lib1
are part of the same build.
The approach from the question supports scenario 1, but not scenario 2.
Instead of using using a variable to communicate a dependency between the two builds, use a target. The CMakeLists.txt
for static_lib1
likely creates a library target like add_library(static_lib1 [...])
. In executable we now simply do target_link_libraries(executable PUBLIC static_lib1)
. This is sufficient to support scenario 2.
To also allow for scenario 1 at the same time, we look at the call to find_package(static_lib1)
in the CMakeLists.txt
for executable
. Instead of providing a variable like before, this call now needs to provide a target static_lib1
for consumption.
So we adapt the find script for static_lib1
to the following behavior:
If a target
static_lib1
already exists, there's nothing to be done and the find script can just return (this is scenario 2).Otherwise, we call
find_library
to locate the library file on disc (as before in the original approach) and then create a new imported target:add_library(static_lib1 STATIC IMPORTED)
. We then configure all relevant properties of the static library to that target. For instance, to add the location of the library file, we could doset_target_properties(static_lib1 PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION ${STATIC_LIB1_LIBRARY} )
To support multi-config generators like MSVC, instead of setting
IMPORTED_LOCATION
andIMPORTED_LINK_INTERFACE_LANGUAGES
, you will want to set the configuration specific properties likeIMPORTED_LOCATION_DEBUG
andIMPORTED_LOCATION_RELEASE
instead. Since this can get quite tedious to do manually, you can have CMake generate this information (and a bunch of other convenient stuff) for you in a package script. The find mechanism for package scripts works slightly different under the hood, but the code in theCMakeLists.txt
forexecutable
will look just the same, a simple call tofind_package(static_lib1)
. The main difference is that this call will then not dispatch to a hand-written find script, but to a package script that was automatically generated by CMake as part of the build process ofstatic_lib1
.
Answered By - ComicSansMS Answer Checked By - Mildred Charles (WPSolving Admin)