From e89ad0f94e6b52cc9f75abe21107c7a2e5d24ca2 Mon Sep 17 00:00:00 2001
From: Zack Galbreath <zack.galbreath@kitware.com>
Date: Mon, 18 Jun 2018 10:49:44 -0400
Subject: install: Allow installing targets created in another directory

Previously, `install(TARGETS)` would only accept targets created in the same
directory scope.  Relax this restriction by searching the global scope when
determining whether or not a target exists.

Fixes: #14444
---
 Help/command/install.rst                                       |  8 ++++++++
 Help/release/dev/subdirectory-installing.rst                   |  5 +++++
 Source/cmInstallCommand.cxx                                    | 10 +++++++---
 Source/cmInstallTargetGenerator.cxx                            |  6 ++++++
 Tests/RunCMake/install/RunCMakeTest.cmake                      |  2 ++
 .../RunCMake/install/TARGETS-InstallFromSubDir-all-check.cmake |  1 +
 Tests/RunCMake/install/TARGETS-InstallFromSubDir.cmake         |  4 ++++
 .../RunCMake/install/TARGETS-InstallFromSubDir/CMakeLists.txt  |  1 +
 8 files changed, 34 insertions(+), 3 deletions(-)
 create mode 100644 Help/release/dev/subdirectory-installing.rst
 create mode 100644 Tests/RunCMake/install/TARGETS-InstallFromSubDir-all-check.cmake
 create mode 100644 Tests/RunCMake/install/TARGETS-InstallFromSubDir.cmake
 create mode 100644 Tests/RunCMake/install/TARGETS-InstallFromSubDir/CMakeLists.txt

diff --git a/Help/command/install.rst b/Help/command/install.rst
index 08cbc56..8b2a971 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -280,6 +280,14 @@ targets that link to the object libraries in their implementation.
 Installing a target with the :prop_tgt:`EXCLUDE_FROM_ALL` target property
 set to ``TRUE`` has undefined behavior.
 
+:command:`install(TARGETS)` can install targets that were created in
+other directories.  When using such cross-directory install rules, running
+``make install`` (or similar) from a subdirectory will not guarantee that
+targets from other directories are up-to-date.  You can use
+:command:`target_link_libraries` or :command:`add_dependencies`
+to ensure that such out-of-directory targets are built before the
+subdirectory-specific install rules are run.
+
 The install destination given to the target install ``DESTINATION`` may
 use "generator expressions" with the syntax ``$<...>``.  See the
 :manual:`cmake-generator-expressions(7)` manual for available expressions.
diff --git a/Help/release/dev/subdirectory-installing.rst b/Help/release/dev/subdirectory-installing.rst
new file mode 100644
index 0000000..04e4676
--- /dev/null
+++ b/Help/release/dev/subdirectory-installing.rst
@@ -0,0 +1,5 @@
+subdirectory-installing
+-----------------------
+
+* The :command:`install(TARGETS)` command may now be used
+  to install targets created outside the current directory.
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 87dcb18..99409c2 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -362,7 +362,12 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
       return false;
     }
     // Lookup this target in the current directory.
-    if (cmTarget* target = this->Makefile->FindLocalNonAliasTarget(tgt)) {
+    cmTarget* target = this->Makefile->FindLocalNonAliasTarget(tgt);
+    if (!target) {
+      // If no local target has been found, find it in the global scope.
+      target = this->Makefile->GetGlobalGenerator()->FindTarget(tgt, true);
+    }
+    if (target) {
       // Found the target.  Check its type.
       if (target->GetType() != cmStateEnums::EXECUTABLE &&
           target->GetType() != cmStateEnums::STATIC_LIBRARY &&
@@ -381,8 +386,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
     } else {
       // Did not find the target.
       std::ostringstream e;
-      e << "TARGETS given target \"" << tgt
-        << "\" which does not exist in this directory.";
+      e << "TARGETS given target \"" << tgt << "\" which does not exist.";
       this->SetError(e.str());
       return false;
     }
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index e0afa2d..8b8f79b 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -440,7 +440,13 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
 
 void cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
 {
+  // Lookup this target in the current directory.
   this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
+  if (!this->Target) {
+    // If no local target has been found, find it in the global scope.
+    this->Target =
+      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
+  }
 }
 
 void cmInstallTargetGenerator::AddTweak(std::ostream& os, Indent indent,
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index f0c02d8..b1add3a 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -72,6 +72,8 @@ if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR NOT "$ENV{CMAKE_OSX_ARCHITECTURES}
   run_install_test(FILES-TARGET_OBJECTS)
 endif()
 
+run_install_test(TARGETS-InstallFromSubDir)
+
 set(run_install_test_components 1)
 run_install_test(FILES-EXCLUDE_FROM_ALL)
 run_install_test(TARGETS-EXCLUDE_FROM_ALL)
diff --git a/Tests/RunCMake/install/TARGETS-InstallFromSubDir-all-check.cmake b/Tests/RunCMake/install/TARGETS-InstallFromSubDir-all-check.cmake
new file mode 100644
index 0000000..1d747c3
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-InstallFromSubDir-all-check.cmake
@@ -0,0 +1 @@
+check_installed([[^bin;bin/myexe(\.exe)?;bin/subexe(\.exe)?$]])
diff --git a/Tests/RunCMake/install/TARGETS-InstallFromSubDir.cmake b/Tests/RunCMake/install/TARGETS-InstallFromSubDir.cmake
new file mode 100644
index 0000000..8615d6e
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-InstallFromSubDir.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+add_executable(myexe main.c)
+add_subdirectory(TARGETS-InstallFromSubDir)
+install(TARGETS myexe subexe DESTINATION bin)
diff --git a/Tests/RunCMake/install/TARGETS-InstallFromSubDir/CMakeLists.txt b/Tests/RunCMake/install/TARGETS-InstallFromSubDir/CMakeLists.txt
new file mode 100644
index 0000000..477d938
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-InstallFromSubDir/CMakeLists.txt
@@ -0,0 +1 @@
+add_executable(subexe ../main.c)
-- 
cgit v0.12