From 55da7e501ef114a3eac835396dae2187b7af96e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20St=C3=BCrmer?= <michael.stuermer@schaeffler.com>
Date: Tue, 13 Dec 2016 08:49:39 +0100
Subject: [PATCH] VS: add support for .NET references with hint paths

---
 Help/manual/cmake-properties.7.rst            |  2 +
 .../VS_DOTNET_REFERENCES_COPY_LOCAL.rst       |  7 ++
 Help/prop_tgt/VS_DOTNET_REFERENCE_refname.rst | 12 +++
 Help/release/dev/vs-dotnet-references.rst     | 13 ++++
 Source/cmVisualStudio10TargetGenerator.cxx    | 73 ++++++++++++++++---
 Source/cmVisualStudio10TargetGenerator.h      |  1 +
 6 files changed, 98 insertions(+), 10 deletions(-)
 create mode 100644 Help/prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL.rst
 create mode 100644 Help/prop_tgt/VS_DOTNET_REFERENCE_refname.rst
 create mode 100644 Help/release/dev/vs-dotnet-references.rst

diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 2bbe2c3e88..971834ee22 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -274,7 +274,9 @@ Properties on Targets
    /prop_tgt/VS_CONFIGURATION_TYPE
    /prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY
    /prop_tgt/VS_DESKTOP_EXTENSIONS_VERSION
+   /prop_tgt/VS_DOTNET_REFERENCE_refname
    /prop_tgt/VS_DOTNET_REFERENCES
+   /prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL
    /prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/VS_GLOBAL_KEYWORD
    /prop_tgt/VS_GLOBAL_PROJECT_TYPES
diff --git a/Help/prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL.rst b/Help/prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL.rst
new file mode 100644
index 0000000000..7641ba5ff6
--- /dev/null
+++ b/Help/prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL.rst
@@ -0,0 +1,7 @@
+VS_DOTNET_REFERENCES_COPY_LOCAL
+-------------------------------
+
+Sets the **Copy Local** property for all .NET hint references in the target
+
+Boolean property to enable/disable copying of .NET hint references to
+output directory. The default is ``ON``.
diff --git a/Help/prop_tgt/VS_DOTNET_REFERENCE_refname.rst b/Help/prop_tgt/VS_DOTNET_REFERENCE_refname.rst
new file mode 100644
index 0000000000..5814005328
--- /dev/null
+++ b/Help/prop_tgt/VS_DOTNET_REFERENCE_refname.rst
@@ -0,0 +1,12 @@
+VS_DOTNET_REFERENCE_<refname>
+-----------------------------
+
+Visual Studio managed project .NET reference with name ``<refname>``
+and hint path.
+
+Adds one .NET reference to generated Visual Studio project. The
+reference will have the name ``<refname>`` and will point to the
+assembly given as value of the property.
+
+See also :prop_tgt:`VS_DOTNET_REFERENCES` and
+:prop_tgt:`VS_DOTNET_REFERENCES_COPY_LOCAL`
diff --git a/Help/release/dev/vs-dotnet-references.rst b/Help/release/dev/vs-dotnet-references.rst
new file mode 100644
index 0000000000..8998afb41c
--- /dev/null
+++ b/Help/release/dev/vs-dotnet-references.rst
@@ -0,0 +1,13 @@
+vs-dotnet-references
+--------------------
+
+* The :ref:`Visual Studio Generators` for VS 2010 and above can
+  now handle .NET references with hintpaths. For this the new
+  target property group :prop_tgt:`VS_DOTNET_REFERENCE_<refname>`
+  was introduced. The ``<refname>`` part of the property name will
+  be the name of the reference, the value will be the actual
+  path to the assembly.
+
+* Copying of referenced assemblies to the output directory can
+  now be disabled using the target property
+  :prop_tgt:`VS_DOTNET_REFERENCES_COPY_LOCAL`.
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 8ab3b044d0..49274c0fb2 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -429,28 +429,81 @@ void cmVisualStudio10TargetGenerator::Generate()
 void cmVisualStudio10TargetGenerator::WriteDotNetReferences()
 {
   std::vector<std::string> references;
+  typedef std::pair<std::string, std::string> HintReference;
+  std::vector<HintReference> hintReferences;
   if (const char* vsDotNetReferences =
         this->GeneratorTarget->GetProperty("VS_DOTNET_REFERENCES")) {
     cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
   }
-  if (!references.empty()) {
+  cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
+  for (cmPropertyMap::const_iterator i = props.begin(); i != props.end();
+       ++i) {
+    if (i->first.find("VS_DOTNET_REFERENCE_") == 0) {
+      std::string name = i->first.substr(20);
+      if (name != "") {
+        std::string path = i->second.GetValue();
+        if (!cmsys::SystemTools::FileIsFullPath(path)) {
+          path = std::string(this->GeneratorTarget->Target->GetMakefile()
+                               ->GetCurrentSourceDirectory()) +
+            "/" + path;
+        }
+        this->ConvertToWindowsSlash(path);
+        hintReferences.push_back(HintReference(name, path));
+      }
+    }
+  }
+  if (!references.empty() || !hintReferences.empty()) {
     this->WriteString("<ItemGroup>\n", 1);
     for (std::vector<std::string>::iterator ri = references.begin();
          ri != references.end(); ++ri) {
-      this->WriteString("<Reference Include=\"", 2);
-      (*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n";
-      this->WriteString("<CopyLocalSatelliteAssemblies>true"
-                        "</CopyLocalSatelliteAssemblies>\n",
-                        3);
-      this->WriteString("<ReferenceOutputAssembly>true"
-                        "</ReferenceOutputAssembly>\n",
-                        3);
-      this->WriteString("</Reference>\n", 2);
+      // if the entry from VS_DOTNET_REFERENCES is an existing file, generate
+      // a new hint-reference and name it from the filename
+      if (cmsys::SystemTools::FileExists(*ri, true)) {
+        std::string name =
+          cmsys::SystemTools::GetFilenameWithoutExtension(*ri);
+        std::string path = *ri;
+        this->ConvertToWindowsSlash(path);
+        hintReferences.push_back(HintReference(name, path));
+      } else {
+        this->WriteDotNetReference(*ri, "");
+      }
+    }
+    for (std::vector<std::pair<std::string, std::string> >::const_iterator i =
+           hintReferences.begin();
+         i != hintReferences.end(); ++i) {
+      this->WriteDotNetReference(i->first, i->second);
     }
     this->WriteString("</ItemGroup>\n", 1);
   }
 }
 
+void cmVisualStudio10TargetGenerator::WriteDotNetReference(
+  std::string const& ref, std::string const& hint)
+{
+  this->WriteString("<Reference Include=\"", 2);
+  (*this->BuildFileStream) << cmVS10EscapeXML(ref) << "\">\n";
+  this->WriteString("<CopyLocalSatelliteAssemblies>true"
+                    "</CopyLocalSatelliteAssemblies>\n",
+                    3);
+  this->WriteString("<ReferenceOutputAssembly>true"
+                    "</ReferenceOutputAssembly>\n",
+                    3);
+  if (!hint.empty()) {
+    const char* privateReference = "True";
+    if (const char* value = this->GeneratorTarget->GetProperty(
+          "VS_DOTNET_REFERENCES_COPY_LOCAL")) {
+      if (cmSystemTools::IsOff(value)) {
+        privateReference = "False";
+      }
+    }
+    this->WriteString("<Private>", 3);
+    (*this->BuildFileStream) << privateReference << "</Private>\n";
+    this->WriteString("<HintPath>", 3);
+    (*this->BuildFileStream) << hint << "</HintPath>\n";
+  }
+  this->WriteString("</Reference>\n", 2);
+}
+
 void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
 {
   std::vector<cmSourceFile const*> resxObjs;
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index e68bf1a8e6..027761eace 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -65,6 +65,7 @@ private:
                     std::vector<cmSourceFile const*> const&);
   void WriteAllSources();
   void WriteDotNetReferences();
+  void WriteDotNetReference(std::string const& ref, std::string const& hint);
   void WriteEmbeddedResourceGroup();
   void WriteWinRTReferences();
   void WriteWinRTPackageCertificateKeyFile();
-- 
GitLab