在CMake项目中编译单个文件?

27

我正在开发一个C++项目,它将被包含在一个更大的项目中。

我发现,在这个更大的项目中(是一个Qt应用程序,并且是使用qmake生成),我能够从Linux命令行编译单个文件,只需将特定文件的相对路径作为参数输入make命令。

另一方面,我在我的项目中使用CMake。 当我修改编译单元的某些代码并且必须修改其头文件时,我不得不等待很长时间来编译其依赖项,然后才能编译其自己的源文件。 但是有一些情况下,我更愿意检查*.cc文件中的源代码是否可编译而不出错。

是否有一种方法可以像qmake那样从CMake生成Makefile? 转换到qmake不再是一个选择。


1
在当前的CMake生成的makefile上尝试运行make help。我相信这将列出如何仅构建单个对象文件的方法。 - John
6个回答

32

您无需在CMake脚本中添加额外的自定义目标,因为CMake生成的Makefile已经包含了每个.cc文件的.o目标。例如,如果您有一个名为mySourceFile.cc的源文件,在您的构建目录中将会有一个Makefile,该Makefile定义了一个名为<Some Path>/mySourceFile.cc.o的目标。如果您进入构建目录,可以使用grepack-grep查找定义此目标的Makefile,然后进入该Makefile所在的目录并进行编译。

例如,假设命令ack-grep mySourceFile.cc.o打印出类似以下内容:

foo/bar/Makefile
119:x/y/z/mySourceFile.o: x/y/z/mySourceFile.cc.o
123:x/y/z/mySourceFile.cc.o:
124:    # recipe for building target

然后,您可以通过执行以下操作构建mySourceFile.cc.o

cd foo/bar && make x/y/z/mySourceFile.cc.o

6
你可以使用 --target 指定 CMake 的构建目标,比如: cmake --build . --target src/foo.cpp.o - il--ya
对我来说,ack-grep mySourceFile.cc.o 没有任何作用,但是 grep -r mySourceFile.cc.o . 给了我大量的结果! - Nike
当我按照这个过程(大约在我最后一条评论的时候)进行时,我得到了make: *** No rule to make target 'CMakeFiles/level/CMakeFiles/level_obj.dir/level.o'. Stop.现在我发现这个消息只会出现在第一次编译不成功的文件上。'make CMakeFiles/level/CMakeFiles/level_obj.dir/level_rdinp.o'可以正常工作。似乎一个文件至少需要被成功编译一次,才能发生这种情况? - Nike
@Nike 当你生成Makefiles时,CMake是否打印了任何错误消息?如果它们都正确生成,那么任何对象文件目标缺少make规则的原因并不明显。尽管如此,您没有提供足够的信息来清楚地说明问题所在,而SO评论并不适合故障排除。 - Ose
CMake没有出现错误。Make出现了一个错误。然后,在第一次make运行期间成功编译的最后一个.o文件上工作,但是在第一次失败的.o文件上不起作用。我同意目标缺乏制作规则直到至少使用make编译一次之前并不明显。我同意SO评论不适合故障排除。对于构建过程中make崩溃一半的情况,您是否尝试过在未编译的第一个文件上执行该过程? - Nike
我不认为我遇到过这个问题,但请记住,我是在6年前写的答案,而且在过去的3年中几乎没有使用C++。如果您的CMake文件中有条件逻辑可以防止添加目标,或者您正在使用代码生成器,我可以想象会出现这个问题。您尝试过上面il--ya的建议了吗? - Ose

22

CMake没有通用的内置方法来做到这一点(这是一个待解决问题),但如果你正在使用Ninja生成器,你可以使用特殊的Ninja语法来构建给定源文件的直接输出。例如,要编译只有foo.o,你可以使用:

ninja /path/to/foo.cpp^

1
谢谢指出来! 我也注意到它适用于源代码外构建,只要您指定的路径是相对于构建位置到原始源代码文件位置。 - mystery_doctor
这似乎是最优雅的解决方案,但是这个命令是否有特定的目录来运行?我是从“build”目录中执行的,然后得到了以下行:log: reading configuration file: ../OpenMolcas/src/level/alfAS.f^ 紧接着是 die: error: unable to read configuration file - Nike
@Nike 你检查过完整的构建是否正常工作了吗(只需从构建目录中运行 ninja 命令)? - nocnokneo
@nocnokneo 我在 HPC 系统上进行了一些测试,但管理员拒绝升级 Ninja 到 1.9.0 之后的版本(即 2019 年版本)。由于 GFortran 只支持更新的版本,而管理员又拒绝升级 Ninja,我不得不放弃它并寻找其他创新性的解决方案来解决我的瓶颈问题。我为此奋斗了大约 3.5 天,认为我可能已经解决了这个问题,但需要等待有权限在 GitLab 上运行管道的人来查看我的提交是否实际被批准。周末并没有帮助。祈祷一切顺利。 - Nike
只是一个小的澄清以帮助你:相对路径是行不通的(例如 ninja ../to/foo.cpp^)。你必须使用完整的路径,就像上面的例子一样。 - undefined

7

并非开箱即用。CMake在主要的makefile中不公开那些“内部”makefile规则。

只有当你考虑CMake在内部使用的文件结构时,才能做到这一点。例如,你可以使用由CMake生成的makefile来编译单个.obj文件。

make -f CMakeFiles/myProg.dir/build.make CMakeFiles/myProg.dir/main.cc.obj

当你遇到像这样的情况时:

当你有类似以下的内容:

cmake_minimum_required(VERSION 3.1)

project(myProg CXX)

file(WRITE "main.cc" "int main()\n{\nreturn 0;\n}") 
add_executable(myProg main.cc)

5

单独构建 src/foo.cpp:

cmake --build . --target src/foo.cpp.o

小修正,它将从使用了此cpp的所有目标构建src/foo.cpp - Andry
您也可以像使用make构建此项目时那样,向cmake传递常规选项,如VERBOSE=1 - Ruslan

1
不,CMake没有内置支持编译单个文件的功能。
您需要为每个目标文件添加一个目标,可以通过一个函数迭代目录中的所有文件来实现。

Ose提供的答案是正确的。但这并不是很有帮助。虽然CMake没有为每个TU提供目标,但它确实为具体的构建系统提供了信息。你需要在那里查找。 - Jan Christoph Uhde
这可能适用于Makefiles。nocnokneo为Ninja提供了解决方案。但一般来说,没有办法。nocnokneo甚至链接了相应的开放错误。 - usr1234567
@usr1234567 你说得对,没有通用的解决方案(例如,如果你使用CMake在Visual Studio中编译单个文件,我会非常惊讶)。但是提问者并没有要求通用的解决方案。他们要求的是适用于make的解决方案。 - Ose

1

有人提出了从 .cpp 文件名中找到目标名称(以 .cpp.o 结尾)的方法,但如果您已经知道一个将触发编译 .cpp 文件的目标名称并且正在使用 ninja,则此建议应更容易。

首先构建目标:

ninja TriggersCppCompilationLib

假设您的文件已更改或尚未构建,ninja将打印完整的目标名称。当您看到名称出现时,请按Enter键以防止覆盖。然后只需从终端复制名称(例如使用tmux复制模式)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接