From bf98cc252f18e761ed9a57d2f7a9304bfbb621de Mon Sep 17 00:00:00 2001
From: Stephen Kelly <steveire@gmail.com>
Date: Wed, 26 Feb 2014 15:59:18 +0100
Subject: Genex: Evaluate TARGET_OBJECTS as a normal expression.

---
 Help/manual/cmake-generator-expressions.7.rst      |  3 +
 Help/release/dev/file-GENERATE-TARGET_OBJECTS.rst  |  6 ++
 Source/cmGeneratorExpressionEvaluator.cxx          | 64 ++++++++++++++++++++++
 Source/cmSourceFile.cxx                            | 12 ++++
 Source/cmSourceFile.h                              |  4 ++
 Tests/GeneratorExpression/CMakeLists.txt           | 14 +++++
 Tests/GeneratorExpression/check_object_files.cmake | 48 ++++++++++++++++
 Tests/GeneratorExpression/objlib1.c                |  5 ++
 Tests/GeneratorExpression/objlib2.c                |  5 ++
 Tests/RunCMake/CMakeLists.txt                      |  1 +
 Tests/RunCMake/TargetObjects/CMakeLists.txt        |  3 +
 Tests/RunCMake/TargetObjects/NoTarget-result.txt   |  1 +
 Tests/RunCMake/TargetObjects/NoTarget-stderr.txt   |  8 +++
 Tests/RunCMake/TargetObjects/NoTarget.cmake        |  2 +
 .../TargetObjects/NotObjlibTarget-result.txt       |  1 +
 .../TargetObjects/NotObjlibTarget-stderr.txt       |  8 +++
 Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake |  4 ++
 Tests/RunCMake/TargetObjects/RunCMakeTest.cmake    |  4 ++
 Tests/RunCMake/TargetObjects/empty.cpp             |  7 +++
 19 files changed, 200 insertions(+)
 create mode 100644 Help/release/dev/file-GENERATE-TARGET_OBJECTS.rst
 create mode 100644 Tests/GeneratorExpression/check_object_files.cmake
 create mode 100644 Tests/GeneratorExpression/objlib1.c
 create mode 100644 Tests/GeneratorExpression/objlib2.c
 create mode 100644 Tests/RunCMake/TargetObjects/CMakeLists.txt
 create mode 100644 Tests/RunCMake/TargetObjects/NoTarget-result.txt
 create mode 100644 Tests/RunCMake/TargetObjects/NoTarget-stderr.txt
 create mode 100644 Tests/RunCMake/TargetObjects/NoTarget.cmake
 create mode 100644 Tests/RunCMake/TargetObjects/NotObjlibTarget-result.txt
 create mode 100644 Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt
 create mode 100644 Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake
 create mode 100644 Tests/RunCMake/TargetObjects/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/TargetObjects/empty.cpp

diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index ac8c3f8..17263d4 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -188,3 +188,6 @@ property is non-empty::
   Content of ``...`` converted to upper case.
 ``$<MAKE_C_IDENTIFIER:...>``
   Content of ``...`` converted to a C identifier.
+``$<TARGET_OBJECTS:objLib>``
+  List of objects resulting from build of ``objLib``. ``objLib`` must be an
+  object of type ``OBJECT_LIBRARY``.
diff --git a/Help/release/dev/file-GENERATE-TARGET_OBJECTS.rst b/Help/release/dev/file-GENERATE-TARGET_OBJECTS.rst
new file mode 100644
index 0000000..853a803
--- /dev/null
+++ b/Help/release/dev/file-GENERATE-TARGET_OBJECTS.rst
@@ -0,0 +1,6 @@
+file-GENERATE-TARGET_OBJECTS
+----------------------------
+
+* The :command:`file(GENERATE)` subcommand learned to evaluate the
+  ``TARGET_OBJECTS``
+  :manual:`generator expression <cmake-generator-expressions(7)>`.
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index 14b2a1a..669694c 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -15,6 +15,8 @@
 #include "cmGeneratorExpressionParser.h"
 #include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorExpression.h"
+#include "cmLocalGenerator.h"
+#include "cmSourceFile.h"
 
 #include <cmsys/String.h>
 
@@ -1240,6 +1242,67 @@ static const struct TargetNameNode : public cmGeneratorExpressionNode
 } targetNameNode;
 
 //----------------------------------------------------------------------------
+static const struct TargetObjectsNode : public cmGeneratorExpressionNode
+{
+  TargetObjectsNode() {}
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content,
+                       cmGeneratorExpressionDAGChecker *) const
+  {
+    std::string tgtName = parameters.front();
+    cmGeneratorTarget* gt =
+                context->Makefile->FindGeneratorTargetToUse(tgtName.c_str());
+    if (!gt)
+      {
+      cmOStringStream e;
+      e << "Objects of target \"" << tgtName
+        << "\" referenced but no such target exists.";
+      reportError(context, content->GetOriginalExpression(), e.str());
+      return std::string();
+      }
+    if (gt->GetType() != cmTarget::OBJECT_LIBRARY)
+      {
+      cmOStringStream e;
+      e << "Objects of target \"" << tgtName
+        << "\" referenced but is not an OBJECT library.";
+      reportError(context, content->GetOriginalExpression(), e.str());
+      return std::string();
+      }
+
+    std::vector<cmSourceFile const*> objectSources;
+    gt->GetObjectSources(objectSources);
+    std::map<cmSourceFile const*, std::string> mapping;
+
+    for(std::vector<cmSourceFile const*>::const_iterator it
+        = objectSources.begin(); it != objectSources.end(); ++it)
+      {
+      mapping[*it];
+      }
+
+    gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
+
+    std::string obj_dir = gt->ObjectDirectory;
+    std::string result;
+    const char* sep = "";
+    for(std::map<cmSourceFile const*, std::string>::const_iterator it
+        = mapping.begin(); it != mapping.end(); ++it)
+      {
+      assert(!it->second.empty());
+      result += sep;
+      std::string objFile = obj_dir + it->second;
+      cmSourceFile* sf = context->Makefile->GetOrCreateSource(objFile, true);
+      sf->SetObjectLibrary(tgtName);
+      sf->SetProperty("EXTERNAL_OBJECT", "1");
+      result += objFile;
+      sep = ";";
+      }
+    return result;
+  }
+} targetObjectsNode;
+
+//----------------------------------------------------------------------------
 static const char* targetPolicyWhitelist[] = {
   0
 #define TARGET_POLICY_STRING(POLICY) \
@@ -1593,6 +1656,7 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
     nodeMap["SEMICOLON"] = &semicolonNode;
     nodeMap["TARGET_PROPERTY"] = &targetPropertyNode;
     nodeMap["TARGET_NAME"] = &targetNameNode;
+    nodeMap["TARGET_OBJECTS"] = &targetObjectsNode;
     nodeMap["TARGET_POLICY"] = &targetPolicyNode;
     nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode;
     nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode;
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index b9b6251..0ab30a2 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -39,6 +39,18 @@ std::string const& cmSourceFile::GetExtension() const
 }
 
 //----------------------------------------------------------------------------
+void cmSourceFile::SetObjectLibrary(std::string const& objlib)
+{
+  this->ObjectLibrary = objlib;
+}
+
+//----------------------------------------------------------------------------
+std::string cmSourceFile::GetObjectLibrary() const
+{
+  return this->ObjectLibrary;
+}
+
+//----------------------------------------------------------------------------
 std::string cmSourceFile::GetLanguage()
 {
   // If the language was set explicitly by the user then use it.
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index 17c96ac..755a2cf 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -97,6 +97,9 @@ public:
    */
   bool Matches(cmSourceFileLocation const&);
 
+  void SetObjectLibrary(std::string const& objlib);
+  std::string GetObjectLibrary() const;
+
 private:
   cmSourceFileLocation Location;
   cmPropertyMap Properties;
@@ -105,6 +108,7 @@ private:
   std::string Language;
   std::string FullPath;
   bool FindFullPathFailed;
+  std::string ObjectLibrary;
 
   bool FindFullPath(std::string* error);
   bool TryFullPath(const std::string& path, const std::string& ext);
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt
index a0e34ef..66b175a 100644
--- a/Tests/GeneratorExpression/CMakeLists.txt
+++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -258,3 +258,17 @@ set(CMP0044_TYPE NEW)
 add_subdirectory(CMP0044 ${CMAKE_BINARY_DIR}/CMP0044-NEW)
 set(CMP0044_TYPE OLD)
 add_subdirectory(CMP0044 ${CMAKE_BINARY_DIR}/CMP0044-OLD)
+
+add_library(objlib OBJECT objlib1.c objlib2.c)
+file(GENERATE
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/objlib_files"
+  CONTENT "$<JOIN:$<TARGET_OBJECTS:objlib>,\n>\n"
+)
+add_custom_target(check_object_files ALL
+  COMMAND ${CMAKE_COMMAND}
+    "-DOBJLIB_LISTFILE=${CMAKE_CURRENT_BINARY_DIR}/objlib_files"
+    -DTEST_CONFIGURATION=${CMAKE_BUILD_TYPE}
+    -DEXPECTED_NUM_OBJECTFILES=2
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake"
+  DEPENDS objlib
+)
diff --git a/Tests/GeneratorExpression/check_object_files.cmake b/Tests/GeneratorExpression/check_object_files.cmake
new file mode 100644
index 0000000..889fe80
--- /dev/null
+++ b/Tests/GeneratorExpression/check_object_files.cmake
@@ -0,0 +1,48 @@
+
+if (NOT EXISTS ${OBJLIB_LISTFILE})
+  message(SEND_ERROR "Object listing file \"${OBJLIB_LISTFILE}\" not found!")
+endif()
+
+file(STRINGS ${OBJLIB_LISTFILE} objlib_files)
+
+list(LENGTH objlib_files num_objectfiles)
+if (NOT EXPECTED_NUM_OBJECTFILES EQUAL num_objectfiles)
+  message(SEND_ERROR "Unexpected number of entries in object list file (${num_objectfiles} instead of ${EXPECTED_NUM_OBJECTFILES})")
+endif()
+
+foreach(objlib_file ${objlib_files})
+  set(file_exists False)
+  if (EXISTS ${objlib_file})
+    set(file_exists True)
+  endif()
+
+  if (NOT file_exists)
+    if (objlib_file MATCHES ".(CURRENT_ARCH)")
+      string(REPLACE "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" "*" config_file "${objlib_file}")
+      string(REPLACE "$(PROJECT_NAME)" "GeneratorExpression" config_file "${config_file}")
+      string(REPLACE "$(CURRENT_ARCH)" "*" config_file "${config_file}")
+      file(GLOB_RECURSE files "${config_file}")
+      list(LENGTH files num_files)
+      if (NOT files)
+        message(SEND_ERROR "Got no files for expression ${config_file}")
+      endif()
+      set(file_exists True)
+    else()
+      foreach(config_macro "$(Configuration)" "$(OutDir)" "$(IntDir)")
+        string(REPLACE "${config_macro}" "${TEST_CONFIGURATION}" config_file "${objlib_file}")
+        list(APPEND attempts ${config_file})
+        if (EXISTS ${config_file})
+          set(file_exists True)
+        endif()
+      endforeach()
+    endif()
+  endif()
+
+  if (NOT file_exists)
+    if(attempts)
+      list(REMOVE_DUPLICATES attempts)
+      set(tried "  Tried ${attempts}")
+    endif()
+    message(SEND_ERROR "File \"${objlib_file}\" does not exist!${tried}")
+  endif()
+endforeach()
diff --git a/Tests/GeneratorExpression/objlib1.c b/Tests/GeneratorExpression/objlib1.c
new file mode 100644
index 0000000..aa8de0a
--- /dev/null
+++ b/Tests/GeneratorExpression/objlib1.c
@@ -0,0 +1,5 @@
+
+void objlib1()
+{
+
+}
diff --git a/Tests/GeneratorExpression/objlib2.c b/Tests/GeneratorExpression/objlib2.c
new file mode 100644
index 0000000..3c7307a
--- /dev/null
+++ b/Tests/GeneratorExpression/objlib2.c
@@ -0,0 +1,5 @@
+
+void objlib2()
+{
+
+}
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 7691f32..31e7805 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -49,6 +49,7 @@ add_RunCMake_test(GeneratorToolset)
 add_RunCMake_test(TargetPropertyGeneratorExpressions)
 add_RunCMake_test(Languages)
 add_RunCMake_test(ObjectLibrary)
+add_RunCMake_test(TargetObjects)
 add_RunCMake_test(find_dependency)
 if(NOT WIN32)
   add_RunCMake_test(PositionIndependentCode)
diff --git a/Tests/RunCMake/TargetObjects/CMakeLists.txt b/Tests/RunCMake/TargetObjects/CMakeLists.txt
new file mode 100644
index 0000000..be9d403
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.4)
+project(${RunCMake_TEST})
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/TargetObjects/NoTarget-result.txt b/Tests/RunCMake/TargetObjects/NoTarget-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NoTarget-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/TargetObjects/NoTarget-stderr.txt b/Tests/RunCMake/TargetObjects/NoTarget-stderr.txt
new file mode 100644
index 0000000..2c4f877
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NoTarget-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at NoTarget.cmake:2 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_OBJECTS:NoTarget>
+
+  Objects of target "NoTarget" referenced but no such target exists.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/TargetObjects/NoTarget.cmake b/Tests/RunCMake/TargetObjects/NoTarget.cmake
new file mode 100644
index 0000000..f203c23
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NoTarget.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT test_output CONTENT $<TARGET_OBJECTS:NoTarget>)
diff --git a/Tests/RunCMake/TargetObjects/NotObjlibTarget-result.txt b/Tests/RunCMake/TargetObjects/NotObjlibTarget-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NotObjlibTarget-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt b/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt
new file mode 100644
index 0000000..bb83934
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at NotObjlibTarget.cmake:4 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_OBJECTS:StaticLib>
+
+  Objects of target "StaticLib" referenced but is not an OBJECT library.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake b/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake
new file mode 100644
index 0000000..c7f8a71
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake
@@ -0,0 +1,4 @@
+
+add_library(StaticLib empty.cpp)
+
+file(GENERATE OUTPUT test_output CONTENT $<TARGET_OBJECTS:StaticLib>)
diff --git a/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake b/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake
new file mode 100644
index 0000000..30b9fee
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake
@@ -0,0 +1,4 @@
+include(RunCMake)
+
+run_cmake(NoTarget)
+run_cmake(NotObjlibTarget)
diff --git a/Tests/RunCMake/TargetObjects/empty.cpp b/Tests/RunCMake/TargetObjects/empty.cpp
new file mode 100644
index 0000000..bfbbdde
--- /dev/null
+++ b/Tests/RunCMake/TargetObjects/empty.cpp
@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int empty()
+{
+  return 0;
+}
-- 
cgit v0.12