summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2022-07-19 17:04:38 +1000
committerCraig Scott <craig.scott@crascit.com>2022-07-19 21:13:54 +1000
commitd6af93546710bb8b034f0687b68b1891b84846bd (patch)
tree50f94b96e4773f2f4e6fa68060528dff2e1c3e44
parent044c22e12129c035b2c8750c61de3e02afa9e5e4 (diff)
downloadcmake-d6af93546710bb8b034f0687b68b1891b84846bd.tar.gz
Help: Add Whitespace And Quoting section to genex manual
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst87
1 files changed, 87 insertions, 0 deletions
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 357a20cb3f..994977229e 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -43,6 +43,93 @@ The above would expand to ``OLD_COMPILER`` if the
:variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less
than 4.2.0.
+Whitespace And Quoting
+======================
+
+Generator expressions are typically parsed after command arguments.
+If a generator expression contains spaces, new lines, semicolons or
+other characters that may be interpreted as command argument separators,
+the whole expression should be surrounded by quotes when passed to a
+command. Failure to do so may result in the expression being split and
+it may no longer be recognized as a generator expression.
+
+When using :command:`add_custom_command` or :command:`add_custom_target`,
+use the ``VERBATIM`` and ``COMMAND_EXPAND_LISTS`` options to obtain robust
+argument splitting and quoting.
+
+.. code-block:: cmake
+
+ # WRONG: Embedded space will be treated as an argument separator.
+ # This ends up not being seen as a generator expression at all.
+ add_custom_target(run_some_tool
+ COMMAND some_tool -I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>
+ VERBATIM
+ )
+
+.. code-block:: cmake
+
+ # Better, but still not robust. Quotes prevent the space from splitting the
+ # expression. However, the tool will receive the expanded value as a single
+ # argument.
+ add_custom_target(run_some_tool
+ COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>"
+ VERBATIM
+ )
+
+.. code-block:: cmake
+
+ # Nearly correct. Using a semicolon to separate arguments and adding the
+ # COMMAND_EXPAND_LISTS option means that paths with spaces will be handled
+ # correctly. Quoting the whole expression ensures it is seen as a generator
+ # expression. But if the target property is empty, we will get a bare -I
+ # with nothing after it.
+ add_custom_target(run_some_tool
+ COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>,;-I>"
+ COMMAND_EXPAND_LISTS
+ VERBATIM
+ )
+
+Using variables to build up a more complex generator expression is also a
+good way to reduce errors and improve readability. The above example can be
+improved further like so:
+
+.. code-block:: cmake
+
+ # The $<BOOL:...> check prevents adding anything if the property is empty,
+ # assuming the property value cannot be one of CMake's false constants.
+ set(prop "$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>")
+ add_custom_target(run_some_tool
+ COMMAND some_tool "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>"
+ COMMAND_EXPAND_LISTS
+ VERBATIM
+ )
+
+A common mistake is to try to split a generator expression across multiple
+lines with indenting:
+
+.. code-block:: cmake
+
+ # WRONG: New lines and spaces all treated as argument separators, so the
+ # generator expression is split and not recognized correctly.
+ target_compile_definitions(tgt PRIVATE
+ $<$<AND:
+ $<CXX_COMPILER_ID:GNU>,
+ $<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>
+ >:HAVE_5_OR_LATER>
+ )
+
+Again, use helper variables with well-chosen names to build up a readable
+expression instead:
+
+.. code-block:: cmake
+
+ set(is_gnu "$<CXX_COMPILER_ID:GNU>")
+ set(v5_or_later "$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>")
+ set(meet_requirements "$<AND:${is_gnu},${v5_or_later}>")
+ target_compile_definitions(tgt PRIVATE
+ "$<${meet_requirements}:HAVE_5_OR_LATER>"
+ )
+
Debugging
=========