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
42 changes: 42 additions & 0 deletions Sources/SWBBuildService/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,47 @@ private struct GetPlatformsDumpMsg: MessageHandler {
}
}

private struct BuildCacheInfoMsg: MessageHandler {
func handle(request: Request, message: BuildCacheInfoRequest) throws -> BuildCacheInfoResponse {
let core = try request.session(for: message).core

let registry = core.toolchainRegistry
let os = core.hostOperatingSystem
let toolchainsToSearch: [Toolchain] = ([registry.defaultToolchain] + registry.toolchains.sorted(by: \.identifier)).compactMap { $0 }
var libclang: Libclang? = nil
for toolchain in toolchainsToSearch {
guard let libclangPath = try? toolchain.lookup(subject: .library(basename: "clang"), operatingSystem: os) else {
continue
}
if let found = core.lookupLibclang(path: libclangPath).libclang {
libclang = found
break
}
}
guard let libclang else {
throw StubError.error("could not load libclang to query the build cache")
}

let onDiskPath = Path(message.casPath).join(message.pluginEnabled ? "plugin" : "builtin")

let casOptions = try ClangCASOptions(libclang).setOnDiskPath(onDiskPath.str)
if let pluginPath = message.pluginPath, !pluginPath.isEmpty {
casOptions.setPluginPath(pluginPath)
} else if message.pluginEnabled, case .xcode(let xcodeDeveloperDir) = core.developerPath {
casOptions.setPluginPath(xcodeDeveloperDir.join(Path("usr/lib/libToolchainCASPlugin.dylib")).str)
}
if let remoteServicePath = message.remoteServicePath, !remoteServicePath.isEmpty {
casOptions.setPluginOption(name: "remote-service-path", value: remoteServicePath)
}

let casDatabases = try ClangCASDatabases(options: casOptions)
guard let onDiskSize = try casDatabases.getOndiskSize() else {
throw StubError.error("the build cache does not support reporting its on-disk size")
}
return BuildCacheInfoResponse(onDiskSize: Int(onDiskSize))
}
}

private struct GetSDKsDumpMsg: MessageHandler {
func handle(request: Request, message: GetSDKsRequest) throws -> StringResponse {
return StringResponse(try request.session(for: message).core.getSDKsDump())
Expand Down Expand Up @@ -1620,6 +1661,7 @@ package struct ServiceMessageHandlers: ServiceExtension {
service.registerMessageHandler(ClearAllCaches.self)

service.registerMessageHandler(GetPlatformsDumpMsg.self)
service.registerMessageHandler(BuildCacheInfoMsg.self)
service.registerMessageHandler(GetSDKsDumpMsg.self)
service.registerMessageHandler(GetToolchainsDumpMsg.self)
service.registerMessageHandler(GetSpecsDumpMsg.self)
Expand Down
4 changes: 3 additions & 1 deletion Sources/SWBCore/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,9 @@ final class WorkspaceSettings: Sendable {
table.push(BuiltinMacros.SDK_EXPLICIT_MODULES_OUTPUT_PATH, Static { BuiltinMacros.namespace.parseString("$(DERIVED_DATA_DIR)/SDKExplicitPrecompiledModules") })

// Add default values for the compilation caching plugin (off-by-default).
table.push(BuiltinMacros.COMPILATION_CACHE_PLUGIN_PATH, Static { BuiltinMacros.namespace.parseString("$(DEVELOPER_USR_DIR)/lib/libToolchainCASPlugin.dylib") })
if case .xcode(_) = core.developerPath {
table.push(BuiltinMacros.COMPILATION_CACHE_PLUGIN_PATH, Static { BuiltinMacros.namespace.parseString("$(DEVELOPER_USR_DIR)/lib/libToolchainCASPlugin.dylib") })
}

// Enable the integrated driver
table.push(BuiltinMacros.SWIFT_USE_INTEGRATED_DRIVER, literal: true)
Expand Down
33 changes: 33 additions & 0 deletions Sources/SWBProtocol/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,37 @@ public struct GetPlatformsRequest: SessionMessage, RequestMessage, Equatable {
}
}

public struct BuildCacheInfoRequest: SessionMessage, RequestMessage, Equatable, SerializableCodable {
public typealias ResponseMessage = BuildCacheInfoResponse

public static let name = "BUILD_CACHE_INFO"

public let sessionHandle: String
public let casPath: String
public let pluginPath: String?
public let remoteServicePath: String?
public let pluginEnabled: Bool

public init(sessionHandle: String, casPath: String, pluginPath: String?, remoteServicePath: String?, pluginEnabled: Bool) {
self.sessionHandle = sessionHandle
self.casPath = casPath
self.pluginPath = pluginPath
self.remoteServicePath = remoteServicePath
self.pluginEnabled = pluginEnabled
}
}

public struct BuildCacheInfoResponse: Message, Equatable, SerializableCodable {
public static let name = "BUILD_CACHE_INFO_RESPONSE"

/// The on-disk size of the build cache in bytes.
public let onDiskSize: Int

public init(onDiskSize: Int) {
self.onDiskSize = onDiskSize
}
}

public struct GetSDKsRequest: SessionMessage, RequestMessage, Equatable {
public typealias ResponseMessage = StringResponse

Expand Down Expand Up @@ -1243,6 +1274,8 @@ public struct IPCMessage: Serializable, Sendable {
GetSpecsRequest.self,
GetStatisticsRequest.self,
GetToolchainsRequest.self,
BuildCacheInfoRequest.self,
BuildCacheInfoResponse.self,
GetBuildSettingsDescriptionRequest.self,
ExecuteCommandLineToolRequest.self,

Expand Down
14 changes: 14 additions & 0 deletions Sources/SwiftBuild/SWBBuildServiceSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ public final class SWBBuildServiceSession: Sendable {
return try msg.output.map { try SWBPreviewTargetDependencyInfo($0) }
}

public func buildCacheInfo(casPath: String, pluginPath: String?, remoteServicePath: String?, pluginEnabled: Bool) async throws -> SWBBuildCacheInfo {
let response = try await service.send(request: BuildCacheInfoRequest(sessionHandle: uid, casPath: casPath, pluginPath: pluginPath, remoteServicePath: remoteServicePath, pluginEnabled: pluginEnabled))
return SWBBuildCacheInfo(onDiskSize: response.onDiskSize)
}

public func describeSchemes(input: [SWBSchemeInput]) async throws -> [SWBSchemeDescription] {
let response: DescribeSchemesResponse = try await handleProductPlannerMessage(makeMessage: { DescribeSchemesRequest(sessionHandle: self.uid, responseChannel: $0, input: input.map(SchemeInput.init)) })
return response.schemes.map(SWBSchemeDescription.init)
Expand Down Expand Up @@ -1039,3 +1044,12 @@ extension SwiftBuildServicePIFObjectType {
}
}
}

public struct SWBBuildCacheInfo: Sendable {
/// The on-disk size of the build cache in bytes.
public let onDiskSize: Int

public init(onDiskSize: Int) {
self.onDiskSize = onDiskSize
}
}
23 changes: 23 additions & 0 deletions Tests/SwiftBuildTests/ServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,29 @@ fileprivate struct ServiceTests {
}
}

@Test(.requireDependencyScannerPlusCaching)
func buildCacheInfo() async throws {
try await withAsyncDeferrable { deferrable in
let service = try await SWBBuildService()
await deferrable.addBlock {
await service.close()
}

let (result, _) = await service.createSession(name: "BUILD_CACHE_INFO", developerPath: nil, cachePath: nil, inferiorProductsPath: nil, environment: nil)
let createdSession = try result.get()
await deferrable.addBlock {
await #expect(throws: Never.self) {
try await createdSession.close()
}
}

try await withTemporaryDirectory { (tmpDir: Path) in
let info = try await createdSession.buildCacheInfo(casPath: tmpDir.str, pluginPath: nil, remoteServicePath: nil, pluginEnabled: false)
#expect(info.onDiskSize >= 0)
}
}
}

/// Test sending a single large (10K) message.
@Test
func largeMessages() async throws {
Expand Down
Loading