From 1d4648cfc898280ee1595e63679118027c877092 Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:40:02 -0400 Subject: [PATCH 1/2] Add build prebuilts support for any version of swift-syntax. (#8994) (#9011) Removes the hardcoding of the contents of the prebuilt libraries. Adds a --version argument to specify which versions of swift-syntax you want to build for. You can have multiple of these. We add a sample build.sh script to the folder to show an example, including finding the most recent prerelease of 602. Also removes the hardcoding of the list of products that go into the library. Instead we ask the package for its list of library products and add them all. Finally, removes the header files since SwiftPM now hooks up the include paths to the CModules from the source in the checkouts. This is needed in 6.2 to support prebuilts generated with that version of the toolchain. I have tested this on all three host platforms including all Linux distros with both architectures and swift-syntax 600, 601, and the latest prerelease of 602 --- Package.swift | 3 + .../BuildPrebuilts.swift | 579 +++++++----------- Sources/swift-build-prebuilts/build.sh | 4 + 3 files changed, 245 insertions(+), 341 deletions(-) create mode 100755 Sources/swift-build-prebuilts/build.sh diff --git a/Package.swift b/Package.swift index 8b8d103d2f6..b06520f092c 100644 --- a/Package.swift +++ b/Package.swift @@ -785,6 +785,9 @@ let package = Package( .product(name: "ArgumentParser", package: "swift-argument-parser"), "Basics", "Workspace", + ], + exclude: [ + "build.sh" ] ), diff --git a/Sources/swift-build-prebuilts/BuildPrebuilts.swift b/Sources/swift-build-prebuilts/BuildPrebuilts.swift index ffa6b7f3fc1..523c7ddae34 100644 --- a/Sources/swift-build-prebuilts/BuildPrebuilts.swift +++ b/Sources/swift-build-prebuilts/BuildPrebuilts.swift @@ -35,88 +35,13 @@ struct Artifact: Codable { var swiftVersion: String? } -// The master list of repos and their versions -struct PrebuiltRepos: Codable { - let url: URL - let versions: [Version] - - struct Version: Identifiable, Codable { - let tag: String - let manifest: Workspace.PrebuiltsManifest - - var id: String { tag } - } -} - -var prebuiltRepos: [PrebuiltRepos] = [ - .init( - url: .init(string: "https://github.com/swiftlang/swift-syntax")!, - versions: [ - .init( - tag:"600.0.1", - manifest: .init(libraries: [ - .init( - name: "MacroSupport", - products: [ - "SwiftBasicFormat", - "SwiftCompilerPlugin", - "SwiftDiagnostics", - "SwiftIDEUtils", - "SwiftOperators", - "SwiftParser", - "SwiftParserDiagnostics", - "SwiftRefactor", - "SwiftSyntax", - "SwiftSyntaxBuilder", - "SwiftSyntaxMacros", - "SwiftSyntaxMacroExpansion", - "SwiftSyntaxMacrosTestSupport", - "SwiftSyntaxMacrosGenericTestSupport", - "_SwiftCompilerPluginMessageHandling", - "_SwiftLibraryPluginProvider", - ] - ), - ]) - ), - .init( - tag:"601.0.1", - manifest: .init(libraries: [ - .init( - name: "MacroSupport", - products: [ - "SwiftBasicFormat", - "SwiftCompilerPlugin", - "SwiftDiagnostics", - "SwiftIDEUtils", - "SwiftIfConfig", - "SwiftLexicalLookup", - "SwiftOperators", - "SwiftParser", - "SwiftParserDiagnostics", - "SwiftRefactor", - "SwiftSyntax", - "SwiftSyntaxBuilder", - "SwiftSyntaxMacros", - "SwiftSyntaxMacroExpansion", - "SwiftSyntaxMacrosTestSupport", - "SwiftSyntaxMacrosGenericTestSupport", - "_SwiftCompilerPluginMessageHandling", - "_SwiftLibraryPluginProvider", - ] - ), - ]) - ), - ] - ), -] - @main struct BuildPrebuilts: AsyncParsableCommand { @Option(help: "The directory to generate the artifacts to.") var stageDir = try! AbsolutePath(validating: FileManager.default.currentDirectoryPath).appending("stage") - @Option(help: "The config file used to determine the prebuilts to build") - var config = try! AbsolutePath(validating: #file).parentDirectory.appending("config.json") + @Option(name: .customLong("version"), help: "swift-syntax versions to build. Multiple are allowed.") + var versions: [String] = ["600.0.1", "601.0.1"] @Flag(help: "Whether to build the prebuilt artifacts") var build = false @@ -192,7 +117,6 @@ struct BuildPrebuilts: AsyncParsableCommand { let srcDir = stageDir.appending("src") let libDir = stageDir.appending("lib") let modulesDir = stageDir.appending("Modules") - let includesDir = stageDir.appending("include") if fileSystem.exists(srcDir) { try fileSystem.removeFileTree(srcDir) @@ -207,136 +131,110 @@ struct BuildPrebuilts: AsyncParsableCommand { try fileSystem.removeFileTree(modulesDir) } - if fileSystem.exists(includesDir) { - try fileSystem.removeFileTree(includesDir) - } + let id = "swift-syntax" + let libraryName = "MacroSupport" + let repoDir = srcDir.appending(id) + let scratchDir = repoDir.appending(".build") + let buildDir = scratchDir.appending("release") + let srcModulesDir = buildDir.appending("Modules") + let prebuiltDir = stageDir.appending(id) - for repo in prebuiltRepos { - let repoDir = srcDir.appending(repo.url.lastPathComponent) - let scratchDir = repoDir.appending(".build") - let buildDir = scratchDir.appending("release") - let srcModulesDir = buildDir.appending("Modules") - let prebuiltDir = stageDir.appending(repo.url.lastPathComponent) + try await shell("git clone https://github.com/swiftlang/swift-syntax.git", cwd: srcDir) - try await shell("git clone \(repo.url)", cwd: srcDir) - - for version in repo.versions { - let versionDir = prebuiltDir.appending(version.tag) - if !fileSystem.exists(versionDir) { - try fileSystem.createDirectory(versionDir, recursive: true) - } + for version in versions { + let versionDir = prebuiltDir.appending(version) + if !fileSystem.exists(versionDir) { + try fileSystem.createDirectory(versionDir, recursive: true) + } - try await shell("git checkout \(version.tag)", cwd: repoDir) + try await shell("git checkout \(version)", cwd: repoDir) + + // Update package with the libraries + let packageFile = repoDir.appending(component: "Package.swift") + let workspace = try Workspace(fileSystem: fileSystem, location: .init(forRootPackage: repoDir, fileSystem: fileSystem)) + let package = try await workspace.loadRootPackage( + at: repoDir, + observabilityScope: ObservabilitySystem { _, diag in print(diag) }.topScope + ) + + // Gather the list of targets for the package products + let libraryTargets = package.targets(forPackage: package) + + var packageContents = try String(contentsOf: packageFile.asURL) + packageContents += """ + package.products += [ + .library(name: "\(libraryName)", type: .static, targets: [ + \(libraryTargets.map({ "\"\($0.name)\"" }).joined(separator: ",")) + ]) + ] + """ + try packageContents.write(to: packageFile.asURL, atomically: true, encoding: .utf8) - // Update package with the libraries - let packageFile = repoDir.appending(component: "Package.swift") - let workspace = try Workspace(fileSystem: fileSystem, location: .init(forRootPackage: repoDir, fileSystem: fileSystem)) - let package = try await workspace.loadRootPackage( - at: repoDir, - observabilityScope: ObservabilitySystem { _, diag in print(diag) }.topScope - ) + // Build + let cModules = libraryTargets.compactMap({ $0 as? ClangModule }) - // Gather the list of targets for the libraries' products - let libraryTargets: [String: [Module]] = version.manifest.libraries.reduce(into: [:]) { - $0[$1.name] = package.targets(forProducts: $1.products) + for platform in Workspace.PrebuiltsManifest.Platform.allCases { + guard canBuild(platform) else { + continue } - var packageContents = try String(contentsOf: packageFile.asURL) - for (library, targets) in libraryTargets { - packageContents += """ - package.products += [ - .library(name: "\(library)", type: .static, targets: [ - \(targets.map({ "\"\($0.name)\"" }).joined(separator: ",")) - ]) - ] - """ + try fileSystem.createDirectory(libDir, recursive: true) + try fileSystem.createDirectory(modulesDir, recursive: true) + + // Clean out the scratch dir + if fileSystem.exists(scratchDir) { + try fileSystem.removeFileTree(scratchDir) } - try packageContents.write(to: packageFile.asURL, atomically: true, encoding: .utf8) // Build - for library in version.manifest.libraries { - let cModules = libraryTargets[library.name]?.compactMap({ $0 as? ClangModule }) ?? [] - - for platform in Workspace.PrebuiltsManifest.Platform.allCases { - guard canBuild(platform) else { - continue - } + let cmd = "swift build -c release -debug-info-format none --arch \(platform.arch) --product \(libraryName)" + try await shell(cmd, cwd: repoDir) - try fileSystem.createDirectory(libDir, recursive: true) - try fileSystem.createDirectory(modulesDir, recursive: true) - try fileSystem.createDirectory(includesDir, recursive: true) + // Copy the library to staging + let lib = "lib\(libraryName).a" + try fileSystem.copy(from: buildDir.appending(lib), to: libDir.appending(lib)) - // Clean out the scratch dir - if fileSystem.exists(scratchDir) { - try fileSystem.removeFileTree(scratchDir) - } - - // Build - let cmd = "swift build -c release -debug-info-format none --arch \(platform.arch) --product \(library.name)" - try await shell(cmd, cwd: repoDir) - - // Copy the library to staging - let lib = "lib\(library.name).a" - try fileSystem.copy(from: buildDir.appending(lib), to: libDir.appending(lib)) - - // Copy the swiftmodules - for file in try fileSystem.getDirectoryContents(srcModulesDir) { - try fileSystem.copy(from: srcModulesDir.appending(file), to: modulesDir.appending(file)) - } - - // Do a deep copy of the C module headers - for cModule in cModules { - let srcIncludeDir = cModule.includeDir - let destIncludeDir = includesDir.appending(cModule.name) - - try fileSystem.createDirectory(destIncludeDir, recursive: true) - try fileSystem.enumerate(directory: srcIncludeDir) { srcPath in - let destPath = destIncludeDir.appending(srcPath.relative(to: srcIncludeDir)) - try fileSystem.createDirectory(destPath.parentDirectory) - try fileSystem.copy(from: srcPath, to: destPath) - } - } + // Copy the swiftmodules + for file in try fileSystem.getDirectoryContents(srcModulesDir) { + try fileSystem.copy(from: srcModulesDir.appending(file), to: modulesDir.appending(file)) + } - // Zip it up - let contentDirs = ["lib", "Modules"] + (cModules.isEmpty ? [] : ["include"]) + // Zip it up + let contentDirs = ["lib", "Modules"] #if os(Windows) - let zipFile = versionDir.appending("\(swiftVersion)-\(library.name)-\(platform).zip") - try await shell("tar -acf \(zipFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) - let contents = try ByteString(Data(contentsOf: zipFile.asURL)) + let zipFile = versionDir.appending("\(swiftVersion)-\(libraryName)-\(platform).zip") + try await shell("tar -acf \(zipFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) + let contents = try ByteString(Data(contentsOf: zipFile.asURL)) #elseif os(Linux) - let tarFile = versionDir.appending("\(swiftVersion)-\(library.name)-\(platform).tar.gz") - try await shell("tar -zcf \(tarFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) - let contents = try ByteString(Data(contentsOf: tarFile.asURL)) + let tarFile = versionDir.appending("\(swiftVersion)-\(libraryName)-\(platform).tar.gz") + try await shell("tar -zcf \(tarFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) + let contents = try ByteString(Data(contentsOf: tarFile.asURL)) #else - let zipFile = versionDir.appending("\(swiftVersion)-\(library.name)-\(platform).zip") - try await shell("zip -r \(zipFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) - let contents = try ByteString(Data(contentsOf: zipFile.asURL)) + let zipFile = versionDir.appending("\(swiftVersion)-\(libraryName)-\(platform).zip") + try await shell("zip -r \(zipFile.pathString) \(contentDirs.joined(separator: " "))", cwd: stageDir) + let contents = try ByteString(Data(contentsOf: zipFile.asURL)) #endif - // Manifest fragment for the zip file - let checksum = SHA256().hash(contents).hexadecimalRepresentation - let artifact = Artifact( - platform: platform, - checksum: checksum, - libraryName: library.name, - products: library.products, - includePath: cModules.map({ $0.includeDir.relative(to: repoDir ) }), - cModules: cModules.map(\.name), - swiftVersion: swiftVersion - ) - - let artifactJsonFile = versionDir.appending("\(swiftVersion)-\(library.name)-\(platform).zip.json") - try fileSystem.writeFileContents(artifactJsonFile, data: encoder.encode(artifact)) - - // Clean up - try fileSystem.removeFileTree(libDir) - try fileSystem.removeFileTree(modulesDir) - try fileSystem.removeFileTree(includesDir) - } + // Manifest fragment for the zip file + let checksum = SHA256().hash(contents).hexadecimalRepresentation + let artifact = Artifact( + platform: platform, + checksum: checksum, + libraryName: libraryName, + products: package.products.map(\.name), + includePath: cModules.map({ $0.includeDir.relative(to: repoDir ) }), + swiftVersion: swiftVersion + ) - try await shell("git restore .", cwd: repoDir) - } + let artifactJsonFile = versionDir.appending("\(swiftVersion)-\(libraryName)-\(platform).zip.json") + try fileSystem.writeFileContents(artifactJsonFile, data: encoder.encode(artifact)) + + // Clean up + try fileSystem.removeFileTree(libDir) + try fileSystem.removeFileTree(modulesDir) } + + try await shell("git restore .", cwd: repoDir) } try fileSystem.changeCurrentWorkingDirectory(to: stageDir) @@ -356,166 +254,167 @@ struct BuildPrebuilts: AsyncParsableCommand { _exit(1) } - for repo in prebuiltRepos { - let prebuiltDir = stageDir.appending(repo.url.lastPathComponent) - for version in repo.versions { - let versionDir = prebuiltDir.appending(version.tag) - - // Load artifacts - let artifacts = try fileSystem.getDirectoryContents(versionDir) - .filter({ $0.hasSuffix(".zip.json") }) - .map { - let data: Data = try fileSystem.readFileContents(versionDir.appending($0)) - var artifact = try decoder.decode(Artifact.self, from: data) - if artifact.swiftVersion == nil || artifact.libraryName == nil { - let regex = try Regex(#"(.+)-([^-]+)-[^-]+.zip.json"#) - if let match = try regex.firstMatch(in: $0), - match.count > 2, - let swiftVersion = match[1].substring, - let libraryName = match[2].substring - { - artifact.swiftVersion = .init(swiftVersion) - artifact.libraryName = .init(libraryName) - } - } - return artifact - } - - // Fetch manifests for requested swift versions - let swiftVersions: Set = .init(artifacts.compactMap(\.swiftVersion)) - var manifests: [String: Workspace.PrebuiltsManifest] = [:] - for swiftVersion in swiftVersions { - let manifestFile = "\(swiftVersion)-manifest.json" - let destination = versionDir.appending(component: manifestFile) - if fileSystem.exists(destination) { - let signedManifest = try decoder.decode( - path: destination, - fileSystem: fileSystem, - as: Workspace.SignedPrebuiltsManifest.self - ) - manifests[swiftVersion] = signedManifest.manifest - } else { - let manifestURL = URL(string: prebuiltsUrl)?.appending(components: repo.url.lastPathComponent, version.tag, manifestFile) - guard let manifestURL else { - print("Invalid URL \(prebuiltsUrl)") - _exit(1) + let id = "swift-syntax" + let prebuiltDir = stageDir.appending(id) + for version in try fileSystem.getDirectoryContents(prebuiltDir) { + let versionDir = prebuiltDir.appending(version) + + // Load artifacts + let artifacts = try fileSystem.getDirectoryContents(versionDir) + .filter({ $0.hasSuffix(".zip.json") }) + .map { + let data: Data = try fileSystem.readFileContents(versionDir.appending($0)) + var artifact = try decoder.decode(Artifact.self, from: data) + if artifact.swiftVersion == nil || artifact.libraryName == nil { + let regex = try Regex(#"(.+)-([^-]+)-[^-]+.zip.json"#) + if let match = try regex.firstMatch(in: $0), + match.count > 2, + let swiftVersion = match[1].substring, + let libraryName = match[2].substring + { + artifact.swiftVersion = .init(swiftVersion) + artifact.libraryName = .init(libraryName) } - - var headers = HTTPClientHeaders() - headers.add(name: "Accept", value: "application/json") - var request = HTTPClient.Request.download( - url: manifestURL, - headers: headers, - fileSystem: fileSystem, - destination: destination - ) - request.options.retryStrategy = .exponentialBackoff( - maxAttempts: 3, - baseDelay: .milliseconds(50) - ) - request.options.validResponseCodes = [200] - - do { - _ = try await httpClient.execute(request) { _, _ in } - } catch { - manifests[swiftVersion] = .init() - continue - } - - let signedManifest = try decoder.decode( - path: destination, - fileSystem: fileSystem, - as: Workspace.SignedPrebuiltsManifest.self - ) - - manifests[swiftVersion] = signedManifest.manifest } + return artifact } - // Merge in the artifacts - for artifact in artifacts { - let swiftVersion = artifact.swiftVersion ?? swiftVersion - var manifest = manifests[swiftVersion, default: version.manifest] - let libraryName = artifact.libraryName ?? manifest.libraries[0].name - var library = manifest.libraries.first(where: { $0.name == libraryName }) ?? .init(name: libraryName) - var newArtifacts = library.artifacts ?? [] - - if let products = artifact.products { - library.products = products + // Fetch manifests for requested swift versions + let swiftVersions: Set = .init(artifacts.compactMap(\.swiftVersion)) + var manifests: [String: Workspace.PrebuiltsManifest] = [:] + for swiftVersion in swiftVersions { + let manifestFile = "\(swiftVersion)-manifest.json" + let destination = versionDir.appending(component: manifestFile) + if fileSystem.exists(destination) { + let signedManifest = try decoder.decode( + path: destination, + fileSystem: fileSystem, + as: Workspace.SignedPrebuiltsManifest.self + ) + manifests[swiftVersion] = signedManifest.manifest + } else { + let manifestURL = URL(string: prebuiltsUrl)?.appending(components: id, version, manifestFile) + guard let manifestURL else { + print("Invalid URL \(prebuiltsUrl)") + _exit(1) } - if let includePath = artifact.includePath { - library.includePath = includePath - } + var headers = HTTPClientHeaders() + headers.add(name: "Accept", value: "application/json") + var request = HTTPClient.Request.download( + url: manifestURL, + headers: headers, + fileSystem: fileSystem, + destination: destination + ) + request.options.retryStrategy = .exponentialBackoff( + maxAttempts: 3, + baseDelay: .milliseconds(50) + ) + request.options.validResponseCodes = [200] - if let cModules = artifact.cModules { - library.cModules = cModules + do { + _ = try await httpClient.execute(request) { _, _ in } + } catch { + manifests[swiftVersion] = .init() + continue } - if let index = newArtifacts.firstIndex(where: { $0.platform == artifact.platform }) { - var oldArtifact = newArtifacts[index] - oldArtifact.checksum = artifact.checksum - newArtifacts[index] = oldArtifact - } else { - newArtifacts.append(.init(platform: artifact.platform, checksum: artifact.checksum)) - } + let signedManifest = try decoder.decode( + path: destination, + fileSystem: fileSystem, + as: Workspace.SignedPrebuiltsManifest.self + ) - library.artifacts = newArtifacts + manifests[swiftVersion] = signedManifest.manifest + } + } - if let index = manifest.libraries.firstIndex(where: { $0.name == libraryName }) { - manifest.libraries[index] = library - } else { - manifest.libraries.append(library) - } + // Merge in the artifacts + for artifact in artifacts { + let swiftVersion = artifact.swiftVersion ?? swiftVersion + guard var manifest = manifests[swiftVersion] else { + continue + } + let libraryName = artifact.libraryName ?? manifest.libraries[0].name + var library = manifest.libraries.first(where: { $0.name == libraryName }) ?? .init(name: libraryName) + var newArtifacts = library.artifacts ?? [] - manifests[swiftVersion] = manifest + if let products = artifact.products { + library.products = products } - if testSigning { - // Use SwiftPM's test certificate chain and private key for testing - let certsPath = try AbsolutePath(validating: #file) - .parentDirectory.parentDirectory.parentDirectory - .appending(components: "Fixtures", "Signing", "Certificates") - privateKeyPathStr = certsPath.appending("Test_rsa_key.pem").pathString - certChainPathStrs = [ - certsPath.appending("Test_rsa.cer").pathString, - certsPath.appending("TestIntermediateCA.cer").pathString, - certsPath.appending("TestRootCA.cer").pathString - ] + if let includePath = artifact.includePath { + library.includePath = includePath + } + + if let cModules = artifact.cModules { + library.cModules = cModules } - guard let privateKeyPathStr else { - fatalError("No private key path provided") + if let index = newArtifacts.firstIndex(where: { $0.platform == artifact.platform }) { + var oldArtifact = newArtifacts[index] + oldArtifact.checksum = artifact.checksum + newArtifacts[index] = oldArtifact + } else { + newArtifacts.append(.init(platform: artifact.platform, checksum: artifact.checksum)) } - let certChainPaths = try certChainPathStrs.map { try make(path: $0) } + library.artifacts = newArtifacts - guard let rootCertPath = certChainPaths.last else { - fatalError("No certificates provided") + if let index = manifest.libraries.firstIndex(where: { $0.name == libraryName }) { + manifest.libraries[index] = library + } else { + manifest.libraries.append(library) } - let privateKeyPath = try make(path: privateKeyPathStr) + manifests[swiftVersion] = manifest + } - try await withTemporaryDirectory { tmpDir in - try fileSystem.copy(from: rootCertPath, to: tmpDir.appending(rootCertPath.basename)) + if testSigning { + // Use SwiftPM's test certificate chain and private key for testing + let certsPath = try AbsolutePath(validating: #file) + .parentDirectory.parentDirectory.parentDirectory + .appending(components: "Fixtures", "Signing", "Certificates") + privateKeyPathStr = certsPath.appending("Test_rsa_key.pem").pathString + certChainPathStrs = [ + certsPath.appending("Test_rsa.cer").pathString, + certsPath.appending("TestIntermediateCA.cer").pathString, + certsPath.appending("TestRootCA.cer").pathString + ] + } - let signer = ManifestSigning( - trustedRootCertsDir: tmpDir, - observabilityScope: ObservabilitySystem { _, diagnostic in print(diagnostic) }.topScope + guard let privateKeyPathStr else { + fatalError("No private key path provided") + } + + let certChainPaths = try certChainPathStrs.map { try make(path: $0) } + + guard let rootCertPath = certChainPaths.last else { + fatalError("No certificates provided") + } + + let privateKeyPath = try make(path: privateKeyPathStr) + + try await withTemporaryDirectory { tmpDir in + try fileSystem.copy(from: rootCertPath, to: tmpDir.appending(rootCertPath.basename)) + + let signer = ManifestSigning( + trustedRootCertsDir: tmpDir, + observabilityScope: ObservabilitySystem { _, diagnostic in print(diagnostic) }.topScope + ) + + for (swiftVersion, manifest) in manifests where !manifest.libraries.flatMap({ $0.artifacts ?? [] }).isEmpty { + let signature = try await signer.sign( + manifest: manifest, + certChainPaths: certChainPaths, + certPrivateKeyPath: privateKeyPath, + fileSystem: fileSystem ) - for (swiftVersion, manifest) in manifests where !manifest.libraries.flatMap({ $0.artifacts ?? [] }).isEmpty { - let signature = try await signer.sign( - manifest: manifest, - certChainPaths: certChainPaths, - certPrivateKeyPath: privateKeyPath, - fileSystem: fileSystem - ) - - let signedManifest = Workspace.SignedPrebuiltsManifest(manifest: manifest, signature: signature) - let manifestFile = versionDir.appending(component: "\(swiftVersion)-manifest.json") - try encoder.encode(signedManifest).write(to: manifestFile.asURL) - } + let signedManifest = Workspace.SignedPrebuiltsManifest(manifest: manifest, signature: signature) + let manifestFile = versionDir.appending(component: "\(swiftVersion)-manifest.json") + try encoder.encode(signedManifest).write(to: manifestFile.asURL) } } } @@ -611,29 +510,27 @@ extension AbsolutePath: ExpressibleByArgument { extension Package { /// The transitive list of targets in this package for the given list of products - func targets(forProducts productNames: [String]) -> [Module] { + func targets(forPackage package: Package) -> [Module] { var targets: [String: Module] = [:] - for productName in productNames { - if let product = products.first(where: { $0.name == productName }) { - for target in product.modules { - if !targets.keys.contains(target.name) { - func transitTarget(_ target: Module) { - for dep in target.dependencies { - switch dep { - case .module(let module, conditions: _): - if !targets.keys.contains(module.name) { - targets[module.name] = module - transitTarget(module) - } - default: - break + for product in package.products where product.type.isLibrary { + for target in product.modules { + if !targets.keys.contains(target.name) { + func transitTarget(_ target: Module) { + for dep in target.dependencies { + switch dep { + case .module(let module, conditions: _): + if !targets.keys.contains(module.name) { + targets[module.name] = module + transitTarget(module) } + default: + break } } - - targets[target.name] = target - transitTarget(target) } + + targets[target.name] = target + transitTarget(target) } } } diff --git a/Sources/swift-build-prebuilts/build.sh b/Sources/swift-build-prebuilts/build.sh new file mode 100755 index 00000000000..95898731484 --- /dev/null +++ b/Sources/swift-build-prebuilts/build.sh @@ -0,0 +1,4 @@ +swift run swift-build-prebuilts --stage-dir ~/swift/stage --build --test-signing \ + --version 600.0.1 \ + --version 601.0.1 \ + --version $(git ls-remote --tags https://github.com/swiftlang/swift-syntax '*.*.*' | cut -d '/' -f 3 | grep ^602 | tail -1) From aff108445db8bb02c918f19750627fb63e009021 Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:08:46 -0400 Subject: [PATCH 2/2] Make sure we don't get backslashes in the Prebuilts Manifest (#9112) We are running into cases where, if the Windows prebuilts build runs last, it's the one producing the prebuilts manifest. Since we're using RelativePath to for the library include paths, it's resulting in Windows paths being used and that fails on the other platforms. This change makes sure we always use forward slashes which will work on all platforms. Note to do this we're copying over the manifest types to the BuildPrebuilts script temporarily to so we don't need to make changes in 6.2.0. We'll revisit this for the next release where we probably should use per platform JSON files. --- Sources/Workspace/Workspace+Prebuilts.swift | 6 +++--- Sources/swift-build-prebuilts/BuildPrebuilts.swift | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Workspace/Workspace+Prebuilts.swift b/Sources/Workspace/Workspace+Prebuilts.swift index d72762501a9..f78913b5926 100644 --- a/Sources/Workspace/Workspace+Prebuilts.swift +++ b/Sources/Workspace/Workspace+Prebuilts.swift @@ -54,7 +54,7 @@ extension Workspace { public let name: String public var products: [String] public var cModules: [String]? - public var includePath: [RelativePath]? + public var includePath: [String]? public var artifacts: [Artifact]? public var id: String { name } @@ -81,7 +81,7 @@ extension Workspace { self.name = name self.products = products self.cModules = cModules - self.includePath = includePath + self.includePath = includePath?.map({ $0.pathString.replacingOccurrences(of: "\\", with: "/") }) self.artifacts = artifacts } } @@ -628,7 +628,7 @@ extension Workspace { path: path, checkoutPath: checkoutPath, products: library.products, - includePath: library.includePath, + includePath: try library.includePath?.map({ try RelativePath(validating: $0) }), cModules: library.cModules ?? [] ) addedPrebuilts.add(managedPrebuilt) diff --git a/Sources/swift-build-prebuilts/BuildPrebuilts.swift b/Sources/swift-build-prebuilts/BuildPrebuilts.swift index 523c7ddae34..09e46bb518d 100644 --- a/Sources/swift-build-prebuilts/BuildPrebuilts.swift +++ b/Sources/swift-build-prebuilts/BuildPrebuilts.swift @@ -30,7 +30,7 @@ struct Artifact: Codable { var checksum: String var libraryName: String? var products: [String]? - var includePath: [RelativePath]? + var includePath: [String]? var cModules: [String]? // deprecated, includePath is the way forward var swiftVersion: String? } @@ -222,7 +222,7 @@ struct BuildPrebuilts: AsyncParsableCommand { checksum: checksum, libraryName: libraryName, products: package.products.map(\.name), - includePath: cModules.map({ $0.includeDir.relative(to: repoDir ) }), + includePath: cModules.map({ $0.includeDir.relative(to: repoDir ).pathString.replacingOccurrences(of: "\\", with: "/") }), swiftVersion: swiftVersion )