AVRSack/AVRsack/ASBuilder.swift

254 lines
11 KiB
Swift
Raw Normal View History

2014-11-28 13:18:53 +00:00
//
// ASBuilder.swift
// AVRsack
//
// Created by Matthias Neeracher on 11/24/14.
// Copyright © 2014-2015 Aere Perennius. All rights reserved.
2014-11-28 13:18:53 +00:00
//
import Foundation
class ASBuilder {
2014-12-01 02:34:53 +00:00
var dir = NSURL()
var task : NSTask?
2014-12-08 04:35:58 +00:00
var continuation: (()->())?
var termination : AnyObject?
init() {
termination = NSNotificationCenter.defaultCenter().addObserverForName(NSTaskDidTerminateNotification,
object: nil, queue: nil, usingBlock:
{ (notification: NSNotification!) in
2014-12-10 14:30:51 +00:00
if notification.object as? NSTask == self.task {
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() {
NSNotificationCenter.defaultCenter().removeObserver(termination!)
}
2014-11-28 13:18:53 +00:00
func setProjectURL(url: NSURL) {
dir = url.URLByDeletingLastPathComponent!.URLByStandardizingPath!
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() {
NSFileManager.defaultManager().removeItemAtURL(dir.URLByAppendingPathComponent("build"), error: nil)
}
2014-11-28 13:18:53 +00:00
func buildProject(board: String, files: ASFileTree) {
2015-02-14 16:43:20 +00:00
let toolChain = (NSApplication.sharedApplication().delegate as! ASApplication).preferences.toolchainPath
2014-11-28 13:18:53 +00:00
task = NSTask()
task!.currentDirectoryPath = dir.path!
task!.launchPath = NSBundle.mainBundle().pathForResource("BuildProject", ofType: "")!
2014-12-01 02:34:53 +00:00
let fileManager = NSFileManager.defaultManager()
2014-11-28 13:18:53 +00:00
let libPath = (ASLibraries.instance().directories as NSArray).componentsJoinedByString(":")
2015-02-14 16:43:20 +00:00
var args = [String]()
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"]!
var corePath = library+"/cores/"+boardProp["build.core"]!
2015-02-14 16:43:20 +00:00
var variantPath : String?
2015-01-07 09:30:28 +00:00
if fileManager.fileExistsAtPath(corePath) {
if let variantName = boardProp["build.variant"] {
variantPath = library+"/variants/"+variantName
if fileManager.fileExistsAtPath(variantPath!) {
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)
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"]!)
args.append("max_size"+boardProp["upload.maximum_size"]!)
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)
if variantPath != nil {
args.append("variant_path="+variantPath!)
}
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
let portPath = ASSerial.fileNameForPort(port)
2015-02-14 16:43:20 +00:00
let toolChain = (NSApplication.sharedApplication().delegate as! ASApplication).preferences.toolchainPath
2014-12-08 02:59:47 +00:00
task = NSTask()
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
let fileManager = NSFileManager.defaultManager()
var logOut : NSFileHandle
2015-01-07 09:30:28 +00:00
if interactive {
let inputPipe = NSPipe()
let outputPipe = NSPipe()
logOut = outputPipe.fileHandleForWriting
task!.standardInput = inputPipe
task!.standardOutput = outputPipe
task!.standardError = outputPipe
} else {
2015-01-07 09:30:28 +00:00
ASSerialWin.portNeededForUpload(port)
let logURL = dir.URLByAppendingPathComponent("build/upload.log")
fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil)
logOut = NSFileHandle(forWritingAtPath: logURL.path!)!
task!.standardOutput = logOut
task!.standardError = logOut
}
2014-12-08 02:59:47 +00:00
2014-12-29 06:35:07 +00:00
let libPath = (ASLibraries.instance().directories as NSArray).componentsJoinedByString(":")
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"]
let verbosity = NSUserDefaults.standardUserDefaults().integerForKey("UploadVerbosity")
var args = Array<String>(count: verbosity, repeatedValue: "-v")
args += [
2014-12-15 01:49:33 +00:00
"-C", toolChain+"/etc/avrdude.conf",
"-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath]
2015-01-07 09:30:28 +00:00
switch mode {
case .Upload:
if hasBootloader {
args += ["-D"]
}
args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent!+".hex:i"]
2015-01-07 09:30:28 +00:00
continuation = {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2*NSEC_PER_SEC)), dispatch_get_main_queue(), {
ASSerialWin.portAvailableAfterUpload(port)
})
}
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 {
let task2 = NSTask()
task2.currentDirectoryPath = dir.path!
task2.launchPath = toolChain+"/bin/avrdude"
task2.arguments = loaderArgs
task2.standardOutput = logOut
task2.standardError = logOut
continuation = {
let cmdLine = task2.launchPath+" "+(loaderArgs as NSArray).componentsJoinedByString(" ")+"\n"
logOut.writeData(cmdLine.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
task2.launch()
self.continuation = {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2*NSEC_PER_SEC)), dispatch_get_main_queue(), {
ASSerialWin.portAvailableAfterUpload(port)
})
}
}
}
case .Interactive:
args += ["-t"]
}
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 {
logOut.writeData("Opening port \(port) at 1200 baud\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
2014-12-29 06:35:07 +00:00
}
if let dummyConnection = ASSerial.openPort(portPath, withSpeed: 1200) {
usleep(50000)
ASSerial.closePort(dummyConnection.fileDescriptor)
sleep(1)
2015-01-02 06:26:10 +00:00
for (var retry=0; retry < 40; ++retry) {
usleep(250000)
if (fileManager.fileExistsAtPath(portPath)) {
2014-12-29 06:35:07 +00:00
if verbosity > 0 {
logOut.writeData("Found port \(port) after \(retry) attempts.\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
}
break;
}
}
}
}
2014-12-08 04:41:00 +00:00
let cmdLine = task!.launchPath+" "+(args as NSArray).componentsJoinedByString(" ")+"\n"
logOut.writeData(cmdLine.dataUsingEncoding(NSUTF8StringEncoding, 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 {
let intSpeed = speed?.toInt() ?? 19200
ASSerialWin.showWindowWithPort(port, task:task!, speed:intSpeed)
task = nil
}
2014-12-08 02:59:47 +00:00
}
2014-12-10 14:30:51 +00:00
func disassembleProject(board: String) {
2015-02-14 16:43:20 +00:00
let toolChain = (NSApplication.sharedApplication().delegate as! ASApplication).preferences.toolchainPath
2014-12-10 14:30:51 +00:00
task = NSTask()
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
let fileManager = NSFileManager.defaultManager()
let logURL = dir.URLByAppendingPathComponent("build/disasm.log")
fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil)
let logOut = NSFileHandle(forWritingAtPath: logURL.path!)!
task!.standardOutput = logOut
task!.standardError = logOut
2014-12-15 01:49:33 +00:00
let showSource = NSUserDefaults.standardUserDefaults().boolForKey("ShowSourceInDisassembly")
var args = showSource ? ["-S"] : []
args += ["-d", "build/"+board+"/"+dir.lastPathComponent!+".elf"]
2014-12-15 01:49:33 +00:00
let cmdLine = task!.launchPath+" "+(args as NSArray).componentsJoinedByString(" ")+"\n"
2014-12-10 14:30:51 +00:00
logOut.writeData(cmdLine.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
task!.arguments = args;
task!.launch()
}
2014-11-28 13:18:53 +00:00
}