Jelajahi Sumber

Genex: Enable use of COMPILE_LANGUAGE for compile options.

Follow-ups will allow the use of the generator expression
for compile definitions and include directories for non-IDE
generators.
Stephen Kelly 11 tahun lalu
induk
melakukan
5c559f1113

+ 30 - 0
Help/manual/cmake-generator-expressions.7.rst

@@ -93,6 +93,32 @@ Available logical expressions are:
   for the 'head' target, an error is reported.  See the
   :manual:`cmake-compile-features(7)` manual for information on
   compile features.
+``$<COMPILE_LANGUAGE:lang>``
+  ``1`` when the language used for compilation unit matches ``lang``,
+  otherwise ``0``.  This expression used to specify compile options for
+  source files of a particular language in a target. For example, to specify
+  the use of the ``-fno-exceptions`` compile option (compiler id checks
+  elided):
+
+  .. code-block:: cmake
+
+    add_executable(myapp main.cpp foo.c bar.cpp)
+    target_compile_options(myapp
+      PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
+    )
+
+  This generator expression has limited use because it is not possible to
+  use it with the Visual Studio generators.  Portable buildsystems would
+  not use this expression, and would create separate libraries for each
+  source file language instead:
+
+  .. code-block:: cmake
+
+    add_library(myapp_c foo.c)
+    add_library(myapp_cxx foo.c)
+    target_compile_options(myapp_cxx PUBLIC -fno-exceptions)
+    add_executable(myapp main.cpp)
+    target_link_libraries(myapp myapp_c myapp_cxx)
 
 Informational Expressions
 =========================
@@ -174,6 +200,10 @@ Available informational expressions are:
 ``$<INSTALL_PREFIX>``
   Content of the install prefix when the target is exported via
   :command:`install(EXPORT)` and empty otherwise.
+``$<COMPILE_LANGUAGE>``
+  The compile language of source files when evaluating compile options. See
+  the unary version for notes about portability of this generator
+  expression.
 
 Output Expressions
 ==================

+ 34 - 0
Source/cmGeneratorExpressionEvaluator.cxx

@@ -827,6 +827,40 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode
           "components of the file(GENERATE) command.");
       return std::string();
       }
+
+    std::vector<std::string> enabledLanguages;
+    cmGlobalGenerator* gg
+        = context->Makefile->GetLocalGenerator()->GetGlobalGenerator();
+    gg->GetEnabledLanguages(enabledLanguages);
+    if (!parameters.empty() &&
+          std::find(enabledLanguages.begin(), enabledLanguages.end(),
+                  parameters.front()) == enabledLanguages.end())
+      {
+      reportError(context, content->GetOriginalExpression(),
+          "$<COMPILE_LANGUAGE:...> Unknown language.");
+      return std::string();
+      }
+
+    std::string genName = gg->GetName();
+    if (genName.find("Visual Studio") != std::string::npos)
+      {
+      reportError(context, content->GetOriginalExpression(),
+          "$<COMPILE_LANGUAGE:...> may not be used with Visual Studio "
+          "generators.");
+      return std::string();
+      }
+    else
+      {
+      if(genName.find("Makefiles") == std::string::npos &&
+              genName.find("Ninja") == std::string::npos &&
+              genName.find("Watcom WMake") == std::string::npos &&
+              genName.find("Xcode") == std::string::npos)
+        {
+        reportError(context, content->GetOriginalExpression(),
+            "$<COMPILE_LANGUAGE:...> not supported for this generator.");
+        return std::string();
+        }
+      }
     if (parameters.empty())
       {
       return context->Language;

+ 2 - 2
Source/cmLocalGenerator.cxx

@@ -1453,7 +1453,7 @@ void cmLocalGenerator::AddCompileOptions(
       {
       cmSystemTools::ParseWindowsCommandLine(targetFlags, opts);
       }
-    target->GetCompileOptions(opts, config);
+    target->GetCompileOptions(opts, config, lang);
     for(std::vector<std::string>::const_iterator i = opts.begin();
         i != opts.end(); ++i)
       {
@@ -1474,7 +1474,7 @@ void cmLocalGenerator::AddCompileOptions(
       this->AppendFlags(flags, targetFlags);
       }
     std::vector<std::string> opts;
-    target->GetCompileOptions(opts, config);
+    target->GetCompileOptions(opts, config, lang);
     for(std::vector<std::string>::const_iterator i = opts.begin();
         i != opts.end(); ++i)
       {

+ 17 - 9
Source/cmTarget.cxx

@@ -2192,7 +2192,8 @@ static void processCompileOptionsInternal(cmTarget const* tgt,
       std::vector<std::string> &options,
       UNORDERED_SET<std::string> &uniqueOptions,
       cmGeneratorExpressionDAGChecker *dagChecker,
-      const std::string& config, bool debugOptions, const char *logName)
+      const std::string& config, bool debugOptions, const char *logName,
+      std::string const& language)
 {
   cmMakefile *mf = tgt->GetMakefile();
 
@@ -2204,7 +2205,8 @@ static void processCompileOptionsInternal(cmTarget const* tgt,
                                               config,
                                               false,
                                               tgt,
-                                              dagChecker),
+                                              dagChecker,
+                                              language),
                                     entryOptions);
     std::string usedOptions;
     for(std::vector<std::string>::iterator
@@ -2238,10 +2240,12 @@ static void processCompileOptions(cmTarget const* tgt,
       std::vector<std::string> &options,
       UNORDERED_SET<std::string> &uniqueOptions,
       cmGeneratorExpressionDAGChecker *dagChecker,
-      const std::string& config, bool debugOptions)
+      const std::string& config, bool debugOptions,
+      std::string const& language)
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
-                                dagChecker, config, debugOptions, "options");
+                                dagChecker, config, debugOptions, "options",
+                                language);
 }
 
 //----------------------------------------------------------------------------
@@ -2271,7 +2275,8 @@ void cmTarget::GetAutoUicOptions(std::vector<std::string> &result,
 
 //----------------------------------------------------------------------------
 void cmTarget::GetCompileOptions(std::vector<std::string> &result,
-                                 const std::string& config) const
+                                 const std::string& config,
+                                 const std::string& language) const
 {
   UNORDERED_SET<std::string> uniqueOptions;
 
@@ -2303,7 +2308,8 @@ void cmTarget::GetCompileOptions(std::vector<std::string> &result,
                             uniqueOptions,
                             &dagChecker,
                             config,
-                            debugOptions);
+                            debugOptions,
+                            language);
 
   std::vector<cmTargetInternals::TargetPropertyEntry*>
     linkInterfaceCompileOptionsEntries;
@@ -2318,7 +2324,8 @@ void cmTarget::GetCompileOptions(std::vector<std::string> &result,
                             uniqueOptions,
                             &dagChecker,
                             config,
-                            debugOptions);
+                            debugOptions,
+                            language);
 
   deleteAndClear(linkInterfaceCompileOptionsEntries);
 }
@@ -2333,7 +2340,7 @@ static void processCompileDefinitions(cmTarget const* tgt,
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
                                 dagChecker, config, debugOptions,
-                                "definitions");
+                                "definitions", std::string());
 }
 
 //----------------------------------------------------------------------------
@@ -2431,7 +2438,8 @@ static void processCompileFeatures(cmTarget const* tgt,
       const std::string& config, bool debugOptions)
 {
   processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
-                                dagChecker, config, debugOptions, "features");
+                                dagChecker, config, debugOptions, "features",
+                                std::string());
 }
 
 //----------------------------------------------------------------------------

+ 2 - 1
Source/cmTarget.h

@@ -577,7 +577,8 @@ public:
   void AppendBuildInterfaceIncludes();
 
   void GetCompileOptions(std::vector<std::string> &result,
-                         const std::string& config) const;
+                         const std::string& config,
+                         const std::string& language) const;
   void GetAutoUicOptions(std::vector<std::string> &result,
                          const std::string& config) const;
   void GetCompileFeatures(std::vector<std::string> &features,

+ 15 - 0
Tests/CMakeCommands/target_compile_options/CMakeLists.txt

@@ -23,6 +23,21 @@ add_executable(consumer
   "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
 )
 
+if (NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+  target_sources(consumer PRIVATE
+    "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c"
+  )
+  target_compile_options(consumer
+    PRIVATE
+      -DCONSUMER_LANG_$<COMPILE_LANGUAGE>
+      -DLANG_IS_CXX=$<COMPILE_LANGUAGE:CXX>
+      -DLANG_IS_C=$<COMPILE_LANGUAGE:C>
+  )
+  target_compile_definitions(consumer
+    PRIVATE -DTEST_LANG_DEFINES
+  )
+endif()
+
 target_compile_options(consumer
   PRIVATE $<$<CXX_COMPILER_ID:GNU>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>>
 )

+ 23 - 0
Tests/CMakeCommands/target_compile_options/consumer.c

@@ -0,0 +1,23 @@
+
+#ifdef TEST_LANG_DEFINES
+  #ifdef CONSUMER_LANG_CXX
+  #error Unexpected CONSUMER_LANG_CXX
+  #endif
+
+  #ifndef CONSUMER_LANG_C
+  #error Expected CONSUMER_LANG_C
+  #endif
+
+  #if !LANG_IS_C
+  #error Expected LANG_IS_C
+  #endif
+
+  #if LANG_IS_CXX
+  #error Unexpected LANG_IS_CXX
+  #endif
+#endif
+
+void consumer_c()
+{
+
+}

+ 18 - 0
Tests/CMakeCommands/target_compile_options/consumer.cpp

@@ -15,4 +15,22 @@
 
 #endif
 
+#ifdef TEST_LANG_DEFINES
+  #ifndef CONSUMER_LANG_CXX
+  #error Expected CONSUMER_LANG_CXX
+  #endif
+
+  #ifdef CONSUMER_LANG_C
+  #error Unexpected CONSUMER_LANG_C
+  #endif
+
+  #if !LANG_IS_CXX
+  #error Expected LANG_IS_CXX
+  #endif
+
+  #if LANG_IS_C
+  #error Unexpected LANG_IS_C
+  #endif
+#endif
+
 int main() { return 0; }

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -208,3 +208,5 @@ add_RunCMake_test(IfacePaths_SOURCES TEST_DIR IfacePaths)
 if(RPMBUILD_EXECUTABLE)
   add_RunCMake_test(CPackRPM)
 endif()
+
+add_RunCMake_test(COMPILE_LANGUAGE-genex)

+ 3 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.1)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 1 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileOptions-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileOptions-stderr-VS.txt

@@ -0,0 +1,8 @@
+CMake Error at CompileOptions.cmake:5 \(target_compile_options\):
+  Error evaluating generator expression:
+
+    \$<COMPILE_LANGUAGE:CXX>
+
+  \$<COMPILE_LANGUAGE:...> may not be used with Visual Studio generators.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileOptions.cmake

@@ -0,0 +1,5 @@
+
+enable_language(CXX)
+
+add_executable(main main.cpp)
+target_compile_options(main PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-DANYTHING>)

+ 6 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake

@@ -0,0 +1,6 @@
+include(RunCMake)
+
+if (RunCMake_GENERATOR MATCHES "Visual Studio")
+    set(RunCMake-stderr-file CompileOptions-stderr-VS.txt)
+    run_cmake(CompileOptions)
+endif()

+ 5 - 0
Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp

@@ -0,0 +1,5 @@
+
+int main()
+{
+    return 0;
+}

+ 1 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_LANGUAGE-unknown-lang-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_LANGUAGE-unknown-lang-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at COMPILE_LANGUAGE-unknown-lang.cmake:4 \(target_compile_options\):
+  Error evaluating generator expression:
+
+    \$<COMPILE_LANGUAGE:CXX>
+
+  \$<COMPILE_LANGUAGE:...> Unknown language.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_LANGUAGE-unknown-lang.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_compile_options(empty PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wall>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake

@@ -23,6 +23,7 @@ run_cmake(COMPILE_LANGUAGE-target_sources)
 run_cmake(COMPILE_LANGUAGE-add_executable)
 run_cmake(COMPILE_LANGUAGE-add_library)
 run_cmake(COMPILE_LANGUAGE-add_test)
+run_cmake(COMPILE_LANGUAGE-unknown-lang)
 
 if(LINKER_SUPPORTS_PDB)
   run_cmake(NonValidTarget-TARGET_PDB_FILE)