-3

Rather than using the command line and the -D flag to set variables, I've been using set() inside the CMakeLists.txt for my simple "learner" one-directory project. The advantage is not having to remember and/or copy and paste long directory names every time I call cmake -DVARIABLE_NAME=path/to/infinity/and/beyond ..

My question is what is the functional difference between the two methods, and what are some simple use cases where I might not want to use set instead of the command line -D flag?

Related Questions

This answer talks about scope of variables in cmake, and may contain the answer to my question, but it is too convoluted for me to follow: What's the CMake syntax to set and use variables?

This answer provides a use case where the two methods are not the same, but is for some specific variable and library I have no knowledge of so can't relate to: Setting "-D" variables inside CMake

Note: My question does not have a specific context but was inspired by following this pytorch tutorial to setup my first C++ project and use pytorch.

starball
  • 20,030
  • 7
  • 43
  • 238
rocksNwaves
  • 5,331
  • 4
  • 38
  • 77
  • `-D` is useful when you want your CMake to be generic. I use `-D` to pass variables from outside into CMake, such as build numbers, additional compilation flags, `CMAKE_BUILD_TYPE` is a common one, etc.. Things that I need to be able to control easily. – ChrisMM Jun 05 '23 at 18:30
  • @ChrisMM are these things you are mentioning the sort of things that change frequently from build to build even within the same project or something? Forgive me, I'm not only new to CMake but also C++ and compilation in general. – rocksNwaves Jun 05 '23 at 18:31
  • Well, the build version changes every single time. Compilation flags I don't change too often, but rely on `CMAKE_BUILD_TYPE` for sure. If I want to do a `Debug` build vs a `RelWithDebInfo` (note: I don't use an IDE for these, this is all done on an automated build machine). – ChrisMM Jun 05 '23 at 18:33
  • 1
    I personally do change flags a lot, at least when setting up projects initially. I typically run a debug build with gcc, one with clang/libc++, and various sanitizers as needed. While I don't change these builds often once they're set up, it's far easier for me to have this behavior managed via `-DCXX_FLAGS=whatever` since it's trivial to set up new configurations without having to touch any of the source files (including `CMakeLists.txt`). – Stephen Newell Jun 05 '23 at 18:38
  • @StephenNewell Once you have your set up straight, do you typically move those variables to `CMakeLists.txt`? I'm also curious about the idea of "scope" as linked in my question, and how that might change your decision-making. I don't understand what scope means in this context without functions and classes, etc. – rocksNwaves Jun 05 '23 at 18:40
  • Thanks to both of you for your enlightening comments. – rocksNwaves Jun 05 '23 at 18:41
  • 2
    No, I never move the variables to `CMakeLists.txt`. With how many configurations I run (I've hit double digits when working with middleware on embedded projects that target multiple boards), it simply scales better to have scripts to invoke cmake with the proper arguments. – Stephen Newell Jun 05 '23 at 18:42
  • @Tsyvarev Thanks for pointing that out. I have fixed it. – rocksNwaves Jun 05 '23 at 19:03

1 Answers1

2

-D sets variables as cache variables (see also Mastering CMake chapter, and variables docs), which means that even though they aren't set in the configuration files, they will be remembered. You can optionally specify a type. If you set the type to PATH or FILEPATH, the value will be converted to an absolute path.

set sets variables. You can pass CACHE to set the variable as a cache variable. You can also set the type using the TYPE argument. Non-cache variables are scoped to blocks, functions, and directory scopes (docs). You can specify a type, and also a docstring.

Both cache and non-cache variables can be referenced using the same syntax (docs and related nuance: policy CMP0126).

Generally speaking, if the variable is something that you think someone building your project might want to have control over, then you should leave it to the user to use -D, and set a "default value" in your CMakeLists.txt by using set(... CACHE ...). Note that for potentially non-cache variables, you can do similar using if(DEFINED ...) to conditionally only set the variable hasn't been set yet. I've written about this also in my answer to "CMakePresets.json vs CMakeSettings.json vs CMakeLists.txt". If the variable is a boolean, there's a shorthand command for defining a cache variable: option.

The advantage is not having to remember and/or copy and paste long directory names every time I call cmake [...]

Note that most shells have history functionality, so you can recall past commands you've executed. Actually, CMake's Preset feature sounds right up your alley. You can create a user-local CMakeUserPresets.json file (put it in your .gitignore) suited exactly for your personal needs. Note that if you are calling CMake to reconfigure and regenerate an already configured and generated buildsystem for the purposes of changing cache variables, there are several tools which can make that easier for you as well, including the bundled interactive dialogs, cmake-gui and ccmake.

starball
  • 20,030
  • 7
  • 43
  • 238
  • 1
    "then you should not be using set and should leave it to the user to use -D" - Normally, the code calls **set** for a variable, which is expected to be defined by a user. But calls it in form `set(..CACHE)`. That call checks whether the variable is already defined (e.g. by a user), and if yes, then the value of the variable is not updated. – Tsyvarev Jun 06 '23 at 09:38
  • 1
    I agree with Tsyvarev: `set(CACHE)` or `option()` should be used to define a default value which has the benefit of also adding a fix for the OP's problem of having to remember the options: cmake-gui can be used to modify these kinds of options without having to remember the exact variable names... – fabian Jun 06 '23 at 16:21
  • @Tsyvarev I edited. better? – starball Jun 06 '23 at 17:51
  • No, I don't find good an advice to *not* use `set` for options expected via `-D`. In opposite, according to my understanding of CMake, in 99% cases every possible `-D` option **should** be accompanied with `set(CACHE)` or `option()` in `CMakeLists.txt`. Among other things, these commands **document** the parameter: specifies its name, type, description. `cmake-gui`, noted by @fabian, and most of other GUI-oriented CMake tools uses these specifications for provide positive experience in using a project. – Tsyvarev Jun 06 '23 at 21:10
  • @Tsyvarev how about now? AFAIK, cmake-gui and ccache won't/can't show you cache variables defined in CMakeLists.txt for fresh buildsystem generation- only reconfigure+regenerate. Is that correct? – starball Jun 06 '23 at 21:28