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 <craig.scott@crascit.com>
This commit is contained in:
Stepanov Igor
2025-06-11 21:11:59 +03:00
committed by Craig Scott
parent b44bc8d1a5
commit 26869fb4ba
5 changed files with 76 additions and 39 deletions

View File

@@ -548,62 +548,92 @@ cmGlobalXCodeGenerator::GenerateBuildCommand(
int jobs, bool /*verbose*/, cmBuildOptions const& /*buildOptions*/,
std::vector<std::string> 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<std::string> 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<GeneratedMakeCommand> 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) };
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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[^
]*

View File

@@ -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" [[
<?xml version="1.0" encoding="UTF-8"?>
<Workspace version = "1.0">