我应该如何在 CMake 中有条件地设置 -isystem 和 -L 标志?

问题描述 投票:0回答:1

我有一个项目要为 Linux 和 Windows 编译。前者的编译是在Linux环境下完成的,后者的编译是在w64devkit中完成的,即不需要考虑单机交叉编译。

对于 Windows 版本,所有编译文件都需要

-isystem $(VCPKG_DIR)/installed/x64-windows/include
编译标志和
-L $(VCPKG_DIR)/installed/x64-windows/lib
链接器标志,其中
$(VCPKG_DIR)
指安装 vcpkg 的基本位置。

(为了简单起见,这个问题的其余部分重点关注包含路径)

这似乎是 CMake 预设的一个很好的应用程序,大致如下:

{
  "version": 1,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 19,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "linux",
      "displayName": "Build for Linux",
      "description": "Linux build using make generator",
      "generator": "Unix Makefiles",
      "binaryDir": "${sourceDir}/build"
    },
    {
      "name": "win64",
      "displayName": "Build for Windows",
      "description": "Windows build using make generator",
      "generator": "Unix Makefiles",
      "binaryDir": "${sourceDir}/build",
      "CMAKE_C_FLAGS": "${VCPKG_DIR}/installed/x64-windows/include"
    }
  ]
}

但是从这个答案中,我了解到我们应该设置包含路径的唯一方法是通过

target_include_directories()
(或
target_sources()
)。

这是有道理的,但我担心的是,在我的项目中,我有很多目录(彼此是对等的),每个目录都有

CMakeLists.txt
。那么,我似乎需要在每个
target_include_directories()
文件中添加一行
CMakeLists.txt
行,例如

target_include_directories(submodule SYSTEM ${VCPKG_DIR}/installed/x64-windows/include)

即我担心的是此行在多个 CMakeLists.txt 文件中的

重复
。这似乎是在一个位置进行整合的良好候选者,其效果是“如果为 Windows 构建,请始终添加
-I  ${VCPKG_DIR}/installed/x64-windows/include
”,这似乎有点像 CMake 预设的意图。

但是,如果使用 CMake Presets 无法实现这一点,或者不是最佳实践,那么如何在构建目标的每个 CMakeLists.txt 文件中添加包含路径?我必须做这样的事情吗:

if(WIN64)
    target_include_directories(my_executable SYSTEM "${VCPKG_DIR}/installed/x64-windows/include")
endif()

我的问题是:什么是最统一(最少重复)的方法,或者条件化跨多个 CMakeLists.txt 的包含路径(以构建目标为条件)的惯用正确方法是什么?换句话说:如果我的逻辑是“在构建

win64
时,始终包含
${VCPKG_DIR}/installed/x64-windows/include
作为系统包含路径”,在 CMake 中实现该功能的最佳(或惯用正确)方法是什么?

cmake include-path compiler-flags linker-flags cmake-presets
1个回答
0
投票

这是给您的一个最小示例。我是在 Linux 上完成的,但步骤在 Windows 上应该是相同的。我假设 CMake 是最新的并且您已经安装了 Ninja。

首先我们安装vcpkg和libuv。在我的笔记本电脑上这花了大约 5 分钟。

$ cd dev
~/dev$ git clone https://github.com/microsoft/vcpkg.git
~/dev$ cd vcpkg
~/dev/vcpkg$ ./bootstrap.sh -disableMetrics
~/dev/vcpkg$ vcpkg install libuv

现在我们创建一个示例项目:

~/dev/vcpkg$ cd ~/dev
~/dev$ mkdir example
~/dev$ cd example
~/dev/example$ touch CMakeLists.txt main.cpp

现在这是

CMakeLists.txt
的内容:

cmake_minimum_required(VERSION 3.28)
project(example)

find_package(libuv REQUIRED)

add_executable(main main.cpp)
target_link_libraries(
  main PRIVATE $<IF:$<TARGET_EXISTS:libuv::uv_a>,libuv::uv_a,libuv::uv>
)

这是

main.cpp
。足以测试我们是否可以找到标题。

#include <uv.h>
int main () { return 0; }

现在构建这个:

$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=$HOME/dev/vcpkg/scripts/buildsystems/vcpkg.cmake
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (25.2s)
-- Generating done (0.0s)
-- Build files have been written to: /home/alex/dev/example/build
$ cmake --build build --verbose
Change Dir: '/home/alex/dev/example/build'

Run Build Command(s): /usr/bin/ninja -v
[1/2] /usr/bin/c++  -isystem /home/alex/dev/vcpkg/installed/x64-linux/include -g -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /home/alex/dev/example/main.cpp
[2/2] : && /usr/bin/c++ -g  CMakeFiles/main.dir/main.cpp.o -o main  /home/alex/dev/vcpkg/installed/x64-linux/debug/lib/libuv.a  -lpthread  -ldl  -lrt && :

在此处的输出中,我可以非常清楚地看到

-isystem .../vcpkg/installed/x64-linux/include

这是有效的,因为链接到目标(即

libuv::uv
libuv::uv_a
)会自动触发库的公共包含路径(即vcpkg/installed/x64-linux/include)到您的目标的
传播
。因为该目标是通过
find_package
导入的,所以 CMake 知道使用
-isystem
而不是
-I
。它还使用静态库的完整路径,而不是冒着
-L
的名称解析问题的风险。无论如何,这是更好的行为。

© www.soinside.com 2019 - 2024. All rights reserved.