summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2020-09-14 19:02:47 (GMT)
committerBrad King <brad.king@kitware.com>2020-09-18 17:02:14 (GMT)
commit8d5f4c4db93959f77dc8fb185e4630df4ec26d98 (patch)
treed71f3fd9552500ecb52217bff53f4a7b37d6e18c /Source
parent2db623f554d5522350214a7c5bacd5ec2dec1b34 (diff)
downloadCMake-8d5f4c4db93959f77dc8fb185e4630df4ec26d98.zip
CMake-8d5f4c4db93959f77dc8fb185e4630df4ec26d98.tar.gz
CMake-8d5f4c4db93959f77dc8fb185e4630df4ec26d98.tar.bz2
Xcode: Switch to the "new build system" for Xcode 12 and above
Provide an option to switch back to the original build system via `-T buildsystem=1`. Fixes: #18088
Diffstat (limited to 'Source')
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx329
-rw-r--r--Source/cmGlobalXCodeGenerator.h16
2 files changed, 318 insertions, 27 deletions
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index cceaea0..b2014e0 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -171,6 +171,11 @@ cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(
{
this->VersionString = version_string;
this->XcodeVersion = version_number;
+ if (this->XcodeVersion >= 120) {
+ this->XcodeBuildSystem = BuildSystem::Twelve;
+ } else {
+ this->XcodeBuildSystem = BuildSystem::One;
+ }
this->RootObject = nullptr;
this->MainGroupChildren = nullptr;
@@ -287,6 +292,8 @@ cm::string_view cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b)
switch (b) {
case cmGlobalXCodeGenerator::BuildSystem::One:
return "1"_s;
+ case cmGlobalXCodeGenerator::BuildSystem::Twelve:
+ return "12"_s;
}
return {};
}
@@ -371,6 +378,8 @@ bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField(
if (key == "buildsystem") {
if (value == "1"_s) {
this->XcodeBuildSystem = BuildSystem::One;
+ } else if (value == "12"_s) {
+ this->XcodeBuildSystem = BuildSystem::Twelve;
} else {
/* clang-format off */
std::string const& e = cmStrCat(
@@ -378,7 +387,21 @@ bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField(
" ", this->GetName(), "\n"
"toolset specification field\n"
" buildsystem=", value, "\n"
- "value is unkonwn. It must be '1'."
+ "value is unkonwn. It must be '1' or '12'."
+ );
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e);
+ return false;
+ }
+ if (this->XcodeBuildSystem == BuildSystem::Twelve &&
+ this->XcodeVersion < 120) {
+ /* clang-format off */
+ std::string const& e = cmStrCat(
+ "Generator\n"
+ " ", this->GetName(), "\n"
+ "toolset specification field\n"
+ " buildsystem=", value, "\n"
+ "is not allowed with Xcode ", this->VersionString, '.'
);
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e);
@@ -479,6 +502,9 @@ cmGlobalXCodeGenerator::GenerateBuildCommand(
}
}
+ if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
+ makeCommand.Add("-parallelizeTargets");
+ }
makeCommand.Add("-configuration", (config.empty() ? "Debug" : config));
if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
@@ -499,8 +525,15 @@ cmGlobalXCodeGenerator::GenerateBuildCommand(
std::unique_ptr<cmLocalGenerator> cmGlobalXCodeGenerator::CreateLocalGenerator(
cmMakefile* mf)
{
- return std::unique_ptr<cmLocalGenerator>(
+ std::unique_ptr<cmLocalGenerator> lg(
cm::make_unique<cmLocalXCodeGenerator>(this, mf));
+ if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
+ // For this build system variant we generate custom commands as
+ // shell scripts directly rather than inside Makefiles.
+ // FIXME: Rename or refactor this option for clarity.
+ lg->SetLinkScriptShell(true);
+ }
+ return lg;
}
void cmGlobalXCodeGenerator::AddExtraIDETargets()
@@ -1692,30 +1725,46 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
cmXCodeObject* preLinkPhase = nullptr;
cmXCodeObject* postBuildPhase = nullptr;
- std::vector<cmSourceFile*> classes;
- if (!gtgt->GetConfigCommonSourceFiles(classes)) {
- return;
- }
- // add all the sources
- std::vector<cmCustomCommand> commands;
- auto& visited = this->CommandsVisited[gtgt];
- for (auto sourceFile : classes) {
- if (sourceFile->GetCustomCommand() && visited.insert(sourceFile).second) {
- commands.push_back(*sourceFile->GetCustomCommand());
+ if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
+ // create prebuild phase
+ preBuildPhase =
+ this->CreateRunScriptBuildPhase("CMake PreBuild Rules", prebuild);
+ // create prelink phase
+ preLinkPhase =
+ this->CreateRunScriptBuildPhase("CMake PreLink Rules", prelink);
+ // create postbuild phase
+ postBuildPhase =
+ this->CreateRunScriptBuildPhase("CMake PostBuild Rules", postbuild);
+ } else {
+ std::vector<cmSourceFile*> classes;
+ if (!gtgt->GetConfigCommonSourceFiles(classes)) {
+ return;
+ }
+ // add all the sources
+ std::vector<cmCustomCommand> commands;
+ auto& visited = this->CommandsVisited[gtgt];
+ for (auto sourceFile : classes) {
+ if (sourceFile->GetCustomCommand() &&
+ visited.insert(sourceFile).second) {
+ commands.push_back(*sourceFile->GetCustomCommand());
+ if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
+ this->CustomCommandRoots[sourceFile].insert(gtgt);
+ }
+ }
}
+ // create custom commands phase
+ legacyCustomCommandsBuildPhase = this->CreateLegacyRunScriptBuildPhase(
+ "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands);
+ // create prebuild phase
+ preBuildPhase = this->CreateLegacyRunScriptBuildPhase(
+ "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild);
+ // create prelink phase
+ preLinkPhase = this->CreateLegacyRunScriptBuildPhase(
+ "CMake PreLink Rules", "preLinkCommands", gtgt, prelink);
+ // create postbuild phase
+ postBuildPhase = this->CreateLegacyRunScriptBuildPhase(
+ "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild);
}
- // create custom commands phase
- legacyCustomCommandsBuildPhase = this->CreateLegacyRunScriptBuildPhase(
- "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands);
- // create prebuild phase
- preBuildPhase = this->CreateLegacyRunScriptBuildPhase(
- "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild);
- // create prelink phase
- preLinkPhase = this->CreateLegacyRunScriptBuildPhase(
- "CMake PreLink Rules", "preLinkCommands", gtgt, prelink);
- // create postbuild phase
- postBuildPhase = this->CreateLegacyRunScriptBuildPhase(
- "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild);
// The order here is the order they will be built in.
// The order "headers, resources, sources" mimics a native project generated
@@ -1727,6 +1776,9 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
if (legacyCustomCommandsBuildPhase) {
buildPhases->AddObject(legacyCustomCommandsBuildPhase);
}
+ if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
+ this->CreateRunScriptBuildPhases(buildPhases, gtgt);
+ }
if (headerBuildPhase) {
buildPhases->AddObject(headerBuildPhase);
}
@@ -1750,6 +1802,199 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
}
}
+void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases(
+ cmXCodeObject* buildPhases, cmGeneratorTarget const* gt)
+{
+ std::vector<cmSourceFile*> sources;
+ if (!gt->GetConfigCommonSourceFiles(sources)) {
+ return;
+ }
+ auto& visited = this->CommandsVisited[gt];
+ for (auto sf : sources) {
+ this->CreateRunScriptBuildPhases(buildPhases, sf, gt, visited);
+ }
+}
+
+void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases(
+ cmXCodeObject* buildPhases, cmSourceFile const* sf,
+ cmGeneratorTarget const* gt, std::set<cmSourceFile const*>& visited)
+{
+ cmCustomCommand const* cc = sf->GetCustomCommand();
+ if (cc && visited.insert(sf).second) {
+ this->CustomCommandRoots[sf].insert(gt);
+ if (std::vector<cmSourceFile*> const* depends = gt->GetSourceDepends(sf)) {
+ for (cmSourceFile const* di : *depends) {
+ this->CreateRunScriptBuildPhases(buildPhases, di, gt, visited);
+ }
+ }
+ cmXCodeObject* buildPhase = this->CreateRunScriptBuildPhase(sf, gt, *cc);
+ buildPhases->AddObject(buildPhase);
+ }
+}
+
+cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
+ cmSourceFile const* sf, cmGeneratorTarget const* gt,
+ cmCustomCommand const& cc)
+{
+ std::set<std::string> allConfigInputs;
+ std::set<std::string> allConfigOutputs;
+
+ std::string shellScript = "set -e\n";
+ for (std::string const& configName : this->CurrentConfigurationTypes) {
+ cmCustomCommandGenerator ccg(cc, configName, this->CurrentLocalGenerator);
+ std::vector<std::string> realDepends;
+ realDepends.reserve(ccg.GetDepends().size());
+ for (auto const& d : ccg.GetDepends()) {
+ std::string dep;
+ if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) {
+ realDepends.emplace_back(std::move(dep));
+ }
+ }
+
+ allConfigInputs.insert(realDepends.begin(), realDepends.end());
+ allConfigOutputs.insert(ccg.GetByproducts().begin(),
+ ccg.GetByproducts().end());
+ allConfigOutputs.insert(ccg.GetOutputs().begin(), ccg.GetOutputs().end());
+
+ shellScript =
+ cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")", configName,
+ "\"; then :\n", this->ConstructScript(ccg), "fi\n");
+ }
+
+ cmXCodeObject* buildPhase =
+ this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
+ buildPhase->AddAttribute("buildActionMask",
+ this->CreateString("2147483647"));
+ cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
+ buildPhase->AddAttribute("files", buildFiles);
+ {
+ std::string name;
+ if (!allConfigOutputs.empty()) {
+ name = cmStrCat("Generate ",
+ this->RelativeToBinary(*allConfigOutputs.begin()));
+ } else {
+ name = sf->GetLocation().GetName();
+ }
+ buildPhase->AddAttribute("name", this->CreateString(name));
+ }
+ buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
+ this->CreateString("0"));
+ buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
+ buildPhase->AddAttribute("shellScript", this->CreateString(shellScript));
+ buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
+
+ bool symbolic = false;
+ {
+ cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
+ for (std::string const& i : allConfigInputs) {
+ inputPaths->AddUniqueObject(this->CreateString(i));
+ if (!symbolic) {
+ if (cmSourceFile* isf =
+ gt->GetLocalGenerator()->GetMakefile()->GetSource(
+ i, cmSourceFileLocationKind::Known)) {
+ symbolic = isf->GetPropertyAsBool("SYMBOLIC");
+ }
+ }
+ }
+ buildPhase->AddAttribute("inputPaths", inputPaths);
+ }
+ {
+ cmXCodeObject* outputPaths =
+ this->CreateObject(cmXCodeObject::OBJECT_LIST);
+ for (std::string const& o : allConfigOutputs) {
+ outputPaths->AddUniqueObject(this->CreateString(o));
+ if (!symbolic) {
+ if (cmSourceFile* osf =
+ gt->GetLocalGenerator()->GetMakefile()->GetSource(
+ o, cmSourceFileLocationKind::Known)) {
+ symbolic = osf->GetPropertyAsBool("SYMBOLIC");
+ }
+ }
+ }
+ buildPhase->AddAttribute("outputPaths", outputPaths);
+ }
+ if (symbolic) {
+ buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1"));
+ }
+
+ return buildPhase;
+}
+
+cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
+ std::string const& name, std::vector<cmCustomCommand> const& commands)
+{
+ if (commands.empty()) {
+ return nullptr;
+ }
+
+ std::set<std::string> allConfigOutputs;
+
+ std::string shellScript = "set -e\n";
+ for (std::string const& configName : this->CurrentConfigurationTypes) {
+ shellScript = cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")",
+ configName, "\"; then :\n");
+ for (cmCustomCommand const& cc : commands) {
+ cmCustomCommandGenerator ccg(cc, configName,
+ this->CurrentLocalGenerator);
+ shellScript = cmStrCat(shellScript, this->ConstructScript(ccg));
+ allConfigOutputs.insert(ccg.GetByproducts().begin(),
+ ccg.GetByproducts().end());
+ }
+ shellScript = cmStrCat(shellScript, "fi\n");
+ }
+
+ cmXCodeObject* buildPhase =
+ this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
+ buildPhase->AddAttribute("buildActionMask",
+ this->CreateString("2147483647"));
+ cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
+ buildPhase->AddAttribute("files", buildFiles);
+ buildPhase->AddAttribute("name", this->CreateString(name));
+ buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
+ this->CreateString("0"));
+ buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
+ buildPhase->AddAttribute("shellScript", this->CreateString(shellScript));
+ buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
+ {
+ cmXCodeObject* outputPaths =
+ this->CreateObject(cmXCodeObject::OBJECT_LIST);
+ for (std::string const& o : allConfigOutputs) {
+ outputPaths->AddUniqueObject(this->CreateString(o));
+ }
+ buildPhase->AddAttribute("outputPaths", outputPaths);
+ }
+ buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1"));
+
+ return buildPhase;
+}
+
+std::string cmGlobalXCodeGenerator::ConstructScript(
+ cmCustomCommandGenerator const& ccg)
+{
+ std::string script;
+ cmLocalGenerator* lg = this->CurrentLocalGenerator;
+ std::string wd = ccg.GetWorkingDirectory();
+ if (wd.empty()) {
+ wd = lg->GetCurrentBinaryDirectory();
+ }
+ wd = lg->ConvertToOutputFormat(wd, cmOutputConverter::SHELL);
+ script = cmStrCat(script, " cd ", wd, "\n");
+ for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
+ std::string cmd = ccg.GetCommand(c);
+ if (cmd.empty()) {
+ continue;
+ }
+ cmSystemTools::ReplaceString(cmd, "/./", "/");
+ cmd = lg->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL);
+ ccg.AppendArguments(c, cmd);
+ cmSystemTools::ReplaceString(cmd, "$(CONFIGURATION)", "$CONFIGURATION");
+ cmSystemTools::ReplaceString(cmd, "$(EFFECTIVE_PLATFORM_NAME)",
+ "$EFFECTIVE_PLATFORM_NAME");
+ script = cmStrCat(script, " ", cmd, '\n');
+ }
+ return script;
+}
+
// This function removes each occurrence of the flag and returns the last one
// (i.e., the dominant flag in GCC)
std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag,
@@ -3713,6 +3958,29 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
if (!this->CreateXCodeTargets(generator, targets)) {
return false;
}
+ for (auto const& ccRoot : this->CustomCommandRoots) {
+ if (ccRoot.second.size() > 1) {
+ std::string e = "The custom command ";
+ std::vector<std::string> const& outputs =
+ ccRoot.first->GetCustomCommand()->GetOutputs();
+ if (!outputs.empty()) {
+ e = cmStrCat(e, "generating\n ", outputs[0]);
+ } else {
+ e = cmStrCat(e, "driven by\n ", ccRoot.first->GetFullPath());
+ }
+ e = cmStrCat(e, "\nis attached to multiple targets:");
+ for (cmGeneratorTarget const* gt : ccRoot.second) {
+ e = cmStrCat(e, "\n ", gt->GetName());
+ }
+ e = cmStrCat(
+ e,
+ "\nbut none of these is a common dependency of the other(s). "
+ "This is not allowed by the Xcode \"new build system\".");
+ generator->IssueMessage(MessageType::FATAL_ERROR, e);
+ return false;
+ }
+ }
+ this->CustomCommandRoots.clear();
}
// loop over all targets and add link and depend info
for (auto t : targets) {
@@ -4007,9 +4275,16 @@ void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings(
xout.StartElement("dict");
if (this->XcodeVersion >= 100) {
xout.Element("key", "BuildSystemType");
- xout.Element("string", "Original");
- xout.Element("key", "DisableBuildSystemDeprecationWarning");
- xout.Element("true");
+ switch (this->XcodeBuildSystem) {
+ case BuildSystem::One:
+ xout.Element("string", "Original");
+ xout.Element("key", "DisableBuildSystemDeprecationWarning");
+ xout.Element("true");
+ break;
+ case BuildSystem::Twelve:
+ xout.Element("string", "Latest");
+ break;
+ }
}
if (hasGeneratedSchemes) {
xout.Element("key",
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index bbf3ee9..fca9c27 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -15,6 +15,7 @@
#include "cmXCodeObject.h"
class cmCustomCommand;
+class cmCustomCommandGenerator;
class cmGeneratorTarget;
class cmGlobalGeneratorFactory;
class cmLocalGenerator;
@@ -116,6 +117,7 @@ public:
enum class BuildSystem
{
One = 1,
+ Twelve = 12,
};
protected:
@@ -233,6 +235,18 @@ private:
cmXCodeObject* CreateLegacyRunScriptBuildPhase(
const char* name, const char* name2, cmGeneratorTarget* target,
const std::vector<cmCustomCommand>&);
+ void CreateRunScriptBuildPhases(cmXCodeObject* buildPhases,
+ cmGeneratorTarget const* gt);
+ void CreateRunScriptBuildPhases(cmXCodeObject* buildPhases,
+ cmSourceFile const* sf,
+ cmGeneratorTarget const* gt,
+ std::set<cmSourceFile const*>& visited);
+ cmXCodeObject* CreateRunScriptBuildPhase(cmSourceFile const* sf,
+ cmGeneratorTarget const* gt,
+ cmCustomCommand const& cc);
+ cmXCodeObject* CreateRunScriptBuildPhase(
+ std::string const& name, std::vector<cmCustomCommand> const& commands);
+ std::string ConstructScript(cmCustomCommandGenerator const& ccg);
void CreateReRunCMakeFile(cmLocalGenerator* root,
std::vector<cmLocalGenerator*> const& gens);
@@ -315,4 +329,6 @@ private:
std::vector<std::string> EnabledLangs;
std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>>
CommandsVisited;
+ std::map<cmSourceFile const*, std::set<cmGeneratorTarget const*>>
+ CustomCommandRoots;
};