ソースを参照

target_compile_options: Add syntax to specify shell strings

Options specified via `COMPILE_OPTIONS` and `INTERFACE_COMPILE_OPTIONS`
are deduplicated, but individual options can legitimately be duplicated
when grouped with other options, e.g.

    -D A -D B

After deduplication that becomes `-D A B`.  Therefore we need a way to
treat groups of options as units during deduplication.  A simple approach
is to specify each group as one option, e.g.

    "-D A" "-D B"

However, that conflicts with options that legitimately have spaces.  To
break this ambiguity, add a `SHELL:` prefix syntax to specify that an
option should be parsed like shell command line arguments after
deduplication, e.g.

    "SHELL:-D A" "SHELL:-D B"

These will survive deduplication intact, and then be parsed to produce
`-D A -D B` on the final command line.

Fixes: #15826
Brad King 7 年 前
コミット
ce0b983216

+ 9 - 0
Help/command/COMPILE_OPTIONS_SHELL.txt

@@ -0,0 +1,9 @@
+The final set of compile options used for a target is constructed by
+accumulating options from the current target and the usage requirements of
+it dependencies.  The set of options is de-duplicated to avoid repetition.
+While beneficial for individual options, the de-duplication step can break
+up option groups.  For example, ``-D A -D B`` becomes ``-D A B``.  One may
+specify a group of options using shell-like quoting along with a ``SHELL:``
+prefix.  The ``SHELL:`` prefix is dropped and the rest of the option string
+is parsed using the :command:`separate_arguments` ``UNIX_COMMAND`` mode.
+For example, ``"SHELL:-D A" "SHELL:-D B"`` becomes ``-D A -D B``.

+ 2 - 0
Help/command/add_compile_options.rst

@@ -21,3 +21,5 @@ Arguments to ``add_compile_options`` may use "generator expressions" with
 the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
 manual for more on defining buildsystem properties.
+
+.. include:: COMPILE_OPTIONS_SHELL.txt

+ 2 - 0
Help/command/target_compile_options.rst

@@ -38,3 +38,5 @@ Arguments to ``target_compile_options`` may use "generator expressions"
 with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
 manual for more on defining buildsystem properties.
+
+.. include:: COMPILE_OPTIONS_SHELL.txt

+ 6 - 0
Help/release/dev/compile-options-shell.rst

@@ -0,0 +1,6 @@
+compile-options-shell
+---------------------
+
+* :command:`target_compile_options` and :command:`add_compile_options`
+  commands gained a ``SHELL:`` prefix to specify a group of related
+  options using shell-like quoting.

+ 17 - 5
Source/cmGeneratorTarget.cxx

@@ -2622,13 +2622,20 @@ std::vector<std::string> cmGeneratorTarget::GetIncludeDirectories(
   return includes;
 }
 
+enum class OptionsParse
+{
+  None,
+  Shell
+};
+
 static void processCompileOptionsInternal(
   cmGeneratorTarget const* tgt,
   const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
   std::vector<std::string>& options,
   std::unordered_set<std::string>& uniqueOptions,
   cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions, const char* logName, std::string const& language)
+  bool debugOptions, const char* logName, std::string const& language,
+  OptionsParse parse)
 {
   for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
     std::vector<std::string> entryOptions;
@@ -2639,7 +2646,12 @@ static void processCompileOptionsInternal(
     std::string usedOptions;
     for (std::string const& opt : entryOptions) {
       if (uniqueOptions.insert(opt).second) {
-        options.push_back(opt);
+        if (parse == OptionsParse::Shell &&
+            cmHasLiteralPrefix(opt, "SHELL:")) {
+          cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, options);
+        } else {
+          options.push_back(opt);
+        }
         if (debugOptions) {
           usedOptions += " * " + opt + "\n";
         }
@@ -2664,7 +2676,7 @@ static void processCompileOptions(
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
                                 dagChecker, config, debugOptions, "options",
-                                language);
+                                language, OptionsParse::Shell);
 }
 
 void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
@@ -2718,7 +2730,7 @@ static void processCompileFeatures(
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
                                 dagChecker, config, debugOptions, "features",
-                                std::string());
+                                std::string(), OptionsParse::None);
 }
 
 void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
@@ -2768,7 +2780,7 @@ static void processCompileDefinitions(
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
                                 dagChecker, config, debugOptions,
-                                "definitions", language);
+                                "definitions", language, OptionsParse::None);
 }
 
 void cmGeneratorTarget::GetCompileDefinitions(

+ 12 - 0
Tests/CompileOptions/CMakeLists.txt

@@ -18,9 +18,21 @@ set_property(TARGET CompileOptions PROPERTY COMPILE_OPTIONS
   "-DTEST_DEFINE"
   "-DNEEDS_ESCAPE=\"E$CAPE\""
   "$<$<CXX_COMPILER_ID:GNU>:-DTEST_DEFINE_GNU>"
+  "SHELL:" # produces no options
   ${c_tests}
   ${cxx_tests}
   )
+if(BORLAND OR WATCOM)
+  # these compilers do not support separate -D flags
+  target_compile_definitions(CompileOptions PRIVATE NO_DEF_TESTS)
+else()
+  set_property(TARGET CompileOptions APPEND PROPERTY COMPILE_OPTIONS
+    "SHELL:-D DEF_A"
+    "$<1:SHELL:-D DEF_B>"
+    "SHELL:-D 'DEF_C' -D \"DEF_D\""
+    [[SHELL:-D "DEF_STR=\"string with spaces\""]]
+    )
+endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|Borland|Embarcadero")
   set_property(TARGET CompileOptions APPEND PROPERTY COMPILE_OPTIONS

+ 25 - 0
Tests/CompileOptions/main.cpp

@@ -12,6 +12,28 @@
 #endif
 #endif
 
+#ifndef NO_DEF_TESTS
+#ifndef DEF_A
+#error Expected definition DEF_A
+#endif
+
+#ifndef DEF_B
+#error Expected definition DEF_B
+#endif
+
+#ifndef DEF_C
+#error Expected definition DEF_C
+#endif
+
+#ifndef DEF_D
+#error Expected definition DEF_D
+#endif
+
+#ifndef DEF_STR
+#error Expected definition DEF_STR
+#endif
+#endif
+
 #include <string.h>
 
 int main()
@@ -19,6 +41,9 @@ int main()
   return (strcmp(NEEDS_ESCAPE, "E$CAPE") == 0
 #ifdef TEST_OCTOTHORPE
           && strcmp(TEST_OCTOTHORPE, "#") == 0
+#endif
+#ifndef NO_DEF_TESTS
+          && strcmp(DEF_STR, "string with spaces") == 0
 #endif
           &&
           strcmp(EXPECTED_C_COMPILER_VERSION, TEST_C_COMPILER_VERSION) == 0 &&