Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/services/ios/xcodebuild-args-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,17 @@ export class XcodebuildArgsService implements IXcodebuildArgsService {
// Introduced in Xcode 14+
// ref: https://forums.swift.org/t/telling-xcode-14-beta-4-to-trust-build-tool-plugins-programatically/59305/5
const skipPackageValidation = "-skipPackagePluginValidation";

// Introduced in Xcode 15+ to trust Swift macros (compiler plugins)
// non-interactively. Required for SPM packages that ship macros
// (e.g. apple/RealityKitScripting), otherwise the build fails with:
// "Macro '...' from package '...' must be enabled before it can be used"
// ref: https://developer.apple.com/documentation/xcode/writing-swift-macros
const skipMacroValidation = "-skipMacroValidation";
const extraArgs: string[] = [
"-scheme",
projectData.projectName,
skipPackageValidation,
skipMacroValidation,
];

const BUILD_SETTINGS_FILE_PATH = path.join(
Expand All @@ -187,6 +193,22 @@ export class XcodebuildArgsService implements IXcodebuildArgsService {
// references: https://medium.com/@iostechset/why-cocoapods-eats-app-icons-79fe729808d4
// https://github.com/CocoaPods/CocoaPods/issues/7003

// Xcode 26 makes Swift "explicitly built modules" the default. A
// regression there prevents macro/compiler-plugin SPM targets from
// resolving their swift-syntax module dependencies, failing with:
// "Unable to resolve module dependency: 'SwiftSyntax'" (and SwiftParser,
// SwiftSyntaxMacros, SwiftCompilerPlugin, SwiftDiagnostics).
// Passed as a command-line build setting so it overrides ALL targets,
// including the package targets we don't control.
// ref: https://forums.swift.org/t/xcode-26-unable-to-find-module-dependency/80516
const explicitModulesProperty = "SWIFT_ENABLE_EXPLICIT_MODULES";
const explicitModulesValue =
this.$xcconfigService.readPropertyValue(
BUILD_SETTINGS_FILE_PATH,
explicitModulesProperty,
) || "NO";
extraArgs.push(`${explicitModulesProperty}=${explicitModulesValue}`);

const deployTargetProperty = "IPHONEOS_DEPLOYMENT_TARGET";
const deployTargetVersion = this.$xcconfigService.readPropertyValue(
BUILD_SETTINGS_FILE_PATH,
Expand Down
37 changes: 34 additions & 3 deletions test/services/ios/xcodebuild-args-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function createTestInjector(data: {
logLevel: string;
hasProjectWorkspace: boolean;
connectedDevices?: any[];
buildXcconfigContent?: string;
}): IInjector {
const injector = new Yok();
injector.register("devicePlatformsConstants", DevicePlatformsConstants);
Expand All @@ -19,8 +20,11 @@ function createTestInjector(data: {
getDevicesForPlatform: () => data.connectedDevices || [],
});
injector.register("fs", {
exists: () => data.hasProjectWorkspace,
readText: () => "",
exists: (filePath: string) =>
filePath.endsWith("build.xcconfig")
? data.buildXcconfigContent !== undefined
: data.hasProjectWorkspace,
readText: () => data.buildXcconfigContent || "",
});
injector.register("logger", {
getLevel: () => data.logLevel,
Expand Down Expand Up @@ -49,7 +53,13 @@ function getCommonArgs() {
}

function getXcodeProjectArgs(data?: { hasProjectWorkspace: boolean }) {
const extraArgs = ["-scheme", projectName, "-skipPackagePluginValidation"];
const extraArgs = [
"-scheme",
projectName,
"-skipPackagePluginValidation",
"-skipMacroValidation",
"SWIFT_ENABLE_EXPLICIT_MODULES=NO",
];
return data && data.hasProjectWorkspace
? [
"-workspace",
Expand All @@ -72,6 +82,27 @@ function getBuildLoggingArgs(logLevel: string): string[] {
}

describe("xcodebuildArgsService", () => {
describe("getXcodeProjectArgs", () => {
it("should allow SWIFT_ENABLE_EXPLICIT_MODULES to be overridden from build.xcconfig", () => {
const injector = createTestInjector({
logLevel: "INFO",
hasProjectWorkspace: false,
buildXcconfigContent: "SWIFT_ENABLE_EXPLICIT_MODULES = YES",
});
const xcodebuildArgsService: IXcodebuildArgsService = injector.resolve(
"xcodebuildArgsService",
);

const actualArgs = xcodebuildArgsService.getXcodeProjectArgs(
<any>{ projectRoot, normalizedPlatformName },
<any>{ projectName, appResourcesDirectoryPath },
);

assert.include(actualArgs, "SWIFT_ENABLE_EXPLICIT_MODULES=YES");
assert.notInclude(actualArgs, "SWIFT_ENABLE_EXPLICIT_MODULES=NO");
});
});

describe("getBuildForSimulatorArgs", () => {
_.each([true, false], (hasProjectWorkspace) => {
_.each(["INFO", "TRACE"], (logLevel) => {
Expand Down