diff --git a/.github/workflows/swift.yaml b/.github/workflows/swift.yaml index 9740de5..5d9ccaf 100644 --- a/.github/workflows/swift.yaml +++ b/.github/workflows/swift.yaml @@ -14,11 +14,11 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v4 - - uses: swift-actions/setup-swift@v1 + - uses: SwiftyLab/setup-swift@latest - name: Pre-install run: | brew update - brew install llvm@18 + brew install llvm@19 # Important: add empty string echo "" >> /Users/runner/.bash_profile echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> /Users/runner/.bash_profile diff --git a/Package.swift b/Package.swift index bf216c6..622a2ae 100644 --- a/Package.swift +++ b/Package.swift @@ -12,6 +12,13 @@ let package = Package( targets: getTargets() ) +struct GetConfigError: Error { + let message: String + init(_ message: String) { + self.message = message + } +} + /// Get LLVM config flags func getLLVMConfig() throws -> ([String], [String], [Int]) { let brewPrefix = { @@ -20,7 +27,7 @@ func getLLVMConfig() throws -> ([String], [String], [Int]) { }() ?? "/usr/local" /// Ensure we have llvm-config in the PATH guard let llvmConfig = which("llvm-config") ?? which("\(brewPrefix)/opt/llvm/bin/llvm-config") else { - throw "Failed to find llvm-config. Ensure llvm-config is installed and in your PATH" + throw GetConfigError("Failed to find llvm-config. Ensure llvm-config is installed and in your PATH") } // Fetch LLVM version let versionStr = run(llvmConfig, args: ["--version"])! @@ -66,7 +73,7 @@ func which(_ name: String) -> String? { run("/usr/bin/which", args: [name]) } -extension String: Error { +extension String { /// Replaces all occurrences of characters in the provided set with the provided string. func replacing(charactersIn characterSet: CharacterSet, with separator: String) -> String @@ -76,7 +83,7 @@ extension String: Error { } } -/// Check Environ,ent variable +/// Check Environment variable func hasEnvironmentVariable(_ name: String) -> Bool { ProcessInfo.processInfo.environment[name] != nil } @@ -84,10 +91,10 @@ func hasEnvironmentVariable(_ name: String) -> Bool { func getTargets() -> [Target] { if hasEnvironmentVariable("CLI_BUILD") { let (cFlags, linkFlags, _) = try! getLLVMConfig() - let customSystemLibrary: Target = .systemLibrary( + let customSystemLibrary = Target.systemLibrary( name: "CLLVM" ) - let llvmTarget: Target = .target( + let llvmTarget = Target.target( name: "LLVM", dependencies: ["CLLVM"], cSettings: [ diff --git a/Source/LLVM/Core/AddressSpace.swift b/Source/LLVM/Core/AddressSpace.swift index 8c3ccf7..5cb9bc9 100644 --- a/Source/LLVM/Core/AddressSpace.swift +++ b/Source/LLVM/Core/AddressSpace.swift @@ -50,7 +50,7 @@ import CLLVM /// - The address space is ultimately an integer value and in theory an address space identifier may take on /// any value. In practice, LLVM guarantees only 24 bits of precision, though higher address space /// identifiers may succeed in being properly represented. -public struct AddressSpace: Equatable { +public struct AddressSpace: Equatable, Sendable { let rawValue: UInt32 /// LLVM's default address space. diff --git a/Source/LLVM/Core/Context.swift b/Source/LLVM/Core/Context.swift index 2468a7f..e4194db 100644 --- a/Source/LLVM/Core/Context.swift +++ b/Source/LLVM/Core/Context.swift @@ -34,6 +34,7 @@ public final class Context: ContextRef { /// recommended that each thread of execution attempting to access the LLVM /// API have its own `Context` instance, rather than rely on this global /// context. + @MainActor public static let global = Context(llvm: LLVMGetGlobalContext()!) /// Creates a new `Context` object. @@ -222,7 +223,7 @@ public final class Context: ContextRef { } } - ///Destroy a context instance. + /// Destroy a context instance. /// /// This should be called for every call to LLVMContextCreate() or memory will be leaked. public func dispose() { diff --git a/Source/LLVM/Core/Core.swift b/Source/LLVM/Core/Core.swift index 3e0bd83..2ebc273 100644 --- a/Source/LLVM/Core/Core.swift +++ b/Source/LLVM/Core/Core.swift @@ -1,4 +1,9 @@ import CLLVM +#if os(Linux) +import Glibc +#else +import Darwin +#endif /// This modules provide an interface to libLLVMCore, which implements the LLVM intermediate representation as well /// as other related types and utilities. diff --git a/utils/llvm-pkg.swift b/utils/llvm-pkg.swift index 5d1672f..e4acf5b 100755 --- a/utils/llvm-pkg.swift +++ b/utils/llvm-pkg.swift @@ -2,9 +2,9 @@ import Foundation #if os(Linux) - let libCPP = "-L/usr/lib -lc++" +let libCPP = "-L/usr/lib -lc++" #elseif os(macOS) - let libCPP = "-lc++" +let libCPP = "-lc++" #endif /// Runs the specified program at the provided path. @@ -25,110 +25,118 @@ func run(_ path: String, args: [String] = []) -> String? { let data = pipe.fileHandleForReading.readDataToEndOfFile() guard let result = String(data: data, encoding: .utf8)? - .trimmingCharacters(in: .whitespacesAndNewlines), - !result.isEmpty else { return nil } + .trimmingCharacters(in: .whitespacesAndNewlines), + !result.isEmpty else { return nil } return result } /// Finds the location of the provided binary on your system. func which(_ name: String) -> String? { - return run("/usr/bin/which", args: [name]) + run("/usr/bin/which", args: [name]) } -extension String: Error { - /// Replaces all occurrences of characters in the provided set with - /// the provided string. - func replacing(charactersIn characterSet: CharacterSet, - with separator: String) -> String { - let components = self.components(separatedBy: characterSet) - return components.joined(separator: separator) - } +extension String { + /// Replaces all occurrences of characters in the provided set with + /// the provided string. + func replacing(charactersIn characterSet: CharacterSet, + with separator: String) -> String + { + let components = components(separatedBy: characterSet) + return components.joined(separator: separator) + } +} + +struct MakeFileError: Error { + let message: String + + init(_ message: String) { + self.message = message + } } func makeFile() throws { - let brewPrefix = { - guard let brew = which("brew") else { return nil } - return run(brew, args: ["--prefix"]) - }() ?? "/usr/local" - - let pkgConfigPath = "\(brewPrefix)/lib/pkgconfig" - let pkgConfigDir = URL(fileURLWithPath: pkgConfigPath) - - // Make /lib/pkgconfig if it doesn't already exist - if !FileManager.default.fileExists(atPath: pkgConfigPath) { - try FileManager.default.createDirectory(at: pkgConfigDir, - withIntermediateDirectories: true) - } - let cllvmPath = pkgConfigDir.appendingPathComponent("llvm.pc") - let brewLLVMConfig = { which("\(brewPrefix)/opt/llvm/bin/llvm-config") } - - /// Ensure we have llvm-config in the PATH - guard let llvmConfig = which("llvm-config-15") ?? which("llvm-config") ?? brewLLVMConfig() else { - throw "Failed to find llvm-config. Ensure llvm-config is installed and " + - "in your PATH" - } - - /// Extract the info we need from llvm-config - - print("Found llvm-config at \(llvmConfig)...") - - let versionStr = run(llvmConfig, args: ["--version"])! - .replacing(charactersIn: .newlines, with: "") - .replacingOccurrences(of: "svn", with: "") - let components = versionStr.components(separatedBy: ".") - .compactMap { Int($0) } - - guard components.count == 3 else { - throw "Invalid version number \(versionStr)" - } - - let version = (components[0], components[1], components[2]) - - guard version >= (15, 0, 0) else { - throw "LLVMSwift requires LLVM version >=15.0.0, but you have \(versionStr)" - } - - print("LLVM version is \(versionStr)") - - let ldFlags = run(llvmConfig, args: ["--ldflags", "--libs", "all", - "--system-libs"])! - .replacing(charactersIn: .newlines, with: " ") - .components(separatedBy: " ") - .filter { !$0.hasPrefix("-W") } - .joined(separator: " ") - - // SwiftPM has a whitelisted set of cflags that it understands, and - // unfortunately that includes almost everything but the include dir. - - let cFlags = run(llvmConfig, args: ["--cflags"])! - .replacing(charactersIn: .newlines, with: "") - .components(separatedBy: " ") - .filter { $0.hasPrefix("-I") } - .joined(separator: " ") - - /// Emit the pkg-config file to the path - - let s = [ - "Name: cllvm", - "Description: The llvm library", - "Version: \(versionStr)", - "Libs: \(ldFlags) \(libCPP)", - "Requires.private:", - "Cflags: \(cFlags)", - ].joined(separator: "\n") - - print("Writing pkg-config file to \(cllvmPath.path)...") - - try s.write(toFile: cllvmPath.path, atomically: true, encoding: .utf8) - - print("\nSuccessfully wrote pkg-config file!") - print("Make sure to re-run this script when you update LLVM.") + let brewPrefix = { + guard let brew = which("brew") else { return nil } + return run(brew, args: ["--prefix"]) + }() ?? "/usr/local" + + let pkgConfigPath = "\(brewPrefix)/lib/pkgconfig" + let pkgConfigDir = URL(fileURLWithPath: pkgConfigPath) + + // Make /lib/pkgconfig if it doesn't already exist + if !FileManager.default.fileExists(atPath: pkgConfigPath) { + try FileManager.default.createDirectory(at: pkgConfigDir, + withIntermediateDirectories: true) + } + let cllvmPath = pkgConfigDir.appendingPathComponent("llvm.pc") + let brewLLVMConfig = { which("\(brewPrefix)/opt/llvm/bin/llvm-config") } + + /// Ensure we have llvm-config in the PATH + guard let llvmConfig = which("llvm-config-15") ?? which("llvm-config") ?? brewLLVMConfig() else { + throw MakeFileError("Failed to find llvm-config. Ensure llvm-config is installed and " + + "in your PATH") + } + + /// Extract the info we need from llvm-config + + print("Found llvm-config at \(llvmConfig)...") + + let versionStr = run(llvmConfig, args: ["--version"])! + .replacing(charactersIn: .newlines, with: "") + .replacingOccurrences(of: "svn", with: "") + let components = versionStr.components(separatedBy: ".") + .compactMap { Int($0) } + + guard components.count == 3 else { + throw MakeFileError("Invalid version number \(versionStr)") + } + + let version = (components[0], components[1], components[2]) + + guard version >= (15, 0, 0) else { + throw MakeFileError("LLVMSwift requires LLVM version >=15.0.0, but you have \(versionStr)") + } + + print("LLVM version is \(versionStr)") + + let ldFlags = run(llvmConfig, args: ["--ldflags", "--libs", "all", + "--system-libs"])! + .replacing(charactersIn: .newlines, with: " ") + .components(separatedBy: " ") + .filter { !$0.hasPrefix("-W") } + .joined(separator: " ") + + // SwiftPM has a whitelisted set of cflags that it understands, and + // unfortunately that includes almost everything but the include dir. + + let cFlags = run(llvmConfig, args: ["--cflags"])! + .replacing(charactersIn: .newlines, with: "") + .components(separatedBy: " ") + .filter { $0.hasPrefix("-I") } + .joined(separator: " ") + + /// Emit the pkg-config file to the path + + let s = [ + "Name: cllvm", + "Description: The llvm library", + "Version: \(versionStr)", + "Libs: \(ldFlags) \(libCPP)", + "Requires.private:", + "Cflags: \(cFlags)", + ].joined(separator: "\n") + + print("Writing pkg-config file to \(cllvmPath.path)...") + + try s.write(toFile: cllvmPath.path, atomically: true, encoding: .utf8) + + print("\nSuccessfully wrote pkg-config file!") + print("Make sure to re-run this script when you update LLVM.") } do { - try makeFile() + try makeFile() } catch { - print("error: \(error)") - exit(-1) + print("error: \(error)") + exit(-1) } -