livebook/app_bundler/lib/templates/macos/Launcher.swift.eex
2022-08-10 15:02:07 +02:00

126 lines
3.4 KiB
Elixir

<%
additional_paths = Enum.map_join(@app_options[:additional_paths], ":", &"\\(resourcePath)/#{&1}")
%>import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
var releaseTask: Process!
var isRunning = false
var initialInput = "open_app"
func applicationDidFinishLaunching(_ aNotification: Notification) {
if !isRunning {
releaseTask = startRelease(initialInput)
isRunning = true
}
}
func applicationWillTerminate(_ n: Notification) {
if (releaseTask.isRunning == true) {
log("terminating release task")
releaseTask.terminate()
}
}
func application(_ app: NSApplication, open urls: [URL]) {
for url in urls {
var input : String
if url.isFileURL {
input = "open_file:\(url.path)"
} else {
input = "open_url:\(url)"
}
if isRunning {
rpc(input)
} else {
initialInput = input
}
}
}
}
func startRelease(_ input : String) -> Process {
let task = buildReleaseTask()
task.environment!["APP_BUILDER_INPUT"] = input
task.arguments = ["start"]
task.terminationHandler = {(t: Process) in
if t.terminationStatus == 0 {
log("release exited with: \(t.terminationStatus)")
} else {
runAlert(messageText: "\(appName) exited with error status \(t.terminationStatus).")
}
NSApp.terminate(nil)
}
try! task.run()
log("release pid: \(task.processIdentifier)")
DispatchQueue.global(qos: .userInteractive).async {
task.waitUntilExit()
}
return task
}
func rpc(_ event: String) {
let input = Pipe()
let task = buildReleaseTask()
task.standardInput = input
input.fileHandleForWriting.write("\(event)\n".data(using: .utf8)!)
task.arguments = ["rpc", "AppBundler.__rpc__()"]
try! task.run()
task.waitUntilExit()
if task.terminationStatus != 0 {
runAlert(messageText: "Something went wrong")
}
}
func buildReleaseTask() -> Process {
let task = Process()
task.launchPath = Bundle.main.path(forResource: "rel/bin/<%= @release.name %>", ofType: "")!
task.environment = ProcessInfo.processInfo.environment
<%= if additional_paths != "" do %>
let resourcePath = Bundle.main.resourcePath ?? ""
let additionalPaths = "<%= additional_paths %>"
let path = task.environment!["PATH"] ?? ""
task.environment!["PATH"] = "\(additionalPaths):\(path)"
<% end %>
task.standardOutput = logFile
task.standardError = logFile
return task
}
func runAlert(messageText: String) {
DispatchQueue.main.sync {
let alert = NSAlert()
alert.alertStyle = .critical
alert.messageText = messageText
alert.informativeText = "Logs available at: \(logPath)"
alert.runModal()
}
}
func log(_ line: String) {
logFile.write("[\(appName)Launcher] \(line)\n".data(using: .utf8)!)
}
let fm = FileManager.default
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
let home = NSHomeDirectory()
let logPath = "\(home)/Library/Logs/\(appName).log"
if !fm.fileExists(atPath: logPath) { fm.createFile(atPath: logPath, contents: Data()) }
let logFile = FileHandle(forUpdatingAtPath: logPath)!
logFile.seekToEndOfFile()
let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate
app.run()