2014-11-28 13:18:53 +00:00
|
|
|
//
|
|
|
|
// ASBuilder.swift
|
|
|
|
// AVRsack
|
|
|
|
//
|
|
|
|
// Created by Matthias Neeracher on 11/24/14.
|
2015-01-02 15:17:10 +00:00
|
|
|
// Copyright © 2014-2015 Aere Perennius. All rights reserved.
|
2014-11-28 13:18:53 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
class ASBuilder {
|
2016-11-14 00:37:13 +00:00
|
|
|
var dir = URL(fileURLWithPath: "/")
|
2016-11-21 19:33:02 +00:00
|
|
|
var task : Process?
|
2014-12-08 04:35:58 +00:00
|
|
|
var continuation: (()->())?
|
|
|
|
var termination : AnyObject?
|
|
|
|
|
|
|
|
init() {
|
2016-11-21 19:33:02 +00:00
|
|
|
termination = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
|
2016-11-13 12:13:40 +00:00
|
|
|
object: nil, queue: nil, using:
|
|
|
|
{ (notification: Notification) in
|
2016-11-21 19:33:02 +00:00
|
|
|
if notification.object as? Process == self.task {
|
2014-12-10 14:30:51 +00:00
|
|
|
if self.task!.terminationStatus == 0 {
|
|
|
|
if let cont = self.continuation {
|
|
|
|
self.continuation = nil
|
|
|
|
cont()
|
|
|
|
}
|
|
|
|
} else {
|
2014-12-08 04:35:58 +00:00
|
|
|
self.continuation = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func finalize() {
|
2016-11-13 11:03:51 +00:00
|
|
|
NotificationCenter.default.removeObserver(termination!)
|
2014-12-08 04:35:58 +00:00
|
|
|
}
|
2014-11-28 13:18:53 +00:00
|
|
|
|
2016-11-14 00:37:13 +00:00
|
|
|
func setProjectURL(url: URL) {
|
|
|
|
dir = url.deletingLastPathComponent().standardizedFileURL
|
2014-11-28 13:18:53 +00:00
|
|
|
}
|
|
|
|
|
2014-12-01 02:34:53 +00:00
|
|
|
func stop() {
|
|
|
|
task?.terminate()
|
|
|
|
task?.waitUntilExit()
|
|
|
|
}
|
|
|
|
|
2014-12-01 03:26:55 +00:00
|
|
|
func cleanProject() {
|
2015-11-16 01:56:33 +00:00
|
|
|
do {
|
2016-11-14 00:37:13 +00:00
|
|
|
try FileManager.default.removeItem(at: dir.appendingPathComponent("build"))
|
2015-11-16 01:56:33 +00:00
|
|
|
} catch _ {
|
|
|
|
}
|
2014-12-01 03:26:55 +00:00
|
|
|
}
|
|
|
|
|
2014-11-28 13:18:53 +00:00
|
|
|
func buildProject(board: String, files: ASFileTree) {
|
2016-11-13 11:03:51 +00:00
|
|
|
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
|
2016-11-21 19:33:02 +00:00
|
|
|
task = Process()
|
2016-11-14 00:37:13 +00:00
|
|
|
task!.currentDirectoryPath = dir.path
|
2016-11-13 11:03:51 +00:00
|
|
|
task!.launchPath = Bundle.main.path(forResource: "BuildProject", ofType: "")!
|
2014-11-28 13:18:53 +00:00
|
|
|
|
2016-11-13 11:03:51 +00:00
|
|
|
let fileManager = FileManager.default
|
|
|
|
let libPath = (ASLibraries.instance().directories as NSArray).componentsJoined(by: ":")
|
2015-02-14 16:43:20 +00:00
|
|
|
var args = [String]()
|
2015-03-17 23:17:55 +00:00
|
|
|
if ASHardware.instance().boards[board] == nil {
|
|
|
|
NSLog("Unable to find board %s\n", board);
|
|
|
|
return
|
|
|
|
}
|
2014-11-28 13:18:53 +00:00
|
|
|
let boardProp = ASHardware.instance().boards[board]!
|
2015-01-07 09:30:28 +00:00
|
|
|
let library = boardProp["library"]!
|
2015-11-16 01:56:33 +00:00
|
|
|
let corePath = library+"/cores/"+boardProp["build.core"]!
|
2015-02-14 16:43:20 +00:00
|
|
|
var variantPath : String?
|
2016-11-13 11:03:51 +00:00
|
|
|
if fileManager.fileExists(atPath: corePath) {
|
2015-01-07 09:30:28 +00:00
|
|
|
if let variantName = boardProp["build.variant"] {
|
|
|
|
variantPath = library+"/variants/"+variantName
|
2016-11-13 11:03:51 +00:00
|
|
|
if fileManager.fileExists(atPath: variantPath!) {
|
2015-01-07 09:30:28 +00:00
|
|
|
args.append("variant="+variantName)
|
|
|
|
} else {
|
|
|
|
variantPath = nil
|
2014-11-30 21:32:14 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-07 09:30:28 +00:00
|
|
|
} else {
|
2014-12-08 04:35:33 +00:00
|
|
|
NSLog("Unable to find core %s\n", boardProp["build.core"]!)
|
2014-11-30 21:32:14 +00:00
|
|
|
return
|
|
|
|
}
|
2014-12-15 01:49:33 +00:00
|
|
|
args.append("toolchain="+toolChain)
|
2016-11-14 00:37:13 +00:00
|
|
|
args.append("project="+dir.lastPathComponent)
|
2014-11-28 13:18:53 +00:00
|
|
|
args.append("board="+board)
|
2014-12-08 04:35:33 +00:00
|
|
|
args.append("mcu="+boardProp["build.mcu"]!)
|
|
|
|
args.append("f_cpu="+boardProp["build.f_cpu"]!)
|
2015-06-07 00:05:45 +00:00
|
|
|
args.append("max_size="+boardProp["upload.maximum_size"]!)
|
2014-12-08 04:35:33 +00:00
|
|
|
args.append("core="+boardProp["build.core"]!)
|
2014-11-28 13:18:53 +00:00
|
|
|
args.append("libs="+libPath)
|
2014-11-30 21:32:14 +00:00
|
|
|
args.append("core_path="+corePath)
|
2015-03-17 23:17:55 +00:00
|
|
|
if let varPath = variantPath {
|
|
|
|
args.append("variant_path="+varPath)
|
2014-11-30 21:32:14 +00:00
|
|
|
}
|
2014-12-29 06:35:07 +00:00
|
|
|
args.append("usb_vid="+(boardProp["build.vid"] ?? "null"));
|
|
|
|
args.append("usb_pid="+(boardProp["build.pid"] ?? "null"));
|
2014-11-28 13:18:53 +00:00
|
|
|
args.append("--")
|
2014-12-01 02:34:53 +00:00
|
|
|
args += files.paths
|
|
|
|
task!.arguments = args;
|
2014-11-28 13:18:53 +00:00
|
|
|
task!.launch()
|
|
|
|
}
|
2015-01-07 09:30:28 +00:00
|
|
|
|
|
|
|
enum Mode {
|
|
|
|
case Upload
|
|
|
|
case BurnBootloader
|
|
|
|
case Interactive
|
|
|
|
}
|
|
|
|
|
|
|
|
func uploadProject(board: String, programmer: String, port: String, mode: Mode = .Upload) {
|
|
|
|
let useProgrammer = mode != .Upload
|
|
|
|
let interactive = mode == .Interactive
|
2016-11-13 11:03:51 +00:00
|
|
|
let portPath = ASSerial.fileName(forPort: port)
|
|
|
|
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
|
2016-11-21 19:33:02 +00:00
|
|
|
task = Process()
|
2016-11-14 00:37:13 +00:00
|
|
|
task!.currentDirectoryPath = dir.path
|
2014-12-15 01:49:33 +00:00
|
|
|
task!.launchPath = toolChain+"/bin/avrdude"
|
2014-12-08 02:59:47 +00:00
|
|
|
|
2016-11-13 11:03:51 +00:00
|
|
|
let fileManager = FileManager.default
|
|
|
|
var logOut : FileHandle
|
2015-01-07 09:30:28 +00:00
|
|
|
if interactive {
|
2016-11-13 11:03:51 +00:00
|
|
|
let inputPipe = Pipe()
|
|
|
|
let outputPipe = Pipe()
|
2015-01-02 15:17:10 +00:00
|
|
|
logOut = outputPipe.fileHandleForWriting
|
|
|
|
task!.standardInput = inputPipe
|
|
|
|
task!.standardOutput = outputPipe
|
|
|
|
task!.standardError = outputPipe
|
|
|
|
} else {
|
2016-11-13 11:03:51 +00:00
|
|
|
ASSerialWin.portNeededForUpload(port: port)
|
|
|
|
let logURL = dir.appendingPathComponent("build/upload.log")
|
2016-11-14 00:37:13 +00:00
|
|
|
fileManager.createFile(atPath: logURL.path, contents: Data(), attributes: nil)
|
|
|
|
logOut = FileHandle(forWritingAtPath: logURL.path)!
|
2015-01-02 15:17:10 +00:00
|
|
|
task!.standardOutput = logOut
|
|
|
|
task!.standardError = logOut
|
|
|
|
}
|
2015-03-17 23:17:55 +00:00
|
|
|
if ASHardware.instance().boards[board] == nil {
|
|
|
|
NSLog("Unable to find board %s\n", board);
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-12-29 06:35:07 +00:00
|
|
|
let boardProp = ASHardware.instance().boards[board]!
|
|
|
|
let progProp = ASHardware.instance().programmers[programmer]
|
2015-01-07 09:30:28 +00:00
|
|
|
let hasBootloader = !useProgrammer && boardProp["upload.protocol"] != nil
|
2014-12-29 06:35:07 +00:00
|
|
|
let leonardish = hasBootloader && (boardProp["bootloader.path"] ?? "").hasPrefix("caterina")
|
|
|
|
let proto = hasBootloader ? boardProp["upload.protocol"] : progProp?["protocol"]
|
|
|
|
let speed = hasBootloader ? boardProp["upload.speed"] : progProp?["speed"]
|
2016-11-13 11:03:51 +00:00
|
|
|
let verbosity = UserDefaults.standard.integer(forKey: "UploadVerbosity")
|
|
|
|
var args = Array<String>(repeating: "-v", count: verbosity)
|
2014-12-29 06:35:07 +00:00
|
|
|
args += [
|
2014-12-15 01:49:33 +00:00
|
|
|
"-C", toolChain+"/etc/avrdude.conf",
|
2016-11-14 00:37:13 +00:00
|
|
|
"-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath!]
|
2015-01-07 09:30:28 +00:00
|
|
|
switch mode {
|
|
|
|
case .Upload:
|
|
|
|
if hasBootloader {
|
|
|
|
args += ["-D"]
|
|
|
|
}
|
2016-11-14 00:37:13 +00:00
|
|
|
args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"]
|
2015-01-07 09:30:28 +00:00
|
|
|
continuation = {
|
2016-11-13 12:27:19 +00:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
|
|
|
|
ASSerialWin.portAvailableAfterUpload(port: port)
|
2015-01-07 09:30:28 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
case .BurnBootloader:
|
|
|
|
var loaderArgs = args
|
|
|
|
args += ["-e"]
|
|
|
|
if let unlock = boardProp["bootloader.unlock_bits"] {
|
|
|
|
args += ["-Ulock:w:"+unlock+":m"]
|
|
|
|
}
|
|
|
|
if let efuse = boardProp["bootloader.extended_fuses"] {
|
|
|
|
args += ["-Uefuse:w:"+efuse+":m"]
|
|
|
|
}
|
|
|
|
let hfuse = boardProp["bootloader.high_fuses"]!
|
|
|
|
let lfuse = boardProp["bootloader.low_fuses"]!
|
|
|
|
args += ["-Uhfuse:w:"+hfuse+":m", "-Ulfuse:w:"+lfuse+":m"]
|
|
|
|
var needPhase2 = false
|
|
|
|
if let loaderPath = boardProp["bootloader.path"] {
|
|
|
|
let loader = boardProp["library"]!+"/bootloaders/"+loaderPath+"/"+boardProp["bootloader.file"]!
|
|
|
|
loaderArgs += ["-Uflash:w:"+loader+":i"]
|
|
|
|
needPhase2 = true
|
|
|
|
}
|
|
|
|
if let lock = boardProp["bootloader.lock_bits"] {
|
|
|
|
loaderArgs += ["-Ulock:w:"+lock+":m"]
|
|
|
|
needPhase2 = true
|
|
|
|
}
|
|
|
|
if needPhase2 {
|
2016-11-21 19:33:02 +00:00
|
|
|
let task2 = Process()
|
2016-11-14 00:37:13 +00:00
|
|
|
task2.currentDirectoryPath = dir.path
|
2015-01-07 09:30:28 +00:00
|
|
|
task2.launchPath = toolChain+"/bin/avrdude"
|
|
|
|
task2.arguments = loaderArgs
|
|
|
|
task2.standardOutput = logOut
|
|
|
|
task2.standardError = logOut
|
|
|
|
continuation = {
|
2016-11-13 11:03:51 +00:00
|
|
|
let cmdLine = task2.launchPath!+" "+(loaderArgs as NSArray).componentsJoined(by: " ")+"\n"
|
|
|
|
logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!)
|
2015-01-07 09:30:28 +00:00
|
|
|
task2.launch()
|
|
|
|
self.continuation = {
|
2016-11-13 12:27:19 +00:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
|
|
|
|
ASSerialWin.portAvailableAfterUpload(port: port)
|
2015-01-07 09:30:28 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case .Interactive:
|
|
|
|
args += ["-t"]
|
2015-01-02 15:17:10 +00:00
|
|
|
}
|
2014-12-08 04:35:58 +00:00
|
|
|
if speed != nil {
|
|
|
|
args.append("-b")
|
|
|
|
args.append(speed!)
|
|
|
|
}
|
2014-12-29 06:35:07 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// For Leonardo & the like, reset by opening port at 1200 baud
|
|
|
|
//
|
|
|
|
if leonardish {
|
|
|
|
if verbosity > 0 {
|
2016-11-13 11:03:51 +00:00
|
|
|
logOut.write("Opening port \(port) at 1200 baud\n".data(using: String.Encoding.utf8, allowLossyConversion: true)!)
|
2014-12-29 06:35:07 +00:00
|
|
|
}
|
2015-01-02 15:17:10 +00:00
|
|
|
if let dummyConnection = ASSerial.openPort(portPath, withSpeed: 1200) {
|
|
|
|
usleep(50000)
|
|
|
|
ASSerial.closePort(dummyConnection.fileDescriptor)
|
|
|
|
sleep(1)
|
2016-11-13 11:03:51 +00:00
|
|
|
for retry in 0 ..< 40 {
|
2015-01-02 06:26:10 +00:00
|
|
|
usleep(250000)
|
2016-11-14 00:37:13 +00:00
|
|
|
if (fileManager.fileExists(atPath: portPath!)) {
|
2014-12-29 06:35:07 +00:00
|
|
|
if verbosity > 0 {
|
2016-11-13 11:03:51 +00:00
|
|
|
logOut.write("Found port \(port) after \(retry) attempts.\n".data(using: String.Encoding.utf8, allowLossyConversion: true)!)
|
2014-12-29 06:35:07 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-11-13 11:03:51 +00:00
|
|
|
let cmdLine = task!.launchPath!+" "+(args as NSArray).componentsJoined(by: " ")+"\n"
|
|
|
|
logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!)
|
2014-12-08 02:59:47 +00:00
|
|
|
task!.arguments = args;
|
|
|
|
task!.launch()
|
2015-01-07 09:30:28 +00:00
|
|
|
if interactive {
|
2015-11-16 01:56:33 +00:00
|
|
|
let intSpeed = (speed != nil) ? Int(speed!) ?? 19200 : 19200
|
2016-11-13 11:03:51 +00:00
|
|
|
ASSerialWin.showWindowWithPort(port: port, task:task!, speed:intSpeed)
|
2015-01-02 15:17:10 +00:00
|
|
|
task = nil
|
|
|
|
}
|
2014-12-08 02:59:47 +00:00
|
|
|
}
|
2014-12-10 14:30:51 +00:00
|
|
|
|
|
|
|
func disassembleProject(board: String) {
|
2016-11-13 11:03:51 +00:00
|
|
|
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
|
2016-11-21 19:33:02 +00:00
|
|
|
task = Process()
|
2016-11-14 00:37:13 +00:00
|
|
|
task!.currentDirectoryPath = dir.path
|
2014-12-15 01:49:33 +00:00
|
|
|
task!.launchPath = toolChain+"/bin/avr-objdump"
|
2014-12-10 14:30:51 +00:00
|
|
|
|
2016-11-13 11:03:51 +00:00
|
|
|
let fileManager = FileManager.default
|
|
|
|
let logURL = dir.appendingPathComponent("build/disasm.log")
|
2016-11-14 00:37:13 +00:00
|
|
|
fileManager.createFile(atPath: logURL.path, contents: Data(), attributes: nil)
|
|
|
|
let logOut = FileHandle(forWritingAtPath: logURL.path)!
|
2014-12-10 14:30:51 +00:00
|
|
|
task!.standardOutput = logOut
|
|
|
|
task!.standardError = logOut
|
|
|
|
|
2016-11-13 11:03:51 +00:00
|
|
|
let showSource = UserDefaults.standard.bool(forKey: "ShowSourceInDisassembly")
|
2014-12-15 01:49:33 +00:00
|
|
|
var args = showSource ? ["-S"] : []
|
2016-11-14 00:37:13 +00:00
|
|
|
args += ["-d", "build/"+board+"/"+dir.lastPathComponent+".elf"]
|
2016-11-13 11:03:51 +00:00
|
|
|
let cmdLine = task!.launchPath!+" "+(args as NSArray).componentsJoined(by: " ")+"\n"
|
2016-11-14 00:37:13 +00:00
|
|
|
logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!)
|
2014-12-10 14:30:51 +00:00
|
|
|
task!.arguments = args;
|
|
|
|
task!.launch()
|
|
|
|
}
|
2016-11-13 11:03:51 +00:00
|
|
|
}
|