Reputation: 54589
I have the following directory layout:
main_folder
+ static_lib1
+ executable
find_package()
to locate 'static_lib1'.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)?
Upvotes: 8
Views: 4052
Reputation: 38
I guess I will leave this answer for posterity since only recently I have searched for a solution to this problem and found out that...
It is possible to override subsequent calls to find_package()
with FetchContent_Declare()
flag OVERRIDE_FIND_PACKAGE
.
Your
add_subdirectory("path/to/static_lib1")
call has to be replaced in main_folder/CMakeLists.txt
with:
include(FetchContent)
FetchContent_Declare(
static_lib1
SOURCE_DIR "path/to/static_lib1"
OVERRIDE_FIND_PACKAGE
)
Any calls to find_package(static_lib1)
will call FetchContent_MakeAvailable()
for you, virtually making it identical to add_subdirectory()
call.
You can read more about OVERRIDE_FIND_PACKAGE
in CMake documentation.
Upvotes: 2
Reputation: 54589
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:
static_lib1
is located somewhere on the disc.main_folder
, where both executable
and static_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 do
set_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
and IMPORTED_LINK_INTERFACE_LANGUAGES
, you will want to set the configuration specific properties like IMPORTED_LOCATION_DEBUG
and IMPORTED_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 the CMakeLists.txt
for executable
will look just the same, a simple call to find_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 of static_lib1
.
Upvotes: 0
Reputation: 34401
In executable's CMakeLists.txt you can check if you are building stand-alone or as part of project:
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
# stand-alone build
find_package(static_lib1)
else()
include_directories(../static_lib1)
link_directories(../static_lib1)
...
target_link_libraries(executable static_lib1)
endif()
Upvotes: 3