立即构建一个工具,以便稍后在同一 CMake 运行中使用它

2023-12-28

我有一个有趣的先有鸡还是先有蛋的问题以及一个潜在的解决方案(请参阅我发布的答案),但该解决方案以一种不寻常的方式使用 CMake。欢迎更好的替代方案或评论。

问题:

该问题的简单版本可以描述为具有以下特征的单个 CMake 项目:

  1. 构建目标之一是一个命令行可执行文件,我将其称为mycomp,其来源是在mycompdir并且无法对该目录的内容进行任何修改。
  2. 该项目包含文本文件(我将其称为foo.my and bar.my)哪个需要mycomp在它们上运行以生成一组 C++ 源代码和标头以及一些CMakeLists.txt定义从这些源构建的库的文件。
  3. 同一项目中的其他构建目标需要链接到生成的库定义的库CMakeLists.txt文件。这些其他目标也有来源#include一些生成的标头。

你可以想到mycomp作为编译器之类的东西,而步骤 2 中的文本文件作为某种源文件。这提出了一个问题,因为 CMake 需要CMakeLists.txt配置时的文件,但是mycomp直到构建时才可用,因此在第一次运行时不可用以创建CMakeLists.txt尽早归档。

无答案:

通常,基于ExternalProject的超级构建安排将是一个潜在的解决方案,但上面是我正在处理的实际项目的相当大的简化,我没有自由将构建分割成不同的部分或执行其他大型任务规模重组工作。


问题的关键是需要mycomp当 CMake 运行时可用,以便生成CMakeLists.txt可以创建文件然后将其拉入add_subdirectory()。实现此目的的一种可能方法是使用execute_process()从主构建运行嵌套的 cmake-and-build。嵌套的 cmake-and-build 将使用与顶级 CMake 运行完全相同的源代码和二进制目录(除非交叉编译)。主顶层总体结构CMakeLists.txt会是这样的:

# Usual CMakeLists.txt setup stuff goes here...

if(EARLY_BUILD)
    # This is the nested build and we will only be asked to
    # build the mycomp target (see (c) below)
    add_subdirectory(mycompdir)

    # End immediately, we don't want anything else in the nested build
    return()
endif()

# This is the main build, setup and execute the nested build
# to ensure the mycomp executable exists before continuing

# (a) When cross compiling, we cannot re-use the same binary dir
#     because the host and target are different architectures
if(CMAKE_CROSSCOMPILING)
    set(workdir "${CMAKE_BINARY_DIR}/host")
    execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${workdir}")
else()
    set(workdir "${CMAKE_BINARY_DIR}")
endif()

# (b) Nested CMake run. May need more -D... options than shown here.
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
                        -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
                        -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
                        -DEARLY_BUILD=ON
                        ${CMAKE_SOURCE_DIR}
               WORKING_DIRECTORY "${workdir}")

# (c) Build just mycomp in the nested build. Don't specify a --config
#     because we cannot know what config the developer will be using
#     at this point. For non-multi-config generators, we've already
#     specified CMAKE_BUILD_TYPE above in (b).
execute_process(COMMAND ${CMAKE_COMMAND} --build . --target mycomp
                WORKING_DIRECTORY "${workdir}")

# (d) We want everything from mycompdir in our main build,
#     not just the mycomp target
add_subdirectory(mycompdir)

# (e) Run mycomp on the sources to generate a CMakeLists.txt in the
#     ${CMAKE_BINARY_DIR}/foobar directory. Note that because we want
#     to support cross compiling, working out the location of the
#     executable is a bit more tricky. We cannot know whether the user
#     wants debug or release build types for multi-config generators
#     so we have to choose one. We cannot query the target properties
#     because they are only known at generate time, which is after here.
#     Best we can do is hardcode some basic logic.
if(MSVC)
    set(mycompsuffix "Debug/mycomp.exe")
elseif(CMAKE_GENERATOR STREQUAL "Xcode")
    set(mycompsuffix "Debug/mycomp")
else()
    set(mycompsuffix "mycomp")
endif()
set(mycomp_EXECUTABLE "${workdir}/mycompdir/${mycompsuffix}")
execute_process(COMMAND "${mycomp_EXECUTABLE}" -outdir foobar ${CMAKE_SOURCE_DIR}/foo.my ${CMAKE_SOURCE_DIR}/bar.my)

# (f) Now pull that generated CMakeLists.txt into the main build.
#     It will create a CMake library target called foobar.
add_subdirectory(${CMAKE_BINARY_DIR}/foobar ${CMAKE_BINARY_DIR}/foobar-build)

# (g) Another target which links to the foobar library
#     and includes headers from there
add_executable(gumby gumby.cpp)
target_link_libraries(gumby PUBLIC foobar)
target_include_directories(gumby PUBLIC foobar)

如果我们不重复使用 (b) 和 (c) 中用于主构建的相同二进制目录,我们最终会构建mycomp两次,这显然是我们想要避免的。对于交叉编译,我们无法避免这种情况,因此在这种情况下我们构建mycomp工具放在单独的二进制目录中。

我已经尝试过上述方法,实际上它似乎适用于引发最初问题的现实世界项目,至少对于 Unix Makefiles、Ninja、Xcode(OS X 和 iOS)和 Visual Studio 生成器来说是这样。这种方法的部分吸引力在于它只需要在顶层添加适量的代码CMakeLists.txt文件。尽管如此,还是应该提出一些意见:

  • 如果编译器或链接器命令为mycomp并且其来源在嵌套构建和主构建之间有任何不同,mycomp目标最终在 (d) 处第二次重建。如果没有差异,mycomp不交叉编译时只构建一次,这正是我们想要的。
  • 我认为没有简单的方法可以将与传递到顶级 CMake 运行的参数完全相同的参数传递给 (b) 处的 CMake 嵌套调用(基本上是所描述的问题)here https://stackoverflow.com/questions/10205986/how-to-capture-cmake-command-line-arguments)。阅读CMakeCache.txt不是一个选项,因为它在第一次调用时不存在,并且无论如何也不会为您提供当前运行中的任何新的或更改的参数。我能做的最好的事情就是设置那些我认为可能会被使用的 CMake 变量,这可能会影响编译器和链接器命令mycomp。当我遇到我发现需要的变量时,可以通过添加越来越多的变量来解决这个问题,但这并不理想。
  • 当重新使用相同的二进制目录时,我们依赖 CMake,直到生成阶段才开始将其任何文件写入二进制目录(好吧,至少直到 (c) 处的构建完成之后)。对于测试的发电机来说,看起来我们没问题,但我不知道是否all发电机开启all平台也遵循这种行为(我无法测试每个组合来找出答案!)。这是我最关心的部分。如果任何人都可以通过推理和/或证据确认这对于所有生成器和平台都是安全的,那将是有价值的(如果您想将其作为单独的答案来解决,则值得投票)。

UPDATE:在对具有不同 CMake 熟悉程度的人员的多个实际项目使用上述策略后,可以得出一些结论。

  • 让嵌套构建重复使用与主构建相同的构建目录有时会导致问题。具体来说,如果用户在嵌套构建完成之后但在主构建完成之前终止 CMake 运行,则CMakeCache.txt文件留下EARLY_BUILD set to ON。这使得所有后续的 CMake 运行都像嵌套构建一样,因此主构建基本上会丢失,直到CMakeCache.txt文件被手动删除。项目之一的某个地方可能出现错误CMakeLists.txt文件也可能会导致类似的情况(未经证实)。在其自己单独的构建目录中执行嵌套构建效果非常好,但没有出现此类问题。

  • 嵌套构建可能应该是Release而不是Debug。如果不重新使用与主构建相同的构建目录(现在我建议这样做),我们不再关心尝试避免编译相同的文件两次,所以不妨使mycomp尽可能快。

  • 使用ccache https://crascit.com/2016/04/09/using-ccache-with-cmake/以便最大限度地减少因使用不同设置两次重建某些文件而产生的成本。实际上,我们发现使用 ccache 通常会使嵌套构建非常快,因为与主构建相比它很少发生变化。

  • 嵌套构建可能需要有CMAKE_BUILD_WITH_INSTALL_RPATH set to FALSE在某些平台上,以便任何库mycomp无需设置环境变量等即可找到需求

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

立即构建一个工具,以便稍后在同一 CMake 运行中使用它 的相关文章

  • 用于区分调试和发布版本的 CMake 变量或属性

    我想为调试和发布版本设置不同的 CMake 变量 我尝试像这样使用 CMAKE CFG INTDIR IF CMAKE CFG INTDIR STREQUAL Debug SET TESTRUNNER DllPlugInTesterd dl
  • CMake - 将预构建库链接到 C# 项目

    我正在使用 CMake 构建 C 库 该库依赖于已构建的库 dll 我似乎无法让图书馆链接到我的图书馆 我尝试过使用target link libraries mylib external lib 我也尝试过暴力破解 reference e
  • CMake:连续编译程序两次

    为了能够进行许多自动优化 我希望能够使用标志编译我的程序 fprofile generate首先 然后运行它生成配置文件 然后使用以下命令重新编译程序 fprofile use反而 这意味着我想连续编译我的程序两次 使用两个不同的CMAKE
  • 如何通过 Cmake 查找 iOS/mac OS X 框架

    Context 我通过此将 Boost C 库打包到一个框架 适用于 iOS 中script https github com davidandreoletti boostoniphone generic 该脚本产生boost框架 当链接到
  • 通过ExternalProject_Add 使用 pybind11 进行 CMake 项目的智能方法

    我正在使用编写一个 python 模块pybind11 with CMake3 9 4 因为方便所以想下载pybind11源文件使用ExternalProject Add in my CMakeLists txt 当我跑步时cmake 它不
  • 在 Ubuntu 16.04 上编译 PCL 1.7,CMake 生成的 Makefile 中出现错误

    我正在尝试让 PCL 1 7 点云库 而不是其他 pcl 在 Ubuntu 16 04 上运行 我最终希望用于 C 的东西 但现在我只是想让这些例子工作 我使用的是 Ubuntu GNU 5 3 1 附带的默认编译器和 Cmake 版本 3
  • 在cmake中设置PKG_CONFIG_PATH

    我已经在本地构建了 opencv 并将其安装到本地目录 不是系统默认目录 opencv pc存在于该本地文件夹中的 pkgconfig 文件夹下 我怎样才能找到这个opencv pc来自 cmake 因为我想从我的程序链接并包含 openc
  • 如何使我的单元测试适应 cmake 和 ctest?

    到目前为止 我已经使用了一个临时的单元测试程序 基本上是由批处理文件自动运行的全部单元测试程序 尽管其中很多都明确地检查了他们的结果 但还有更多的作弊行为 他们将结果转储到版本控制的文本文件中 测试结果中的任何更改都会被颠覆标记 我可以轻松
  • 如何将 gnatmake/gnatbind/gnatlink 集成到 C/Ada 代码的 CMake 文件中?

    我用几种语言 C C Fortran77 Fortran90 编写了代码 并且可以使用 CMake 编译它 没有任何问题 效果很完美 现在 我想在用 C 编写的 main 中添加一些 Ada 函数 并且我想通过 CMake 编译它 鉴于我无
  • CMake“无法运行 MSBUILD.exe”命令错误

    当我想为 opencv 3 3 0 创建 Visual Studio 15 2017 make 文件时 它给了我以下错误消息 error in configuration process project files maybe invali
  • Clang 与 CLion:无法获取编译器信息

    我尝试通过更改在 CLion 中从 gcc 切换到 clang工具链偏爱 但现在 cmake 失败并显示以下内容 Cannot get compiler information Compiler exited with error code
  • 检查 cmake 中顺序不正确的可选目标

    我目前正在开发一个使用 cmake 作为构建系统的大型软件项目 但我有一个问题要检查另一个目标是否存在 或将存在 例如 有根 CMakeLists txt 和两个模块 可以选择将其作为子文件夹添加到软件项目中 A CMakeLists tx
  • 如何将新的 Visual Studio 2017 cmake 功能与 gitlab CI 运行程序结合使用

    我以直接的方式为 Linux 设置了 gitlab ci runner cmake make make test 大致如下 variables GIT SUBMODULE STRATEGY recursive stages build te
  • 如何使用cmake查找库?

    要将可执行文件与驻留在标准位置的库链接 可以在 CmakeLists txt 文件中执行以下操作 create executable generate mesh generate mesh cpp target link libraries
  • CMake - 未定义参考

    我正在尝试将 gtest 包含到我的项目中 问题是我在 GTest 中收到未定义的引用错误 我正在尝试在 Gtest 中测试 Node 类 在节点的构造函数中 我使用类记录器 尽管我已将库记录器添加到 gtest target 中 但我仍然
  • 将 cmake 与 nmake x64 一起使用

    我使用 cmake 生成 NMake 文件 一切正常 我使用 x64 工具集 没有 Visual Studio 只有 SDK 因此我输入 nmake 但它生成 x86 构建而不是 x64 构建 你知道为什么吗 以及如何强制 nmake 以
  • 使用 CMake 编译时更改头文件位置会导致缺少 vtable 错误

    对于一个大型 C 项目 我需要从 qmake 过渡到 CMake 但是在处理一个玩具示例时 我遇到了一些我不理解的行为 示例代码具有单个头文件 当该头文件移动到子目录中时 我收到 MainWindow 类缺少 vtable 的错误 CMak
  • 我可以安装共享导入库吗?

    我有一个外部项目和一个导入的共享库 include 目录和 implib 都可以正常工作 但尝试安装共享库 dll 失败并出现以下错误 install TARGETS given target my shared lib which doe
  • 是否可以让 cmake 构建文件(CMakeLists.txt)不在 CLion 的根目录中

    是否可以让 cmake 构建文件 CMakeLists txt 不在 CLion 的根目录中 我目前正在开发的项目中 cmake 构建文件不在 CLion 项目的根目录中 在 out Debug 目录中 我希望 CLion 打开该项目的根目
  • 使用 CLion IDE 运行 Opengl 程序 [重复]

    这个问题在这里已经有答案了 我知道这个问题已经被问过 但提供的解决方案确实对我不起作用 我想通过 CLion IDE 运行我的 OpenGL 程序 我可以通过 Ubuntu 中的终端运行相同的程序 gcc progname c lglut

随机推荐

  • DataTriggerBehavior 不适用于 Enum?

    我正在尝试使用DataTriggerBehavior来自行为 SDK 但它似乎不适用于枚举 否则我做错了什么 您可以假设DataContext对于这些例子来说是这样的 INotifyPropertyChanged已实现 但我不打算在这里展示
  • 即使服务帐户具有“所有者”权限,也会引发权限不足的错误

    In 谷歌云平台我创建了一个服务账户并分配了OWNER and 服务帐户参与者 role 当我运行下面的命令时 gcloud container clusters get credentials travis test zone us ce
  • 我有一些关于 WebView 的问题

    我无法正确使用WebView 当我运行我的应用程序时 出现 使用什么 窗口和按钮 浏览器 Chrome 排序 单击 取消 时没有任何反应 我的代码没有错误 我真的不知道如何正确使用WebView 请帮我 使用什么 窗口 https i st
  • Redis 流水线执行顺序

    我正在使用 rub redis gem 想知道我是否这样做 例如 redis pipelined do REDIS del users current user id i unread REDIS lpush users current u
  • 如何附加到切片指针接收器

    我有一个切片的类型别名 当切片是指针接收器时 我希望能够附加到切片 或切片中的过滤器 package main import fmt type itself string func h itself appendToItself test
  • 需要同意才能将文件的所有权转让给其他用户 [Google Drive API]

    我有一个与 1 个用户 所有者 Gmail 帐户 和第二个用户 作者 Google 服务帐户 如 iam gserviceaccount com 共享的 Google 表格文件 我使用此函数由用户 2 从用户 1 复制表 并再次使复制文件的
  • 管理 JavaScript 应用程序的时区和 DST 问题

    我正在尝试创建一个调度应用程序 前端 UI是使用JavaScript开发的 后端是一个 ASP NET Web Api 应用程序 使用 MSSQL 服务器作为数据库 从 UI 中 用户将安排一个可以每天 每周 每月运行的作业 每个作业最多可
  • 使用 Nokogiri(而不是 Tidy)清理 HTML

    The tidygem 不再维护 并且存在多个内存泄漏问题 有些人建议使用Nokogiri 我目前正在使用以下方法清理 HTML Nokogiri HTML DocumentFragment parse html to html 不过我有两
  • 从 Eclipse 运行时,Java JFrame 窗口不出现

    一个非常简单的问题 我尝试运行一个非常简单的演示来从 Eclipse 创建并显示一个窗口框架 但没有任何反应 没有错误 没有窗口 代码运行完成 我添加了断点并确保代码按预期运行 该代码直接来自 Java 教程 FrameDemo 我只是重命
  • 删除 WWW 的表单。并在输入上打印结果?

    我需要制作一个类似于 缩短链接 网站使用的表格 它应该简单地删除 WWW 并回显结果 以便我稍后在其周围添加我的代码 例如 如果用户输入 www pizza com blablabla 单击输入应显示 pizza com blablabla
  • 创建一个简单但灵活的模板引擎

    我正在尝试构建一个基本的模板引擎 就像已经作为开源提供的模板引擎一样 我正在使用搜索和替换技术 然而 由于搜索和替换必须进行硬编码 因此不太灵活 我的意思是 作为一个例子 我正在使用这样的东西 templateMarkup div clas
  • 无法使 Oauth PHP 扩展正常工作

    当我尝试使用 PHP 进行 oAuth 之旅时 我感到很抓狂 我正在使用 MAC OSX 10 7 4 MAMP PHP 并且我指向 MAMP 环境中的 php 我已经下载了最新的 oAuth php 扩展 1 2 2 我跑过 pecl i
  • IN r,如何将摘要结合在一起

    假设我有 5 组数据的 5 个摘要 我怎样才能得到这些数字或将摘要合并为 1 而不是 5 V1 V2 V3 V4 Min 670 2 Min 682 3 Min 690 7 Min 637 6 1st Qu 739 9 1st Qu 737
  • 解压后无法删除压缩文件

    我试图在 Windows 上解压内容后删除压缩文件 内容可以存储在 zip 中的文件夹结构中 我正在使用with声明并认为这会关闭类似文件的对象 sourcevar 和 zip 文件 我删除了与保存源文件相关的代码行 import zipf
  • AVX2 的 gcc 目标禁用 SSE 指令集

    我们有一个要使用 AVX2 编译的翻译单元 仅此一个 它预先告诉 GCC 文件中的第一行 pragma GCC target arch core avx2 tune core avx2 这曾经适用于 GCC 4 8 和 4 9 但从 6 开
  • SqlAlchemy 的外键问题

    我收到错误 无法从字符串 MACHINE IE 解析 rfc1738 URL 当我尝试导入以下内容时 class MACHINE declarative base tablename MACHINE MACHINE UID Column I
  • 替换 OpenXML 中的内容控件

    我需要一些东西作为占位符 我起初将内容控制视为解决方案 但我遇到了一些问题 然后 我考虑将 CustomXML 添加到 docx 但由于 i4i 诉讼而放弃了这一做法 然后我决定通过 OpenXML SDK 2 0 简单地更改内容控件的文本
  • 如何使用 Prisma 动态生成的包部署到 Vercel

    我正在使用 Prisma 和 Vercel Prisma 动态生成 Prisma 客户端 但 Vercel 会缓存旧客户端并且不会重建它 除非我登录 Vercel 并单击 重新部署 这会强制它重新安装所有软件包 有没有办法强制这个包在我每次
  • viewDidLoad 中的边界和帧大小

    我推了一个视图控制器 mainVC 并向其添加一些子视图 大多数这些视图都是在用户采取某些操作时按需构建的 当我构建每个视图时 我参考mainVC view bounds调整视图大小 根据需求大小构建的就很好 但是第一个 我添加到viewD
  • 立即构建一个工具,以便稍后在同一 CMake 运行中使用它

    我有一个有趣的先有鸡还是先有蛋的问题以及一个潜在的解决方案 请参阅我发布的答案 但该解决方案以一种不寻常的方式使用 CMake 欢迎更好的替代方案或评论 问题 该问题的简单版本可以描述为具有以下特征的单个 CMake 项目 构建目标之一是一