shashashamti2008
shashashamti2008

Reputation: 2337

Accessing external libraries loaded with FetchContent located in subdirectories in the main CMakeLists.txt

I have the following directory structure for my app. The libraries in the external directory all use fetchcontent command.

myapp
│
│ CMakeLists.txt
│
├───data
│ data.csv
│
├───external
│ ├───csv-parser
│ │ CMakeLists.txt
│ │
│ ├───eigen
│ │ CMakeLists.txt
│ │
│ └───fmt
│ CMakeLists.txt
│
├───include
│ myapp.h
│
├───out
│ └───build
│
├───src
│ main.cpp
│
└───tests   

The CMakeLists.txt in the csv-parser folder is as follows.

cmake_minimum_required(VERSION 3.24)
project(libcsvparser VERSION 1.0.0)

set(CMAKE_CXX_STANDARD 20)

include(FetchContent)
include(CMakePrintHelpers)

FetchContent_Declare(csv_parser
        GIT_REPOSITORY https://github.com/ben-strasser/fast-cpp-csv-parser.git)
FetchContent_MakeAvailable(csv_parser)
cmake_print_variables(csv_parser_SOURCE_DIR csv_parser_BINARY_DIR)

The CMakeLists.txt in the fmt folder is as follows.

cmake_minimum_required(VERSION 3.24)
project(libfmt VERSION 1.0.0)

set(CMAKE_CXX_STANDARD 20)

include(FetchContent)
include(CMakePrintHelpers)

FetchContent_Declare(fmt GIT_REPOSITORY https://github.com/fmtlib/fmt)
FetchContent_MakeAvailable(fmt)
cmake_print_variables(fmt_SOURCE_DIR fmt_BINARY_DIR)

The CMakeLists.txt in the eigen folder is as follows.

cmake_minimum_required(VERSION 3.24)
project(libeigen VERSION 1.0.0)

set(CMAKE_CXX_STANDARD 20)

include(FetchContent)
include(CMakePrintHelpers)

FetchContent_Declare(eigen
        GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git)
FetchContent_MakeAvailable(eigen)
cmake_print_variables(eigen_SOURCE_DIR eigen_BINARY_DIR)

For all the external libraries, I get the correct ???_SOURCE_DIR and ???_BINARY_DIR printed during the build.

The main CMakeLists.txt is as follows.

cmake_minimum_required(VERSION 3.24)
project(myapp VERSION 1.0.0)

include(FetchContent)
include(CMakePrintHelpers)

cmake_print_variables(CMAKE_CXX_FLAGS)

set(CMAKE_CXX_STANDARD 20)

add_subdirectory(external/csv-parser)
add_subdirectory(external/eigen)
add_subdirectory(external/fmt)

cmake_print_variables(csv_parser_SOURCE_DIR)
cmake_print_variables(eigen_SOURCE_DIR)
cmake_print_variables(fmt_SOURCE_DIR)

add_executable(myapp)
target_sources(myapp PRIVATE src/main.cpp)
target_include_directories(myapp PUBLIC include)
target_include_directories(myapp PUBLIC csv_parser_SOURCE_DIR)
target_include_directories(myapp PUBLIC eigen_SOURCE_DIR)
target_include_directories(myapp PUBLIC fmt_SOURCE_DIR)

I am having trouble accessing the external libraries in the main CMakeLists.txt and during its build the following commands will print empty as if the add_subdirectory did not work correctly.:

csv_parser_SOURCE_DIR=""
eigen_SOURCE_DIR=""
fmt_SOURCE_DIR=""

Because of this my Visual Studio does not recognize where the following include files are and I can't build the myapp target.

#include "Eigen/Dense"
#include "fmt/core.h"
#include <csv.h>

Upvotes: 0

Views: 510

Answers (2)

Tsyvarev
Tsyvarev

Reputation: 66118

If a subproject follows the target-oriented style ("modern CMake"), then one rarely need to know its source or binary directory: for consume the subproject in the CMakeLists.txt you could just link with the target(s) created by the subproject.

E.g. in case of Eigen you could write

target_link_libraries(myapp PUBLIC Eigen3::Eigen)

(How to add Eigen library to a cmake c++ project via FetchContent).

If a subproject doesn't follow the target-oriented style, and instead of target_include_directories uses include_directories, then you could "fix" that problem by adding missing properties in the same CMakeLists.txt where FetchContent for that project is called. So the rest of your code could use target_link_libraries as in the previous case.

E.g. csv-parser contains a single header and doesn't provide CMakeLists.txt. You may create an IMPORTED target which represents a header-only library:

# in csv-parser/CMakeLists.txt
add_library(csv_parser INTERFACE)
target_include_directories(csv_parser INTERFACE ${csv_parser_SOURCE_DIR})

and link with that target in outer code:

# in top-level CMakeLists.txt
target_link_libraries(app PUBLIC csv_parser)

Upvotes: 1

gwanhun
gwanhun

Reputation: 41

The most simplest way is to set the variable, {project}_SROUCE_DIR, to PARENT_SCOPE.
For example, you can add below code in your CMakeLists.txt of the fmt directory.

set(fmt_SOURCE_DIR ${fmt_SOURCE_DIR} PARENT_SCOPE)

The reason why main CMakeLists could not access that variable is the variable scope related problem.

{project}_SOURCE_DIR variable is valid while configuring {project} subdirectory.

Upvotes: 1

Related Questions