diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6b07d29 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +env: + global: + - LC_CTYPE=en_US.UTF-8 +matrix: + include: + - os: osx + language: objective-c + osx_image: xcode8.2 + before_install: + - export PATH=/usr/local/opt/llvm/bin:"${PATH}" + - brew install llvm + - sudo swift utils/make-pkgconfig.swift + script: + - swift test +notifications: + slack: + secure: ek/+U+e44bqP8+QCHojy2LhrN9iwY3N/TNNqNG5FZrp09Vidrd5KXWJOXFxlGrpeWdgTpi089YbEdTfxpcDIudUqDqLwPzS7wePiG2cEC1OT6l3yrhI4AvOe7EsNSOX8gzkuEnmrZVHwLLGe7JeR7JIQKoHMZsBcPYDnO8kRP0Ei3zOh47YUn75SE87egAgZOVBDbZYO3GWRa4WX64s8gaQYQ9a7EoUY0oX9rQ48FJs3rmEIhvIXdcOj9bGX7+o0j7l+IFial/Qh+B6bp4XkZU/tUVP6cuNVI1vxE1weVGCBhgt5wLhXTMewzoE5D1IgMZHVuzIBcDbBthSzQRttLSlYar6xTjXtRtOnb8tqZMWfUj3HBYCFYqtz7PGnZ3IflEVsPJW6tgSsoeB6egjzb8APP9mvhm8+zb1jQG1dqXLWErMjWqhlyPVPmHrxU2w/OLWLAJPY94GVmLnSuOw2pSz41spuEY80JcVVzoRbAOQWrwAujq2S3k93yvKpGq4eaT72Mt8g1CyZesByvzcLk99LEJSpqOIxUqXBd4RwHhay/sq8LllyyqY8ORsxEgwQluOAjEhATO/t/HUsu2ndn1k38U1c4HqXW7FDs1hffYEzZ/PGxciCS6Vt1bfST+iq34pzqpanENQCnX6mSR+D+M7mHlCWdsUihmxEcs5knuM= diff --git a/Package.pins b/Package.pins new file mode 100644 index 0000000..be12b55 --- /dev/null +++ b/Package.pins @@ -0,0 +1,12 @@ +{ + "autoPin": true, + "pins": [ + { + "package": "cclang", + "reason": null, + "repositoryURL": "https://github.com/trill-lang/cclang", + "version": "0.0.1" + } + ], + "version": 1 +} \ No newline at end of file diff --git a/Sources/Clang/EvalResult.swift b/Sources/Clang/EvalResult.swift index 836fc6c..71f6404 100644 --- a/Sources/Clang/EvalResult.swift +++ b/Sources/Clang/EvalResult.swift @@ -3,56 +3,55 @@ import cclang #endif protocol EvalResultKind { - var clang: CXEvalResultKind { get } + var clang: CXEvalResult { get } } public struct IntResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult public var value: Int { return Int(clang_EvalResult_getAsInt(clang)) } } public struct FloatResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult public var value: Double { - return Int(clang_EvalResult_getAsDouble(clang)) + return Double(clang_EvalResult_getAsDouble(clang)) } } public struct ObjCStrLiteralResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult public var value: String { - return String(cString: clang_EvalResult_getAsString(clang)) + return String(cString: clang_EvalResult_getAsStr(clang)) } } public struct StrLiteralResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult public var value: String { - return String(cString: clang_EvalResult_getAsString(clang)) + return String(cString: clang_EvalResult_getAsStr(clang)) } } public struct CFStrResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult public var value: String { - return String(cString: clang_EvalResult_getAsString(clang)) + return String(cString: clang_EvalResult_getAsStr(clang)) } } public struct OtherResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult } public struct UnExposedResult: EvalResultKind { - let clang: CXEvalResultKind + let clang: CXEvalResult } /// Converts a CXEvalResultKind to a EvalResultKind, returning `nil` if it was unsuccessful -func convertEvalResultKind(_ clang: CXEvalResultKind) -> EvalResultKind? { - if <#clang thing is null?#> { return nil } - switch <#Get clang kind#> { +func convertEvalResultKind(_ clang: CXEvalResult) -> EvalResultKind? { + switch clang_EvalResult_getKind(clang) { case CXEval_Int: return IntResult(clang: clang) case CXEval_Float: return FloatResult(clang: clang) case CXEval_ObjCStrLiteral: return ObjCStrLiteralResult(clang: clang) diff --git a/Sources/Clang/Utilities.swift b/Sources/Clang/Utilities.swift index d53c0fa..fb3af93 100644 --- a/Sources/Clang/Utilities.swift +++ b/Sources/Clang/Utilities.swift @@ -21,7 +21,7 @@ extension CXString { } extension Collection where Iterator.Element == String, IndexDistance == Int { - func withUnsafeCStringBuffer(_ f: (UnsafeMutableBufferPointer?>) throws -> Result) rethrows -> Result { + func withUnsafeCStringBuffer(_ f: @escaping (UnsafeMutableBufferPointer?>) throws -> Result) rethrows -> Result { let ptr = UnsafeMutablePointer?>.allocate(capacity: self.count) defer { freelist(ptr, count: self.count) } for (idx, str) in enumerated() { @@ -29,9 +29,9 @@ extension Collection where Iterator.Element == String, IndexDistance == Int { ptr[idx] = strdup(cStr) } } - let constPtr = unsafeBitCast(ptr, to: UnsafeMutablePointer?>.self) - + return try ptr.withMemoryRebound(to: Optional>.self, capacity: self.count) { constPtr in return try f(UnsafeMutableBufferPointer(start: constPtr, count: self.count)) + } } } diff --git a/utils/make-pkgconfig.swift b/utils/make-pkgconfig.swift new file mode 100644 index 0000000..3cefbfc --- /dev/null +++ b/utils/make-pkgconfig.swift @@ -0,0 +1,119 @@ +#!/usr/bin/env swift +import Foundation + +/// Runs the specified program at the provided path. +/// - parameter path: The full path of the executable you +/// wish to run. +/// - parameter args: The arguments you wish to pass to the +/// process. +/// - returns: The standard output of the process, or nil if it was empty. +func run(_ path: String, args: [String] = []) -> String? { + print("Running \(path) \(args.joined(separator: " "))...") + let pipe = Pipe() + let process = Process() + process.launchPath = path + process.arguments = args + process.standardOutput = pipe + process.launch() + process.waitUntilExit() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + guard let result = String(data: data, encoding: .utf8)? + .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]) +} + +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) + } +} + +func makeFile() throws { + let pkgConfigPath = "/usr/local/lib/pkgconfig" + let pkgConfigDir = URL(fileURLWithPath: pkgConfigPath) + + // Make /usr/local/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("cclang.pc") + + /// Ensure we have llvm-config in the PATH + guard let llvmConfig = which("llvm-config-3.9") ?? which("llvm-config") 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 version = run(llvmConfig, args: ["--version"])! + .replacing(charactersIn: .newlines, with: "") + + guard version.hasPrefix("3.9") else { + throw "ClangSwift requires LLVM version >=3.9.0, but you have \(version)" + } + + let libFlags = [ + "-L/usr/local/Cellar/llvm/3.9.1/lib", + "-lclangEdit", + "-lclangFrontendTool", + "-lclang", + "-lclangAST", + "-lclangLex", + "-lclangBasic", + "-lclangDriver", + "-lclangAnalysis", + "-lclangIndex", + "-lclangASTMatchers", + "-lclangSema", + "-lclangParse", + ].joined(separator: " ") + + let cFlags = "-I/usr/local/Cellar/llvm/3.9.1/include" + // SwiftPM has a whitelisted set of cflags that it understands, and + // unfortunately that includes almost everything but the include dir. + + /// Emit the pkg-config file to the path + + let s = [ + "Name: cclang", + "Description: The llvm library", + "Version: \(version)", + "Libs: \(libFlags)", + "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() +} catch { +#if os(Linux) + // FIXME: Printing the thrown error that here crashes on Linux. + print("Unexpected error occured while writing the config file. Check permissions and try again.") +#else + print("error: \(error)") +#endif + exit(-1) +}