From 22b43b0009f37c41b310dd26a4ecb00361975aaa Mon Sep 17 00:00:00 2001
From: Nils Gladitz <nilsgladitz@gmail.com>
Date: Fri, 11 Jan 2019 21:29:58 +0100
Subject: VS: Add support for VS_DEBUGGER_* properties on custom targets

Visual studio itself supports the corresponding `LocalDebugger*`
properties on utility targets; support generating them from CMake as
well.
---
 Help/release/dev/vs-debug-utility-targets.rst      |   6 ++
 Source/cmVisualStudio10TargetGenerator.cxx         | 101 +++++++++++----------
 .../VS10Project/VsDebuggerCommand-check.cmake      |  36 ++++----
 Tests/RunCMake/VS10Project/VsDebuggerCommand.cmake |   3 +-
 .../VsDebuggerCommandArguments-check.cmake         |  36 ++++----
 .../VS10Project/VsDebuggerCommandArguments.cmake   |   3 +-
 .../VS10Project/VsDebuggerEnvironment-check.cmake  |  36 ++++----
 .../VS10Project/VsDebuggerEnvironment.cmake        |   3 +-
 .../VS10Project/VsDebuggerWorkingDir-check.cmake   |  36 ++++----
 .../VS10Project/VsDebuggerWorkingDir.cmake         |   3 +-
 10 files changed, 142 insertions(+), 121 deletions(-)
 create mode 100644 Help/release/dev/vs-debug-utility-targets.rst

diff --git a/Help/release/dev/vs-debug-utility-targets.rst b/Help/release/dev/vs-debug-utility-targets.rst
new file mode 100644
index 0000000..02e5262
--- /dev/null
+++ b/Help/release/dev/vs-debug-utility-targets.rst
@@ -0,0 +1,6 @@
+vs-debug-utility-targets
+------------------------
+
+* :ref:`Visual Studio Generators` for VS 2010 and above learned
+  to support the ``VS_DEBUGGER_*`` properties on targets created
+  via :command:`add_custom_target`.
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index dd90269..c685918 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -2283,6 +2283,58 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
   e1.Element("_ProjectFileVersion", "10.0.20506.1");
   for (std::string const& config : this->Configurations) {
     const std::string cond = this->CalcCondition(config);
+
+    if (ttype <= cmStateEnums::UTILITY) {
+      if (const char* workingDir = this->GeneratorTarget->GetProperty(
+            "VS_DEBUGGER_WORKING_DIRECTORY")) {
+        cmGeneratorExpression ge;
+        std::unique_ptr<cmCompiledGeneratorExpression> cge =
+          ge.Parse(workingDir);
+        std::string genWorkingDir =
+          cge->Evaluate(this->LocalGenerator, config);
+
+        e1.WritePlatformConfigTag("LocalDebuggerWorkingDirectory", cond,
+                                  genWorkingDir);
+      }
+
+      if (const char* environment =
+            this->GeneratorTarget->GetProperty("VS_DEBUGGER_ENVIRONMENT")) {
+        cmGeneratorExpression ge;
+        std::unique_ptr<cmCompiledGeneratorExpression> cge =
+          ge.Parse(environment);
+        std::string genEnvironment =
+          cge->Evaluate(this->LocalGenerator, config);
+
+        e1.WritePlatformConfigTag("LocalDebuggerEnvironment", cond,
+                                  genEnvironment);
+      }
+
+      if (const char* debuggerCommand =
+            this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) {
+
+        cmGeneratorExpression ge;
+        std::unique_ptr<cmCompiledGeneratorExpression> cge =
+          ge.Parse(debuggerCommand);
+        std::string genDebuggerCommand =
+          cge->Evaluate(this->LocalGenerator, config);
+
+        e1.WritePlatformConfigTag("LocalDebuggerCommand", cond,
+                                  genDebuggerCommand);
+      }
+
+      if (const char* commandArguments = this->GeneratorTarget->GetProperty(
+            "VS_DEBUGGER_COMMAND_ARGUMENTS")) {
+        cmGeneratorExpression ge;
+        std::unique_ptr<cmCompiledGeneratorExpression> cge =
+          ge.Parse(commandArguments);
+        std::string genCommandArguments =
+          cge->Evaluate(this->LocalGenerator, config);
+
+        e1.WritePlatformConfigTag("LocalDebuggerCommandArguments", cond,
+                                  genCommandArguments);
+      }
+    }
+
     if (ttype >= cmStateEnums::UTILITY) {
       e1.WritePlatformConfigTag(
         "IntDir", cond, "$(Platform)\\$(Configuration)\\$(ProjectName)\\");
@@ -2347,55 +2399,6 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
         e1.WritePlatformConfigTag("ExcludePath", cond, sdkExcludeDirectories);
       }
 
-      if (const char* workingDir = this->GeneratorTarget->GetProperty(
-            "VS_DEBUGGER_WORKING_DIRECTORY")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(workingDir);
-        std::string genWorkingDir =
-          cge->Evaluate(this->LocalGenerator, config);
-
-        e1.WritePlatformConfigTag("LocalDebuggerWorkingDirectory", cond,
-                                  genWorkingDir);
-      }
-
-      if (const char* environment =
-            this->GeneratorTarget->GetProperty("VS_DEBUGGER_ENVIRONMENT")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(environment);
-        std::string genEnvironment =
-          cge->Evaluate(this->LocalGenerator, config);
-
-        e1.WritePlatformConfigTag("LocalDebuggerEnvironment", cond,
-                                  genEnvironment);
-      }
-
-      if (const char* debuggerCommand =
-            this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) {
-
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(debuggerCommand);
-        std::string genDebuggerCommand =
-          cge->Evaluate(this->LocalGenerator, config);
-
-        e1.WritePlatformConfigTag("LocalDebuggerCommand", cond,
-                                  genDebuggerCommand);
-      }
-
-      if (const char* commandArguments = this->GeneratorTarget->GetProperty(
-            "VS_DEBUGGER_COMMAND_ARGUMENTS")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(commandArguments);
-        std::string genCommandArguments =
-          cge->Evaluate(this->LocalGenerator, config);
-
-        e1.WritePlatformConfigTag("LocalDebuggerCommandArguments", cond,
-                                  genCommandArguments);
-      }
-
       std::string name =
         cmSystemTools::GetFilenameWithoutLastExtension(targetNameFull);
       e1.WritePlatformConfigTag("TargetName", cond, name);
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerCommand-check.cmake b/Tests/RunCMake/VS10Project/VsDebuggerCommand-check.cmake
index 440f9f2..00288d4 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerCommand-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerCommand-check.cmake
@@ -1,22 +1,24 @@
-set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
-if(NOT EXISTS "${vcProjectFile}")
-  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
-  return()
-endif()
+foreach(target foo bar)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+    return()
+  endif()
 
-set(debuggerCommandSet FALSE)
+  set(debuggerCommandSet FALSE)
 
-file(STRINGS "${vcProjectFile}" lines)
-foreach(line IN LISTS lines)
-  if(line MATCHES "^ *<LocalDebuggerCommand[^>]*>([^<>]+)</LocalDebuggerCommand>$")
-    if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-command foo")
-        message(STATUS "foo.vcxproj has debugger command set")
-        set(debuggerCommandSet TRUE)
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<LocalDebuggerCommand[^>]*>([^<>]+)</LocalDebuggerCommand>$")
+      if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-command foo")
+          message(STATUS "${target}.vcxproj has debugger command set")
+          set(debuggerCommandSet TRUE)
+      endif()
     endif()
+  endforeach()
+
+  if(NOT debuggerCommandSet)
+    set(RunCMake_TEST_FAILED "LocalDebuggerCommand not found or not set correctly.")
+    return()
   endif()
 endforeach()
-
-if(NOT debuggerCommandSet)
-  set(RunCMake_TEST_FAILED "LocalDebuggerCommand not found or not set correctly.")
-  return()
-endif()
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerCommand.cmake b/Tests/RunCMake/VS10Project/VsDebuggerCommand.cmake
index 5dcb6d1..54f9154 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerCommand.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerCommand.cmake
@@ -1,5 +1,6 @@
 enable_language(CXX)
 add_library(foo foo.cpp)
+add_custom_target(bar)
 
-set_target_properties(foo PROPERTIES
+set_target_properties(foo bar PROPERTIES
     VS_DEBUGGER_COMMAND "my-debugger-command $<TARGET_PROPERTY:foo,NAME>")
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments-check.cmake b/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments-check.cmake
index b2e0a43..87555d1 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments-check.cmake
@@ -1,22 +1,24 @@
-set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
-if(NOT EXISTS "${vcProjectFile}")
-  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
-  return()
-endif()
+foreach(target foo bar)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+    return()
+  endif()
 
-set(debuggerCommandArgumentsSet FALSE)
+  set(debuggerCommandArgumentsSet FALSE)
 
-file(STRINGS "${vcProjectFile}" lines)
-foreach(line IN LISTS lines)
-  if(line MATCHES "^ *<LocalDebuggerCommandArguments[^>]*>([^<>]+)</LocalDebuggerCommandArguments>$")
-    if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-command-arguments foo")
-        message(STATUS "foo.vcxproj has debugger command arguments set")
-        set(debuggerCommandArgumentsSet TRUE)
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<LocalDebuggerCommandArguments[^>]*>([^<>]+)</LocalDebuggerCommandArguments>$")
+      if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-command-arguments foo")
+          message(STATUS "${target}.vcxproj has debugger command arguments set")
+          set(debuggerCommandArgumentsSet TRUE)
+      endif()
     endif()
+  endforeach()
+
+  if(NOT debuggerCommandArgumentsSet)
+    set(RunCMake_TEST_FAILED "LocalDebuggerCommandArguments not found or not set correctly.")
+    return()
   endif()
 endforeach()
-
-if(NOT debuggerCommandArgumentsSet)
-  set(RunCMake_TEST_FAILED "LocalDebuggerCommandArguments not found or not set correctly.")
-  return()
-endif()
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments.cmake b/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments.cmake
index aa87cdc..1f7d0be 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerCommandArguments.cmake
@@ -1,5 +1,6 @@
 enable_language(CXX)
 add_library(foo foo.cpp)
+add_custom_target(bar)
 
-set_target_properties(foo PROPERTIES
+set_target_properties(foo bar PROPERTIES
     VS_DEBUGGER_COMMAND_ARGUMENTS "my-debugger-command-arguments $<TARGET_PROPERTY:foo,NAME>")
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerEnvironment-check.cmake b/Tests/RunCMake/VS10Project/VsDebuggerEnvironment-check.cmake
index 2427ad4..fee121a 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerEnvironment-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerEnvironment-check.cmake
@@ -1,22 +1,24 @@
-set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
-if(NOT EXISTS "${vcProjectFile}")
-  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
-  return()
-endif()
+foreach(target foo bar)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+    return()
+  endif()
 
-set(debuggerEnvironmentSet FALSE)
+  set(debuggerEnvironmentSet FALSE)
 
-file(STRINGS "${vcProjectFile}" lines)
-foreach(line IN LISTS lines)
-  if(line MATCHES "^ *<LocalDebuggerEnvironment[^>]*>([^<>]+)</LocalDebuggerEnvironment>$")
-    if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-environment foo")
-        message(STATUS "foo.vcxproj has debugger environment set")
-        set(debuggerEnvironmentSet TRUE)
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<LocalDebuggerEnvironment[^>]*>([^<>]+)</LocalDebuggerEnvironment>$")
+      if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-environment foo")
+          message(STATUS "${target}.vcxproj has debugger environment set")
+          set(debuggerEnvironmentSet TRUE)
+      endif()
     endif()
+  endforeach()
+
+  if(NOT debuggerEnvironmentSet)
+    set(RunCMake_TEST_FAILED "LocalDebuggerEnvironment not found or not set correctly.")
+    return()
   endif()
 endforeach()
-
-if(NOT debuggerEnvironmentSet)
-  set(RunCMake_TEST_FAILED "LocalDebuggerEnvironment not found or not set correctly.")
-  return()
-endif()
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerEnvironment.cmake b/Tests/RunCMake/VS10Project/VsDebuggerEnvironment.cmake
index d5bec4c..c594f35 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerEnvironment.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerEnvironment.cmake
@@ -1,5 +1,6 @@
 enable_language(CXX)
 add_library(foo foo.cpp)
+add_custom_target(bar)
 
-set_target_properties(foo PROPERTIES
+set_target_properties(foo bar PROPERTIES
     VS_DEBUGGER_ENVIRONMENT "my-debugger-environment $<TARGET_PROPERTY:foo,NAME>")
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir-check.cmake b/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir-check.cmake
index 6a142f8..9d3f2a8 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir-check.cmake
@@ -1,22 +1,24 @@
-set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
-if(NOT EXISTS "${vcProjectFile}")
-  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
-  return()
-endif()
+foreach(target foo bar)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+    return()
+  endif()
 
-set(debuggerWorkDirSet FALSE)
+  set(debuggerWorkDirSet FALSE)
 
-file(STRINGS "${vcProjectFile}" lines)
-foreach(line IN LISTS lines)
-  if(line MATCHES "^ *<LocalDebuggerWorkingDirectory[^>]*>([^<>]+)</LocalDebuggerWorkingDirectory>$")
-    if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-directory foo")
-        message(STATUS "foo.vcxproj has debugger working dir set")
-        set(debuggerWorkDirSet TRUE)
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<LocalDebuggerWorkingDirectory[^>]*>([^<>]+)</LocalDebuggerWorkingDirectory>$")
+      if("${CMAKE_MATCH_1}" STREQUAL "my-debugger-directory foo")
+          message(STATUS "${target}.vcxproj has debugger working dir set")
+          set(debuggerWorkDirSet TRUE)
+      endif()
     endif()
+  endforeach()
+
+  if(NOT debuggerWorkDirSet)
+    set(RunCMake_TEST_FAILED "LocalDebuggerWorkingDirectory not found or not set correctly.")
+    return()
   endif()
 endforeach()
-
-if(NOT debuggerWorkDirSet)
-  set(RunCMake_TEST_FAILED "LocalDebuggerWorkingDirectory not found or not set correctly.")
-  return()
-endif()
diff --git a/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir.cmake b/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir.cmake
index 36daed0..69ed85c 100644
--- a/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir.cmake
+++ b/Tests/RunCMake/VS10Project/VsDebuggerWorkingDir.cmake
@@ -1,5 +1,6 @@
 enable_language(CXX)
 add_library(foo foo.cpp)
+add_custom_target(bar)
 
-set_target_properties(foo PROPERTIES
+set_target_properties(foo bar PROPERTIES
     VS_DEBUGGER_WORKING_DIRECTORY "my-debugger-directory $<TARGET_PROPERTY:foo,NAME>")
-- 
cgit v0.12