|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2025 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +import Foundation |
| 14 | +import Helpers |
| 15 | +import Logging |
| 16 | + |
| 17 | +import struct SystemPackage.FilePath |
| 18 | + |
| 19 | +package struct FreeBSDRecipe: SwiftSDKRecipe { |
| 20 | + package func applyPlatformOptions(metadata: inout SwiftSDKMetadataV4, paths: PathsConfiguration, targetTriple: Triple, isForEmbeddedSwift: Bool) { |
| 21 | + // Inside of the SDK, the sysroot directory has the same name as the |
| 22 | + // target triple. |
| 23 | + let targetTripleString = targetTriple.triple |
| 24 | + let properties = SwiftSDKMetadataV4.TripleProperties( |
| 25 | + sdkRootPath: targetTripleString, |
| 26 | + toolsetPaths: ["toolset.json"] |
| 27 | + ) |
| 28 | + metadata.targetTriples = [ |
| 29 | + targetTripleString: properties |
| 30 | + ] |
| 31 | + } |
| 32 | + |
| 33 | + package func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple, isForEmbeddedSwift: Bool |
| 34 | + ) { |
| 35 | + // The toolset data is always the same. It just instructs Swift and LLVM |
| 36 | + // to use the LLVM linker lld instead of whatever the system linker is. |
| 37 | + // It also instructs the linker to set the runtime paths so that the |
| 38 | + // dynamic linker can find the Swift runtime libraries. |
| 39 | + let swiftCompilerOptions = Toolset.ToolProperties(extraCLIOptions: ["-Xclang-linker", "-fuse-ld=lld", "-Xclang-linker", "-Wl,-rpath", "-Xclang-linker", "-Wl,/usr/local/swift/lib:/usr/local/swift/lib/swift/freebsd"]) |
| 40 | + toolset.swiftCompiler = swiftCompilerOptions |
| 41 | + } |
| 42 | + |
| 43 | + /// The FreeBSD version that we are targeting. |
| 44 | + package let freeBSD: FreeBSD |
| 45 | + |
| 46 | + /// A toolchain compiled for FreeBSD whose contents we should use for the |
| 47 | + /// SDK. |
| 48 | + /// |
| 49 | + /// If this is nil, then the resulting SDK won't be able to compile Swift |
| 50 | + /// code and will only be able to build code for C and C++. |
| 51 | + private let sourceSwiftToolchain: FilePath? |
| 52 | + |
| 53 | + /// The triple of the target architecture that the SDK will support. |
| 54 | + private let mainTargetTriple: Triple |
| 55 | + |
| 56 | + /// The target architecture that the SDK will support (e.g., aarch64). |
| 57 | + private let architecture: String |
| 58 | + |
| 59 | + /// The default filename of the produced SDK. |
| 60 | + package var defaultArtifactID: String { |
| 61 | + """ |
| 62 | + FreeBSD_\(self.freeBSD.version)_\(self.mainTargetTriple.archName) |
| 63 | + """ |
| 64 | + } |
| 65 | + |
| 66 | + /// The logging object used by this class for debugging. |
| 67 | + package let logger: Logging.Logger |
| 68 | + |
| 69 | + /// Toolchain paths needed for cross-compilation. This is a dictionary that |
| 70 | + /// maps paths in the toolchain to the destination path in the SDK. We do |
| 71 | + /// this because our packaging script for FreeBSD installs Swift content |
| 72 | + /// in /usr/local/swift for ease of packaging, but there's no need to do so |
| 73 | + /// in the SDK. |
| 74 | + private let neededToolchainPaths = [ |
| 75 | + "usr/local/swift/lib/swift": "usr/lib/swift", |
| 76 | + "usr/local/swift/lib/swift_static": "usr/lib/swift_static", |
| 77 | + "usr/local/swift/include/swift": "usr/include/swift", |
| 78 | + "usr/local/swift/include": "usr/include/swift", |
| 79 | + ] |
| 80 | + |
| 81 | + private func baseSysURL() -> String { |
| 82 | + // The FreeBSD package system uses arm64 instead of aarch64 in its URLs. |
| 83 | + let architectureString: String |
| 84 | + if mainTargetTriple.arch == .aarch64 { |
| 85 | + architectureString = "arm64" |
| 86 | + } else { |
| 87 | + architectureString = architecture |
| 88 | + } |
| 89 | + |
| 90 | + let majorVersion = freeBSD.majorVersion |
| 91 | + let minorVersion = freeBSD.minorVersion |
| 92 | + return "https://download.freebsd.org/ftp/releases/\(architectureString)/\(majorVersion).\(minorVersion)-RELEASE/base.txz" |
| 93 | + } |
| 94 | + |
| 95 | + package func makeSwiftSDK(generator: SwiftSDKGenerator, engine: Helpers.QueryEngine, httpClient: some HTTPClientProtocol) async throws -> SwiftSDKProduct { |
| 96 | + let swiftSDKRootPath = generator.pathsConfiguration.swiftSDKRootPath |
| 97 | + try await generator.createDirectoryIfNeeded(at: swiftSDKRootPath) |
| 98 | + logger.debug("swiftSDKRootPath = \(swiftSDKRootPath)") |
| 99 | + |
| 100 | + // Create the sysroot directory. This is where all of the FreeBSD content |
| 101 | + // as well as the Swift toolchain will be copied to. |
| 102 | + let sysrootDir = swiftSDKRootPath.appending(mainTargetTriple.triple) |
| 103 | + try await generator.createDirectoryIfNeeded(at: sysrootDir) |
| 104 | + logger.debug("sysrootDir = \(sysrootDir)") |
| 105 | + |
| 106 | + let cachePath = generator.pathsConfiguration.artifactsCachePath |
| 107 | + logger.debug("cachePath = \(cachePath)") |
| 108 | + |
| 109 | + // Download the FreeBSD base system if we don't have it in the cache. |
| 110 | + let freebsdBaseSystemTarballPath = cachePath.appending("FreeBSD-\(freeBSD.version)-base.txz") |
| 111 | + let freebsdBaseSystemUrl = URL(string: baseSysURL())! |
| 112 | + if await !generator.doesFileExist(at: freebsdBaseSystemTarballPath) { |
| 113 | + try await httpClient.downloadFile(from: freebsdBaseSystemUrl, to: freebsdBaseSystemTarballPath) |
| 114 | + } |
| 115 | + |
| 116 | + // Extract the FreeBSD base system into the sysroot. |
| 117 | + let neededPathsInSysroot = ["lib", "usr/include", "usr/lib"] |
| 118 | + try await generator.untar( |
| 119 | + file: freebsdBaseSystemTarballPath, |
| 120 | + into: sysrootDir, |
| 121 | + paths: neededPathsInSysroot, |
| 122 | + ) |
| 123 | + |
| 124 | + // If the user provided a Swift toolchain, then also copy its contents |
| 125 | + // into the sysroot. We don't need the entire toolchain, only the libraries |
| 126 | + // and headers. |
| 127 | + if let sourceSwiftToolchain { |
| 128 | + // If the toolchain is a directory, then we need to expand it. |
| 129 | + let pathToCompleteToolchain: FilePath |
| 130 | + if await generator.doesDirectoryExist(at: sourceSwiftToolchain) { |
| 131 | + pathToCompleteToolchain = sourceSwiftToolchain |
| 132 | + } else { |
| 133 | + let expandedToolchainName = "ExpandedSwiftToolchain-FreeBSD-\(freeBSD.version)" |
| 134 | + pathToCompleteToolchain = cachePath.appending(expandedToolchainName) |
| 135 | + |
| 136 | + if await generator.doesFileExist(at: pathToCompleteToolchain) { |
| 137 | + try await generator.removeFile(at: pathToCompleteToolchain) |
| 138 | + } |
| 139 | + |
| 140 | + logger.debug("Expanding archived Swift toolchain at \(sourceSwiftToolchain) into \(pathToCompleteToolchain)") |
| 141 | + try await generator.createDirectoryIfNeeded(at: pathToCompleteToolchain) |
| 142 | + try await generator.untar( |
| 143 | + file: sourceSwiftToolchain, |
| 144 | + into: pathToCompleteToolchain |
| 145 | + ) |
| 146 | + } |
| 147 | + |
| 148 | + logger.debug("Copying required items from toolchain into SDK") |
| 149 | + for (sourcePath, destinationPath) in neededToolchainPaths { |
| 150 | + let sourcePath = pathToCompleteToolchain.appending(sourcePath) |
| 151 | + let destinationPath = sysrootDir.appending(destinationPath) |
| 152 | + |
| 153 | + logger.debug("Copying item in toolchain at path \(sourcePath) into SDK at \(destinationPath)") |
| 154 | + try await generator.createDirectoryIfNeeded(at: destinationPath.removingLastComponent()) |
| 155 | + try await generator.copy(from: sourcePath, to: destinationPath) |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + // Return the path to the newly created SDK. |
| 160 | + return .init(sdkDirPath: swiftSDKRootPath, hostTriples: nil) |
| 161 | + } |
| 162 | + |
| 163 | + public init(freeBSDVersion: FreeBSD, mainTargetTriple: Triple, sourceSwiftToolchain: FilePath?, logger: Logging.Logger) { |
| 164 | + self.freeBSD = freeBSDVersion |
| 165 | + self.mainTargetTriple = mainTargetTriple |
| 166 | + self.architecture = mainTargetTriple.archName |
| 167 | + self.logger = logger |
| 168 | + self.sourceSwiftToolchain = sourceSwiftToolchain |
| 169 | + } |
| 170 | +} |
0 commit comments