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
1 change: 1 addition & 0 deletions Sources/SWBTaskConstruction/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ add_library(SWBTaskConstruction
TaskProducers/StandardTaskProducer.swift
TaskProducers/TaskProducer.swift
TaskProducers/TaskProducerExtensionPoint.swift
TaskProducers/WorkspaceTaskProducers/CASConfigFileTaskProducer.swift
TaskProducers/WorkspaceTaskProducers/BuildDependencyInfoTaskProducer.swift
TaskProducers/WorkspaceTaskProducers/CreateBuildDirectoryTaskProducer.swift
TaskProducers/WorkspaceTaskProducers/HeadermapVFSTaskProducer.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ private struct WorkspaceProductPlanBuilder {
HeadermapVFSTaskProducer(context: globalTaskProducerContext, targetContexts: targetContexts),
PCHModuleMapTaskProducer(context: globalTaskProducerContext, targetContexts: targetContexts),
BuildDependencyInfoTaskProducer(context: globalTaskProducerContext, targetContexts: targetContexts),
CASConfigFileTaskProducer(context: globalTaskProducerContext, targetContexts: targetContexts),
] + (globalProductPlan.planRequest.buildRequest.enableIndexBuildArena ? [IndexBuildVFSDirectoryRemapTaskProducer(context: globalTaskProducerContext)] : [])

for taskProducerExtension in await taskProducerExtensions(globalTaskProducerContext.workspaceContext) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SWBCore
import SWBUtil
import SWBMacro
import Foundation
import SWBProtocol

final class CASConfigFileTaskProducer: StandardTaskProducer, TaskProducer {
private let targetContexts: [TaskProducerContext]

init(context globalContext: TaskProducerContext, targetContexts: [TaskProducerContext]) {
self.targetContexts = targetContexts
super.init(globalContext)
}

func generateTasks() async -> [any SWBCore.PlannedTask] {
var tasks = [any PlannedTask]()
do {
let casConfigFiles = try Dictionary(try await targetContexts.concurrentMap(maximumParallelism: 100) { (targetContext: TaskProducerContext) async throws -> (Path, ByteString)? in
let scope = targetContext.settings.globalScope

// If compilation caching is not on, then there is no file to write.
// The condition here is more relax than the actual check in the compile task generation
// since it won't hurt if the file is not used.
guard scope.evaluate(BuiltinMacros.CLANG_ENABLE_COMPILE_CACHE) || scope.evaluate(BuiltinMacros.SWIFT_ENABLE_COMPILE_CACHE) else {
return nil
}

// FIXME: we need consistent CAS configuration across all languages.
if !scope.evaluate(BuiltinMacros.COMPILATION_CACHE_REMOTE_SERVICE_PATH).isEmpty && !scope.evaluate(BuiltinMacros.COMPILATION_CACHE_REMOTE_SUPPORTED_LANGUAGES).isEmpty {
return nil
}

let casOpts = try CASOptions.create(scope, .compiler(.other(dialectName: "swift")))
struct CASConfig: Encodable {
let CASPath: String
let PluginPath: String?
}
let content = try JSONEncoder().encode(CASConfig(CASPath: casOpts.casPath.str, PluginPath: casOpts.pluginPath?.str))
let path = scope.evaluate(BuiltinMacros.TARGET_TEMP_DIR).join(".cas-config")
return (path, ByteString(content))
}.compactMap { $0 }, uniquingKeysWith: { first, second in
guard first == second else {
throw StubError.error("Unexpected difference in CAS config file.\nPath: \(first.asString)\nContent:\(second.asString)")
}
return first
})

for (configFilePath, configFileContent) in casConfigFiles {
await appendGeneratedTasks(&tasks) { delegate in
context.writeFileSpec.constructFileTasks(CommandBuildContext(producer: context, scope: context.settings.globalScope, inputs: [], output: configFilePath), delegate, contents: configFileContent, permissions: nil, preparesForIndexing: true, additionalTaskOrderingOptions: [.immediate])
}
}
} catch {
self.context.error(error.localizedDescription)
}
return tasks
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ fileprivate struct ClangCompilationCachingTests: CoreBasedTests {
}
#expect(try readMetrics("one") == #"{"global":{"clangCacheHits":0,"clangCacheMisses":1,"swiftCacheHits":0,"swiftCacheMisses":0},"tasks":{"CompileC":{"cacheMisses":1,"headerDependenciesNotValidatedTasks":1,"moduleDependenciesNotValidatedTasks":1}}}"#)

let CASConfigPath = tmpDirPath.join("Test/aProject/build/aProject.build/Debug\(runDestination == .macOS ? "": "-" + runDestination.platform)/Library.build/.cas-config")

#expect(try tester.fs.read(CASConfigPath).asString.contains("\"CASPath\":"))
if usePlugin {
#expect(try tester.fs.read(CASConfigPath).asString.contains("\"PluginPath\":"))
}

// Touch the source file to trigger a new scan.
try await tester.fs.updateTimestamp(testWorkspace.sourceRoot.join("aProject/file.c"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ fileprivate struct SwiftCompilationCachingTests: CoreBasedTests {
}
#expect(try readMetrics("one").contains("\"swiftCacheHits\":0,\"swiftCacheMisses\":\(numCompile)"))

#expect(try tester.fs.read(tmpDirPath.join("Test/aProject/build/aProject.build/Debug-iphoneos/Application.build/.cas-config")).asString.contains("\"CASPath\":"))

// touch a file, clean build folder, and rebuild.
try await tester.fs.updateTimestamp(testWorkspace.sourceRoot.join("aProject/App.swift"))
try await tester.checkBuild(runDestination: .anyiOSDevice, buildCommand: .cleanBuildFolder(style: .regular), body: { _ in })
Expand Down
Loading