From 071f8e78dda152d8759539fa390b25c7f58d3cc5 Mon Sep 17 00:00:00 2001 From: Gregor Jasny Date: Mon, 26 Dec 2016 18:07:24 +0100 Subject: Apple: Add support for static frameworks Closes: #16432 --- Help/command/add_library.rst | 2 +- Help/command/install.rst | 6 ++- Help/prop_tgt/FRAMEWORK.rst | 4 +- Help/release/dev/static-frameworks.rst | 6 +++ Source/cmGeneratorTarget.cxx | 3 +- Source/cmGlobalXCodeGenerator.cxx | 39 ++++++++++++++-- Source/cmInstallCommand.cxx | 42 ++++++++++++----- Source/cmTarget.cxx | 3 +- Tests/Framework/CMakeLists.txt | 47 +++++++++---------- Tests/RunCMake/Framework/FrameworkLayout.cmake | 5 ++- .../Framework/FrameworkTypeSHARED-build-stdout.txt | 1 + .../Framework/FrameworkTypeSTATIC-build-stdout.txt | 1 + Tests/RunCMake/Framework/RunCMakeTest.cmake | 29 ++++++++++-- Tests/RunCMake/XcodeProject/XcodeBundles.cmake | 52 ++++++++++++++++------ 14 files changed, 178 insertions(+), 62 deletions(-) create mode 100644 Help/release/dev/static-frameworks.rst create mode 100644 Tests/RunCMake/Framework/FrameworkTypeSHARED-build-stdout.txt create mode 100644 Tests/RunCMake/Framework/FrameworkTypeSTATIC-build-stdout.txt diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index 36adcbe..af75a39 100644 --- a/Help/command/add_library.rst +++ b/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 diff --git a/Help/command/install.rst b/Help/command/install.rst index d57dd75..70087a4 100644 --- a/Help/command/install.rst +++ b/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 diff --git a/Help/prop_tgt/FRAMEWORK.rst b/Help/prop_tgt/FRAMEWORK.rst index 8120c36..495d30e 100644 --- a/Help/prop_tgt/FRAMEWORK.rst +++ b/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 diff --git a/Help/release/dev/static-frameworks.rst b/Help/release/dev/static-frameworks.rst new file mode 100644 index 0000000..eae80bd --- /dev/null +++ b/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. diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index dcf3764..6ce8140 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/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")); } diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d448315..8627cf2 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/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"; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index fadebb4..4c331c7 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -471,17 +471,39 @@ bool cmInstallCommand::HandleTargetsMode(std::vector 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: { diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 8c62c48..fe3472d 100644 --- a/Source/cmTarget.cxx +++ b/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")); } diff --git a/Tests/Framework/CMakeLists.txt b/Tests/Framework/CMakeLists.txt index 271aaf1..a313c2c 100644 --- a/Tests/Framework/CMakeLists.txt +++ b/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) diff --git a/Tests/RunCMake/Framework/FrameworkLayout.cmake b/Tests/RunCMake/Framework/FrameworkLayout.cmake index 5184755..ae32134 100644 --- a/Tests/RunCMake/Framework/FrameworkLayout.cmake +++ b/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 $) diff --git a/Tests/RunCMake/Framework/FrameworkTypeSHARED-build-stdout.txt b/Tests/RunCMake/Framework/FrameworkTypeSHARED-build-stdout.txt new file mode 100644 index 0000000..8d90f5f --- /dev/null +++ b/Tests/RunCMake/Framework/FrameworkTypeSHARED-build-stdout.txt @@ -0,0 +1 @@ +.*/Framework: Mach-O[^\n]* dynamically linked shared library.* diff --git a/Tests/RunCMake/Framework/FrameworkTypeSTATIC-build-stdout.txt b/Tests/RunCMake/Framework/FrameworkTypeSTATIC-build-stdout.txt new file mode 100644 index 0000000..c9f50b6 --- /dev/null +++ b/Tests/RunCMake/Framework/FrameworkTypeSTATIC-build-stdout.txt @@ -0,0 +1 @@ +.*/Framework: current ar archive random library.* diff --git a/Tests/RunCMake/Framework/RunCMakeTest.cmake b/Tests/RunCMake/Framework/RunCMakeTest.cmake index 1f7d54e..e64892d 100644 --- a/Tests/RunCMake/Framework/RunCMakeTest.cmake +++ b/Tests/RunCMake/Framework/RunCMakeTest.cmake @@ -1,9 +1,10 @@ include(RunCMake) -function(framework_layout_test Name Toolchain) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${Toolchain}FrameworkLayout-build) +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}") @@ -14,6 +15,26 @@ endfunction() # build check cannot cope with multi-configuration generators directory layout if(NOT RunCMake_GENERATOR STREQUAL "Xcode") - framework_layout_test(iOSFrameworkLayout-build ios) - framework_layout_test(OSXFrameworkLayout-build osx) + 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) diff --git a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake index ad4268d..833eb85 100644 --- a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake +++ b/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 - "$" "$.old") + "$" "$.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 - "$" "$.old") + "$" "$.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 + "$" "$.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 + "$" "$.old") + + add_dependencies(StaticFrameworkExtTest StaticFrameworkExt) endif() # Bundle -- cgit v0.12