From f2daa025e3bf05f89a745e6a65fea9537e0a035d Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Tue, 22 Sep 2020 12:57:50 -0400
Subject: {get,set}_property: Add support for referencing binary directories

Index directories by their binary directory path in addition to their
source directory path.

Fixes: #19262
---
 Help/command/get_directory_property.rst |  7 ++++++-
 Help/command/get_property.rst           | 13 ++++++++-----
 Help/command/set_property.rst           | 10 +++++++---
 Help/release/dev/binary-dir-props.rst   |  7 +++++++
 Source/cmGlobalGenerator.cxx            | 10 +++++-----
 Source/cmSetPropertyCommand.cxx         |  2 +-
 Tests/Properties/CMakeLists.txt         | 28 ++++++++++++++++++++++++++++
 Tests/Properties/SubDir/CMakeLists.txt  |  2 ++
 8 files changed, 64 insertions(+), 15 deletions(-)
 create mode 100644 Help/release/dev/binary-dir-props.rst
 create mode 100644 Tests/Properties/SubDir/CMakeLists.txt

diff --git a/Help/command/get_directory_property.rst b/Help/command/get_directory_property.rst
index 218efa9..39015cc 100644
--- a/Help/command/get_directory_property.rst
+++ b/Help/command/get_directory_property.rst
@@ -8,9 +8,14 @@ Get a property of ``DIRECTORY`` scope.
   get_directory_property(<variable> [DIRECTORY <dir>] <prop-name>)
 
 Stores a property of directory scope in the named ``<variable>``.
+
 The ``DIRECTORY`` argument specifies another directory from which
 to retrieve the property value instead of the current directory.
-The specified directory must have already been traversed by CMake.
+It may reference either a source directory, or since CMake 3.19,
+a binary directory.  Relative paths are treated as relative to the
+current source directory.  CMake must already know about the directory,
+either by having added it through a call to :command:`add_subdirectory`
+or being the top level directory.
 
 If the property is not defined for the nominated directory scope,
 an empty string is returned.  In the case of ``INHERITED`` properties,
diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst
index 0602518..870c934 100644
--- a/Help/command/get_property.rst
+++ b/Help/command/get_property.rst
@@ -30,7 +30,9 @@ It must be one of the following:
 ``DIRECTORY``
   Scope defaults to the current directory but another
   directory (already processed by CMake) may be named by the
-  full or relative path ``<dir>``.
+  full or relative path ``<dir>``.  The ``<dir>`` may reference either a
+  source directory, or since CMake 3.19, a binary directory.
+  Relative paths are treated as relative to the current source directory.
   See also the :command:`get_directory_property` command.
 
 ``TARGET``
@@ -44,10 +46,11 @@ It must be one of the following:
 
   ``DIRECTORY <dir>``
     The source file property will be read from the ``<dir>`` directory's
-    scope.  CMake must already know about that source directory, either by
-    having added it through a call to :command:`add_subdirectory` or ``<dir>``
-    being the top level source directory.  Relative paths are treated as
-    relative to the current source directory.
+    scope.  The ``<dir>`` may reference either a source directory, or
+    since CMake 3.19, a binary directory.  CMake must already know about
+    the directory, either by having added it through a call
+    to :command:`add_subdirectory` or ``<dir>`` being the top level directory.
+    Relative paths are treated as relative to the current source directory.
 
   ``TARGET_DIRECTORY <target>``
     The source file property will be read from the directory scope in which
diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst
index 93c2d9c..b5c1613 100644
--- a/Help/command/set_property.rst
+++ b/Help/command/set_property.rst
@@ -26,8 +26,11 @@ It must be one of the following:
   Scope is unique and does not accept a name.
 
 ``DIRECTORY``
-  Scope defaults to the current directory but another directory
+  Scope defaults to the current directory but other directories
   (already processed by CMake) may be named by full or relative path.
+  Each path may reference either a source directory, or since CMake 3.19,
+  a binary directory.
+  Relative paths are treated as relative to the current source directory.
   See also the :command:`set_directory_properties` command.
 
 ``TARGET``
@@ -42,8 +45,9 @@ It must be one of the following:
 
   ``DIRECTORY <dirs>...``
     The source file property will be set in each of the ``<dirs>``
-    directories' scopes.  CMake must already know about each of these
-    source directories, either by having added them through a call to
+    directories' scopes.  Each path may reference either a source directory,
+    or since CMake 3.19, a binary directory.  CMake must already know about
+    each of these directories, either by having added them through a call to
     :command:`add_subdirectory` or it being the top level source directory.
     Relative paths are treated as relative to the current source directory.
 
diff --git a/Help/release/dev/binary-dir-props.rst b/Help/release/dev/binary-dir-props.rst
new file mode 100644
index 0000000..b36d86d
--- /dev/null
+++ b/Help/release/dev/binary-dir-props.rst
@@ -0,0 +1,7 @@
+binary-dir-props
+----------------
+
+* The :command:`set_property`, :command:`get_property`,
+  and :command:`get_directory_property` commands' ``DIRECTORY``
+  options now accept references to binary directory paths,
+  such as the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index ce1a4ec..86b01bc 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -2353,13 +2353,13 @@ std::string cmGlobalGenerator::IndexGeneratorTargetUniquely(
 
 void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
 {
-  // FIXME: add_subdirectory supports multiple build directories
-  // sharing the same source directory.  We currently index only the
-  // first one, because that is what FindMakefile has always returned.
-  // All of its callers will need to be modified to support looking
-  // up directories by build directory path.
+  // We index by both source and binary directory.  add_subdirectory
+  // supports multiple build directories sharing the same source directory.
+  // The source directory index will reference only the first time it is used.
   this->MakefileSearchIndex.insert(
     MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
+  this->MakefileSearchIndex.insert(
+    MakefileMap::value_type(mf->GetCurrentBinaryDirectory(), mf));
 }
 
 void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx
index 6ca763b..df6a38a 100644
--- a/Source/cmSetPropertyCommand.cxx
+++ b/Source/cmSetPropertyCommand.cxx
@@ -113,7 +113,7 @@ bool HandleSourceFileDirectoryScopes(
           "given non-existent target for TARGET_DIRECTORY ", target_name));
         return false;
       }
-      cmProp target_source_dir = target->GetProperty("SOURCE_DIR");
+      cmProp target_source_dir = target->GetProperty("BINARY_DIR");
       cmMakefile* target_dir_mf =
         status.GetMakefile().GetGlobalGenerator()->FindMakefile(
           *target_source_dir);
diff --git a/Tests/Properties/CMakeLists.txt b/Tests/Properties/CMakeLists.txt
index 162a178..a1158c6 100644
--- a/Tests/Properties/CMakeLists.txt
+++ b/Tests/Properties/CMakeLists.txt
@@ -165,6 +165,34 @@ add_library(maindirtest SHARED)
 generate_file_for_set_property_test(32 maindirtest)
 generate_file_for_set_property_test(33 maindirtest)
 
+# Set/get properties by binary directory path.
+add_subdirectory(SubDir SubDirA)
+get_property(dir_prop_top DIRECTORY PROPERTY dir_prop_top)
+if(NOT dir_prop_top STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirA")
+  message(SEND_ERROR "dir_prop_top unexpected value after SubDirA:\n ${dir_prop_top}")
+endif()
+add_subdirectory(SubDir SubDirB)
+get_property(dir_prop_top DIRECTORY PROPERTY dir_prop_top)
+if(NOT dir_prop_top STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirB")
+  message(SEND_ERROR "dir_prop_top unexpected value after SubDirB:\n ${dir_prop_top}")
+endif()
+get_property(dir_prop_subA DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/SubDirA PROPERTY dir_prop_sub)
+if(NOT dir_prop_subA STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirA")
+  message(SEND_ERROR "SubDirA property dir_prop_sub incorrect:\n ${dir_prop_subA}")
+endif()
+get_property(dir_prop_subB DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/SubDirB PROPERTY dir_prop_sub)
+if(NOT dir_prop_subB STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirB")
+  message(SEND_ERROR "SubDirB property dir_prop_sub incorrect:\n ${dir_prop_subB}")
+endif()
+get_directory_property(dir_prop_subA DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/SubDirA dir_prop_sub)
+if(NOT dir_prop_subA STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirA")
+  message(SEND_ERROR "SubDirA property dir_prop_sub incorrect:\n ${dir_prop_subA}")
+endif()
+get_directory_property(dir_prop_subB DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/SubDirB dir_prop_sub)
+if(NOT dir_prop_subB STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/SubDirB")
+  message(SEND_ERROR "SubDirB property dir_prop_sub incorrect:\n ${dir_prop_subB}")
+endif()
+
 add_subdirectory(SubDir2)
 
 set(src_prefix "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/")
diff --git a/Tests/Properties/SubDir/CMakeLists.txt b/Tests/Properties/SubDir/CMakeLists.txt
new file mode 100644
index 0000000..f34cc8c
--- /dev/null
+++ b/Tests/Properties/SubDir/CMakeLists.txt
@@ -0,0 +1,2 @@
+set_property(DIRECTORY PROPERTY dir_prop_sub ${CMAKE_CURRENT_BINARY_DIR})
+set_property(DIRECTORY ${CMAKE_BINARY_DIR} PROPERTY dir_prop_top ${CMAKE_CURRENT_BINARY_DIR})
-- 
cgit v0.12