Browse Source

Merge topic '16432-static-frameworks'

071f8e78 Apple: Add support for static frameworks
d525754e Xcode: Refactor RunCMake.Framework test to prepare for static frameworks
45405f00 Xcode: Ignore Xcode project warning until issue is fixed
50e1c105 Makefile: For static libraries remove only the "real" lib before creating
8643ca75 Makefile: Re-order list of files to clean
Brad King 8 years ago
parent
commit
f012a95836

+ 1 - 1
Help/command/add_library.rst

@@ -33,7 +33,7 @@ type is ``STATIC`` or ``SHARED`` based on whether the current value of the
 variable :variable:`BUILD_SHARED_LIBS` is ``ON``.  For ``SHARED`` and
 ``MODULE`` libraries the :prop_tgt:`POSITION_INDEPENDENT_CODE` target
 property is set to ``ON`` automatically.
-A ``SHARED`` library may be marked with the :prop_tgt:`FRAMEWORK`
+A ``SHARED`` or ``STATIC`` library may be marked with the :prop_tgt:`FRAMEWORK`
 target property to create an OS X Framework.
 
 If a library does not export any symbols, it must not be declared as a

+ 4 - 2
Help/command/install.rst

@@ -90,8 +90,10 @@ project.  There are five kinds of target files that may be installed:
 ``ARCHIVE``, ``LIBRARY``, ``RUNTIME``, ``FRAMEWORK``, and ``BUNDLE``.
 Executables are treated as ``RUNTIME`` targets, except that those
 marked with the ``MACOSX_BUNDLE`` property are treated as ``BUNDLE``
-targets on OS X.  Static libraries are always treated as ``ARCHIVE``
-targets.  Module libraries are always treated as ``LIBRARY`` targets.
+targets on OS X.  Static libraries are treated as ``ARCHIVE`` targets,
+except that those marked with the ``FRAMEWORK`` property are treated
+as ``FRAMEWORK`` targets on OS X.
+Module libraries are always treated as ``LIBRARY`` targets.
 For non-DLL platforms shared libraries are treated as ``LIBRARY``
 targets, except that those marked with the ``FRAMEWORK`` property are
 treated as ``FRAMEWORK`` targets on OS X.  For DLL platforms the DLL

+ 2 - 2
Help/prop_tgt/FRAMEWORK.rst

@@ -1,9 +1,9 @@
 FRAMEWORK
 ---------
 
-Build ``SHARED`` library as Framework Bundle on the OS X and iOS.
+Build ``SHARED`` or ``STATIC`` library as Framework Bundle on the OS X and iOS.
 
-If a ``SHARED`` library target has this property set to ``TRUE`` it will be
+If such a library target has this property set to ``TRUE`` it will be
 built as a framework when built on the OS X and iOS.  It will have the
 directory structure required for a framework and will be suitable to
 be used with the ``-framework`` option

+ 6 - 0
Help/release/dev/static-frameworks.rst

@@ -0,0 +1,6 @@
+static-frameworks
+-----------------
+
+* The :prop_tgt:`FRAMEWORK` property could now also be applied to
+  static libraries on Apple targets.  It will result in a proper
+  Framework but with a static library inside.

+ 2 - 1
Source/cmGeneratorTarget.cxx

@@ -5308,7 +5308,8 @@ bool cmGeneratorTarget::IsLinkable() const
 
 bool cmGeneratorTarget::IsFrameworkOnApple() const
 {
-  return (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+  return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+           this->GetType() == cmStateEnums::STATIC_LIBRARY) &&
           this->Makefile->IsOn("APPLE") &&
           this->GetPropertyAsBool("FRAMEWORK"));
 }

+ 35 - 4
Source/cmGlobalXCodeGenerator.cxx

@@ -1816,8 +1816,34 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
 
   // Handle settings for each target type.
   switch (gtgt->GetType()) {
-    case cmStateEnums::OBJECT_LIBRARY:
-    case cmStateEnums::STATIC_LIBRARY: {
+    case cmStateEnums::STATIC_LIBRARY:
+      if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
+        std::string fw_version = gtgt->GetFrameworkVersion();
+        buildSettings->AddAttribute("FRAMEWORK_VERSION",
+                                    this->CreateString(fw_version));
+        const char* ext = gtgt->GetProperty("BUNDLE_EXTENSION");
+        if (ext) {
+          buildSettings->AddAttribute("WRAPPER_EXTENSION",
+                                      this->CreateString(ext));
+        }
+
+        std::string plist = this->ComputeInfoPListLocation(gtgt);
+        // Xcode will create the final version of Info.plist at build time,
+        // so let it replace the framework name. This avoids creating
+        // a per-configuration Info.plist file.
+        this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
+          gtgt, "$(EXECUTABLE_NAME)", plist.c_str());
+        buildSettings->AddAttribute("INFOPLIST_FILE",
+                                    this->CreateString(plist));
+        buildSettings->AddAttribute("MACH_O_TYPE",
+                                    this->CreateString("staticlib"));
+      } else {
+        buildSettings->AddAttribute("LIBRARY_STYLE",
+                                    this->CreateString("STATIC"));
+      }
+      break;
+
+    case cmStateEnums::OBJECT_LIBRARY: {
       buildSettings->AddAttribute("LIBRARY_STYLE",
                                   this->CreateString("STATIC"));
       break;
@@ -2336,8 +2362,10 @@ const char* cmGlobalXCodeGenerator::GetTargetFileType(
 
   switch (target->GetType()) {
     case cmStateEnums::OBJECT_LIBRARY:
-    case cmStateEnums::STATIC_LIBRARY:
       return "archive.ar";
+    case cmStateEnums::STATIC_LIBRARY:
+      return (target->GetPropertyAsBool("FRAMEWORK") ? "wrapper.framework"
+                                                     : "archive.ar");
     case cmStateEnums::MODULE_LIBRARY:
       if (target->IsXCTestOnApple())
         return "wrapper.cfbundle";
@@ -2367,8 +2395,11 @@ const char* cmGlobalXCodeGenerator::GetTargetProductType(
 
   switch (target->GetType()) {
     case cmStateEnums::OBJECT_LIBRARY:
-    case cmStateEnums::STATIC_LIBRARY:
       return "com.apple.product-type.library.static";
+    case cmStateEnums::STATIC_LIBRARY:
+      return (target->GetPropertyAsBool("FRAMEWORK")
+                ? "com.apple.product-type.framework"
+                : "com.apple.product-type.library.static");
     case cmStateEnums::MODULE_LIBRARY:
       if (target->IsXCTestOnApple())
         return "com.apple.product-type.bundle.unit-test";

+ 32 - 10
Source/cmInstallCommand.cxx

@@ -471,17 +471,39 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
         }
       } break;
       case cmStateEnums::STATIC_LIBRARY: {
-        // Static libraries use ARCHIVE properties.
-        if (!archiveArgs.GetDestination().empty()) {
-          archiveGenerator =
-            CreateInstallTargetGenerator(target, archiveArgs, false);
+        // If it is marked with FRAMEWORK property use the FRAMEWORK set of
+        // INSTALL properties. Otherwise, use the LIBRARY properties.
+        if (target.IsFrameworkOnApple()) {
+          // When in namelink only mode skip frameworks.
+          if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
+            continue;
+          }
+
+          // Use the FRAMEWORK properties.
+          if (!frameworkArgs.GetDestination().empty()) {
+            frameworkGenerator =
+              CreateInstallTargetGenerator(target, frameworkArgs, false);
+          } else {
+            std::ostringstream e;
+            e << "TARGETS given no FRAMEWORK DESTINATION for static library "
+                 "FRAMEWORK target \""
+              << target.GetName() << "\".";
+            this->SetError(e.str());
+            return false;
+          }
         } else {
-          std::ostringstream e;
-          e << "TARGETS given no ARCHIVE DESTINATION for static library "
-               "target \""
-            << target.GetName() << "\".";
-          this->SetError(e.str());
-          return false;
+          // Static libraries use ARCHIVE properties.
+          if (!archiveArgs.GetDestination().empty()) {
+            archiveGenerator =
+              CreateInstallTargetGenerator(target, archiveArgs, false);
+          } else {
+            std::ostringstream e;
+            e << "TARGETS given no ARCHIVE DESTINATION for static library "
+                 "target \""
+              << target.GetName() << "\".";
+            this->SetError(e.str());
+            return false;
+          }
         }
       } break;
       case cmStateEnums::MODULE_LIBRARY: {

+ 18 - 17
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -597,12 +597,26 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   // Clean files associated with this library.
   std::vector<std::string> libCleanFiles;
   libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
-    this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath));
-  if (targetNameReal != targetName) {
+    this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal));
+
+  std::vector<std::string> commands1;
+  // Add a command to remove any existing files for this library.
+  // for static libs only
+  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
+    this->LocalGenerator->AppendCleanCommand(commands1, libCleanFiles,
+                                             this->GeneratorTarget, "target");
+    this->LocalGenerator->CreateCDCommand(
+      commands1, this->Makefile->GetCurrentBinaryDirectory(),
+      this->LocalGenerator->GetBinaryDirectory());
+    commands.insert(commands.end(), commands1.begin(), commands1.end());
+    commands1.clear();
+  }
+
+  if (targetName != targetNameReal) {
     libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
-      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal));
+      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath));
   }
-  if (targetNameSO != targetName && targetNameSO != targetNameReal) {
+  if (targetNameSO != targetNameReal && targetNameSO != targetName) {
     libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
       this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathSO));
   }
@@ -634,19 +648,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   }
 #endif
 
-  std::vector<std::string> commands1;
-  // Add a command to remove any existing files for this library.
-  // for static libs only
-  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
-    this->LocalGenerator->AppendCleanCommand(commands1, libCleanFiles,
-                                             this->GeneratorTarget, "target");
-    this->LocalGenerator->CreateCDCommand(
-      commands1, this->Makefile->GetCurrentBinaryDirectory(),
-      this->LocalGenerator->GetBinaryDirectory());
-    commands.insert(commands.end(), commands1.begin(), commands1.end());
-    commands1.clear();
-  }
-
   // Add the pre-build and pre-link rules building but not when relinking.
   if (!relink) {
     this->LocalGenerator->AppendCustomCommands(

+ 2 - 1
Source/cmTarget.cxx

@@ -451,7 +451,8 @@ bool cmTarget::HasImportLibrary() const
 
 bool cmTarget::IsFrameworkOnApple() const
 {
-  return (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+  return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+           this->GetType() == cmStateEnums::STATIC_LIBRARY) &&
           this->Makefile->IsOn("APPLE") &&
           this->GetPropertyAsBool("FRAMEWORK"));
 }

+ 24 - 23
Tests/Framework/CMakeLists.txt

@@ -57,29 +57,30 @@ add_custom_target(fooCustom ALL COMMAND ${CMAKE_COMMAND} -E copy foo-post-build
 add_dependencies(fooCustom foo)
 
 # Make a static library and apply the framework properties to it to verify
-# that everything still builds correctly, but it will not actually produce
-# a framework... The framework properties only apply when the library type
-# is SHARED.
+# that everything still builds correctly. Xcode prior to version 5 does not
+# support static Frameworks.
 #
-add_library(fooStatic STATIC
-  foo.cxx
-  foo.h
-  foo2.h
-  fooExtensionlessResource
-  fooPublic.h
-  fooPublicExtensionlessHeader
-  fooPrivate.h
-  fooPrivateExtensionlessHeader
-  fooNeither.h
-  fooBoth.h
-  test.lua
-  fooDeepPublic.h
-)
-set_target_properties(fooStatic PROPERTIES
-  FRAMEWORK TRUE
-  FRAMEWORK_VERSION none
-)
-add_executable(barStatic bar.cxx)
-target_link_libraries(barStatic fooStatic)
+if(NOT XCODE OR NOT XCODE_VERSION VERSION_LESS 5)
+  add_library(fooStatic STATIC
+    foo.cxx
+    foo.h
+    foo2.h
+    fooExtensionlessResource
+    fooPublic.h
+    fooPublicExtensionlessHeader
+    fooPrivate.h
+    fooPrivateExtensionlessHeader
+    fooNeither.h
+    fooBoth.h
+    test.lua
+    fooDeepPublic.h
+  )
+  set_target_properties(fooStatic PROPERTIES
+    FRAMEWORK TRUE
+    FRAMEWORK_VERSION none
+  )
+  add_executable(barStatic bar.cxx)
+  target_link_libraries(barStatic fooStatic)
+endif()
 
 include(CPack)

+ 1 - 2
Tests/RunCMake/CMakeLists.txt

@@ -276,8 +276,7 @@ if(XCODE_VERSION AND NOT "${XCODE_VERSION}" VERSION_LESS 3)
   add_RunCMake_test(XcodeProject -DXCODE_VERSION=${XCODE_VERSION})
 endif()
 
-if(NOT XCODE
-    AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
     AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0)
   add_RunCMake_test(Framework)
 endif()

+ 4 - 1
Tests/RunCMake/Framework/FrameworkLayout.cmake

@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.4)
 enable_language(C)
 
-add_library(Framework SHARED
+add_library(Framework ${FRAMEWORK_TYPE}
             foo.c
             foo.h
             res.txt)
@@ -9,3 +9,6 @@ set_target_properties(Framework PROPERTIES
                       FRAMEWORK TRUE
                       PUBLIC_HEADER foo.h
                       RESOURCE "res.txt")
+
+add_custom_command(TARGET Framework POST_BUILD
+                   COMMAND /usr/bin/file $<TARGET_FILE:Framework>)

+ 1 - 0
Tests/RunCMake/Framework/FrameworkTypeSHARED-build-stdout.txt

@@ -0,0 +1 @@
+.*/Framework: Mach-O[^\n]* dynamically linked shared library.*

+ 1 - 0
Tests/RunCMake/Framework/FrameworkTypeSTATIC-build-stdout.txt

@@ -0,0 +1 @@
+.*/Framework: current ar archive random library.*

+ 38 - 31
Tests/RunCMake/Framework/RunCMakeTest.cmake

@@ -1,33 +1,40 @@
 include(RunCMake)
 
-# iOS
-
-set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/iOSFrameworkLayout-build)
-set(RunCMake_TEST_NO_CLEAN 1)
-set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/ios.cmake")
-
-file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
-file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-
-run_cmake(FrameworkLayout)
-run_cmake_command(iOSFrameworkLayout-build ${CMAKE_COMMAND} --build .)
-
-unset(RunCMake_TEST_BINARY_DIR)
-unset(RunCMake_TEST_NO_CLEAN)
-unset(RunCMake_TEST_OPTIONS)
-
-# OSX
-
-set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/OSXFrameworkLayout-build)
-set(RunCMake_TEST_NO_CLEAN 1)
-set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/osx.cmake")
-
-file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
-file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-
-run_cmake(FrameworkLayout)
-run_cmake_command(OSXFrameworkLayout-build ${CMAKE_COMMAND} --build .)
-
-unset(RunCMake_TEST_BINARY_DIR)
-unset(RunCMake_TEST_NO_CLEAN)
-unset(RunCMake_TEST_OPTIONS)
+function(framework_layout_test Name Toolchain Type)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${Toolchain}${Type}FrameworkLayout-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/${Toolchain}.cmake")
+  list(APPEND RunCMake_TEST_OPTIONS "-DFRAMEWORK_TYPE=${Type}")
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(FrameworkLayout)
+  run_cmake_command(${Name} ${CMAKE_COMMAND} --build .)
+endfunction()
+
+# build check cannot cope with multi-configuration generators directory layout
+if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
+  framework_layout_test(iOSFrameworkLayout-build ios SHARED)
+  framework_layout_test(iOSFrameworkLayout-build ios STATIC)
+  framework_layout_test(OSXFrameworkLayout-build osx SHARED)
+  framework_layout_test(OSXFrameworkLayout-build osx STATIC)
+endif()
+
+function(framework_type_test Toolchain Type)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${Toolchain}${Type}FrameworkType-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/${Toolchain}.cmake")
+  list(APPEND RunCMake_TEST_OPTIONS "-DFRAMEWORK_TYPE=${Type}")
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(FrameworkLayout)
+  run_cmake_command(FrameworkType${Type}-build ${CMAKE_COMMAND} --build .)
+endfunction()
+
+framework_type_test(ios SHARED)
+framework_type_test(ios STATIC)
+framework_type_test(osx SHARED)
+framework_type_test(osx STATIC)

+ 1 - 1
Tests/RunCMake/RunCMake.cmake

@@ -101,7 +101,7 @@ function(run_cmake test)
   endif()
   foreach(o out err)
     string(REGEX REPLACE "\r\n" "\n" actual_std${o} "${actual_std${o}}")
-    string(REGEX REPLACE "(^|\n)((==[0-9]+==|BullseyeCoverage|[a-z]+\\([0-9]+\\) malloc:|Error kstat returned|[^\n]*from Time Machine by path|[^\n]*Bullseye Testing Technology)[^\n]*\n)+" "\\1" actual_std${o} "${actual_std${o}}")
+    string(REGEX REPLACE "(^|\n)((==[0-9]+==|BullseyeCoverage|[a-z]+\\([0-9]+\\) malloc:|Error kstat returned|[^\n]*is a member of multiple groups|[^\n]*from Time Machine by path|[^\n]*Bullseye Testing Technology)[^\n]*\n)+" "\\1" actual_std${o} "${actual_std${o}}")
     string(REGEX REPLACE "\n+$" "" actual_std${o} "${actual_std${o}}")
     set(expect_${o} "")
     if(DEFINED expect_std${o})

+ 39 - 13
Tests/RunCMake/XcodeProject/XcodeBundles.cmake

@@ -50,30 +50,56 @@ if (NOT TEST_IOS AND NOT TEST_WATCHOS AND NOT TEST_TVOS)
   add_dependencies(AppBundleExtTest AppBundleExt)
 endif()
 
-# Framework (not supported for iOS on Xcode < 6)
+# Shared Framework (not supported for iOS on Xcode < 6)
 
 if(NOT TEST_IOS OR NOT XCODE_VERSION VERSION_LESS 6)
-  add_library(Framework SHARED main.c)
-  set_target_properties(Framework PROPERTIES FRAMEWORK TRUE)
+  add_library(SharedFramework SHARED main.c)
+  set_target_properties(SharedFramework PROPERTIES FRAMEWORK TRUE)
 
-  add_custom_target(FrameworkTest ALL
+  add_custom_target(SharedFrameworkTest ALL
     COMMAND ${CMAKE_COMMAND} -E copy
-      "$<TARGET_FILE:Framework>" "$<TARGET_FILE:Framework>.old")
+      "$<TARGET_FILE:SharedFramework>" "$<TARGET_FILE:SharedFramework>.old")
 
-  add_dependencies(FrameworkTest Framework)
+  add_dependencies(SharedFrameworkTest SharedFramework)
 
   # with custom extension
 
-  add_library(FrameworkExt SHARED main.c)
-  set_target_properties(FrameworkExt PROPERTIES FRAMEWORK TRUE)
-  set_target_properties(FrameworkExt PROPERTIES BUNDLE_EXTENSION "foo")
-  install(TARGETS FrameworkExt FRAMEWORK DESTINATION FooExtension)
+  add_library(SharedFrameworkExt SHARED main.c)
+  set_target_properties(SharedFrameworkExt PROPERTIES FRAMEWORK TRUE)
+  set_target_properties(SharedFrameworkExt PROPERTIES BUNDLE_EXTENSION "foo")
+  install(TARGETS SharedFrameworkExt FRAMEWORK DESTINATION FooExtension)
 
-  add_custom_target(FrameworkExtTest ALL
+  add_custom_target(SharedFrameworkExtTest ALL
     COMMAND ${CMAKE_COMMAND} -E copy
-      "$<TARGET_FILE:FrameworkExt>" "$<TARGET_FILE:FrameworkExt>.old")
+      "$<TARGET_FILE:SharedFrameworkExt>" "$<TARGET_FILE:SharedFrameworkExt>.old")
 
-  add_dependencies(FrameworkExtTest FrameworkExt)
+  add_dependencies(SharedFrameworkExtTest SharedFrameworkExt)
+endif()
+
+# Static Framework (not supported for Xcode < 6)
+
+if(NOT XCODE_VERSION VERSION_LESS 6)
+  add_library(StaticFramework STATIC main.c)
+  set_target_properties(StaticFramework PROPERTIES FRAMEWORK TRUE)
+
+  add_custom_target(StaticFrameworkTest ALL
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "$<TARGET_FILE:StaticFramework>" "$<TARGET_FILE:StaticFramework>.old")
+
+  add_dependencies(StaticFrameworkTest StaticFramework)
+
+  # with custom extension
+
+  add_library(StaticFrameworkExt STATIC main.c)
+  set_target_properties(StaticFrameworkExt PROPERTIES FRAMEWORK TRUE)
+  set_target_properties(StaticFrameworkExt PROPERTIES BUNDLE_EXTENSION "foo")
+  install(TARGETS StaticFrameworkExt FRAMEWORK DESTINATION StaticFooExtension)
+
+  add_custom_target(StaticFrameworkExtTest ALL
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "$<TARGET_FILE:StaticFrameworkExt>" "$<TARGET_FILE:StaticFrameworkExt>.old")
+
+  add_dependencies(StaticFrameworkExtTest StaticFrameworkExt)
 endif()
 
 # Bundle