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:
#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?
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 How to pass macro definition from "make" command line arguments (-D) to C source code?, 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 Is there a safe way to edit a cache variable from the command line? (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>}")
.