Преглед изворни кода

Merge topic 'add_support_for_clr_targets'

312527de47 document COMMON_LANGUAGE_RUNTIME target properties
4b7a82b4ed cmVisualStudio10TargetGenerator: set /clr compiler flag from property
20e31fb4c9 cmExportFileGenerator: add target property for managed targets
411a22706a cmGeneratorTarget: add handling of managed assemblies to HasImportLibrary()
fb433ff283 cmGeneratorTarget: Make import library checks config-aware
4c1f33961f cmGeneratorTarget: add GetManagedType() and CheckManagedType() methods
6c517a9f8d cmGeneratorTarget: add HasLanguage() as wrapper for GetLanguages()

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1916
Brad King пре 7 година
родитељ
комит
498be66fd7

+ 2 - 0
Help/manual/cmake-properties.7.rst

@@ -143,6 +143,7 @@ Properties on Targets
    /prop_tgt/C_EXTENSIONS
    /prop_tgt/C_STANDARD
    /prop_tgt/C_STANDARD_REQUIRED
+   /prop_tgt/COMMON_LANGUAGE_RUNTIME
    /prop_tgt/COMPATIBLE_INTERFACE_BOOL
    /prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MAX
    /prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN
@@ -186,6 +187,7 @@ Properties on Targets
    /prop_tgt/GNUtoMS
    /prop_tgt/HAS_CXX
    /prop_tgt/IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
+   /prop_tgt/IMPORTED_COMMON_LANGUAGE_RUNTIME
    /prop_tgt/IMPORTED_CONFIGURATIONS
    /prop_tgt/IMPORTED_GLOBAL
    /prop_tgt/IMPORTED_IMPLIB_CONFIG

+ 19 - 0
Help/prop_tgt/COMMON_LANGUAGE_RUNTIME.rst

@@ -0,0 +1,19 @@
+COMMON_LANGUAGE_RUNTIME
+-----------------------
+
+By setting this target property, the target is configured to build with
+``C++/CLI`` support.
+
+The Visual Studio generator defines the ``clr`` parameter depending on
+the value of ``COMMON_LANGUAGE_RUNTIME``:
+
+* property not set: native C++ (i.e. default)
+* property set but empty: mixed unmanaged/managed C++
+* property set to any non empty value: managed C++
+
+Supported values: ``""``, ``"pure"``, ``"safe"``
+
+This property is only evaluated :ref:`Visual Studio Generators` for
+VS 2010 and above.
+
+See also :prop_tgt:`IMPORTED_COMMON_LANGUAGE_RUNTIME`

+ 8 - 0
Help/prop_tgt/IMPORTED_COMMON_LANGUAGE_RUNTIME.rst

@@ -0,0 +1,8 @@
+IMPORTED_COMMON_LANGUAGE_RUNTIME
+--------------------------------
+
+Property to define if the target uses ``C++/CLI``.
+
+Ignored for non-imported targets.
+
+See also the :prop_tgt:`COMMON_LANGUAGE_RUNTIME` target property.

+ 8 - 0
Help/release/dev/managed-target-property.rst

@@ -0,0 +1,8 @@
+target property COMMON_LANGUAGE_RUNTIME
+---------------------------------------
+
+* The :prop_tgt:`COMMON_LANGUAGE_RUNTIME` target property was introduced
+  to configure the use of managed C++ for :ref:`Visual Studio Generators`
+  for VS 2010 and above.
+* To support ``C++/CLI`` for imported targets, the
+  :prop_tgt:`IMPORTED_COMMON_LANGUAGE_RUNTIME` was added.

+ 3 - 2
Source/cmExportBuildFileGenerator.cxx

@@ -224,13 +224,14 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
     }
 
     // Add the import library for windows DLLs.
-    if (target->HasImportLibrary() &&
+    if (target->HasImportLibrary(config) &&
         mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
       std::string prop = "IMPORTED_IMPLIB";
       prop += suffix;
       std::string value =
         target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact);
-      target->GetImplibGNUtoMS(value, value, "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
+      target->GetImplibGNUtoMS(config, value, value,
+                               "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
       properties[prop] = value;
     }
   }

+ 14 - 0
Source/cmExportFileGenerator.cxx

@@ -777,6 +777,20 @@ void cmExportFileGenerator::SetImportDetailProperties(
       properties[prop] = m.str();
     }
   }
+
+  // Add information if this target is a managed target
+  if (target->GetManagedType(config) !=
+      cmGeneratorTarget::ManagedType::Native) {
+    std::string prop = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
+    prop += suffix;
+    std::string propval;
+    if (auto* p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
+      propval = p;
+    }
+    // TODO: make sure propval is set to non-empty string for
+    //       CSharp targets (i.e. force ManagedType::Managed).
+    properties[prop] = propval;
+  }
 }
 
 template <typename T>

+ 2 - 1
Source/cmGeneratorExpressionNode.cxx

@@ -1675,7 +1675,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
                     "executables with ENABLE_EXPORTS.");
       return std::string();
     }
-    cmStateEnums::ArtifactType artifact = target->HasImportLibrary()
+    cmStateEnums::ArtifactType artifact =
+      target->HasImportLibrary(context->Config)
       ? cmStateEnums::ImportLibraryArtifact
       : cmStateEnums::RuntimeBinaryArtifact;
     return target->GetFullPath(context->Config, artifact);

+ 78 - 6
Source/cmGeneratorTarget.cxx

@@ -4978,6 +4978,16 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
     }
   }
 
+  // Get information if target is managed assembly.
+  {
+    std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
+    if (auto pc = this->GetProperty(linkProp + suffix)) {
+      info.Managed = this->CheckManagedType(pc);
+    } else if (auto p = this->GetProperty(linkProp)) {
+      info.Managed = this->CheckManagedType(p);
+    }
+  }
+
   // Get the cyclic repetition count.
   if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
     std::string linkProp = "IMPORTED_LINK_INTERFACE_MULTIPLICITY";
@@ -5195,6 +5205,18 @@ void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages,
   }
 }
 
+bool cmGeneratorTarget::HasLanguage(std::string const& language,
+                                    std::string const& config,
+                                    bool exclusive) const
+{
+  std::set<std::string> languages;
+  this->GetLanguages(languages, config);
+  // add linker language (if it is different from compiler languages)
+  languages.insert(this->GetLinkerLanguage(config));
+  return (languages.size() == 1 || !exclusive) &&
+    languages.count(language) > 0;
+}
+
 void cmGeneratorTarget::ComputeLinkImplementationLanguages(
   const std::string& config, cmOptionalLinkImplementation& impl) const
 {
@@ -5381,16 +5403,17 @@ std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const
   return "";
 }
 
-bool cmGeneratorTarget::HasImplibGNUtoMS() const
+bool cmGeneratorTarget::HasImplibGNUtoMS(std::string const& config) const
 {
-  return this->HasImportLibrary() && this->GetPropertyAsBool("GNUtoMS");
+  return this->HasImportLibrary(config) && this->GetPropertyAsBool("GNUtoMS");
 }
 
-bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& gnuName,
+bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config,
+                                         std::string const& gnuName,
                                          std::string& out,
                                          const char* newExt) const
 {
-  if (this->HasImplibGNUtoMS() && gnuName.size() > 6 &&
+  if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 &&
       gnuName.substr(gnuName.size() - 6) == ".dll.a") {
     out = gnuName.substr(0, gnuName.size() - 6);
     out += newExt ? newExt : ".lib";
@@ -5405,11 +5428,14 @@ bool cmGeneratorTarget::IsExecutableWithExports() const
           this->GetPropertyAsBool("ENABLE_EXPORTS"));
 }
 
-bool cmGeneratorTarget::HasImportLibrary() const
+bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
 {
   return (this->IsDLLPlatform() &&
           (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
-           this->IsExecutableWithExports()));
+           this->IsExecutableWithExports()) &&
+          // Assemblies which have only managed code do not have
+          // import libraries.
+          this->GetManagedType(config) != ManagedType::Managed);
 }
 
 std::string cmGeneratorTarget::GetSupportDirectory() const
@@ -5462,3 +5488,49 @@ bool cmGeneratorTarget::IsCFBundleOnApple() const
   return (this->GetType() == cmStateEnums::MODULE_LIBRARY &&
           this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE"));
 }
+
+cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType(
+  std::string const& propval) const
+{
+  // The type of the managed assembly (mixed unmanaged C++ and C++/CLI,
+  // or only C++/CLI) does only depend on whether the property is an empty
+  // string or contains any value at all. In Visual Studio generators
+  // this propval is prepended with /clr[:] which results in:
+  //
+  // 1. propval does not exist: no /clr flag, unmanaged target, has import
+  //                            lib
+  // 2. empty propval:          add /clr as flag, mixed unmanaged/managed
+  //                            target, has import lib
+  // 3. any value (safe,pure):  add /clr:[propval] as flag, target with
+  //                            managed code only, no import lib
+  return propval.empty() ? ManagedType::Mixed : ManagedType::Managed;
+}
+
+cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
+  const std::string& config) const
+{
+  // Only libraries and executables can be managed targets.
+  if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
+      this->GetType() != cmStateEnums::STATIC_LIBRARY &&
+      this->GetType() != cmStateEnums::EXECUTABLE) {
+    return ManagedType::Undefined;
+  }
+
+  // Check imported target.
+  if (this->IsImported()) {
+    if (cmGeneratorTarget::ImportInfo const* info =
+          this->GetImportInfo(config)) {
+      return info->Managed;
+    }
+    return ManagedType::Undefined;
+  }
+
+  // Check for explicitly set clr target property.
+  if (auto* clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
+    return this->CheckManagedType(clr);
+  }
+
+  // TODO: need to check if target is a CSharp target here.
+  //       If yes: return ManagedType::Managed.
+  return ManagedType::Native;
+}

+ 27 - 4
Source/cmGeneratorTarget.h

@@ -364,6 +364,12 @@ public:
   void GetLanguages(std::set<std::string>& languages,
                     std::string const& config) const;
 
+  // Evaluate if the target uses the given language for compilation
+  // and/or linking. If 'exclusive' is true, 'language' is expected
+  // to be the only language used for the target.
+  bool HasLanguage(std::string const& language, std::string const& config,
+                   bool exclusive = true) const;
+
   void GetObjectLibrariesCMP0026(
     std::vector<cmGeneratorTarget*>& objlibs) const;
 
@@ -566,17 +572,17 @@ public:
   std::string GetLinkerLanguage(const std::string& config) const;
 
   /** Does this target have a GNU implib to convert to MS format?  */
-  bool HasImplibGNUtoMS() const;
+  bool HasImplibGNUtoMS(std::string const& config) const;
 
   /** Convert the given GNU import library name (.dll.a) to a name with a new
       extension (.lib or ${CMAKE_IMPORT_LIBRARY_SUFFIX}).  */
-  bool GetImplibGNUtoMS(std::string const& gnuName, std::string& out,
-                        const char* newExt = nullptr) const;
+  bool GetImplibGNUtoMS(std::string const& config, std::string const& gnuName,
+                        std::string& out, const char* newExt = nullptr) const;
 
   bool IsExecutableWithExports() const;
 
   /** Return whether or not the target has a DLL import library.  */
-  bool HasImportLibrary() const;
+  bool HasImportLibrary(std::string const& config) const;
 
   /** Get a build-tree directory in which to place target support files.  */
   std::string GetSupportDirectory() const;
@@ -597,6 +603,19 @@ public:
   /** Return whether this target is a CFBundle (plugin) on Apple.  */
   bool IsCFBundleOnApple() const;
 
+  /** Assembly types. The order of the values of this enum is relevant
+      because of smaller/larger comparison operations! */
+  enum ManagedType
+  {
+    Undefined = 0, // target is no lib or executable
+    Native,        // target compiles to unmanaged binary.
+    Mixed,         // target compiles to mixed (managed and unmanaged) binary.
+    Managed        // target compiles to managed binary.
+  };
+
+  /** Return the type of assembly this target compiles to. */
+  ManagedType GetManagedType(const std::string& config) const;
+
   struct SourceFileFlags GetTargetSourceFileFlags(
     const cmSourceFile* sf) const;
 
@@ -741,10 +760,12 @@ private:
   {
     ImportInfo()
       : NoSOName(false)
+      , Managed(Native)
       , Multiplicity(0)
     {
     }
     bool NoSOName;
+    ManagedType Managed;
     unsigned int Multiplicity;
     std::string Location;
     std::string SOName;
@@ -838,6 +859,8 @@ private:
   bool ComputePDBOutputDir(const std::string& kind, const std::string& config,
                            std::string& out) const;
 
+  ManagedType CheckManagedType(std::string const& propval) const;
+
 public:
   const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure(
     const std::string& config) const;

+ 4 - 4
Source/cmInstallTargetGenerator.cxx

@@ -135,7 +135,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(
       filesFrom.push_back(std::move(from1));
       filesTo.push_back(std::move(to1));
       std::string targetNameImportLib;
-      if (this->Target->GetImplibGNUtoMS(targetNameImport,
+      if (this->Target->GetImplibGNUtoMS(config, targetNameImport,
                                          targetNameImportLib)) {
         filesFrom.push_back(fromDirConfig + targetNameImportLib);
         filesTo.push_back(toDir + targetNameImportLib);
@@ -201,7 +201,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(
       filesFrom.push_back(std::move(from1));
       filesTo.push_back(std::move(to1));
       std::string targetNameImportLib;
-      if (this->Target->GetImplibGNUtoMS(targetNameImport,
+      if (this->Target->GetImplibGNUtoMS(config, targetNameImport,
                                          targetNameImportLib)) {
         filesFrom.push_back(fromDirConfig + targetNameImportLib);
         filesTo.push_back(toDir + targetNameImportLib);
@@ -398,7 +398,7 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
                                targetNamePDB, config);
     if (nameType == NameImplib) {
       // Use the import library name.
-      if (!target->GetImplibGNUtoMS(targetNameImport, fname,
+      if (!target->GetImplibGNUtoMS(config, targetNameImport, fname,
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
         fname = targetNameImport;
       }
@@ -419,7 +419,7 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
                             targetNameImport, targetNamePDB, config);
     if (nameType == NameImplib) {
       // Use the import library name.
-      if (!target->GetImplibGNUtoMS(targetNameImport, fname,
+      if (!target->GetImplibGNUtoMS(config, targetNameImport, fname,
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
         fname = targetNameImport;
       }

+ 2 - 2
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -477,8 +477,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
       this->LocalGenerator->GetCurrentBinaryDirectory(),
       targetFullPathImport));
     std::string implib;
-    if (this->GeneratorTarget->GetImplibGNUtoMS(targetFullPathImport,
-                                                implib)) {
+    if (this->GeneratorTarget->GetImplibGNUtoMS(
+          this->ConfigName, targetFullPathImport, implib)) {
       exeCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
         this->LocalGenerator->GetCurrentBinaryDirectory(), implib));
     }

+ 2 - 2
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -641,8 +641,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
       this->LocalGenerator->GetCurrentBinaryDirectory(),
       targetFullPathImport));
     std::string implib;
-    if (this->GeneratorTarget->GetImplibGNUtoMS(targetFullPathImport,
-                                                implib)) {
+    if (this->GeneratorTarget->GetImplibGNUtoMS(
+          this->ConfigName, targetFullPathImport, implib)) {
       libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
         this->LocalGenerator->GetCurrentBinaryDirectory(), implib));
     }

+ 1 - 1
Source/cmMakefileTargetGenerator.cxx

@@ -1402,7 +1402,7 @@ std::string cmMakefileTargetGenerator::GetLinkRule(
   const std::string& linkRuleVar)
 {
   std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar);
-  if (this->GeneratorTarget->HasImplibGNUtoMS()) {
+  if (this->GeneratorTarget->HasImplibGNUtoMS(this->ConfigName)) {
     std::string ruleVar = "CMAKE_";
     ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
     ruleVar += "_GNUtoMS_RULE";

+ 2 - 2
Source/cmNinjaNormalTargetGenerator.cxx

@@ -482,7 +482,7 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd()
     const char* linkCmd = mf->GetDefinition(linkCmdVar);
     if (linkCmd) {
       std::string linkCmdStr = linkCmd;
-      if (this->GetGeneratorTarget()->HasImplibGNUtoMS()) {
+      if (this->GetGeneratorTarget()->HasImplibGNUtoMS(this->ConfigName)) {
         std::string ruleVar = "CMAKE_";
         ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
         ruleVar += "_GNUtoMS_RULE";
@@ -881,7 +881,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
       targetOutputImplib, cmOutputConverter::SHELL);
     vars["TARGET_IMPLIB"] = impLibPath;
     EnsureParentDirectoryExists(impLibPath);
-    if (genTarget.HasImportLibrary()) {
+    if (genTarget.HasImportLibrary(cfgName)) {
       byproducts.push_back(targetOutputImplib);
     }
   }

+ 16 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -2418,6 +2418,22 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
       clOptions.AddFlag("AssemblerListingLocation", asmLocation);
     }
   }
+
+  // check for managed C++ assembly compiler flag. This overrides any
+  // /clr* compiler flags which may be defined in the flags variable(s).
+  if (this->ProjectType != csproj) {
+    // TODO: add check here, if /clr was defined manually and issue
+    //       warning that this is discouraged.
+    if (auto* clr =
+          this->GeneratorTarget->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
+      std::string clrString = clr;
+      if (!clrString.empty()) {
+        clrString = ":" + clrString;
+      }
+      flags += " /clr" + clrString;
+    }
+  }
+
   clOptions.Parse(flags.c_str());
   clOptions.Parse(defineFlags.c_str());
   std::vector<std::string> targetDefines;