From 26869fb4ba22321b6249f234901c5fbf64455b5a Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Wed, 11 Jun 2025 21:11:59 +0300 Subject: [PATCH] cmake --build: Fix building multiple targets in Xcode workspace Extend commit 844d79916a (cmake --build: Add support for driving Xcode workspaces, 2025-06-02) to support multiple `--target` arguments. `xcodebuild -scheme` cannot be repeated in a single call, so call it multiple times instead. Issue: #26958 Co-Authored-By: Craig Scott --- Source/cmGlobalXCodeGenerator.cxx | 106 +++++++++++------- .../RunCMake/XcodeProject/RunCMakeTest.cmake | 1 + .../XcodeWorkspace-build-stdout.txt | 2 +- .../XcodeWorkspace-build2-stdout.txt | 4 + .../XcodeProject/XcodeWorkspace.cmake | 2 + 5 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 Tests/RunCMake/XcodeProject/XcodeWorkspace-build2-stdout.txt diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 16234cd046..27fdfaccf5 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -548,62 +548,92 @@ cmGlobalXCodeGenerator::GenerateBuildCommand( int jobs, bool /*verbose*/, cmBuildOptions const& /*buildOptions*/, std::vector const& makeOptions) { + std::string const xcodebuild = + this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()); + + std::string const workspacePath = cmStrCat(projectName, ".xcworkspace"); + std::string const projectPath = cmStrCat(projectName, ".xcodeproj"); + // If an external tool created a workspace then build it instead. - std::string projectPath = cmStrCat(projectName, ".xcworkspace"); - bool const isWorkspace = cmSystemTools::FileIsDirectory(projectPath); - if (!isWorkspace) { - projectPath = cmStrCat(projectName, ".xcodeproj"); - } + bool const isWorkspace = cmSystemTools::FileIsDirectory(workspacePath); std::string const targetFlag = isWorkspace ? "-scheme" : "-target"; - std::string const projectFlag = isWorkspace ? "-workspace" : "-project"; - GeneratedMakeCommand makeCommand; - // now build the test - makeCommand.Add( - this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand())); - - if (!projectName.empty()) { - makeCommand.Add(projectFlag, projectPath); - } - if (cm::contains(targetNames, "clean")) { - makeCommand.Add("clean"); - makeCommand.Add(targetFlag, "ALL_BUILD"); - } else { - makeCommand.Add("build"); - if (targetNames.empty() || - ((targetNames.size() == 1) && targetNames.front().empty())) { - makeCommand.Add(targetFlag, "ALL_BUILD"); - } else { - for (auto const& tname : targetNames) { - if (!tname.empty()) { - makeCommand.Add(targetFlag, tname); - } - } - } - } + std::vector requiredArgs; if (isWorkspace) { - makeCommand.Add( - "-destination", - cmStrCat("generic/platform=", this->GetAppleSpecificPlatformName())); + requiredArgs.insert(requiredArgs.end(), { "-workspace", workspacePath }); + } else { + requiredArgs.insert(requiredArgs.end(), { "-project", projectPath }); + } + + bool const isCleanBuild = cm::contains(targetNames, "clean"); + bool const isTargetEmpty = targetNames.empty() || + ((targetNames.size() == 1) && targetNames.front().empty()); + + if (isCleanBuild) { + requiredArgs.push_back("clean"); + } else { + requiredArgs.push_back("build"); + } + + requiredArgs.insert(requiredArgs.end(), + { "-configuration", config.empty() ? "Debug" : config }); + + if (isWorkspace) { + requiredArgs.insert( + requiredArgs.end(), + { "-destination", + cmStrCat("generic/platform=", this->GetAppleSpecificPlatformName()) }); } if ((this->XcodeBuildSystem >= BuildSystem::Twelve) || (jobs != cmake::NO_BUILD_PARALLEL_LEVEL)) { - makeCommand.Add("-parallelizeTargets"); + requiredArgs.push_back("-parallelizeTargets"); } - makeCommand.Add("-configuration", (config.empty() ? "Debug" : config)); if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) && (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) { - makeCommand.Add("-jobs", std::to_string(jobs)); + requiredArgs.insert(requiredArgs.end(), { "-jobs", std::to_string(jobs) }); } if (this->XcodeVersion >= 70) { - makeCommand.Add("-hideShellScriptEnvironment"); + requiredArgs.push_back("-hideShellScriptEnvironment"); } - makeCommand.Add(makeOptions.begin(), makeOptions.end()); + + requiredArgs.insert(requiredArgs.end(), makeOptions.begin(), + makeOptions.end()); + + if (isWorkspace && !isCleanBuild && targetNames.size() > 1) { + // For workspaces we need a separate command for each target, + // because xcodebuild can pass only one -scheme arg + std::vector makeCommands; + for (auto const& target : targetNames) { + if (target.empty()) { + continue; + } + GeneratedMakeCommand makeCommand; + makeCommand.Add(xcodebuild); + makeCommand.Add(requiredArgs.cbegin(), requiredArgs.cend()); + makeCommand.Add(targetFlag, target); + makeCommands.emplace_back(std::move(makeCommand)); + } + return makeCommands; + } + + if (isTargetEmpty || isCleanBuild) { + requiredArgs.insert(requiredArgs.end(), { targetFlag, "ALL_BUILD" }); + } else { + for (auto const& target : targetNames) { + if (target.empty()) { + continue; + } + requiredArgs.insert(requiredArgs.end(), { targetFlag, target }); + } + } + GeneratedMakeCommand makeCommand; + makeCommand.Add(xcodebuild); + makeCommand.Add(requiredArgs.cbegin(), requiredArgs.cend()); return { std::move(makeCommand) }; } diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index 16a235ded7..18f2c21594 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake @@ -184,6 +184,7 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 12) run_cmake(XcodeWorkspace) set(RunCMake_TEST_NO_CLEAN 1) run_cmake_command(XcodeWorkspace-build ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(XcodeWorkspace-build2 ${CMAKE_COMMAND} --build . --config Debug --target custom1 custom2) endblock() endif() diff --git a/Tests/RunCMake/XcodeProject/XcodeWorkspace-build-stdout.txt b/Tests/RunCMake/XcodeProject/XcodeWorkspace-build-stdout.txt index 5d3768e683..752e1e1541 100644 --- a/Tests/RunCMake/XcodeProject/XcodeWorkspace-build-stdout.txt +++ b/Tests/RunCMake/XcodeProject/XcodeWorkspace-build-stdout.txt @@ -1 +1 @@ -xcodebuild -workspace XcodeWorkspace\.xcworkspace build -scheme ALL_BUILD -destination generic/platform=MacOS +xcodebuild -workspace XcodeWorkspace\.xcworkspace build -configuration Debug -destination generic/platform=MacOS -parallelizeTargets -hideShellScriptEnvironment -scheme ALL_BUILD diff --git a/Tests/RunCMake/XcodeProject/XcodeWorkspace-build2-stdout.txt b/Tests/RunCMake/XcodeProject/XcodeWorkspace-build2-stdout.txt new file mode 100644 index 0000000000..a71ce57b8b --- /dev/null +++ b/Tests/RunCMake/XcodeProject/XcodeWorkspace-build2-stdout.txt @@ -0,0 +1,4 @@ +.*xcodebuild -workspace XcodeWorkspace\.xcworkspace build -configuration Debug -destination generic/platform=MacOS -parallelizeTargets -hideShellScriptEnvironment -scheme custom1[^ +]* +.*xcodebuild -workspace XcodeWorkspace\.xcworkspace build -configuration Debug -destination generic/platform=MacOS -parallelizeTargets -hideShellScriptEnvironment -scheme custom2[^ +]* diff --git a/Tests/RunCMake/XcodeProject/XcodeWorkspace.cmake b/Tests/RunCMake/XcodeProject/XcodeWorkspace.cmake index bf7626a03b..82d4a1a7de 100644 --- a/Tests/RunCMake/XcodeProject/XcodeWorkspace.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeWorkspace.cmake @@ -1,5 +1,7 @@ enable_language(C) add_executable(main main.c) +add_custom_target(custom1) +add_custom_target(custom2) file(WRITE "${CMAKE_BINARY_DIR}/XcodeWorkspace.xcworkspace/contents.xcworkspacedata" [[