Skip to content

Feat: Update to Swift 6 and fix related issues #6

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

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/swift.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 12 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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"])!
Expand Down Expand Up @@ -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
Expand All @@ -76,18 +83,18 @@ extension String: Error {
}
}

/// Check Environ,ent variable
/// Check Environment variable
func hasEnvironmentVariable(_ name: String) -> Bool {
ProcessInfo.processInfo.environment[name] != nil
}

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: [
Expand Down
2 changes: 1 addition & 1 deletion Source/LLVM/Core/AddressSpace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion Source/LLVM/Core/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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() {
Expand Down
5 changes: 5 additions & 0 deletions Source/LLVM/Core/Core.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
196 changes: 102 additions & 94 deletions utils/llvm-pkg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 <brew-prefix>/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 <brew-prefix>/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)
}