From 023188ffb48cc35ebab7cabbafefcd6dd31b750d Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Fri, 28 Sep 2018 17:30:22 +0200
Subject: INTERFACE_POSITION_INDEPENDENT_CODE: add generator expressions
 support

Fixes: #16532
---
 .../INTERFACE_POSITION_INDEPENDENT_CODE.rst        |  6 ++++
 .../dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst    |  5 +++
 Source/cmGeneratorExpressionDAGChecker.cxx         | 12 +++++++
 Source/cmGeneratorExpressionDAGChecker.h           |  1 +
 Source/cmGeneratorExpressionNode.cxx               |  3 +-
 Source/cmGeneratorTarget.cxx                       | 37 ++++++++++++++++------
 .../PositionIndependentCode/Genex1-result.txt      |  1 +
 .../PositionIndependentCode/Genex1-stderr.txt      |  3 ++
 .../RunCMake/PositionIndependentCode/Genex1.cmake  |  9 ++++++
 .../PositionIndependentCode/Genex2-result.txt      |  1 +
 .../PositionIndependentCode/Genex2-stderr.txt      |  3 ++
 .../RunCMake/PositionIndependentCode/Genex2.cmake  |  9 ++++++
 .../PositionIndependentCode/RunCMakeTest.cmake     |  2 ++
 13 files changed, 82 insertions(+), 10 deletions(-)
 create mode 100644 Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex1-result.txt
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex1.cmake
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex2-result.txt
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt
 create mode 100644 Tests/RunCMake/PositionIndependentCode/Genex2.cmake

diff --git a/Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst b/Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst
index ea700df..4336d71 100644
--- a/Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst
+++ b/Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst
@@ -14,3 +14,9 @@ undefined, then consumers will determine their
 :prop_tgt:`POSITION_INDEPENDENT_CODE` property by other means.  Consumers
 must ensure that the targets that they link to have a consistent
 requirement for their ``INTERFACE_POSITION_INDEPENDENT_CODE`` property.
+
+Contents of ``INTERFACE_POSITION_INDEPENDENT_CODE`` may use
+"generator expressions" with the syntax ``$<...>``.  See the
+:manual:`cmake-generator-expressions(7)` manual for available expressions.
+See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem
+properties.
diff --git a/Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst b/Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst
new file mode 100644
index 0000000..7732ff6
--- /dev/null
+++ b/Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst
@@ -0,0 +1,5 @@
+INTERFACE_POSITION_INDEPENDENT_CODE
+-----------------------------------
+
+* :prop_tgt:`INTERFACE_POSITION_INDEPENDENT_CODE` target property gains the
+  support of :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 8d57441..56eb2bf 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -166,6 +166,18 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression()
   return top->Property == "TARGET_GENEX_EVAL" || top->Property == "GENEX_EVAL";
 }
 
+bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression()
+{
+  const cmGeneratorExpressionDAGChecker* top = this;
+  const cmGeneratorExpressionDAGChecker* parent = this->Parent;
+  while (parent) {
+    top = parent;
+    parent = parent->Parent;
+  }
+
+  return top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
+}
+
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
   cmGeneratorTarget const* tgt)
 {
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index a5134c3..1525c39 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -66,6 +66,7 @@ struct cmGeneratorExpressionDAGChecker
                    const std::string& expr);
 
   bool EvaluatingGenexExpression();
+  bool EvaluatingPICExpression();
   bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr);
 
 #define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index f901215..49b97fb 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1225,7 +1225,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
     const char* prop = target->GetProperty(propertyName);
 
     if (dagCheckerParent) {
-      if (dagCheckerParent->EvaluatingGenexExpression()) {
+      if (dagCheckerParent->EvaluatingGenexExpression() ||
+          dagCheckerParent->EvaluatingPICExpression()) {
         // No check required.
       } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
 #define TRANSITIVE_PROPERTY_COMPARE(PROPERTY)                                 \
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 1663400..a278a7f 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -4268,21 +4268,35 @@ std::string compatibilityAgree(CompatibleType t, bool dominant)
 }
 
 template <typename PropertyType>
-PropertyType getTypedProperty(cmGeneratorTarget const* tgt,
-                              const std::string& prop);
+PropertyType getTypedProperty(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
 
 template <>
 bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
-                            const std::string& prop)
+                            const std::string& prop,
+                            cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  return tgt->GetPropertyAsBool(prop);
+  if (genexInterpreter == nullptr) {
+    return tgt->GetPropertyAsBool(prop);
+  }
+
+  const char* value = tgt->GetProperty(prop);
+  return cmSystemTools::IsOn(genexInterpreter->Evaluate(value, prop));
 }
 
 template <>
-const char* getTypedProperty<const char*>(cmGeneratorTarget const* tgt,
-                                          const std::string& prop)
+const char* getTypedProperty<const char*>(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  return tgt->GetProperty(prop);
+  const char* value = tgt->GetProperty(prop);
+
+  if (genexInterpreter == nullptr) {
+    return value;
+  }
+
+  return genexInterpreter->Evaluate(value, prop).c_str();
 }
 
 template <typename PropertyType>
@@ -4423,6 +4437,11 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
   }
 
   std::string interfaceProperty = "INTERFACE_" + p;
+  std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter(
+    p == "POSITION_INDEPENDENT_CODE" ? new cmGeneratorExpressionInterpreter(
+                                         tgt->GetLocalGenerator(), config, tgt)
+                                     : nullptr);
+
   for (cmGeneratorTarget const* theTarget : deps) {
     // An error should be reported if one dependency
     // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
@@ -4434,8 +4453,8 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
 
     const bool ifaceIsSet = std::find(propKeys.begin(), propKeys.end(),
                                       interfaceProperty) != propKeys.end();
-    PropertyType ifacePropContent =
-      getTypedProperty<PropertyType>(theTarget, interfaceProperty);
+    PropertyType ifacePropContent = getTypedProperty<PropertyType>(
+      theTarget, interfaceProperty, genexInterpreter.get());
 
     std::string reportEntry;
     if (ifaceIsSet) {
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex1-result.txt b/Tests/RunCMake/PositionIndependentCode/Genex1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt b/Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt
new file mode 100644
index 0000000..c242a05
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt
@@ -0,0 +1,3 @@
+CMake Error: Property POSITION_INDEPENDENT_CODE on target "conflict1" does
+not match the INTERFACE_POSITION_INDEPENDENT_CODE property requirement
+of dependency "genex_pic".
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex1.cmake b/Tests/RunCMake/PositionIndependentCode/Genex1.cmake
new file mode 100644
index 0000000..a91be3e
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex1.cmake
@@ -0,0 +1,9 @@
+
+add_library(genex_pic UNKNOWN IMPORTED)
+# PIC is ON if sibling target is a library, OFF if it is an executable
+set_property(TARGET genex_pic PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE $<NOT:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>>)
+
+
+add_library(conflict1 STATIC main.cpp)
+set_property(TARGET conflict1 PROPERTY POSITION_INDEPENDENT_CODE OFF)
+target_link_libraries(conflict1 PRIVATE genex_pic)
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex2-result.txt b/Tests/RunCMake/PositionIndependentCode/Genex2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt b/Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt
new file mode 100644
index 0000000..735a926
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt
@@ -0,0 +1,3 @@
+CMake Error: Property POSITION_INDEPENDENT_CODE on target "conflict2" does
+not match the INTERFACE_POSITION_INDEPENDENT_CODE property requirement
+of dependency "genex_pic".
diff --git a/Tests/RunCMake/PositionIndependentCode/Genex2.cmake b/Tests/RunCMake/PositionIndependentCode/Genex2.cmake
new file mode 100644
index 0000000..fb0a5db
--- /dev/null
+++ b/Tests/RunCMake/PositionIndependentCode/Genex2.cmake
@@ -0,0 +1,9 @@
+
+add_library(genex_pic UNKNOWN IMPORTED)
+# PIC is ON if sibling target is a library, OFF if it is an executable
+set_property(TARGET genex_pic PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE $<NOT:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>>)
+
+
+add_executable(conflict2 main.cpp)
+set_property(TARGET conflict2 PROPERTY POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(conflict2 PRIVATE genex_pic)
diff --git a/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake b/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake
index 6a67e3c..20187d3 100644
--- a/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake
@@ -7,3 +7,5 @@ run_cmake(Conflict4)
 run_cmake(Conflict5)
 run_cmake(Conflict6)
 run_cmake(Debug)
+run_cmake(Genex1)
+run_cmake(Genex2)
-- 
cgit v0.12