Commit eb5e33ba authored by Alexander Akhundzhanov's avatar Alexander Akhundzhanov Committed by Craig Scott
Browse files

Xcode: Add support for embedding app extensions


Co-Authored-By: Craig Scott's avatarCraig Scott <craig.scott@crascit.com>
parent f62a2bf4
......@@ -402,7 +402,9 @@ Properties on Targets
/prop_tgt/XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY
/prop_tgt/XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY
/prop_tgt/XCODE_EMBED_type
/prop_tgt/XCODE_EMBED_type_CODE_SIGN_ON_COPY
/prop_tgt/XCODE_EMBED_type_PATH
/prop_tgt/XCODE_EMBED_type_REMOVE_HEADERS_ON_COPY
/prop_tgt/XCODE_EXPLICIT_FILE_TYPE
/prop_tgt/XCODE_GENERATE_SCHEME
/prop_tgt/XCODE_LINK_BUILD_PHASE_MODE
......
......@@ -6,3 +6,8 @@ XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY
Tell the :generator:`Xcode` generator to perform code signing for all the
frameworks and libraries that are embedded using the
:prop_tgt:`XCODE_EMBED_FRAMEWORKS <XCODE_EMBED_<type>>` property.
.. versionadded:: 3.21
This property was generalized to other types of embedded items. See
:prop_tgt:`XCODE_EMBED_<type>_CODE_SIGN_ON_COPY` for the more general form.
......@@ -6,3 +6,9 @@ XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY
Tell the :generator:`Xcode` generator to remove headers from all the
frameworks that are embedded using the
:prop_tgt:`XCODE_EMBED_FRAMEWORKS <XCODE_EMBED_<type>>` property.
.. versionadded:: 3.21
This property was generalized to other types of embedded items. See
:prop_tgt:`XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY` for the more
general form.
......@@ -5,10 +5,20 @@ XCODE_EMBED_<type>
Tell the :generator:`Xcode` generator to embed the specified list of items into
the target bundle. ``<type>`` specifies the embed build phase to use.
See the Xcode documentation for the base location of each ``<type>``.
The supported values for ``<type>`` are:
``FRAMEWORKS``
The specified items will be added to the ``Embed Frameworks`` build phase.
The items can be CMake target names or paths to frameworks or libraries.
``APP_EXTENSIONS``
.. versionadded:: 3.21
The specified items will be added to the ``Embed App Extensions`` build phase.
They must be CMake target names.
Currently, the only supported value for ``<type>`` is ``FRAMEWORKS``.
The specified items will be added to the ``Embed Frameworks`` build phase.
The items can be CMake target names or paths to frameworks or libraries.
See also :prop_tgt:`XCODE_EMBED_<type>_PATH`,
:prop_tgt:`XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY` and
:prop_tgt:`XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY`.
:prop_tgt:`XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY` and
:prop_tgt:`XCODE_EMBED_<type>_CODE_SIGN_ON_COPY`.
XCODE_EMBED_<type>_CODE_SIGN_ON_COPY
------------------------------------
.. versionadded:: 3.20
Boolean property used only by the :generator:`Xcode` generator. It specifies
whether to perform code signing for the items that are embedded using the
:prop_tgt:`XCODE_EMBED_<type>` property.
The supported values for ``<type>`` are:
``FRAMEWORKS``
``APP_EXTENSIONS``
.. versionadded:: 3.21
If a ``XCODE_EMBED_<type>_CODE_SIGN_ON_COPY`` property is not defined on the
target, no code signing on copy will be performed for that ``<type>``.
......@@ -3,7 +3,16 @@ XCODE_EMBED_<type>_PATH
.. versionadded:: 3.20
Tell the :generator:`Xcode` generator the relative path to use when embedding
the items specified by :prop_tgt:`XCODE_EMBED_<type>`. The path is relative
This property is used only by the :generator:`Xcode` generator. When defined,
it specifies the relative path to use when embedding the items specified by
:prop_tgt:`XCODE_EMBED_<type>`. The path is relative
to the base location of the ``Embed XXX`` build phase associated with
``<type>``. See the Xcode documentation for the base location of each
``<type>``.
The supported values for ``<type>`` are:
``FRAMEWORKS``
``APP_EXTENSIONS``
.. versionadded:: 3.21
XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY
-----------------------------------------
.. versionadded:: 3.20
Boolean property used only by the :generator:`Xcode` generator. It specifies
whether to remove headers from all the frameworks that are embedded using the
:prop_tgt:`XCODE_EMBED_<type>` property.
The supported values for ``<type>`` are:
``FRAMEWORKS``
If the ``XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY`` property is not
defined, headers will not be removed on copy by default.
``APP_EXTENSIONS``
.. versionadded:: 3.21
If the ``XCODE_EMBED_APP_EXTENSIONS_REMOVE_HEADERS_ON_COPY`` property is not
defined, headers WILL be removed on copy by default.
xcode_app_extensions
--------------------
* The :prop_tgt:`XCODE_EMBED_APP_EXTENSIONS <XCODE_EMBED_<type>>` target property
was added to tell the :generator:`Xcode` generator to embed app extensions
such as iMessage sticker packs.
Aspects of the embedding can be customized with the
:prop_tgt:`XCODE_EMBED_APP_EXTENSIONS_PATH <XCODE_EMBED_<type>>`,
:prop_tgt:`XCODE_EMBED_APP_EXTENSIONS_CODE_SIGN_ON_COPY <XCODE_EMBED_<type>_CODE_SIGN_ON_COPY>` and
:prop_tgt:`XCODE_EMBED_APP_EXTENSIONS_REMOVE_HEADERS_ON_COPY <XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY>`
properties.
......@@ -3728,7 +3728,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
}
}
void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
void cmGlobalXCodeGenerator::AddEmbeddedObjects(
cmXCodeObject* target, const std::string& copyFilesBuildPhaseName,
const std::string& embedPropertyName, const std::string& dstSubfolderSpec,
int actionsOnByDefault)
{
cmGeneratorTarget* gt = target->GetTarget();
if (!gt) {
......@@ -3744,7 +3747,7 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
if (!(isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
return;
}
cmProp files = gt->GetProperty("XCODE_EMBED_FRAMEWORKS");
cmProp files = gt->GetProperty(embedPropertyName);
if (!files) {
return;
}
......@@ -3752,16 +3755,15 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
// Create an "Embedded Frameworks" build phase
auto* copyFilesBuildPhase =
this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
std::string copyFilesBuildPhaseName = "Embed Frameworks";
std::string destinationFrameworks = "10";
copyFilesBuildPhase->SetComment(copyFilesBuildPhaseName);
copyFilesBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
this->CreateString(destinationFrameworks));
this->CreateString(dstSubfolderSpec));
copyFilesBuildPhase->AddAttribute(
"name", this->CreateString(copyFilesBuildPhaseName));
if (cmProp fwEmbedPath = gt->GetProperty("XCODE_EMBED_FRAMEWORKS_PATH")) {
if (cmProp fwEmbedPath =
gt->GetProperty(cmStrCat(embedPropertyName, "_PATH"))) {
copyFilesBuildPhase->AddAttribute("dstPath",
this->CreateString(*fwEmbedPath));
} else {
......@@ -3775,10 +3777,10 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
for (std::string const& relFile : relFiles) {
cmXCodeObject* buildFile{ nullptr };
std::string filePath = relFile;
auto* genTarget = FindGeneratorTarget(relFile);
auto* genTarget = this->FindGeneratorTarget(relFile);
if (genTarget) {
// This is a target - get it's product path reference
auto* xcTarget = FindXCodeTarget(genTarget);
auto* xcTarget = this->FindXCodeTarget(genTarget);
if (!xcTarget) {
cmSystemTools::Error("Can not find a target for " +
genTarget->GetName());
......@@ -3792,18 +3794,18 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
" is missing product reference");
continue;
}
auto it = FileRefToEmbedBuildFileMap.find(fileRefObject);
if (it == FileRefToEmbedBuildFileMap.end()) {
auto it = this->FileRefToEmbedBuildFileMap.find(fileRefObject);
if (it == this->FileRefToEmbedBuildFileMap.end()) {
buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
buildFile->AddAttribute("fileRef", fileRefObject);
FileRefToEmbedBuildFileMap[fileRefObject] = buildFile;
this->FileRefToEmbedBuildFileMap[fileRefObject] = buildFile;
} else {
buildFile = it->second;
}
} else if (cmSystemTools::IsPathToFramework(relFile)) {
// This is a regular string path - create file reference
auto it = EmbeddedLibRefs.find(relFile);
if (it == EmbeddedLibRefs.end()) {
auto it = this->EmbeddedLibRefs.find(relFile);
if (it == this->EmbeddedLibRefs.end()) {
cmXCodeObject* fileRef =
this->CreateXCodeFileReferenceFromPath(relFile, gt, "", nullptr);
if (fileRef) {
......@@ -3829,16 +3831,25 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
cmXCodeObject* settings =
this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
const auto& rmHeadersProp =
gt->GetSafeProperty("XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY");
if (cmIsOn(rmHeadersProp)) {
bool removeHeaders = actionsOnByDefault & RemoveHeadersOnCopyByDefault;
if (auto prop = gt->GetProperty(
cmStrCat(embedPropertyName, "_REMOVE_HEADERS_ON_COPY"))) {
removeHeaders = cmIsOn(*prop);
}
if (removeHeaders) {
attrs->AddObject(this->CreateString("RemoveHeadersOnCopy"));
}
const auto& codeSignProp =
gt->GetSafeProperty("XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY");
if (cmIsOn(codeSignProp)) {
bool codeSign = actionsOnByDefault & CodeSignOnCopyByDefault;
if (auto prop =
gt->GetProperty(cmStrCat(embedPropertyName, "_CODE_SIGN_ON_COPY"))) {
codeSign = cmIsOn(*prop);
}
if (codeSign) {
attrs->AddObject(this->CreateString("CodeSignOnCopy"));
}
settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
buildFile->AddAttributeIfNotEmpty("settings", settings);
if (!buildFiles->HasObject(buildFile)) {
......@@ -3847,11 +3858,30 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
}
copyFilesBuildPhase->AddAttribute("files", buildFiles);
auto* buildPhases = target->GetAttribute("buildPhases");
// Insert embed build phase right before the post-build command
// Embed-something build phases must be inserted before the post-build
// command because that command is expected to be last
buildPhases->InsertObject(buildPhases->GetObjectCount() - 1,
copyFilesBuildPhase);
}
void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
{
static const auto dstSubfolderSpec = "10";
this->AddEmbeddedObjects(target, "Embed Frameworks",
"XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec,
NoActionOnCopyByDefault);
}
void cmGlobalXCodeGenerator::AddEmbeddedAppExtensions(cmXCodeObject* target)
{
static const auto dstSubfolderSpec = "13";
this->AddEmbeddedObjects(target, "Embed App Extensions",
"XCODE_EMBED_APP_EXTENSIONS", dstSubfolderSpec,
RemoveHeadersOnCopyByDefault);
}
bool cmGlobalXCodeGenerator::CreateGroups(
std::vector<cmLocalGenerator*>& generators)
{
......@@ -4231,6 +4261,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
for (auto t : targets) {
this->AddDependAndLinkInformation(t);
this->AddEmbeddedFrameworks(t);
this->AddEmbeddedAppExtensions(t);
// Inherit project-wide values for any target-specific search paths.
this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS");
this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS");
......
......@@ -132,6 +132,13 @@ protected:
}
private:
enum EmbedActionFlags
{
NoActionOnCopyByDefault = 0,
CodeSignOnCopyByDefault = 1,
RemoveHeadersOnCopyByDefault = 2,
};
bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf);
bool ProcessGeneratorToolsetField(std::string const& key,
std::string const& value, cmMakefile* mf);
......@@ -196,7 +203,13 @@ private:
const char* attribute);
cmXCodeObject* CreateUtilityTarget(cmGeneratorTarget* gtgt);
void AddDependAndLinkInformation(cmXCodeObject* target);
void AddEmbeddedObjects(cmXCodeObject* target,
const std::string& copyFilesBuildPhaseName,
const std::string& embedPropertyName,
const std::string& dstSubfolderSpec,
int actionsOnByDefault);
void AddEmbeddedFrameworks(cmXCodeObject* target);
void AddEmbeddedAppExtensions(cmXCodeObject* target);
void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
cmXCodeObject* buildSettings,
const std::string& configName);
......
......@@ -545,7 +545,7 @@ endif()
if(XCODE_VERSION)
add_RunCMake_test(XcodeProject -DXCODE_VERSION=${XCODE_VERSION})
add_RunCMake_test(XcodeProject-Embed)
add_RunCMake_test(XcodeProject-Embed -DXCODE_VERSION=${XCODE_VERSION})
# This test can take a very long time due to lots of combinations.
# Use a long default timeout and provide an option to customize it.
......
include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
findAttribute(${test} "RemoveHeadersOnCopy" TRUE)
findAttribute(${test} "CodeSignOnCopy" FALSE)
include(${CMAKE_CURRENT_LIST_DIR}/EmbedAppExtensions.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
findAttribute(${test} "RemoveHeadersOnCopy" TRUE)
findAttribute(${test} "CodeSignOnCopy" FALSE)
include(${CMAKE_CURRENT_LIST_DIR}/EmbedAppExtensions.cmake)
add_library(app_extension MODULE Empty.txt)
set_target_properties(app_extension PROPERTIES
LINKER_LANGUAGE CXX
BUNDLE YES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app.app_extension"
XCODE_PRODUCT_TYPE "com.apple.product-type.app-extension"
XCODE_EXPLICIT_FILE_TYPE "wrapper.app-extension"
)
add_executable(app MACOSX_BUNDLE main.m)
add_dependencies(app app_extension)
set_target_properties(app PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
XCODE_EMBED_APP_EXTENSIONS app_extension
MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app"
)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>SomeExtension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.example.app.app_extension</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>app</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
<key>NSExtensionPrincipalClass</key>
<string>SomeExtensionBrowserViewController</string>
</dict>
</dict>
</plist>
......@@ -41,3 +41,34 @@ endfunction()
TestFlagsOn(EmbedFrameworksFlagsOnNoSubdir)
TestFlagsOn(EmbedFrameworksFlagsOnWithSubdir)
function(TestAppExtension platform)
set(testName EmbedAppExtensions-${platform})
if(NOT platform STREQUAL "macOS")
set(RunCMake_TEST_OPTIONS -DCMAKE_SYSTEM_NAME=${platform})
endif()
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${testName}-build)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(${testName})
run_cmake_command(${testName}-build
${CMAKE_COMMAND} --build ${RunCMake_TEST_BINARY_DIR}
--config Debug
--target app
)
endfunction()
# Isolate device tests from host architecture selection.
unset(ENV{CMAKE_OSX_ARCHITECTURES})
if(XCODE_VERSION VERSION_GREATER_EQUAL 8)
# The various flag on/off combinations are tested by the EmbedFrameworks...
# tests, so we don't duplicate all the combinations here. We only verify the
# defaults, which is to remove headers on copy, but not code sign.
TestAppExtension(macOS)
TestAppExtension(iOS)
endif()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment