summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2020-11-29 21:54:05 (GMT)
committerKitware Robot <kwrobot@kitware.com>2020-11-29 21:54:15 (GMT)
commitea212834c0a3260455913b121f3a2974bfe2b6b5 (patch)
tree2d27d586da87b51e6137520c82ce007c88eaa0e9
parentfc941e2e7112d306bf4eb05e90a849d587831c55 (diff)
parent5651901c54ac74912110ba0d2395b25a96570326 (diff)
downloadCMake-ea212834c0a3260455913b121f3a2974bfe2b6b5.zip
CMake-ea212834c0a3260455913b121f3a2974bfe2b6b5.tar.gz
CMake-ea212834c0a3260455913b121f3a2974bfe2b6b5.tar.bz2
Merge topic 'xcode-embed-frameworks'
5651901c54 Xcode: add support for embedding frameworks Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Cameron Lowell Palmer <cameron.palmer@icloud.com> Merge-request: !5418
-rw-r--r--Help/manual/cmake-properties.7.rst4
-rw-r--r--Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY.rst8
-rw-r--r--Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY.rst8
-rw-r--r--Help/prop_tgt/XCODE_EMBED_type.rst14
-rw-r--r--Help/prop_tgt/XCODE_EMBED_type_PATH.rst9
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx134
-rw-r--r--Source/cmGlobalXCodeGenerator.h3
-rw-r--r--Source/cmXCodeObject.h7
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff-check.cmake14
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake7
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake3
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-check.cmake14
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake7
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake3
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-check.cmake14
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake8
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake2
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake43
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/func.m6
-rw-r--r--Tests/RunCMake/XcodeProject-Embed/main.m6
22 files changed, 317 insertions, 1 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index cb9579e..f322f0b 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -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
diff --git a/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY.rst b/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY.rst
new file mode 100644
index 0000000..7b68126
--- /dev/null
+++ b/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY.rst
@@ -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.
diff --git a/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY.rst b/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY.rst
new file mode 100644
index 0000000..29f8c5c
--- /dev/null
+++ b/Help/prop_tgt/XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY.rst
@@ -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.
diff --git a/Help/prop_tgt/XCODE_EMBED_type.rst b/Help/prop_tgt/XCODE_EMBED_type.rst
new file mode 100644
index 0000000..90c5bc7
--- /dev/null
+++ b/Help/prop_tgt/XCODE_EMBED_type.rst
@@ -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`.
diff --git a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
new file mode 100644
index 0000000..887cf57
--- /dev/null
+++ b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
@@ -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>``.
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index a881b74..78c7538 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -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);
}
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index ab5eeb2..3cc4efe 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -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;
diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h
index 78d4727..ab7f99e 100644
--- a/Source/cmXCodeObject.h
+++ b/Source/cmXCodeObject.h
@@ -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);
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 5b0b055..3ea9dbe 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -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.
diff --git a/Tests/RunCMake/XcodeProject-Embed/CMakeLists.txt b/Tests/RunCMake/XcodeProject-Embed/CMakeLists.txt
new file mode 100644
index 0000000..0400d1a
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.19)
+project(${RunCMake_TEST} LANGUAGES C)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff-check.cmake
new file mode 100644
index 0000000..9cc03b9
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff-check.cmake
@@ -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")
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake
new file mode 100644
index 0000000..f4fe07f
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake
@@ -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
+)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake
new file mode 100644
index 0000000..e4ea55d
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake
@@ -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()
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-check.cmake
new file mode 100644
index 0000000..3f62640
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-check.cmake
@@ -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")
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake
new file mode 100644
index 0000000..79d8d77
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake
@@ -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
+)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake
new file mode 100644
index 0000000..57c79ea
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake
@@ -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()
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-check.cmake
new file mode 100644
index 0000000..3f62640
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-check.cmake
@@ -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")
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake
new file mode 100644
index 0000000..4c78199
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake
@@ -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"
+)
diff --git a/Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake b/Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake
new file mode 100644
index 0000000..64e2f95
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake
@@ -0,0 +1,2 @@
+add_library(sharedFrameworkExt SHARED func.m)
+set_target_properties(sharedFrameworkExt PROPERTIES FRAMEWORK TRUE)
diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
new file mode 100644
index 0000000..0dc1cf3
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
@@ -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)
diff --git a/Tests/RunCMake/XcodeProject-Embed/func.m b/Tests/RunCMake/XcodeProject-Embed/func.m
new file mode 100644
index 0000000..29c6689
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/func.m
@@ -0,0 +1,6 @@
+#import <CoreFoundation/CoreFoundation.h>
+
+int func()
+{
+ return 1;
+}
diff --git a/Tests/RunCMake/XcodeProject-Embed/main.m b/Tests/RunCMake/XcodeProject-Embed/main.m
new file mode 100644
index 0000000..72e425d
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/main.m
@@ -0,0 +1,6 @@
+#import <CoreFoundation/CoreFoundation.h>
+
+int main(int argc, char** argv)
+{
+ return 0;
+}