Skip to content

Commit 68021aa

Browse files
committed
Fix Several Windows Debugging Issues
Fixes up some issues with Windows debugging found in the latest 6.0 toolchain: - Add Testing.dll to the PATH same as XCTest.dll - Don't append .exe when debugging testing binaries - TODO: Don't follow incorrectly created symlink at `.\build\debug`
1 parent 5a4786f commit 68021aa

File tree

3 files changed

+130
-71
lines changed

3 files changed

+130
-71
lines changed

src/debugger/buildConfig.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,19 @@ export class TestingConfigurationFactory {
191191
...swiftRuntimeEnv(),
192192
...configuration.folder(this.ctx.workspaceFolder).testEnvironmentVariables,
193193
};
194-
// On Windows, add XCTest.dll to the Path
194+
// On Windows, add XCTest.dll/Testing.dll to the Path
195195
// and run the .xctest executable from the .build directory.
196196
const runtimePath = this.ctx.workspaceContext.toolchain.runtimePath;
197197
const xcTestPath = this.ctx.workspaceContext.toolchain.xcTestPath;
198198
if (xcTestPath && xcTestPath !== runtimePath) {
199199
testEnv.Path = `${xcTestPath};${testEnv.Path ?? process.env.Path}`;
200200
}
201201

202+
const swiftTestingPath = this.ctx.workspaceContext.toolchain.swiftTestingPath;
203+
if (swiftTestingPath && swiftTestingPath !== runtimePath) {
204+
testEnv.Path = `${swiftTestingPath};${testEnv.Path ?? process.env.Path}`;
205+
}
206+
202207
return {
203208
...this.baseConfig,
204209
program: await this.testExecutableOutputPath(),

src/debugger/debugAdapterFactory.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,14 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
7777
): Promise<vscode.DebugConfiguration> {
7878
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
7979
// Fix the program path on Windows to include the ".exe" extension
80-
if (this.platform === "win32" && path.extname(launchConfig.program) !== ".exe") {
80+
if (
81+
this.platform === "win32" &&
82+
launchConfig.testType === undefined &&
83+
path.extname(launchConfig.program) !== ".exe"
84+
) {
8185
launchConfig.program += ".exe";
8286
}
87+
8388
// Delegate to CodeLLDB if that's the debug adapter we have selected
8489
if (DebugAdapter.getDebugAdapterType(this.swiftVersion) === "lldb-vscode") {
8590
launchConfig.type = "lldb";

src/toolchain/toolchain.ts

+118-69
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { Sanitizer } from "./Sanitizer";
3131
interface InfoPlist {
3232
DefaultProperties: {
3333
XCTEST_VERSION: string | undefined;
34+
SWIFT_TESTING_VERSION: string | undefined;
3435
};
3536
}
3637

@@ -107,6 +108,7 @@ export class SwiftToolchain {
107108
public defaultSDK?: string,
108109
public customSDK?: string,
109110
public xcTestPath?: string,
111+
public swiftTestingPath?: string,
110112
public swiftPMTestingHelperPath?: string
111113
) {}
112114

@@ -125,6 +127,12 @@ export class SwiftToolchain {
125127
runtimePath,
126128
customSDK ?? defaultSDK
127129
);
130+
const swiftTestingPath = await this.getSwiftTestingPath(
131+
targetInfo,
132+
swiftVersion,
133+
runtimePath,
134+
customSDK ?? defaultSDK
135+
);
128136
const swiftPMTestingHelperPath = await this.getSwiftPMTestingHelperPath(toolchainPath);
129137

130138
return new SwiftToolchain(
@@ -137,6 +145,7 @@ export class SwiftToolchain {
137145
defaultSDK,
138146
customSDK,
139147
xcTestPath,
148+
swiftTestingPath,
140149
swiftPMTestingHelperPath
141150
);
142151
}
@@ -638,6 +647,31 @@ export class SwiftToolchain {
638647
return undefined;
639648
}
640649

650+
/**
651+
* @param targetInfo swift target info
652+
* @param swiftVersion parsed swift version
653+
* @param runtimePath path to Swift runtime
654+
* @param sdkroot path to swift SDK
655+
* @returns path to folder where xctest can be found
656+
*/
657+
private static async getSwiftTestingPath(
658+
targetInfo: SwiftTargetInfo,
659+
swiftVersion: Version,
660+
runtimePath: string | undefined,
661+
sdkroot: string | undefined
662+
): Promise<string | undefined> {
663+
if (process.platform !== "win32") {
664+
return undefined;
665+
}
666+
return this.getWindowsPlatformDLLPath(
667+
"Testing",
668+
targetInfo,
669+
swiftVersion,
670+
runtimePath,
671+
sdkroot
672+
);
673+
}
674+
641675
/**
642676
* @param targetInfo swift target info
643677
* @param swiftVersion parsed swift version
@@ -663,80 +697,95 @@ export class SwiftToolchain {
663697
return path.join(developerDir, "usr", "bin");
664698
}
665699
case "win32": {
666-
// look up runtime library directory for XCTest alternatively
667-
const fallbackPath =
668-
runtimePath !== undefined &&
669-
(await pathExists(path.join(runtimePath, "XCTest.dll")))
670-
? runtimePath
671-
: undefined;
672-
if (!sdkroot) {
673-
return fallbackPath;
674-
}
675-
const platformPath = path.dirname(path.dirname(path.dirname(sdkroot)));
676-
const platformManifest = path.join(platformPath, "Info.plist");
677-
if ((await pathExists(platformManifest)) !== true) {
678-
if (fallbackPath) {
679-
return fallbackPath;
680-
}
681-
vscode.window.showWarningMessage(
682-
"XCTest not found due to non-standardized library layout. Tests explorer won't work as expected."
683-
);
684-
return undefined;
685-
}
686-
const data = await fs.readFile(platformManifest, "utf8");
687-
let infoPlist;
688-
try {
689-
infoPlist = plist.parse(data) as unknown as InfoPlist;
690-
} catch (error) {
691-
vscode.window.showWarningMessage(
692-
`Unable to parse ${platformManifest}: ${error}`
693-
);
694-
return undefined;
695-
}
696-
const version = infoPlist.DefaultProperties.XCTEST_VERSION;
697-
if (!version) {
698-
throw Error("Info.plist is missing the XCTEST_VERSION key.");
699-
}
700-
701-
if (swiftVersion.isGreaterThanOrEqual(new Version(5, 7, 0))) {
702-
let bindir: string;
703-
const arch = targetInfo.target?.triple.split("-", 1)[0];
704-
switch (arch) {
705-
case "x86_64":
706-
bindir = "bin64";
707-
break;
708-
case "i686":
709-
bindir = "bin32";
710-
break;
711-
case "aarch64":
712-
bindir = "bin64a";
713-
break;
714-
default:
715-
throw Error(`unsupported architecture ${arch}`);
716-
}
717-
return path.join(
718-
platformPath,
719-
"Developer",
720-
"Library",
721-
`XCTest-${version}`,
722-
"usr",
723-
bindir
724-
);
725-
} else {
726-
return path.join(
727-
platformPath,
728-
"Developer",
729-
"Library",
730-
`XCTest-${version}`,
731-
"usr",
732-
"bin"
733-
);
734-
}
700+
return await this.getWindowsPlatformDLLPath(
701+
"XCTest",
702+
targetInfo,
703+
swiftVersion,
704+
runtimePath,
705+
sdkroot
706+
);
735707
}
736708
}
737709
return undefined;
738710
}
739711

712+
private static async getWindowsPlatformDLLPath(
713+
type: "XCTest" | "Testing",
714+
targetInfo: SwiftTargetInfo,
715+
swiftVersion: Version,
716+
runtimePath: string | undefined,
717+
sdkroot: string | undefined
718+
): Promise<string | undefined> {
719+
// look up runtime library directory for XCTest/Testing alternatively
720+
const fallbackPath =
721+
runtimePath !== undefined && (await pathExists(path.join(runtimePath, `${type}.dll`)))
722+
? runtimePath
723+
: undefined;
724+
if (!sdkroot) {
725+
return fallbackPath;
726+
}
727+
728+
const platformPath = path.dirname(path.dirname(path.dirname(sdkroot)));
729+
const platformManifest = path.join(platformPath, "Info.plist");
730+
if ((await pathExists(platformManifest)) !== true) {
731+
if (fallbackPath) {
732+
return fallbackPath;
733+
}
734+
vscode.window.showWarningMessage(
735+
`${type} not found due to non-standardized library layout. Tests explorer won't work as expected.`
736+
);
737+
return undefined;
738+
}
739+
const data = await fs.readFile(platformManifest, "utf8");
740+
let infoPlist;
741+
try {
742+
infoPlist = plist.parse(data) as unknown as InfoPlist;
743+
} catch (error) {
744+
vscode.window.showWarningMessage(`Unable to parse ${platformManifest}: ${error}`);
745+
return undefined;
746+
}
747+
const plistKey = type === "XCTest" ? "XCTEST_VERSION" : "SWIFT_TESTING_VERSION";
748+
const version = infoPlist.DefaultProperties[plistKey];
749+
if (!version) {
750+
throw Error(`Info.plist is missing the ${plistKey} key.`);
751+
}
752+
753+
if (swiftVersion.isGreaterThanOrEqual(new Version(5, 7, 0))) {
754+
let bindir: string;
755+
const arch = targetInfo.target?.triple.split("-", 1)[0];
756+
switch (arch) {
757+
case "x86_64":
758+
bindir = "bin64";
759+
break;
760+
case "i686":
761+
bindir = "bin32";
762+
break;
763+
case "aarch64":
764+
bindir = "bin64a";
765+
break;
766+
default:
767+
throw Error(`unsupported architecture ${arch}`);
768+
}
769+
return path.join(
770+
platformPath,
771+
"Developer",
772+
"Library",
773+
`${type}-${version}`,
774+
"usr",
775+
bindir
776+
);
777+
} else {
778+
return path.join(
779+
platformPath,
780+
"Developer",
781+
"Library",
782+
`${type}-${version}`,
783+
"usr",
784+
"bin"
785+
);
786+
}
787+
}
788+
740789
/** @returns swift target info */
741790
private static async getSwiftTargetInfo(): Promise<SwiftTargetInfo> {
742791
try {

0 commit comments

Comments
 (0)