-
Notifications
You must be signed in to change notification settings - Fork 128
Merge dumped dependency info #897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| private import Foundation | ||
| public import SWBUtil | ||
| import SWBMacro | ||
|
|
||
| public final class BuildDependencyInfoSpec: CommandLineToolSpec, SpecImplementationType, @unchecked Sendable { | ||
| public static let identifier = "com.apple.tools.build-dependency-info" | ||
|
|
||
| public static func construct(registry: SpecRegistry, proxy: SpecProxy) -> Spec { | ||
| return Self.init(registry: registry) | ||
| } | ||
|
|
||
| public init(registry: SpecRegistry) { | ||
| let proxy = SpecProxy(identifier: Self.identifier, domain: "", path: Path(""), type: Self.self, classType: nil, basedOn: nil, data: ["ExecDescription": PropertyListItem("Merging build dependency info")], localizedStrings: nil) | ||
| super.init(createSpecParser(for: proxy, registry: registry), nil, isGeneric: false) | ||
| } | ||
|
|
||
| required init(_ parser: SpecParser, _ basedOnSpec: Spec?) { | ||
| super.init(parser, basedOnSpec, isGeneric: false) | ||
| } | ||
|
|
||
| override public func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) async { | ||
| fatalError("unexpected direct invocation") | ||
| } | ||
|
|
||
| public func createTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, dumpDependencyPaths: [Path]) async { | ||
| delegate.createTask(type: self, ruleInfo: ["BuildDependencyInfo"], commandLine: ["builtin-build-dependency-info"] + dumpDependencyPaths.map { $0.str }, environment: EnvironmentBindings(), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: dumpDependencyPaths, outputs: [cbc.output], action: delegate.taskActionCreationDelegate.createBuildDependencyInfoTaskAction(), preparesForIndexing: false, enableSandboxing: false) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 BuildDependencyInfoTaskProducer: StandardTaskProducer, TaskProducer { | ||
| private let targetContexts: [TaskProducerContext] | ||
|
|
||
| init(context globalContext: TaskProducerContext, targetContexts: [TaskProducerContext]) { | ||
| self.targetContexts = targetContexts | ||
| super.init(globalContext) | ||
| } | ||
|
|
||
| func generateTasks() async -> [any PlannedTask] { | ||
| let components = context.globalProductPlan.planRequest.buildRequest.parameters.action.buildComponents | ||
| guard components.contains("build") else { | ||
| return [] | ||
| } | ||
|
|
||
| let output = context.settings.globalScope.evaluate(BuiltinMacros.BUILD_DIR).join("BuildDependencyInfo.json") | ||
| let dumpDependencyPaths: [Path] = targetContexts.compactMap { | ||
| guard let target = $0.configuredTarget?.target as? SWBCore.StandardTarget else { | ||
| return nil | ||
| } | ||
| guard target.sourcesBuildPhase?.buildFiles.isEmpty == false else { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note there are some edge cases where empty targets still do some work (generation of the apple versioning .c file for example), I think there is code somewhere to handle that. If it doesn't hurt, you might just want to not skip based on this.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right that we should handle this, but it seems like today the knowledge only exists inside |
||
| return nil | ||
| } | ||
| if $0.settings.globalScope.evaluate(BuiltinMacros.DUMP_DEPENDENCIES) { | ||
| return $0.settings.globalScope.evaluate(BuiltinMacros.DUMP_DEPENDENCIES_OUTPUT_PATH) | ||
| } else { | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| if dumpDependencyPaths.isEmpty { | ||
| return [] | ||
| } | ||
|
|
||
| var tasks = [any PlannedTask]() | ||
| await appendGeneratedTasks(&tasks) { delegate in | ||
| await context.buildDependencyInfoSpec.createTasks( | ||
| CommandBuildContext(producer: context, scope: context.settings.globalScope, inputs: dumpDependencyPaths.map { FileToBuild(context: context, absolutePath: $0) }, output: output, commandOrderingInputs: []), | ||
| delegate, | ||
| dumpDependencyPaths: dumpDependencyPaths | ||
| ) | ||
| } | ||
| return tasks | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 ArgumentParser | ||
| import Foundation | ||
|
|
||
| public import SWBCore | ||
| internal import SWBMacro | ||
| internal import SWBProtocol | ||
| import SWBUtil | ||
|
|
||
| public final class BuildDependencyInfoTaskAction: TaskAction { | ||
| public override class var toolIdentifier: String { | ||
| return "build-dependency-info" | ||
| } | ||
|
|
||
| private struct Options: ParsableArguments { | ||
| @Argument var inputs: [Path] | ||
| } | ||
|
|
||
| public override func performTaskAction(_ task: any ExecutableTask, dynamicExecutionDelegate: any DynamicTaskExecutionDelegate, executionDelegate: any TaskExecutionDelegate, clientDelegate: any TaskExecutionClientDelegate, outputDelegate: any TaskOutputDelegate) async -> CommandResult { | ||
| guard let outputPath = task.outputPaths.first else { | ||
| outputDelegate.emitError("could not determine output path") | ||
| return .failed | ||
| } | ||
|
|
||
| do { | ||
| let options = try Options.parse(Array(task.commandLineAsStrings.dropFirst())) | ||
|
|
||
| var errors = [String]() | ||
| var targets = [BuildDependencyInfo.TargetDependencyInfo]() | ||
| for dumpDependencyPath in options.inputs { | ||
| let dumpDependencyData = try Data(contentsOf: URL(fileURLWithPath: dumpDependencyPath.str)) | ||
| let dumpDependencyInfo = try JSONDecoder().decode(BuildDependencyInfo.self, from: dumpDependencyData) | ||
| errors.append(contentsOf: dumpDependencyInfo.errors) | ||
| targets.append(contentsOf: dumpDependencyInfo.targets) | ||
| } | ||
|
|
||
| let dependencyInfo = BuildDependencyInfo(targets: targets, errors: errors) | ||
| let outputData = try JSONEncoder(outputFormatting: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]).encode(dependencyInfo) | ||
| let outputURL = URL(fileURLWithPath: outputPath.str) | ||
| try outputData.write(to: outputURL) | ||
| } catch { | ||
| outputDelegate.emitError(error.localizedDescription) | ||
| return .failed | ||
| } | ||
|
|
||
| return .succeeded | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dumpDependencyPaths here could probably be cbc.inputs