1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

Xcode: add support for embedding frameworks

This commit also prepares for embedding things other than
frameworks. In the future, we may want to embed resources and
other types supported by Xcode, so the target properties have
been documented in a way that clearly signals the future intent.
This commit is contained in:
Gusts Kaksis
2020-10-24 13:41:13 +03:00
committed by Craig Scott
parent 70f11b9d3c
commit 5651901c54
22 changed files with 317 additions and 1 deletions

View File

@@ -396,6 +396,10 @@ Properties on Targets
/prop_tgt/WIN32_EXECUTABLE
/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS
/prop_tgt/XCODE_ATTRIBUTE_an-attribute
/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_PATH
/prop_tgt/XCODE_EXPLICIT_FILE_TYPE
/prop_tgt/XCODE_GENERATE_SCHEME
/prop_tgt/XCODE_LINK_BUILD_PHASE_MODE

View File

@@ -0,0 +1,8 @@
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY
----------------------------------------
.. versionadded:: 3.20
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.

View File

@@ -0,0 +1,8 @@
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY
---------------------------------------------
.. versionadded:: 3.20
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.

View File

@@ -0,0 +1,14 @@
XCODE_EMBED_<type>
------------------
.. versionadded:: 3.20
Tell the :generator:`Xcode` generator to embed the specified list of items into
the target bundle. ``<type>`` specifies the embed build phase to use.
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`.

View File

@@ -0,0 +1,9 @@
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
to the base location of the ``Embed XXX`` build phase associated with
``<type>``.

View File

@@ -773,7 +773,9 @@ void cmGlobalXCodeGenerator::ClearXCodeObjects()
this->TargetGroup.clear();
this->FileRefs.clear();
this->ExternalLibRefs.clear();
this->EmbeddedLibRefs.clear();
this->FileRefToBuildFileMap.clear();
this->FileRefToEmbedBuildFileMap.clear();
this->CommandsVisited.clear();
}
@@ -1197,7 +1199,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
}
}
// Make a copy so that we can override it later
std::string path = fullpath;
std::string path = cmSystemTools::CollapseFullPath(fullpath);
// Compute the extension without leading '.'.
std::string ext = cmSystemTools::GetFilenameLastExtension(path);
if (!ext.empty()) {
@@ -1793,6 +1795,10 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
if (frameworkBuildPhase) {
buildPhases->AddObject(frameworkBuildPhase);
}
// When this build phase is present, it must be last. More build phases may
// be added later for embedding things and they will insert themselves just
// before this last build phase.
if (postBuildPhase) {
buildPhases->AddObject(postBuildPhase);
}
@@ -3632,6 +3638,130 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
}
}
void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
{
cmGeneratorTarget* gt = target->GetTarget();
if (!gt) {
cmSystemTools::Error("Error no target on xobject\n");
return;
}
if (!gt->IsInBuildSystem()) {
return;
}
bool isFrameworkTarget = gt->IsFrameworkOnApple();
bool isBundleTarget = gt->GetPropertyAsBool("MACOSX_BUNDLE");
bool isCFBundleTarget = gt->IsCFBundleOnApple();
if (!(isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
return;
}
cmProp files = gt->GetProperty("XCODE_EMBED_FRAMEWORKS");
if (!files) {
return;
}
// 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));
copyFilesBuildPhase->AddAttribute(
"name", this->CreateString(copyFilesBuildPhaseName));
if (cmProp fwEmbedPath = gt->GetProperty("XCODE_EMBED_FRAMEWORKS_PATH")) {
copyFilesBuildPhase->AddAttribute("dstPath",
this->CreateString(*fwEmbedPath));
} else {
copyFilesBuildPhase->AddAttribute("dstPath", this->CreateString(""));
}
copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
// Collect all embedded frameworks and add them to build phase
std::vector<std::string> relFiles = cmExpandedList(*files);
for (std::string const& relFile : relFiles) {
cmXCodeObject* buildFile{ nullptr };
std::string filePath = relFile;
auto* genTarget = FindGeneratorTarget(relFile);
if (genTarget) {
// This is a target - get it's product path reference
auto* xcTarget = FindXCodeTarget(genTarget);
if (!xcTarget) {
cmSystemTools::Error("Can not find a target for " +
genTarget->GetName());
continue;
}
// Add the target output file as a build reference for other targets
// to link against
auto* fileRefObject = xcTarget->GetAttribute("productReference");
if (!fileRefObject) {
cmSystemTools::Error("Target " + genTarget->GetName() +
" is missing product reference");
continue;
}
auto it = FileRefToEmbedBuildFileMap.find(fileRefObject);
if (it == FileRefToEmbedBuildFileMap.end()) {
buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
buildFile->AddAttribute("fileRef", fileRefObject);
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()) {
cmXCodeObject* fileRef =
this->CreateXCodeFileReferenceFromPath(relFile, gt, "", nullptr);
if (fileRef) {
buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
buildFile->SetComment(fileRef->GetComment());
buildFile->AddAttribute("fileRef",
this->CreateObjectReference(fileRef));
}
if (!buildFile) {
cmSystemTools::Error("Can't create build file for " + relFile);
continue;
}
this->EmbeddedLibRefs.emplace(filePath, buildFile);
} else {
buildFile = it->second;
}
}
if (!buildFile) {
cmSystemTools::Error("Can't find a build file for " + relFile);
continue;
}
// Set build file configuration
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)) {
attrs->AddObject(this->CreateString("RemoveHeadersOnCopy"));
}
const auto& codeSignProp =
gt->GetSafeProperty("XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY");
if (cmIsOn(codeSignProp)) {
attrs->AddObject(this->CreateString("CodeSignOnCopy"));
}
settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
buildFile->AddAttributeIfNotEmpty("settings", settings);
if (!buildFiles->HasObject(buildFile)) {
buildFiles->AddObject(buildFile);
}
}
copyFilesBuildPhase->AddAttribute("files", buildFiles);
auto* buildPhases = target->GetAttribute("buildPhases");
// Insert embed build phase right before the post-build command
buildPhases->InsertObject(buildPhases->GetObjectCount() - 1,
copyFilesBuildPhase);
}
bool cmGlobalXCodeGenerator::CreateGroups(
std::vector<cmLocalGenerator*>& generators)
{
@@ -4010,7 +4140,9 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
// loop over all targets and add link and depend info
for (auto t : targets) {
this->AddDependAndLinkInformation(t);
this->AddEmbeddedFrameworks(t);
}
if (this->XcodeBuildSystem == BuildSystem::One) {
this->CreateXCodeDependHackMakefile(targets);
}

View File

@@ -187,6 +187,7 @@ private:
const std::string& configName);
cmXCodeObject* CreateUtilityTarget(cmGeneratorTarget* gtgt);
void AddDependAndLinkInformation(cmXCodeObject* target);
void AddEmbeddedFrameworks(cmXCodeObject* target);
void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
cmXCodeObject* buildSettings,
const std::string& configName);
@@ -324,8 +325,10 @@ private:
std::map<std::string, cmXCodeObject*> TargetGroup;
std::map<std::string, cmXCodeObject*> FileRefs;
std::map<std::string, cmXCodeObject*> ExternalLibRefs;
std::map<std::string, cmXCodeObject*> EmbeddedLibRefs;
std::map<cmGeneratorTarget const*, cmXCodeObject*> XCodeObjectMap;
std::map<cmXCodeObject*, cmXCodeObject*> FileRefToBuildFileMap;
std::map<cmXCodeObject*, cmXCodeObject*> FileRefToEmbedBuildFileMap;
std::vector<std::string> Architectures;
std::string ObjectDirArchDefault;
std::string ObjectDirArch;

View File

@@ -81,6 +81,13 @@ public:
void SetObject(cmXCodeObject* value) { this->Object = value; }
cmXCodeObject* GetObject() { return this->Object; }
void AddObject(cmXCodeObject* value) { this->List.push_back(value); }
size_t GetObjectCount() { return this->List.size(); }
void InsertObject(size_t position, cmXCodeObject* value)
{
if (position < GetObjectCount()) {
this->List.insert(this->List.begin() + position, value);
}
}
void PrependObject(cmXCodeObject* value)
{
this->List.insert(this->List.begin(), value);

View File

@@ -513,6 +513,7 @@ endif()
if(XCODE_VERSION)
add_RunCMake_test(XcodeProject -DXCODE_VERSION=${XCODE_VERSION})
add_RunCMake_test(XcodeProject-Embed)
# 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.

View File

@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.19)
project(${RunCMake_TEST} LANGUAGES C)
include(${RunCMake_TEST}.cmake)

View File

@@ -0,0 +1,14 @@
function(findAttribute project attr)
execute_process(
COMMAND grep ${attr} ${RunCMake_TEST_BINARY_DIR}/${project}.xcodeproj/project.pbxproj
OUTPUT_VARIABLE output_var
RESULT_VARIABLE result_var
)
if(NOT result_var)
set(RunCMake_TEST_FAILED "${attr} attribute is set" PARENT_SCOPE)
endif()
endfunction()
findAttribute(${test} "RemoveHeadersOnCopy")
findAttribute(${test} "CodeSignOnCopy")

View File

@@ -0,0 +1,7 @@
add_executable(app MACOSX_BUNDLE main.m)
set_target_properties(app PROPERTIES
XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY OFF
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY OFF
)

View File

@@ -0,0 +1,3 @@
if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/sharedFrameworkExt.framework)
set(RunCMake_TEST_FAILED "Framework was not embedded at the expected location")
endif()

View File

@@ -0,0 +1,14 @@
function(findAttribute project attr)
execute_process(
COMMAND grep ${attr} ${RunCMake_TEST_BINARY_DIR}/${project}.xcodeproj/project.pbxproj
OUTPUT_VARIABLE output_var
RESULT_VARIABLE result_var
)
if(result_var)
set(RunCMake_TEST_FAILED "${attr} attribute not set" PARENT_SCOPE)
endif()
endfunction()
findAttribute(${test} "RemoveHeadersOnCopy")
findAttribute(${test} "CodeSignOnCopy")

View File

@@ -0,0 +1,7 @@
add_executable(app MACOSX_BUNDLE main.m)
set_target_properties(app PROPERTIES
XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY ON
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
)

View File

@@ -0,0 +1,3 @@
if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/subdir/sharedFrameworkExt.framework)
set(RunCMake_TEST_FAILED "Framework was not embedded at the expected location")
endif()

View File

@@ -0,0 +1,14 @@
function(findAttribute project attr)
execute_process(
COMMAND grep ${attr} ${RunCMake_TEST_BINARY_DIR}/${project}.xcodeproj/project.pbxproj
OUTPUT_VARIABLE output_var
RESULT_VARIABLE result_var
)
if(result_var)
set(RunCMake_TEST_FAILED "${attr} attribute not set" PARENT_SCOPE)
endif()
endfunction()
findAttribute(${test} "RemoveHeadersOnCopy")
findAttribute(${test} "CodeSignOnCopy")

View File

@@ -0,0 +1,8 @@
add_executable(app MACOSX_BUNDLE main.m)
set_target_properties(app PROPERTIES
XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY ON
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
XCODE_EMBED_FRAMEWORKS_PATH "subdir"
)

View File

@@ -0,0 +1,2 @@
add_library(sharedFrameworkExt SHARED func.m)
set_target_properties(sharedFrameworkExt PROPERTIES FRAMEWORK TRUE)

View File

@@ -0,0 +1,43 @@
include(RunCMake)
# Build a framework that the other tests will use and treat as external.
# Always build in the Debug configuration so that the path to the framework
# is predictable.
function(ExternalFramework)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExternalFramework-build)
set(externalFramework ${RunCMake_TEST_BINARY_DIR}/Debug/sharedFrameworkExt.framework PARENT_SCOPE)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(ExternalFramework)
run_cmake_command(ExternalFramework-build
${CMAKE_COMMAND} --build ${RunCMake_TEST_BINARY_DIR}
--config Debug
--target sharedFrameworkExt
)
endfunction()
ExternalFramework()
set(RunCMake_TEST_OPTIONS -DEXTERNAL_FWK=${externalFramework})
run_cmake(EmbedFrameworksFlagsOff)
function(TestFlagsOn testName)
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()
TestFlagsOn(EmbedFrameworksFlagsOnNoSubdir)
TestFlagsOn(EmbedFrameworksFlagsOnWithSubdir)

View File

@@ -0,0 +1,6 @@
#import <CoreFoundation/CoreFoundation.h>
int func()
{
return 1;
}

View File

@@ -0,0 +1,6 @@
#import <CoreFoundation/CoreFoundation.h>
int main(int argc, char** argv)
{
return 0;
}