You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake使用ExternalProject管理仅头文件库的疑问:为何需两次构建命令及同时使用include_directories与target_include_directories

Let's break down your two questions one by one, and also fix some key issues in your CMake setup along the way.

1. Why do you need two separate CMake runs?

The core problem here lies in how ExternalProject_Add operates: it executes the download (and build/install steps, if configured) during the build phase of your CMake project, not during the initial configure phase.

Here's what happens in each step:

  • First run (GET_LIBS=ON, BUILD_MY_PROJECTS=OFF):
    CMake configures the project but skips the executable-building block entirely. When you run cmake --build, the ExternalProject_Add(eigen) target triggers, cloning the Eigen repo into ${CMAKE_BINARY_DIR}/install/eigen.
  • Second run (GET_LIBS=OFF, BUILD_MY_PROJECTS=ON):
    Now that Eigen's source files are already present in the target directory, CMake can properly configure your executable target and locate the required header files during the configure phase.

This two-step approach is a basic "superbuild" setup, but it's far from ideal. You can simplify this to a single build by either:

  • Using FetchContent (CMake 3.11+) instead of ExternalProject_Add for header-only libraries (it downloads dependencies during the configure phase, eliminating the need for two separate runs)
  • Structuring your superbuild properly, where ExternalProject_Add drives both dependency download and your main project's build.

2. Why do both include_directories and target_include_directories(INTERFACE) seem necessary?

This is caused by a misuse of the INTERFACE visibility in target_include_directories:

  • INTERFACE means the include directory is only passed to other targets that depend on my_exe, not to my_exe itself. Since my_exe is an executable (not a library that other targets link against), using INTERFACE here does nothing for the executable's own compilation.
  • include_directories adds the path globally, so all targets (including my_exe) can see it—which is why your build works when you include it. But global includes are bad practice, as they can lead to naming conflicts or unintended side effects across your project.

The simple fix:

Replace INTERFACE with PRIVATE (or PUBLIC if you ever convert my_exe into a library that other targets depend on). This applies the include directory directly to my_exe during its compilation, making include_directories completely unnecessary:

if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
# Remove the global include_directories line entirely
target_include_directories(my_exe PRIVATE "${CMAKE_BINARY_DIR}/install/eigen/")
endif ()

Bonus: Clean up your Eigen setup with FetchContent

Your current ExternalProject_Add has redundant settings (like CMAKE_INSTALL_PREFIX, which does nothing since you set INSTALL_COMMAND ""). For header-only libraries, FetchContent is a much cleaner alternative. Here's a rewritten version that eliminates the two-step build entirely:

cmake_minimum_required(VERSION 3.19)
project(superbuild LANGUAGES CXX)

if (BUILD_MY_PROJECTS)
  include(FetchContent)
  FetchContent_Declare(
    eigen
    GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
    GIT_TAG 3.4.0
  )
  FetchContent_MakeAvailable(eigen)

  add_executable(my_exe main.cpp)
  # Eigen automatically sets up a target Eigen3::Eigen with FetchContent
  target_link_libraries(my_exe PRIVATE Eigen3::Eigen)
endif ()

With this setup, you can run a single command to both download Eigen and build your executable:

cmake -DBUILD_MY_PROJECTS=ON -G "Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release

No more separate GET_LIBS runs needed—FetchContent handles downloading during the configure phase if the repo isn't already present.


内容的提问来源于stack exchange,提问作者PetrasVestartasEPFL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 00:22:35