瀏覽代碼

VS: Treat libraries ending in `.targets` as msbuild imports

Generate `<Import Project="..." .../>` to import the `.targets` files
into `.vcxproj` files.

Closes: #16340
Soji Yamakawa 9 年之前
父節點
當前提交
883bd34a1f

+ 5 - 0
Help/command/target_link_libraries.rst

@@ -53,6 +53,11 @@ Each ``<item>`` may be:
   :ref:`usage requirement <Target Usage Requirements>`.  This has the same
   effect as passing the framework directory as an include directory.
 
+  On :ref:`Visual Studio Generators` for VS 2010 and above, library files
+  ending in ``.targets`` will be treated as MSBuild targets files and
+  imported into generated project files.  This is not supported by other
+  generators.
+
 * **A plain library name**: The generated link line will ask the linker
   to search for the library (e.g. ``foo`` becomes ``-lfoo`` or ``foo.lib``).
 

+ 6 - 0
Help/release/dev/vs_targets_file_as_library.rst

@@ -0,0 +1,6 @@
+vs_targets_file_as_library
+--------------------------
+
+* :ref:`Visual Studio Generators` learned to treat files passed to
+  :command:`target_link_libraries` whose names end in ``.targets``
+  as MSBuild targets files to be imported into generated project files.

+ 1 - 0
Modules/Platform/Windows.cmake

@@ -17,6 +17,7 @@ set(CMAKE_IMPORT_LIBRARY_SUFFIX ".lib")
 set(CMAKE_EXECUTABLE_SUFFIX ".exe")          # .exe
 set(CMAKE_LINK_LIBRARY_SUFFIX ".lib")
 set(CMAKE_DL_LIBS "")
+set(CMAKE_EXTRA_LINK_EXTENSIONS ".targets")
 
 set(CMAKE_FIND_LIBRARY_PREFIXES "")
 set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")

+ 114 - 4
Source/cmVisualStudio10TargetGenerator.cxx

@@ -170,6 +170,12 @@ static std::string cmVS10EscapeComment(std::string comment)
   return echoable;
 }
 
+static bool cmVS10IsTargetsFile(std::string const& path)
+{
+  std::string const ext = cmSystemTools::GetFilenameLastExtension(path);
+  return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0;
+}
+
 cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
   cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg)
 {
@@ -277,6 +283,9 @@ void cmVisualStudio10TargetGenerator::Generate()
     if (!this->ComputeLinkOptions()) {
       return;
     }
+    if (!this->ComputeLibOptions()) {
+      return;
+    }
   }
   std::string path = this->LocalGenerator->GetCurrentBinaryDirectory();
   path += "/";
@@ -481,6 +490,7 @@ void cmVisualStudio10TargetGenerator::Generate()
     1);
   this->WriteTargetSpecificReferences();
   this->WriteString("<ImportGroup Label=\"ExtensionTargets\">\n", 1);
+  this->WriteTargetsFileReferences();
   if (this->GlobalGenerator->IsMasmEnabled()) {
     this->WriteString("<Import Project=\"$(VCTargetsPath)\\"
                       "BuildCustomizations\\masm.targets\" />\n",
@@ -596,6 +606,31 @@ void cmVisualStudio10TargetGenerator::WriteTargetSpecificReferences()
   }
 }
 
+void cmVisualStudio10TargetGenerator::WriteTargetsFileReferences()
+{
+  for (std::vector<TargetsFileAndConfigs>::iterator i =
+         this->TargetsFileAndConfigsVec.begin();
+       i != this->TargetsFileAndConfigsVec.end(); ++i) {
+    TargetsFileAndConfigs const& tac = *i;
+    this->WriteString("<Import Project=\"", 3);
+    (*this->BuildFileStream) << tac.File << "\" ";
+    (*this->BuildFileStream) << "Condition=\"";
+    (*this->BuildFileStream) << "Exists('" << tac.File << "')";
+    if (!tac.Configs.empty()) {
+      (*this->BuildFileStream) << " And (";
+      for (size_t j = 0; j < tac.Configs.size(); ++j) {
+        if (j > 0) {
+          (*this->BuildFileStream) << " Or ";
+        }
+        (*this->BuildFileStream) << "'$(Configuration)'=='" << tac.Configs[j]
+                                 << "'";
+      }
+      (*this->BuildFileStream) << ")";
+    }
+    (*this->BuildFileStream) << "\" />\n";
+  }
+}
+
 void cmVisualStudio10TargetGenerator::WriteWinRTReferences()
 {
   std::vector<std::string> references;
@@ -2239,9 +2274,16 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions(
   }
   // add the libraries for the target to libs string
   cmComputeLinkInformation& cli = *pcli;
-  this->AddLibraries(cli, libVec);
+  std::vector<std::string> vsTargetVec;
+  this->AddLibraries(cli, libVec, vsTargetVec);
   linkOptions.AddFlag("AdditionalDependencies", libVec);
 
+  // Populate TargetsFileAndConfigsVec
+  for (std::vector<std::string>::iterator ti = vsTargetVec.begin();
+       ti != vsTargetVec.end(); ++ti) {
+    this->AddTargetsFileAndConfigPair(*ti, config);
+  }
+
   std::vector<std::string> const& ldirs = cli.GetDirectories();
   std::vector<std::string> linkDirs;
   for (std::vector<std::string>::const_iterator d = ldirs.begin();
@@ -2396,6 +2438,49 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions(
   return true;
 }
 
+bool cmVisualStudio10TargetGenerator::ComputeLibOptions()
+{
+  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
+    for (std::vector<std::string>::const_iterator i =
+           this->Configurations.begin();
+         i != this->Configurations.end(); ++i) {
+      if (!this->ComputeLibOptions(*i)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool cmVisualStudio10TargetGenerator::ComputeLibOptions(
+  std::string const& config)
+{
+  cmComputeLinkInformation* pcli =
+    this->GeneratorTarget->GetLinkInformation(config.c_str());
+  if (!pcli) {
+    cmSystemTools::Error(
+      "CMake can not compute cmComputeLinkInformation for target: ",
+      this->Name.c_str());
+    return false;
+  }
+
+  cmComputeLinkInformation& cli = *pcli;
+  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  const ItemVector& libs = cli.GetItems();
+  std::string currentBinDir =
+    this->LocalGenerator->GetCurrentBinaryDirectory();
+  for (ItemVector::const_iterator l = libs.begin(); l != libs.end(); ++l) {
+    if (l->IsPath && cmVS10IsTargetsFile(l->Value)) {
+      std::string path = this->LocalGenerator->ConvertToRelativePath(
+        currentBinDir, l->Value.c_str());
+      this->ConvertToWindowsSlash(path);
+      this->AddTargetsFileAndConfigPair(path, config);
+    }
+  }
+
+  return true;
+}
+
 void cmVisualStudio10TargetGenerator::WriteLinkOptions(
   std::string const& config)
 {
@@ -2420,10 +2505,11 @@ void cmVisualStudio10TargetGenerator::WriteLinkOptions(
 }
 
 void cmVisualStudio10TargetGenerator::AddLibraries(
-  cmComputeLinkInformation& cli, std::vector<std::string>& libVec)
+  cmComputeLinkInformation& cli, std::vector<std::string>& libVec,
+  std::vector<std::string>& vsTargetVec)
 {
   typedef cmComputeLinkInformation::ItemVector ItemVector;
-  ItemVector libs = cli.GetItems();
+  ItemVector const& libs = cli.GetItems();
   std::string currentBinDir =
     this->LocalGenerator->GetCurrentBinaryDirectory();
   for (ItemVector::const_iterator l = libs.begin(); l != libs.end(); ++l) {
@@ -2431,7 +2517,11 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
       std::string path = this->LocalGenerator->ConvertToRelativePath(
         currentBinDir, l->Value.c_str());
       this->ConvertToWindowsSlash(path);
-      libVec.push_back(path);
+      if (cmVS10IsTargetsFile(l->Value)) {
+        vsTargetVec.push_back(path);
+      } else {
+        libVec.push_back(path);
+      }
     } else if (!l->Target ||
                l->Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
       libVec.push_back(l->Value);
@@ -2439,6 +2529,26 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
   }
 }
 
+void cmVisualStudio10TargetGenerator::AddTargetsFileAndConfigPair(
+  std::string const& targetsFile, std::string const& config)
+{
+  for (std::vector<TargetsFileAndConfigs>::iterator i =
+         this->TargetsFileAndConfigsVec.begin();
+       i != this->TargetsFileAndConfigsVec.end(); ++i) {
+    if (cmSystemTools::ComparePath(targetsFile, i->File)) {
+      if (std::find(i->Configs.begin(), i->Configs.end(), config) ==
+          i->Configs.end()) {
+        i->Configs.push_back(config);
+      }
+      return;
+    }
+  }
+  TargetsFileAndConfigs entry;
+  entry.File = targetsFile;
+  entry.Configs.push_back(config);
+  this->TargetsFileAndConfigsVec.push_back(entry);
+}
+
 void cmVisualStudio10TargetGenerator::WriteMidlOptions(
   std::string const& /*config*/, std::vector<std::string> const& includes)
 {

+ 14 - 1
Source/cmVisualStudio10TargetGenerator.h

@@ -41,6 +41,12 @@ private:
   {
   };
 
+  struct TargetsFileAndConfigs
+  {
+    std::string File;
+    std::vector<std::string> Configs;
+  };
+
   std::string ConvertPath(std::string const& path, bool forceRelative);
   void ConvertToWindowsSlash(std::string& s);
   void WriteString(const char* line, int indentLevel);
@@ -77,6 +83,7 @@ private:
                                std::string const& version);
   void WriteCommonMissingFiles(const std::string& manifestFile);
   void WriteTargetSpecificReferences();
+  void WriteTargetsFileReferences();
 
   bool ComputeClOptions();
   bool ComputeClOptions(std::string const& configName);
@@ -92,6 +99,8 @@ private:
                         std::vector<std::string> const& includes);
   bool ComputeLinkOptions();
   bool ComputeLinkOptions(std::string const& config);
+  bool ComputeLibOptions();
+  bool ComputeLibOptions(std::string const& config);
   void WriteLinkOptions(std::string const& config);
   void WriteMidlOptions(std::string const& config,
                         std::vector<std::string> const& includes);
@@ -106,7 +115,10 @@ private:
   void WriteApplicationTypeSettings();
   bool OutputSourceSpecificFlags(cmSourceFile const* source);
   void AddLibraries(cmComputeLinkInformation& cli,
-                    std::vector<std::string>& libVec);
+                    std::vector<std::string>& libVec,
+                    std::vector<std::string>& vsTargetVec);
+  void AddTargetsFileAndConfigPair(std::string const& targetsFile,
+                                   std::string const& config);
   void WriteLibOptions(std::string const& config);
   void WriteManifestOptions(std::string const& config);
   void WriteEvents(std::string const& configName);
@@ -138,6 +150,7 @@ private:
   OptionsMap LinkOptions;
   std::string PathToVcxproj;
   std::vector<std::string> Configurations;
+  std::vector<TargetsFileAndConfigs> TargetsFileAndConfigsVec;
   cmGeneratorTarget* GeneratorTarget;
   cmMakefile* Makefile;
   std::string Platform;