英文:
How can I support build-time variables for Makefiles in CMake?
问题
我已经完成了一些多平台(例如嵌入式与主机运行时环境)的CMake项目,但有一个看似简单的用例我无法完全理解。我所知道的典型CMake用法如下:
mkdir <build-dir>
cd <build-dir>
cmake -DCMAKE_TOOLCHAIN_FILE=../srcs/cmake/toolchain.cmake ../srcs
make
在这里,我正在设置一个构建目录,进入该目录,运行CMake以设置"makefile构建系统"(即Makefile和辅助文件集),然后构建项目。这一切都很好。
为了澄清我的问题,考虑一个简单的程序:
#include <stdio.h>
int main(int, char**)
{
#if defined(CFG_TEST)
printf("Test configuration.\n");
// 或者 #pragma message "Test configuration."
#else
printf("Normal configuration.\n");
// 或者 #pragma message "Normal configuration."
#endif
return 0;
}
我的想法是,我想能够轻松地在以下两种情况之间切换:
gcc demo.c -o demo
和
gcc -DCFG_TEST demo.c -o demo
换句话说,CMake生成了Makefile,当我运行它们(通过make
)时,如何在不必重新运行CMake的情况下将一个简单的#define
传递给C/C++编译器?这有意义吗?
如果这不可行或与CMake的哲学不符,通过命令行执行等效的CMake的最简单方法是什么?换句话说,如何处理类似cmake -DCFG_TEST ...
的情况,以便将其传递给编译器?
英文:
While I have done some multi-platform (e.g. embedded vs. host run-time environments) CMake "projects", I have this seemingly simple use case that I cannot quite figure out. The typical CMake usage I know of is like this:
mkdir <build-dir>
cd <build-dir>
cmake -DCMAKE_TOOLCHAIN_FILE=../srcs/cmake/toolchain.cmake ../srcs
make
Here I am setting up a build directory, moving to it, running cmake to set up a "makefile build system" (i.e. set of Makefile and helper files), then building things. This is all fine and dandy.
For clarity in my question, consider a trivial program:
<!-- language: lang-c -->
#include <stdio.h>
int main(int, char**)
{
#if defined(CFG_TEST)
printf("Test configuration.\n");
// or #pragma message "Test configuration."
#else
printf("Normal configuration.\n");
// or #pragma message "Normal configuration."
#endif
return 0;
}
The thinking here is I'd like to just be able to switch between something like:
gcc demo.c -o demo
and
gcc -DCFG_TEST demo.c -o demo
In other words, CMake generates the Makefiles, and when I run them (via make
), how can I pass a simple #define
into the C/C++ compiler without having to rerun CMake? Does that even make sense?
If this isn't feasible or conducive with the CMake philosophy, what is the simplest way to do the equivalent CMake via command line? In other words, how do I do handle something like cmake -DCFG_TEST ...
and have that result in passing -DCFG_TEST
to the compiler?
答案1
得分: 1
以下是翻译好的部分:
-
关于如何通过CMake命令行传递宏定义:
考虑一下不同的方式。CMake是一个构建系统生成器。一旦配置了CMake,生成任意数量的构建系统就很容易了。你可以生成两个构建系统:一个用于“正常”模式,另一个用于“测试”模式:
cmake -S srcs -B build_normal cmake -S srcs -B build_test -DCMAKE_CXX_FLAGS="-DCFG_TEST"
要将宏定义传递给编译器,可以使用
add_compile_definitions
或target_compile_definitions
。例如:target_compile_definitions(my_target PUBLIC "CFG_TEST=${CFG_TEST}")
这是在CMake中处理这个问题的相当“惯用”方式。这种方法的权衡成本是两个构建系统及其生成的文件的磁盘空间成本,但好处是能够并排使用它们:你可以使用一个,然后快速切换到另一个,无需切换标志并重新配置 + 重新构建。
-
关于如何在不重新运行CMake的情况下将宏定义传递给C/C++编译器是否合理:
我不确定你所询问的是否可能 - 至少在使用
Unix Makefiles
生成器且不构建修改后的CMake可执行文件的情况下。在编写Makefile时,可以指定变量并像$(foo)
一样使用它们。据我所知,CMake生成的Makefile没有用户可以自由使用的“悬浮”变量,用于将其插入到编译命令中。例如,我在我的机器上为一个非常小的CMake项目生成了一个Unix Makefiles构建系统,其目标的生成
CMakeFiles/example.dir/build.make
文件中有如下一行:/usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /.../test.cpp -o CMakeFiles/example.dir/test.cpp.s
并且这些标志被生成到目标的
CMakeFiles/example.dir/flags.make
文件中,如下所示:CXX_DEFINES = ... CXX_INCLUDES = ... CXX_FLAGS = ...
这些值来自像
CMAKE_CXX_FLAGS
、target_compile_options
、target_compile_definitions
等变量。因此,在调用生成的构建系统时,你可以在 https://stackoverflow.com/q/9052792/11107541 中找到的方法,技术上可以执行类似这样的操作:
make CXX_FLAGS=-DCFG_TEST
或者(参见cmake --build 命令文档中关于“
--
”的含义):cmake --build <build_dir> -- CXX_FLAGS=-DCFG_TEST
但这将覆盖你告诉CMake为Makefile变量
CXX_FLAGS
生成的值,因此如果你真的想这样做,你需要复制CXX_FLAGS
的完整生成值,然后在命令行上覆盖它以及你想要添加的任何内容,以避免遗漏任何内容。如果你想要插入自己的Makefile变量“槽”,而不是覆盖生成的变量(例如
CXX_DEFINES
、CXX_INCLUDES
和CXX_FLAGS
),我相当确定你需要构建和安装一个修改后的CMake版本。需要修改的文件将是 Source/cmMakefileTargetGenerator.cxx 中的cmMakefileTargetGenerator::WriteObjectRuleFiles
。如果你决定在重新生成之前修改已配置的构建系统的变量缓存(根据项目的具体情况,这实际上可能并不会花费太多时间),你可以使用像
ccmake
或cmake-gui
这样的CMake缓存编辑工具,然后运行配置命令,然后调用构建系统。或者查看 https://stackoverflow.com/q/49302576/11107541(TL;DR,你还可以使用-D
参数)。你可以编辑CMAKE_CXX_FLAGS
变量或类似的变量,或者定义一个自己的变量,然后将其用作编译定义,如target_compile_definitions(<target_name> <visibility> "<name_of_your_cache_variable>=${<name_of_your_cache_variable>}")
。
英文:
I'll start with your last question:
> If this isn't feasible or conducive with the CMake philosophy, what is the simplest way to do the equivalent at the cmake command line? In other words, how do I do handle something like cmake -DCFG_TEST ...
and have that result in passing -DCFG_TEST
to the compiler?
Think a little bit outside the box. CMake is a buildsystem generator. Once you configure CMake, it's quite easy to generate as many buildsystems as you want. You could generate two buildsystems: one for your "normal" mode and one for your "test" mode:
cmake -S srcs -B build_normal
cmake -S srcs -B build_test -DCMAKE_CXX_FLAGS="-DCFG_TEST"
To pass that as a macro to the compiler, either use add_compile_definitions
or target_compile_definitions
. Ex.
target_compile_definitions(my_target PUBLIC "CFG_TEST=${CFG_TEST}")
I'd say this is a pretty "idiomatic" way to do things in CMake. The tradeoff cost is the disk space cost of two buildsystems and their artifacts, but the benefit is being able to have them side-by-side: You can use one, and then quickly use the other without having to switch a flag and reconfigure + rebuild.
That being said, to answer your initial question,
> how can I pass a simple #define into the C/C++ compiler without having to rerun CMake? Does that even make sense?
I'm not sure if what you're asking for is possible- at least with the Unix Makefiles
generator and without building a modified CMake executable.
When writing Makefiles, you can specify variables and use them like $(foo)
. As far as I know, CMake's generated Makefiles don't have any "free-floating" variables for the user's use that it takes and plugs into the compile commands.
For example, a really small CMake project I generated a Unix Makefiles buildsystem for on my machine has a line in its target's generated CMakeFiles/example.dir/build.make
file like this:
/usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /.../test.cpp -o CMakeFiles/example.dir/test.cpp.s
and the flags are generated into the target's CMakeFiles/example.dir/flags.make
like this:
CXX_DEFINES = ...
CXX_INCLUDES = ...
CXX_FLAGS = ...
And the values of those things come from things like CMAKE_CXX_FLAGS
, target_compile_options
, target_compile_definitions
, etc.
So when invoking your generated buildsystem, following https://stackoverflow.com/q/9052792/11107541, you could technically do something like this:
make CXX_FLAGS=-DCFG_TEST
or (see the cmake --build
command docs for what "--
" means):
cmake --build <build_dir> -- CXX_FLAGS=-DCFG_TEST
But that would override the value of whatever you told CMake to generate for the CXX_FLAGS
Makefile variable, so if you really wanted to do that, you'd need to copy the full generated value of CXX_FLAGS
and then override it on the commandline with a paste of it plus whatever you want to add to avoid missing anything.
If you wanted to insert your own Makefile variable "slots" instead of overwriting the generated ones like CXX_DEFINES
, CXX_INCLUDES
, and CXX_FLAGS
, I'm pretty sure you'd need to build and install a modified version of CMake. The relevant file to modify would be cmMakefileTargetGenerator::WriteObjectRuleFiles
in the Source/cmMakefileTargetGenerator.cxx.
In the event that you decide you are okay with modifying the variable cache of a configured buildsystem and regenerating it before rebuilds (which honestly might not be a big deal at all (in terms of time taken) depending on the specifics of your project), you can use a CMake cache-editing tool like ccmake
or cmake-gui
, then run the configure command and then invoke the buildsystem. Or see https://stackoverflow.com/q/49302576/11107541 (TL;DR you can also use the -D
argument). You'd either be editing the CMAKE_CXX_FLAGS
variable or similar variable, or one that you define youself and then use as a compile definition like target_compile_definitions(<target_name> <visibility> "<name_of_your_cache_variable>=${<name_of_your_cache_variable>}")
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论