Skip to content
Open
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
8 changes: 4 additions & 4 deletions swift/swan-swiftc/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 18 additions & 14 deletions swift/swan-swiftc/Package.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
// swift-tools-version:5.1
// swift-tools-version:6.0

import PackageDescription

let package = Package(
name: "swan-swiftc",
products: [
.executable(name: "swan-swiftc", targets: ["swan-swiftc"])
],
dependencies: [
.package(url: "https://github.com/mtynior/ColorizeSwift.git", from: "1.5.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1")
],
targets: [
.target(
name: "swan-swiftc",
dependencies: ["ArgumentParser", "ColorizeSwift"])
]
name: "swan-swiftc",
products: [
.executable(name: "swan-swiftc", targets: ["swan-swiftc"])
],
dependencies: [
.package(url: "https://github.com/mtynior/ColorizeSwift.git", from: "1.5.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
],
targets: [
.executableTarget(
name: "swan-swiftc",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ColorizeSwift", package: "ColorizeSwift"),
]
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,93 +22,100 @@ import ColorizeSwift
import Foundation

struct Constants {
static let defaultSwanDir = "swan-dir/"
static let swiftcLog = "swiftc.log"
static let defaultSwanDir = URL(fileURLWithPath: "swan-dir/")
static let swiftcLog = "swiftc.log"
static let defaultSDK = SDK.macosx
}

extension String: Error {}
enum SDK: String, ExpressibleByArgument {
case driverkit = "driverkit"
case ios = "iphoneos"
case iphonesimulator = "iphonesimulator"
case macosx = "macosx"
case appletvos = "appletvos"
case appletvsimulator = "appletvsimulator"
case xros = "xros"
case watchos = "watchos"
case watchsimulator = "watchsimulator"
}

extension URL: @retroactive ExpressibleByArgument {
public init(argument: String) {
self = URL(fileURLWithPath: argument).absoluteURL
}
}

@main
struct SWANSwiftcBuild: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Build and dump SIL for a Swift application using swiftc.")

// Ignore the warning generated from this.
@Option(default: Constants.defaultSwanDir, help: "Output directory for SIL.")
var swanDir: String?
@Argument(help: "Prefix these arguments with --")
abstract: "Build and dump SIL for a Swift application using swiftc."
)

@Option(help: "Output directory for SIL.")
var swanDir: URL = Constants.defaultSwanDir

@Argument(help: "Additional arguments to pass to swiftc. Prefix these arguments with --")
var swiftcArgs: [String]

init() { }


@Option(help: "The SDK to use.")
var sdk: SDK = Constants.defaultSDK

lazy var srcCopyDir = swanDir.appendingPathComponent("src")
lazy var swiftcLog = swanDir.appendingPathComponent(Constants.swiftcLog)

func generateSwiftcArgs() -> [String] {
return ["swiftc"] + self.swiftcArgs + [
["xcrun", "--sdk", self.sdk.rawValue, "swiftc"] + self.swiftcArgs + [
"-emit-sil",
"-Xfrontend",
"-gsil",
"-sil-based-debuginfo",
"-Xllvm",
"-sil-print-debuginfo",
"-Xllvm",
"-sil-print-before=SerializeSILPass"
"-sil-print-before=SerializeSILPass",
]
}

func printStatus(_ msg: String) {
print(msg.foregroundColor(.steelBlue1_2).bold())
}

func printFailure(_ msg: String) {
print(msg.foregroundColor(.red).bold())
}

func printWarning(_ msg: String) {
print(msg.foregroundColor(.orange3).bold())
}

func run() throws {

let outputDir = URL(fileURLWithPath: self.swanDir!)
let srcCopyDir = outputDir.appendingPathComponent("src")

let swiftcLog = outputDir.appendingPathComponent(Constants.swiftcLog)

var outputSilFileName = "out.sil"

mutating func validate() throws {
do {
try FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: swanDir, withIntermediateDirectories: true)
} catch {
printFailure("The output directory could not be created at " + outputDir.path
+ ".\nReason: " + error.localizedDescription)
printFailure(
"The output directory could not be created at " + swanDir.path
+ ".\nReason: " + error.localizedDescription)
throw ExitCode.failure
}

if (FileManager().fileExists(atPath: srcCopyDir.path)) {
try FileManager().removeItem(atPath: srcCopyDir.path)
}

try? FileManager.default.removeItem(at: srcCopyDir)

do {
try FileManager.default.createDirectory(at: srcCopyDir, withIntermediateDirectories: true)
} catch {
printFailure("The src directory could not be created at " + srcCopyDir.path
+ ".\nReason: " + error.localizedDescription)
printFailure(
"The src directory could not be created at " + srcCopyDir.path
+ ".\nReason: " + error.localizedDescription)
throw ExitCode.failure
}

// swan-swiftc doesn't actually expect multiple source files
// I'm not sure what the output would look like for that
try self.swiftcArgs.forEach { (str) in
if (str.hasSuffix(".swift")) {
outputSilFileName = str + ".sil"
try FileManager().copyItem(atPath: URL(fileURLWithPath: str).path, toPath: srcCopyDir.appendingPathComponent(str).path)
}
}

}

mutating func runSwiftC() throws -> String {
let args = generateSwiftcArgs()
printStatus("Running " + args.joined(separator: " "))

let task = Process()
let pipe = Pipe()

task.launchPath = URL(string: "/usr/bin/env")?.absoluteString
task.arguments = args
task.standardInput = FileHandle.nullDevice
Expand All @@ -121,49 +128,73 @@ struct SWANSwiftcBuild: ParsableCommand {
let data = pipe.fileHandleForReading.readDataToEndOfFile()

task.waitUntilExit()

let output: String = String(data: data, encoding: String.Encoding.utf8)!


let end = DispatchTime.now()
let nanoTime = (end.uptimeNanoseconds - start.uptimeNanoseconds)
let timeInterval = Int(round(Double(nanoTime) / 1_000_000_000))

printStatus("\nswiftc finished in \(timeInterval.description)s")

do {
try output.write(to: swiftcLog, atomically: true, encoding: String.Encoding.utf8)
printStatus("swiftc output written to \(Constants.swiftcLog)")
} catch {
printFailure("Could not write swiftc output to " + Constants.swiftcLog + "\nReason: " + error.localizedDescription)
return
printFailure(
"Could not write swiftc output to " + Constants.swiftcLog + "\nReason: "
+ error.localizedDescription)
throw ExitCode.failure
}
if (task.terminationStatus != 0) {

if task.terminationStatus != 0 {
printFailure("\nswiftc failed. Please see \(swiftcLog.relativeString)\n")
throw ExitCode.failure
}

print("")


return output
}

mutating func run() throws {
var outputSilFileName = "out.sil"

// swan-swiftc doesn't actually expect multiple source files
// I'm not sure what the output would look like for that
try self.swiftcArgs.forEach { (str) in
if str.hasSuffix(".swift") {
let path = URL(fileURLWithPath: str)

outputSilFileName = path.lastPathComponent + ".sil"
let copyPath = srcCopyDir.appendingPathComponent(path.lastPathComponent)

do {
try FileManager.default.copyItem(at: path, to: copyPath)
} catch {
printFailure(
"Could not copy file from \(path) to \(copyPath)\nReason: \(error.localizedDescription)"
)
throw ExitCode.failure
}
}
}

let output = try runSwiftC()

var sil = output.components(separatedBy: "\nsil_stage canonical")[1]
sil = "sil_stage canonical\(sil)\n\n"
let filename = outputDir.appendingPathComponent(outputSilFileName)

let filename = swanDir.appendingPathComponent(outputSilFileName)
do {
try sil.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
} catch {
printFailure("Could not write SIL to \(filename)\nReason: \(error.localizedDescription)")
}

do {
// Delete unneeded generated file '-.gsil_0.sil'
try FileManager().removeItem(at: URL(fileURLWithPath: "-.gsil_0.sil"))
} catch {}

printStatus("\nSIL written to \(outputDir.path)")

// Delete unneeded generated file '-.sil_dbg_0.sil'
try? FileManager().removeItem(at: URL(fileURLWithPath: "-.sil_dbg_0.sil"))

printStatus("\nSIL written to \(swanDir.path)")
}

}

SWANSwiftcBuild.main()
Loading