@@ -31,6 +31,7 @@ import { Sanitizer } from "./Sanitizer";
3131interface InfoPlist {
3232 DefaultProperties : {
3333 XCTEST_VERSION : string | undefined ;
34+ SWIFT_TESTING_VERSION : string | undefined ;
3435 } ;
3536}
3637
@@ -50,6 +51,7 @@ interface SwiftTargetInfo {
5051 compilerVersion : string ;
5152 target ?: {
5253 triple : string ;
54+ unversionedTriple : string ;
5355 [ name : string ] : string | string [ ] ;
5456 } ;
5557 paths : {
@@ -97,18 +99,22 @@ export function getDarwinTargetTriple(target: DarwinCompatibleTarget): string |
9799}
98100
99101export class SwiftToolchain {
102+ public swiftVersionString : string ;
103+
100104 constructor (
101105 public swiftFolderPath : string , // folder swift executable in $PATH was found in
102106 public toolchainPath : string , // toolchain folder. One folder up from swift bin folder. This is to support toolchains without usr folder
103- public swiftVersionString : string , // Swift version as a string, including description
107+ private targetInfo : SwiftTargetInfo ,
104108 public swiftVersion : Version , // Swift version as semVar variable
105109 public runtimePath ?: string , // runtime library included in output from `swift -print-target-info`
106- private defaultTarget ?: string ,
107110 public defaultSDK ?: string ,
108111 public customSDK ?: string ,
109112 public xcTestPath ?: string ,
113+ public swiftTestingPath ?: string ,
110114 public swiftPMTestingHelperPath ?: string
111- ) { }
115+ ) {
116+ this . swiftVersionString = targetInfo . compilerVersion ;
117+ }
112118
113119 static async create ( ) : Promise < SwiftToolchain > {
114120 const swiftFolderPath = await this . getSwiftFolderPath ( ) ;
@@ -125,22 +131,32 @@ export class SwiftToolchain {
125131 runtimePath ,
126132 customSDK ?? defaultSDK
127133 ) ;
134+ const swiftTestingPath = await this . getSwiftTestingPath (
135+ targetInfo ,
136+ swiftVersion ,
137+ runtimePath ,
138+ customSDK ?? defaultSDK
139+ ) ;
128140 const swiftPMTestingHelperPath = await this . getSwiftPMTestingHelperPath ( toolchainPath ) ;
129141
130142 return new SwiftToolchain (
131143 swiftFolderPath ,
132144 toolchainPath ,
133- targetInfo . compilerVersion ,
145+ targetInfo ,
134146 swiftVersion ,
135147 runtimePath ,
136- targetInfo . target ?. triple ,
137148 defaultSDK ,
138149 customSDK ,
139150 xcTestPath ,
151+ swiftTestingPath ,
140152 swiftPMTestingHelperPath
141153 ) ;
142154 }
143155
156+ public get unversionedTriple ( ) : string | undefined {
157+ return this . targetInfo . target ?. unversionedTriple ;
158+ }
159+
144160 /** build flags */
145161 public get buildFlags ( ) : BuildFlags {
146162 return new BuildFlags ( this ) ;
@@ -445,8 +461,8 @@ export class SwiftToolchain {
445461 if ( this . runtimePath ) {
446462 str += `\nRuntime Library Path: ${ this . runtimePath } ` ;
447463 }
448- if ( this . defaultTarget ) {
449- str += `\nDefault Target: ${ this . defaultTarget } ` ;
464+ if ( this . targetInfo . target ?. triple ) {
465+ str += `\nDefault Target: ${ this . targetInfo . target ?. triple } ` ;
450466 }
451467 if ( this . defaultSDK ) {
452468 str += `\nDefault SDK: ${ this . defaultSDK } ` ;
@@ -638,6 +654,31 @@ export class SwiftToolchain {
638654 return undefined ;
639655 }
640656
657+ /**
658+ * @param targetInfo swift target info
659+ * @param swiftVersion parsed swift version
660+ * @param runtimePath path to Swift runtime
661+ * @param sdkroot path to swift SDK
662+ * @returns path to folder where xctest can be found
663+ */
664+ private static async getSwiftTestingPath (
665+ targetInfo : SwiftTargetInfo ,
666+ swiftVersion : Version ,
667+ runtimePath : string | undefined ,
668+ sdkroot : string | undefined
669+ ) : Promise < string | undefined > {
670+ if ( process . platform !== "win32" ) {
671+ return undefined ;
672+ }
673+ return this . getWindowsPlatformDLLPath (
674+ "Testing" ,
675+ targetInfo ,
676+ swiftVersion ,
677+ runtimePath ,
678+ sdkroot
679+ ) ;
680+ }
681+
641682 /**
642683 * @param targetInfo swift target info
643684 * @param swiftVersion parsed swift version
@@ -663,80 +704,95 @@ export class SwiftToolchain {
663704 return path . join ( developerDir , "usr" , "bin" ) ;
664705 }
665706 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- }
707+ return await this . getWindowsPlatformDLLPath (
708+ "XCTest" ,
709+ targetInfo ,
710+ swiftVersion ,
711+ runtimePath ,
712+ sdkroot
713+ ) ;
735714 }
736715 }
737716 return undefined ;
738717 }
739718
719+ private static async getWindowsPlatformDLLPath (
720+ type : "XCTest" | "Testing" ,
721+ targetInfo : SwiftTargetInfo ,
722+ swiftVersion : Version ,
723+ runtimePath : string | undefined ,
724+ sdkroot : string | undefined
725+ ) : Promise < string | undefined > {
726+ // look up runtime library directory for XCTest/Testing alternatively
727+ const fallbackPath =
728+ runtimePath !== undefined && ( await pathExists ( path . join ( runtimePath , `${ type } .dll` ) ) )
729+ ? runtimePath
730+ : undefined ;
731+ if ( ! sdkroot ) {
732+ return fallbackPath ;
733+ }
734+
735+ const platformPath = path . dirname ( path . dirname ( path . dirname ( sdkroot ) ) ) ;
736+ const platformManifest = path . join ( platformPath , "Info.plist" ) ;
737+ if ( ( await pathExists ( platformManifest ) ) !== true ) {
738+ if ( fallbackPath ) {
739+ return fallbackPath ;
740+ }
741+ vscode . window . showWarningMessage (
742+ `${ type } not found due to non-standardized library layout. Tests explorer won't work as expected.`
743+ ) ;
744+ return undefined ;
745+ }
746+ const data = await fs . readFile ( platformManifest , "utf8" ) ;
747+ let infoPlist ;
748+ try {
749+ infoPlist = plist . parse ( data ) as unknown as InfoPlist ;
750+ } catch ( error ) {
751+ vscode . window . showWarningMessage ( `Unable to parse ${ platformManifest } : ${ error } ` ) ;
752+ return undefined ;
753+ }
754+ const plistKey = type === "XCTest" ? "XCTEST_VERSION" : "SWIFT_TESTING_VERSION" ;
755+ const version = infoPlist . DefaultProperties [ plistKey ] ;
756+ if ( ! version ) {
757+ throw Error ( `Info.plist is missing the ${ plistKey } key.` ) ;
758+ }
759+
760+ if ( swiftVersion . isGreaterThanOrEqual ( new Version ( 5 , 7 , 0 ) ) ) {
761+ let bindir : string ;
762+ const arch = targetInfo . target ?. triple . split ( "-" , 1 ) [ 0 ] ;
763+ switch ( arch ) {
764+ case "x86_64" :
765+ bindir = "bin64" ;
766+ break ;
767+ case "i686" :
768+ bindir = "bin32" ;
769+ break ;
770+ case "aarch64" :
771+ bindir = "bin64a" ;
772+ break ;
773+ default :
774+ throw Error ( `unsupported architecture ${ arch } ` ) ;
775+ }
776+ return path . join (
777+ platformPath ,
778+ "Developer" ,
779+ "Library" ,
780+ `${ type } -${ version } ` ,
781+ "usr" ,
782+ bindir
783+ ) ;
784+ } else {
785+ return path . join (
786+ platformPath ,
787+ "Developer" ,
788+ "Library" ,
789+ `${ type } -${ version } ` ,
790+ "usr" ,
791+ "bin"
792+ ) ;
793+ }
794+ }
795+
740796 /** @returns swift target info */
741797 private static async getSwiftTargetInfo ( ) : Promise < SwiftTargetInfo > {
742798 try {
0 commit comments