diff options
author | Brad King <brad.king@kitware.com> | 2020-09-14 19:02:47 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2020-09-18 17:02:14 (GMT) |
commit | 8d5f4c4db93959f77dc8fb185e4630df4ec26d98 (patch) | |
tree | d71f3fd9552500ecb52217bff53f4a7b37d6e18c /Source | |
parent | 2db623f554d5522350214a7c5bacd5ec2dec1b34 (diff) | |
download | CMake-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.cxx | 329 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.h | 16 |
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; }; |