Skip to content

Commit a816d6d

Browse files
committed
Add some test utility functions for Windows
This allows detecting the components present within a Visual Studio installation for better test control.
1 parent 7911075 commit a816d6d

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

Sources/SWBWindowsPlatform/Plugin.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
public import SWBUtil
14-
import SWBCore
14+
public import SWBCore
1515
import Foundation
1616

1717
@PluginExtensionSystemActor public func initializePlugin(_ manager: PluginManager) {
@@ -22,10 +22,10 @@ import Foundation
2222
manager.register(WindowsSDKRegistryExtension(), type: SDKRegistryExtensionPoint.self)
2323
}
2424

25-
final class WindowsPlugin: Sendable {
25+
public final class WindowsPlugin: Sendable {
2626
private let vsInstallations = AsyncSingleValueCache<[VSInstallation], any Error>()
2727

28-
func cachedVSInstallations() async throws -> [VSInstallation] {
28+
public func cachedVSInstallations() async throws -> [VSInstallation] {
2929
try await vsInstallations.value {
3030
// Always pass localFS because this will be cached, and executes a process on the host system so there's no reason to pass in any proxy.
3131
try await VSInstallation.findInstallations(fs: localFS)
@@ -39,10 +39,10 @@ struct WindowsPlatformSpecsExtension: SpecificationsExtension {
3939
}
4040
}
4141

42-
struct WindowsEnvironmentExtension: EnvironmentExtension {
43-
let plugin: WindowsPlugin
42+
@_spi(Testing) public struct WindowsEnvironmentExtension: EnvironmentExtension {
43+
public let plugin: WindowsPlugin
4444

45-
func additionalEnvironmentVariables(context: any EnvironmentExtensionAdditionalEnvironmentVariablesContext) async throws -> [String: String] {
45+
@_spi(Testing) public func additionalEnvironmentVariables(context: any EnvironmentExtensionAdditionalEnvironmentVariablesContext) async throws -> [String: String] {
4646
if context.hostOperatingSystem == .windows {
4747
// Add the environment variable for the MSVC toolset for Swift and Clang to find it
4848
let vcToolsInstallDir = "VCToolsInstallDir"

Sources/SWBWindowsPlatform/VSInstallation.swift

+22
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,29 @@ public import SWBUtil
1515
import SWBCore
1616

1717
public struct VSInstallation: Decodable, Sendable {
18+
public struct Component: Decodable, Sendable {
19+
public struct ID: Decodable, Sendable, RawRepresentable {
20+
public let rawValue: String
21+
22+
public init(_ string: String) {
23+
self.rawValue = string
24+
}
25+
26+
public init?(rawValue: String) {
27+
self.rawValue = rawValue
28+
}
29+
30+
public init(from decoder: any Swift.Decoder) throws {
31+
rawValue = try String(from: decoder)
32+
}
33+
}
34+
35+
public let id: Component.ID
36+
}
37+
1838
public let installationPath: Path
1939
public let installationVersion: Version
40+
public let packages: [Component]
2041

2142
/// Returns a list of all Visual Studio installations on the system, ordered from newest version and last installed to oldest.
2243
///
@@ -31,6 +52,7 @@ public struct VSInstallation: Decodable, Sendable {
3152
"-prerelease",
3253
"-sort",
3354
"-format", "json",
55+
"-include", "packages",
3456
"-utf8",
3557
]
3658
let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: vswhere.str), arguments: args)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SWBCore
14+
@_spi(Testing) import SWBWindowsPlatform
15+
16+
extension VSInstallation.Component.ID {
17+
static let visualCppBuildTools_x86_x64 = Self("Microsoft.VisualStudio.Component.VC.Tools.x86.x64")
18+
static let visualCppBuildTools_arm = Self("Microsoft.VisualStudio.Component.VC.Tools.ARM")
19+
static let visualCppBuildTools_arm64 = Self("Microsoft.VisualStudio.Component.VC.Tools.ARM64")
20+
static let visualCppBuildTools_arm64ec = Self("Microsoft.VisualStudio.Component.VC.Tools.ARM64EC")
21+
}
22+
23+
extension Core {
24+
func hasVisualStudioComponent(_ name: VSInstallation.Component.ID) async throws -> Bool {
25+
try await pluginManager.extensions(of: EnvironmentExtensionPoint.self).compactMap({ $0 as? WindowsEnvironmentExtension }).first?.plugin.cachedVSInstallations().first?.packages.contains(where: { $0.id == name }) == true
26+
}
27+
}

0 commit comments

Comments
 (0)