Skip to content
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

Add support for a prebuilt swift-syntax library for macros #8142

Merged
merged 25 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4de8400
[WIP] Add support for prebuilt libraries, specifically swift-syntax
dschaefer2 Nov 27, 2024
ecbfd4e
Get bootstrap and tests building.
dschaefer2 Nov 27, 2024
d8bb3fd
Add progress messages for prebuilts download.
dschaefer2 Nov 28, 2024
a725ca6
Started utility to build prebuilts.
dschaefer2 Dec 2, 2024
c6f8b18
Add publishing of prebuilts for Windows.
dschaefer2 Dec 3, 2024
e371880
Fix linking on Windows.
Dec 3, 2024
1961868
Make sure update prebuilts is called from all paths.
dschaefer2 Dec 4, 2024
2f2bf48
Complete prebuilts generator.
dschaefer2 Dec 10, 2024
6ebb14f
Merge branch 'main' of https://github.com/swiftlang/swift-package-man…
dschaefer2 Dec 10, 2024
2dc8caa
Fix when there is no manifest file yet.
dschaefer2 Dec 10, 2024
5b4d66f
Fix up Linux distro selectors.
dschaefer2 Dec 11, 2024
15a3811
Use absolute path to tar.exe.
dschaefer2 Dec 12, 2024
3521f30
Completed initial test suite.
dschaefer2 Dec 16, 2024
fd036a6
Added option to turn off, default is off until more testing.
dschaefer2 Dec 16, 2024
511542c
Merge remote-tracking branch 'origin/main' into prebuilts
dschaefer2 Dec 16, 2024
aaa883a
Add new source file to CMakeLists.
dschaefer2 Dec 17, 2024
97311e4
Added missing source to Workspace CMakeLists.txt file.
dschaefer2 Dec 17, 2024
66d016c
Fix tests on Windows.
dschaefer2 Dec 17, 2024
3f09afa
Better way to finding tar on Windows plus other review comments.
dschaefer2 Dec 18, 2024
8481aee
Remove debug print from ZipArchiver.
dschaefer2 Dec 18, 2024
2e91718
Adopt ArgumentParser and AbsolutePath in the prebuilt build script.
dschaefer2 Dec 20, 2024
d30d2ae
Some final cleanup based on comments.
dschaefer2 Dec 20, 2024
fcdaf0d
Final fixes to get the build and tests on Windows.
dschaefer2 Dec 20, 2024
7eafe65
Switch to using AsyncProcess in the build prebuilts script.
dschaefer2 Dec 20, 2024
0e6631f
Fix AsyncProcess call from BuildPrebuilts on Windows.
dschaefer2 Dec 20, 2024
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
8 changes: 8 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,14 @@ let package = Package(
name: "swift-package-registry",
dependencies: ["Commands", "PackageRegistryCommand"]
),
.executableTarget(
/** Utility to produce the artifacts for prebuilts */
name: "swift-build-prebuilts",
dependencies: [
"Basics",
"Workspace",
]
),

// MARK: Support for Swift macros, should eventually move to a plugin-based solution

Expand Down
4 changes: 3 additions & 1 deletion Sources/Basics/Archiver/ZipArchiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ public struct ZipArchiver: Archiver, Cancellable {
}

#if os(Windows)
let process = AsyncProcess(arguments: ["tar.exe", "xf", archivePath.pathString, "-C", destinationPath.pathString])
// FileManager lost the ability to detect tar.exe as executable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if that's related to swiftlang/swift-foundation#860

// It's part of system32 anyway so use the absolute path.
let process = AsyncProcess(arguments: ["C:\\Windows\\system32\\tar.exe", "xf", archivePath.pathString, "-C", destinationPath.pathString])
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
#else
let process = AsyncProcess(arguments: ["unzip", archivePath.pathString, "-d", destinationPath.pathString])
#endif
Expand Down
6 changes: 6 additions & 0 deletions Sources/Basics/Collections/IdentifiableSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,9 @@ extension IdentifiableSet: Hashable {
}
}
}

extension IdentifiableSet: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Element...) {
self.init(elements)
}
}
36 changes: 36 additions & 0 deletions Sources/Commands/CommandWorkspaceDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,42 @@ final class CommandWorkspaceDelegate: WorkspaceDelegate {
self.progressHandler(step, total, "Downloading \(artifacts)")
}

/// The workspace has started downloading a binary artifact.
func willDownloadPrebuilt(from url: String, fromCache: Bool) {
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
if fromCache {
self.outputHandler("Fetching package prebuilt \(url) from cache", false)
} else {
self.outputHandler("Downloading package prebuilt \(url)", false)
}
}

/// The workspace has finished downloading a binary artifact.
func didDownloadPrebuilt(
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
from url: String,
result: Result<(path: AbsolutePath, fromCache: Bool), Error>,
duration: DispatchTimeInterval
) {
guard case .success(let fetchDetails) = result, !self.observabilityScope.errorsReported else {
return
}

if fetchDetails.fromCache {
self.outputHandler("Fetched \(url) from cache (\(duration.descriptionInSeconds))", false)
} else {
self.outputHandler("Downloaded \(url) (\(duration.descriptionInSeconds))", false)
}
}

/// The workspace is downloading a binary artifact.
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
func downloadingPrebuilt(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) {

}

/// The workspace finished downloading all binary artifacts.
func didDownloadAllPrebuilts() {
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved

}

// registry signature handlers

func onUnsignedRegistryPackage(registryURL: URL, package: PackageModel.PackageIdentity, version: TSCUtility.Version, completion: (Bool) -> Void) {
Expand Down
6 changes: 6 additions & 0 deletions Sources/CoreCommands/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ public struct CachingOptions: ParsableArguments {
self.init(rawValue: argument)
}
}

/// Whether to use macro prebuilts or not
@Flag(name: .customLong("experimental-prebuilts"),
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
inversion: .prefixedEnableDisable,
help: "Whether to use prebuilt swift-syntax libraries for macros")
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
public var usePrebuilts: Bool = false
}

public struct LoggingOptions: ParsableArguments {
Expand Down
4 changes: 3 additions & 1 deletion Sources/CoreCommands/SwiftCommandState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public typealias WorkspaceDelegateProvider = (
_ progressHandler: @escaping (Int64, Int64, String?) -> Void,
_ inputHandler: @escaping (String, (String?) -> Void) -> Void
) -> WorkspaceDelegate

public typealias WorkspaceLoaderProvider = (_ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope)
-> WorkspaceLoader

Expand Down Expand Up @@ -470,7 +471,8 @@ public final class SwiftCommandState {
// TODO: should supportsAvailability be a flag as well?
.init(url: $0, supportsAvailability: true)
},
manifestImportRestrictions: .none
manifestImportRestrictions: .none,
usePrebuilts: options.caching.usePrebuilts
),
cancellator: self.cancellator,
initializationWarningHandler: { self.observabilityScope.emit(warning: $0) },
Expand Down
13 changes: 12 additions & 1 deletion Sources/PackageGraph/ModulesGraph+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extension ModulesGraph {
requiredDependencies: [PackageReference] = [],
unsafeAllowedPackages: Set<PackageReference> = [],
binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]],
prebuilts: [PackageIdentity: [String: PrebuiltLibrary]],
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
shouldCreateMultipleTestProducts: Bool = false,
createREPLProduct: Bool = false,
customPlatformsRegistry: PlatformRegistry? = .none,
Expand All @@ -47,6 +48,7 @@ extension ModulesGraph {
requiredDependencies: requiredDependencies,
unsafeAllowedPackages: unsafeAllowedPackages,
binaryArtifacts: binaryArtifacts,
prebuilts: prebuilts,
shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts,
createREPLProduct: createREPLProduct,
traitConfiguration: nil,
Expand All @@ -69,6 +71,7 @@ extension ModulesGraph {
requiredDependencies: [PackageReference] = [],
unsafeAllowedPackages: Set<PackageReference> = [],
binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]],
prebuilts: [PackageIdentity: [String: PrebuiltLibrary]], // Product name to library mapping
shouldCreateMultipleTestProducts: Bool = false,
createREPLProduct: Bool = false,
traitConfiguration: TraitConfiguration? = nil,
Expand Down Expand Up @@ -212,7 +215,8 @@ extension ModulesGraph {
productFilter: node.productFilter,
path: packagePath,
additionalFileRules: additionalFileRules,
binaryArtifacts: binaryArtifacts[node.identity] ?? [:],
binaryArtifacts: binaryArtifacts[node.identity] ?? [:],
prebuilts: prebuilts,
shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts,
testEntryPointPath: testEntryPointPath,
createREPLProduct: manifest.packageKind.isRoot ? createREPLProduct : false,
Expand Down Expand Up @@ -246,6 +250,7 @@ extension ModulesGraph {
manifestToPackage: manifestToPackage,
rootManifests: root.manifests,
unsafeAllowedPackages: unsafeAllowedPackages,
prebuilts: prebuilts,
platformRegistry: customPlatformsRegistry ?? .default,
platformVersionProvider: platformVersionProvider,
fileSystem: fileSystem,
Expand Down Expand Up @@ -377,6 +382,7 @@ private func createResolvedPackages(
// FIXME: This shouldn't be needed once <rdar://problem/33693433> is fixed.
rootManifests: [PackageIdentity: Manifest],
unsafeAllowedPackages: Set<PackageReference>,
prebuilts: [PackageIdentity: [String: PrebuiltLibrary]],
platformRegistry: PlatformRegistry,
platformVersionProvider: PlatformVersionProvider,
fileSystem: FileSystem,
Expand Down Expand Up @@ -665,6 +671,11 @@ private func createResolvedPackages(
}
}

if let package = productRef.package, prebuilts[.plain(package)]?[productRef.name] != nil {
// using a prebuilt instead.
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
continue
}

// Find the product in this package's dependency products.
// Look it up by ID if module aliasing is used, otherwise by name.
let product = lookupByProductIDs ? productDependencyMap[productRef.identity] : productDependencyMap[productRef.name]
Expand Down
2 changes: 2 additions & 0 deletions Sources/PackageGraph/ModulesGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ public func loadModulesGraph(
fileSystem: FileSystem,
manifests: [Manifest],
binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]] = [:],
prebuilts: [PackageIdentity: [String: PrebuiltLibrary]] = [:],
explicitProduct: String? = .none,
shouldCreateMultipleTestProducts: Bool = false,
createREPLProduct: Bool = false,
Expand Down Expand Up @@ -451,6 +452,7 @@ public func loadModulesGraph(
.swiftpmFileTypes,
externalManifests: externalManifests,
binaryArtifacts: binaryArtifacts,
prebuilts: prebuilts,
shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts,
createREPLProduct: createREPLProduct,
customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets,
Expand Down
58 changes: 58 additions & 0 deletions Sources/PackageLoading/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,32 @@ public struct BinaryArtifact {
}
}

/// A structure representing a prebuilt library to be used instead of a source dependency
public struct PrebuiltLibrary {
/// The package reference.
public let packageRef: PackageReference

/// The name of the binary target the artifact corresponds to.
public let libraryName: String

/// The path to the extracted prebuilt artifacts
public let path: AbsolutePath

/// The products in the library
public let products: [String]

/// The C modules that need their includes directory added to the include path
public let cModules: [String]

public init(packageRef: PackageReference, libraryName: String, path: AbsolutePath, products: [String], cModules: [String]) {
self.packageRef = packageRef
self.libraryName = libraryName
self.path = path
self.products = products
self.cModules = cModules
}
}

/// Helper for constructing a package following the convention system.
///
/// The 'builder' here refers to the builder pattern and not any build system
Expand Down Expand Up @@ -295,6 +321,9 @@ public final class PackageBuilder {
/// Information concerning the different downloaded or local (archived) binary target artifacts.
private let binaryArtifacts: [String: BinaryArtifact]

/// Prebuilts that may referenced from this package's targets
private let prebuilts: [PackageIdentity: [String: PrebuiltLibrary]]
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved

/// Create multiple test products.
///
/// If set to true, one test product will be created for each test target.
Expand Down Expand Up @@ -351,6 +380,7 @@ public final class PackageBuilder {
path: AbsolutePath,
additionalFileRules: [FileRuleDescription],
binaryArtifacts: [String: BinaryArtifact],
prebuilts: [PackageIdentity: [String: PrebuiltLibrary]],
shouldCreateMultipleTestProducts: Bool = false,
testEntryPointPath: AbsolutePath? = nil,
warnAboutImplicitExecutableTargets: Bool = true,
Expand All @@ -365,6 +395,7 @@ public final class PackageBuilder {
self.packagePath = path
self.additionalFileRules = additionalFileRules
self.binaryArtifacts = binaryArtifacts
self.prebuilts = prebuilts
self.shouldCreateMultipleTestProducts = shouldCreateMultipleTestProducts
self.testEntryPointPath = testEntryPointPath
self.createREPLProduct = createREPLProduct
Expand Down Expand Up @@ -1211,6 +1242,33 @@ public final class PackageBuilder {
table.add(assignment, for: .SWIFT_ACTIVE_COMPILATION_CONDITIONS)
}

// Add in flags for prebuilts
let prebuiltLibraries: [String: PrebuiltLibrary] = target.dependencies.reduce(into: .init()) {
guard case let .product(name: name, package: package, moduleAliases: _, condition: _) = $1,
let package = package,
let prebuilt = prebuilts[.plain(package)]?[name]
else {
return
}

$0[prebuilt.libraryName] = prebuilt
}

for prebuilt in prebuiltLibraries.values {
let libDir = prebuilt.path.appending(component: "lib").pathString
var ldFlagsAssignment = BuildSettings.Assignment()
ldFlagsAssignment.values = ["\(libDir)/lib\(prebuilt.libraryName).a"]
table.add(ldFlagsAssignment, for: .OTHER_LDFLAGS)

var includeDirs: [AbsolutePath] = [prebuilt.path.appending(component: "Modules")]
for cModule in prebuilt.cModules {
includeDirs.append(prebuilt.path.appending(components: "include", cModule))
}
var includeAssignment = BuildSettings.Assignment()
includeAssignment.values = includeDirs.map({ "-I\($0.pathString)" })
table.add(includeAssignment, for: .OTHER_SWIFT_FLAGS)
}

return table
}

Expand Down
1 change: 1 addition & 0 deletions Sources/Workspace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library(Workspace
Workspace+Editing.swift
Workspace+Manifests.swift
Workspace+PackageContainer.swift
Workspace+Prebuilts.swift
Workspace+Registry.swift
Workspace+ResolvedPackages.swift
Workspace+Signing.swift
Expand Down
109 changes: 109 additions & 0 deletions Sources/Workspace/ManagedPrebuilt.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2024 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 Basics
import PackageModel

extension Workspace {
/// A downloaded prebuilt managed by the workspace.
public struct ManagedPrebuilt {
/// The package reference.
public let packageRef: PackageReference

/// The name of the binary target the artifact corresponds to.
public let libraryName: String

/// The path to the extracted prebuilt artifacts
public let path: AbsolutePath

/// The products in the library
public let products: [String]

/// The C modules that need their includes directory added to the include path
public let cModules: [String]
}
}

extension Workspace.ManagedPrebuilt: CustomStringConvertible {
public var description: String {
return "<ManagedArtifact: \(self.packageRef.identity).\(self.libraryName)>"
}
}

// MARK: - ManagedArtifacts

extension Workspace {
/// A collection of managed artifacts which have been downloaded.
public final class ManagedPrebuilts {
/// A mapping from package identity, to target name, to ManagedArtifact.
private var artifactMap: [PackageIdentity: [String: ManagedPrebuilt]]

internal var artifacts: AnyCollection<ManagedPrebuilt> {
AnyCollection(self.artifactMap.values.lazy.flatMap{ $0.values })
}

init() {
self.artifactMap = [:]
}

init(_ artifacts: [ManagedPrebuilt]) throws {
let artifactsByPackagePath = Dictionary(grouping: artifacts, by: { $0.packageRef.identity })
self.artifactMap = try artifactsByPackagePath.mapValues{ artifacts in
// rdar://86857825 do not use Dictionary(uniqueKeysWithValues:) as it can crash the process when input is incorrect such as in older versions of SwiftPM
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
var map = [String: ManagedPrebuilt]()
for artifact in artifacts {
if map[artifact.libraryName] != nil {
throw StringError("binary artifact for '\(artifact.libraryName)' already exists in managed artifacts")
}
map[artifact.libraryName] = artifact
}
return map
}
}

public subscript(packageIdentity packageIdentity: PackageIdentity, targetName targetName: String) -> ManagedPrebuilt? {
self.artifactMap[packageIdentity]?[targetName]
}

public func add(_ artifact: ManagedPrebuilt) {
self.artifactMap[artifact.packageRef.identity, default: [:]][artifact.libraryName] = artifact
}

public func remove(packageIdentity: PackageIdentity, targetName: String) {
self.artifactMap[packageIdentity]?[targetName] = nil
}
}
}

extension Workspace.ManagedPrebuilts: Collection {
public var startIndex: AnyIndex {
self.artifacts.startIndex
}

public var endIndex: AnyIndex {
self.artifacts.endIndex
}

public subscript(index: AnyIndex) -> Workspace.ManagedPrebuilt {
self.artifacts[index]
}

public func index(after index: AnyIndex) -> AnyIndex {
self.artifacts.index(after: index)
}
}

extension Workspace.ManagedPrebuilts: CustomStringConvertible {
dschaefer2 marked this conversation as resolved.
Show resolved Hide resolved
public var description: String {
"<ManagedArtifacts: \(Array(self.artifacts))>"
}
}
Loading