From 5a1af142f120ccc6834efcf786e346b06e0f05c6 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Wed, 22 May 2019 09:56:19 -0400
Subject: Genex: Fix value lifetimes in nested TARGET_PROPERTY evaluation

For special properties like `INCLUDE_DIRECTORIES`, the pointer returned
by `cmTarget::GetProperty` is only valid until the next time the same
special property is queried on *any* target.  When evaluating a nested
`TARGET_PROPERTY` generator expression we may look up such a property
more than once on different targets.  Fix `TargetPropertyNode::Evaluate`
to store the lookup result in locally owned memory earlier.

Fixes: #19286
---
 Source/cmGeneratorExpressionNode.cxx                   | 11 ++++++++---
 Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake  |  1 +
 .../TARGET_PROPERTY-INCLUDE_DIRECTORIES.cmake          | 18 ++++++++++++++++++
 3 files changed, 27 insertions(+), 3 deletions(-)
 create mode 100644 Tests/RunCMake/GeneratorExpression/TARGET_PROPERTY-INCLUDE_DIRECTORIES.cmake

diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 709355a..68ef170 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1215,7 +1215,12 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
         break;
     }
 
-    const char* prop = target->GetProperty(propertyName);
+    std::string prop;
+    bool haveProp = false;
+    if (const char* p = target->GetProperty(propertyName)) {
+      prop = p;
+      haveProp = true;
+    }
 
     if (dagCheckerParent) {
       if (dagCheckerParent->EvaluatingGenexExpression() ||
@@ -1235,7 +1240,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
         }
 #undef TRANSITIVE_PROPERTY_COMPARE
 
-        if (!prop) {
+        if (!haveProp) {
           return std::string();
         }
       } else {
@@ -1291,7 +1296,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
       }
     }
 
-    if (!prop) {
+    if (!haveProp) {
       if (target->IsImported() ||
           target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
         return linkedTargetsContent;
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
index 8abf70d..68a0172 100644
--- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -53,6 +53,7 @@ run_cmake_with_options(TARGET_FILE_BASE_NAME -DCMAKE_BUILD_TYPE:STRING=Debug)
 run_cmake_with_options(TARGET_FILE_BASE_NAME-imported-target -DCMAKE_BUILD_TYPE:STRING=Debug)
 run_cmake(TARGET_FILE_BASE_NAME-non-valid-target)
 run_cmake(TARGET_LINKER_FILE_BASE_NAME-non-valid-target)
+run_cmake(TARGET_PROPERTY-INCLUDE_DIRECTORIES)
 run_cmake(TARGET_PROPERTY-LOCATION)
 run_cmake(TARGET_PROPERTY-SOURCES)
 run_cmake(LINK_ONLY-not-linking)
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_PROPERTY-INCLUDE_DIRECTORIES.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_PROPERTY-INCLUDE_DIRECTORIES.cmake
new file mode 100644
index 0000000..cb6f4d8
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_PROPERTY-INCLUDE_DIRECTORIES.cmake
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.14)
+enable_language(C)
+
+add_library(foo1 STATIC empty.c)
+target_include_directories(foo1 PUBLIC include)
+target_link_libraries(foo1 PRIVATE foo2 foo3 foo4)
+
+add_library(foo2 STATIC empty.c)
+target_include_directories(foo2 PUBLIC $<TARGET_PROPERTY:foo1,INCLUDE_DIRECTORIES>)
+
+add_library(foo3 STATIC empty.c)
+target_include_directories(foo3 PUBLIC $<TARGET_PROPERTY:foo2,INCLUDE_DIRECTORIES>)
+
+add_library(foo4 STATIC empty.c)
+target_include_directories(foo4 PUBLIC $<TARGET_PROPERTY:foo3,INCLUDE_DIRECTORIES>)
+
+# Evaluate a genex that looks up INCLUDE_DIRECTORIES on multiple targets.
+file(GENERATE OUTPUT out.txt CONTENT "$<TARGET_PROPERTY:foo4,INCLUDE_DIRECTORIES>")
-- 
cgit v0.12