Start work on ASBuilder

This commit is contained in:
Matthias Neeracher 2014-11-28 14:18:53 +01:00 committed by Matthias Neeracher
parent 7ae5a4b215
commit 9b770623a7
7 changed files with 245 additions and 11 deletions

View File

@ -13,6 +13,8 @@
9501D8091A17025C0034C530 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9501D8081A17025C0034C530 /* Images.xcassets */; }; 9501D8091A17025C0034C530 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9501D8081A17025C0034C530 /* Images.xcassets */; };
9501D80C1A17025C0034C530 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9501D80A1A17025C0034C530 /* MainMenu.xib */; }; 9501D80C1A17025C0034C530 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9501D80A1A17025C0034C530 /* MainMenu.xib */; };
9501D8181A17025C0034C530 /* AVRsackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9501D8171A17025C0034C530 /* AVRsackTests.swift */; }; 9501D8181A17025C0034C530 /* AVRsackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9501D8171A17025C0034C530 /* AVRsackTests.swift */; };
951CD1741A23C9FC0066C1A1 /* ASBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */; };
951CD1771A2615000066C1A1 /* BuildProject in Resources */ = {isa = PBXBuildFile; fileRef = 951CD1761A2615000066C1A1 /* BuildProject */; };
95468DDF1A228BE600668EE2 /* ASHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95468DDE1A228BE600668EE2 /* ASHardware.swift */; }; 95468DDF1A228BE600668EE2 /* ASHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95468DDE1A228BE600668EE2 /* ASHardware.swift */; };
95468DE31A228E1300668EE2 /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 95468DE21A228E1300668EE2 /* Defaults.plist */; }; 95468DE31A228E1300668EE2 /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 95468DE21A228E1300668EE2 /* Defaults.plist */; };
95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BF80EA1A185C9E0004A693 /* ASFileTree.swift */; }; 95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BF80EA1A185C9E0004A693 /* ASFileTree.swift */; };
@ -69,6 +71,8 @@
9501D8111A17025C0034C530 /* AVRsackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AVRsackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9501D8111A17025C0034C530 /* AVRsackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AVRsackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9501D8161A17025C0034C530 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 9501D8161A17025C0034C530 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9501D8171A17025C0034C530 /* AVRsackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVRsackTests.swift; sourceTree = "<group>"; }; 9501D8171A17025C0034C530 /* AVRsackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVRsackTests.swift; sourceTree = "<group>"; };
951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASBuilder.swift; sourceTree = "<group>"; };
951CD1761A2615000066C1A1 /* BuildProject */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = BuildProject; sourceTree = "<group>"; };
95468DDE1A228BE600668EE2 /* ASHardware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASHardware.swift; sourceTree = "<group>"; }; 95468DDE1A228BE600668EE2 /* ASHardware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASHardware.swift; sourceTree = "<group>"; };
95468DE21A228E1300668EE2 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = "<group>"; }; 95468DE21A228E1300668EE2 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = "<group>"; };
958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AVRsack-Bridging-Header.h"; sourceTree = "<group>"; }; 958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AVRsack-Bridging-Header.h"; sourceTree = "<group>"; };
@ -120,11 +124,7 @@
9501D7FE1A17025C0034C530 /* AVRsack */ = { 9501D7FE1A17025C0034C530 /* AVRsack */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */, 951CD1751A2614C40066C1A1 /* Source */,
9501D8011A17025C0034C530 /* ASApplication.swift */,
9501D8031A17025C0034C530 /* ASProjDoc.swift */,
95BF80EA1A185C9E0004A693 /* ASFileTree.swift */,
95468DDE1A228BE600668EE2 /* ASHardware.swift */,
95BF80EC1A185CD90004A693 /* Resources */, 95BF80EC1A185CD90004A693 /* Resources */,
9501D7FF1A17025C0034C530 /* Supporting Files */, 9501D7FF1A17025C0034C530 /* Supporting Files */,
95EA32671A17BAA600F66EB0 /* Frameworks */, 95EA32671A17BAA600F66EB0 /* Frameworks */,
@ -136,6 +136,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
9501D8001A17025C0034C530 /* Info.plist */, 9501D8001A17025C0034C530 /* Info.plist */,
958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */,
); );
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
@ -157,6 +158,19 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
951CD1751A2614C40066C1A1 /* Source */ = {
isa = PBXGroup;
children = (
9501D8011A17025C0034C530 /* ASApplication.swift */,
9501D8031A17025C0034C530 /* ASProjDoc.swift */,
95BF80EA1A185C9E0004A693 /* ASFileTree.swift */,
95468DDE1A228BE600668EE2 /* ASHardware.swift */,
951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */,
951CD1761A2615000066C1A1 /* BuildProject */,
);
name = Source;
sourceTree = "<group>";
};
95BF80EC1A185CD90004A693 /* Resources */ = { 95BF80EC1A185CD90004A693 /* Resources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -302,6 +316,7 @@
9501D8071A17025C0034C530 /* ASProjDoc.xib in Resources */, 9501D8071A17025C0034C530 /* ASProjDoc.xib in Resources */,
9501D8091A17025C0034C530 /* Images.xcassets in Resources */, 9501D8091A17025C0034C530 /* Images.xcassets in Resources */,
9501D80C1A17025C0034C530 /* MainMenu.xib in Resources */, 9501D80C1A17025C0034C530 /* MainMenu.xib in Resources */,
951CD1771A2615000066C1A1 /* BuildProject in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -321,6 +336,7 @@
files = ( files = (
9501D8041A17025C0034C530 /* ASProjDoc.swift in Sources */, 9501D8041A17025C0034C530 /* ASProjDoc.swift in Sources */,
95468DDF1A228BE600668EE2 /* ASHardware.swift in Sources */, 95468DDF1A228BE600668EE2 /* ASHardware.swift in Sources */,
951CD1741A23C9FC0066C1A1 /* ASBuilder.swift in Sources */,
9501D8021A17025C0034C530 /* ASApplication.swift in Sources */, 9501D8021A17025C0034C530 /* ASApplication.swift in Sources */,
95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */, 95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */,
); );

40
AVRsack/ASBuilder.swift Normal file
View File

@ -0,0 +1,40 @@
//
// ASBuilder.swift
// AVRsack
//
// Created by Matthias Neeracher on 11/24/14.
// Copyright © 2014 Aere Perennius. All rights reserved.
//
import Foundation
class ASBuilder {
var dir = NSURL()
var task : NSTask?
func setProjectURL(url: NSURL) {
dir = url.URLByDeletingLastPathComponent!.standardizedURL!
}
func buildProject(board: String, files: ASFileTree) {
task = NSTask()
task!.currentDirectoryPath = dir.path!
task!.launchPath = NSBundle.mainBundle().pathForResource("BuildProject", ofType: "")!
let libPath = (ASLibraries.instance().directories as NSArray).componentsJoinedByString(":")
var args = [NSString]()
let boardProp = ASHardware.instance().boards[board]!
args.append("board="+board)
args.append("mcu="+boardProp["build.mcu"])
args.append("f_cpu="+boardProp["build.f_cpu"])
args.append("core="+boardProp["build.core"])
args.append("variant="+boardProp["build.variant"])
args.append("libs="+libPath)
args.append("--")
args += files.paths
task!.arguments = args;
task!.launch()
files.paths
}
}

View File

@ -76,6 +76,9 @@ class ASFileNode {
assertionFailure("Undefined item type in file hierarchy") assertionFailure("Undefined item type in file hierarchy")
} }
} }
func paths(rootPath: NSString) -> [NSString] {
return [NSString]()
}
} }
class ASFileGroup : ASFileNode { class ASFileGroup : ASFileNode {
@ -109,7 +112,6 @@ class ASFileGroup : ASFileNode {
child.apply(closure) child.apply(closure)
} }
} }
func childrenPropertyList(rootPath: NSString) -> [AnyObject] { func childrenPropertyList(rootPath: NSString) -> [AnyObject] {
return children.map() { (node) in node.propertyList(rootPath) } return children.map() { (node) in node.propertyList(rootPath) }
} }
@ -117,6 +119,13 @@ class ASFileGroup : ASFileNode {
return [kTypeKey: kNodeType, kNameKey: name, kExpandedKey: expanded, return [kTypeKey: kNodeType, kNameKey: name, kExpandedKey: expanded,
kChildrenKey: childrenPropertyList(rootPath)] kChildrenKey: childrenPropertyList(rootPath)]
} }
override func paths(rootPath: NSString) -> [NSString] {
var allPaths = [NSString]()
for child in children {
allPaths += child.paths(rootPath)
}
return allPaths
}
} }
class ASProject : ASFileGroup { class ASProject : ASFileGroup {
@ -168,10 +177,12 @@ class ASFileItem : ASFileNode {
let resComp = Array(count: relCount-matchComp, repeatedValue: "..")+pathComp[matchComp..<pathCount] let resComp = Array(count: relCount-matchComp, repeatedValue: "..")+pathComp[matchComp..<pathCount]
return "/".join(resComp) return "/".join(resComp)
} }
override func propertyList(rootPath: NSString) -> AnyObject { override func propertyList(rootPath: NSString) -> AnyObject {
return [kTypeKey: kNodeTypeFile, kKindKey: type.rawValue, kPathKey: relativePath(rootPath)] return [kTypeKey: kNodeTypeFile, kKindKey: type.rawValue, kPathKey: relativePath(rootPath)]
} }
override func paths(rootPath: NSString) -> [NSString] {
return [relativePath(rootPath)]
}
} }
class ASFileTree : NSObject, NSOutlineViewDataSource { class ASFileTree : NSObject, NSOutlineViewDataSource {
@ -197,6 +208,9 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
func readPropertyList(prop: NSDictionary) { func readPropertyList(prop: NSDictionary) {
root = ASFileNode.readPropertyList(prop, rootURL:dir) as ASProject root = ASFileNode.readPropertyList(prop, rootURL:dir) as ASProject
} }
var paths : [NSString] {
return root.paths(dir.path!)
}
// MARK: Outline Data Source // MARK: Outline Data Source
func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {

View File

@ -8,7 +8,22 @@
import Foundation import Foundation
typealias ASPropertyEntry = [String: String] class ASPropertyEntry {
private var storage = [String: String]()
subscript(key: String) -> String {
get {
if let value = storage[key] {
return value
} else {
return ""
}
}
set (newValue) {
storage[key] = newValue
}
}
}
typealias ASProperties = [String: ASPropertyEntry] typealias ASProperties = [String: ASPropertyEntry]
private func subdirectories(path: NSString) -> [NSString] { private func subdirectories(path: NSString) -> [NSString] {
@ -26,7 +41,7 @@ private func subdirectories(path: NSString) -> [NSString] {
return subDirs return subDirs
} }
let hardwareInstance = ASHardware() private let hardwareInstance = ASHardware()
class ASHardware { class ASHardware {
class func instance() -> ASHardware { return hardwareInstance } class func instance() -> ASHardware { return hardwareInstance }
let directories = [NSString]() let directories = [NSString]()
@ -90,3 +105,33 @@ class ASHardware {
} }
} }
} }
private let librariesInstance = ASLibraries()
class ASLibraries {
class func instance() -> ASLibraries { return librariesInstance }
let directories = [NSString]()
let libraries = [NSString]()
init() {
//
// Gather hardware directories
//
let userDefaults = NSUserDefaults.standardUserDefaults()
let fileManager = NSFileManager.defaultManager()
if let arduinoPath = userDefaults.stringForKey("Arduino") {
let arduinoLibrariesPath = arduinoPath + "/Contents/Resources/Java/libraries"
let dirs = subdirectories(arduinoLibrariesPath)
if dirs.count > 0 {
directories.append(arduinoLibrariesPath)
libraries += dirs
}
}
for sketchDir in userDefaults.objectForKey("Sketchbooks") as [NSString] {
let librariesPath = sketchDir + "/libraries"
let dirs = subdirectories(librariesPath)
if dirs.count > 0 {
directories.append(librariesPath)
libraries += dirs
}
}
}
}

View File

@ -13,11 +13,15 @@ private var keyboardHandler : ACEKeyboardHandler = .Ace
class ASProjDoc: NSDocument, NSOutlineViewDelegate { class ASProjDoc: NSDocument, NSOutlineViewDelegate {
@IBOutlet weak var editor : ACEView! @IBOutlet weak var editor : ACEView!
@IBOutlet weak var outline : NSOutlineView! @IBOutlet weak var outline : NSOutlineView!
let files : ASFileTree = ASFileTree() let files = ASFileTree()
let builder = ASBuilder()
var mainEditor : ASFileNode? var mainEditor : ASFileNode?
var currentTheme : UInt = 0 var currentTheme : UInt = 0
var fontSize : UInt = 12 var fontSize : UInt = 12
var themeObserver : AnyObject? var themeObserver : AnyObject?
var board : String = "uno"
var programmer : String = ""
var port : String = ""
let kVersionKey = "Version" let kVersionKey = "Version"
let kCurVersion = 1.0 let kCurVersion = 1.0
@ -25,6 +29,9 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
let kThemeKey = "Theme" let kThemeKey = "Theme"
let kFontSizeKey = "FontSize" let kFontSizeKey = "FontSize"
let kBindingsKey = "Bindings" let kBindingsKey = "Bindings"
let kBoardKey = "Board"
let kProgrammerKey = "Programmer"
let kPortKey = "Port"
// MARK: Initialization / Finalization // MARK: Initialization / Finalization
@ -51,6 +58,9 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
themeObserver = NSNotificationCenter.defaultCenter().addObserverForName(kBindingsKey, object: nil, queue: nil, usingBlock: { (NSNotification) in themeObserver = NSNotificationCenter.defaultCenter().addObserverForName(kBindingsKey, object: nil, queue: nil, usingBlock: { (NSNotification) in
self.editor.setKeyboardHandler(keyboardHandler) self.editor.setKeyboardHandler(keyboardHandler)
}) })
board = userDefaults.stringForKey(kBoardKey)!
programmer = userDefaults.stringForKey(kProgrammerKey)!
port = userDefaults.stringForKey(kPortKey)!
} }
override func finalize() { override func finalize() {
saveCurEditor() saveCurEditor()
@ -123,6 +133,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
success = importProject(url.URLByDeletingLastPathComponent!, error: outError) success = importProject(url.URLByDeletingLastPathComponent!, error: outError)
if success { if success {
files.setProjectURL(fileURL!) files.setProjectURL(fileURL!)
builder.setProjectURL(fileURL!)
fileURL = projectURL fileURL = projectURL
success = writeToURL(projectURL, ofType: "Project", forSaveOperation: .SaveAsOperation, originalContentsURL: nil, error: outError) success = writeToURL(projectURL, ofType: "Project", forSaveOperation: .SaveAsOperation, originalContentsURL: nil, error: outError)
} }
@ -136,6 +147,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
return false return false
} }
files.setProjectURL(fileURL!) files.setProjectURL(fileURL!)
builder.setProjectURL(fileURL!)
let projectData : NSDictionary = NSPropertyListSerialization.propertyListFromData(data, mutabilityOption: .Immutable, format: nil, errorDescription: nil) as NSDictionary let projectData : NSDictionary = NSPropertyListSerialization.propertyListFromData(data, mutabilityOption: .Immutable, format: nil, errorDescription: nil) as NSDictionary
let projectVersion = projectData[kVersionKey] as Double let projectVersion = projectData[kVersionKey] as Double
assert(projectVersion <= floor(kCurVersion+1.0), "Project version too new for this app") assert(projectVersion <= floor(kCurVersion+1.0), "Project version too new for this app")
@ -228,6 +240,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
// MARK: Build / Upload // MARK: Build / Upload
@IBAction func buildProject(AnyObject) { @IBAction func buildProject(AnyObject) {
builder.buildProject(board, files: files)
} }
} }

100
AVRsack/BuildProject Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/ruby
#
# BuildProject board=... mcu=... -- FILE1 FILE2 FILE3
#
# AVRsack
#
# Created by Matthias Neeracher on 11/26/14.
# Copyright © 2014 Aere Perennius. All rights reserved.
#
require 'fileutils.rb'
BUILD = {
'board' => 'uno',
'mcu' => 'atmega328p',
'f_cpu' => 16000000,
'core' => 'arduino',
'variant' => 'standard',
}
def parseArguments
while ARGV.length > 0 do
param = ARGV.shift
break if param == '--'
param =~ /(\S+?)=(\S*)/
BUILD[$1] = $2
end
end
def createBuildDirectory
$BUILD_DIR = "build/#{BUILD['board']}"
$SKETCH_DIR = "#{$BUILD_DIR}/sketch"
FileUtils::mkdir_p "#{$SKETCH_DIR}", :verbose => true
end
def parseInoFiles
$LIBPATH = BUILD['libs'].split(':').reverse
$LIBRARIES = []
ARGV.each_index do |arg|
if ARGV[arg] =~ /\.ino$/
outName = "#{$SKETCH_DIR}/"+File.basename(ARGV[arg], '.ino')+".cpp"
outFile = File.open(outName, 'w')
File.open(ARGV[arg], 'r') do |ino|
contents = ino.read
# Find protypes:
prototypes = contents.dup
# - Strip comments, quoted strings, and preprocessor directives
prototypes.gsub!(%r{'(?:[^']|\\')+'|"(?:[^"]|\\")*"|//.*?$|/\*.*?\*/|^\s*?#.*?$}m, ' ')
# Collapse braces
while prototypes.sub!(/(\{)(?:[^{}]+|\{[^{}]*\})/m, '\1') do
end
existingProto = {}
prototypes.scan(/[\w\[\]\*]+\s+[&\[\]\*\w\s]+\([&,\[\]\*\w\s]*\)(?=\s*;)/) {|p|
existingProto[smashSpaces(p)] = true
}
proto = []
prototypes.scan(/[\w\[\]\*]+\s+[&\[\]\*\w\s]+\([&,\[\]\*\w\s]*\)(?=\s*{)/) {|p|
p = smashSpaces(p)
proto << p unless existingProto[p]
}
contents.each_line do |line|
if line =~ /^\s*#include\s+[<"](.*)[">]\s*(#.*)?$/
addLibrary($1)
end
end
end
outFile.close
ARGV[arg] = outName
end
end
end
def smashSpaces(s)
return s.gsub(/(\W)\s+(\W)/, '\1\2').gsub(/\s+/, ' ')
end
def addLibrary(header)
$LIBPATH.each do |path|
Dir.glob("#{path}/*").each do |lib|
if File.exists?("#{lib}/#{header}")
$LIBRARIES << lib
end
end
end
end
parseArguments
createBuildDirectory
parseInoFiles
File.open("#{$BUILD_DIR}/Rakefile", 'w') do |rakeFile|
rakeFile.print <<END_RAKE
LIBRARIES=#{$LIBRARIES.join(':')}
FILES = [
#{ARGV.join('\n')}
]
END_RAKE
end

View File

@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>Port</key>
<string></string>
<key>Programmer</key>
<string>arduino</string>
<key>Board</key>
<string>uno</string>
<key>Bindings</key> <key>Bindings</key>
<string>Ace</string> <string>Ace</string>
<key>FontSize</key> <key>FontSize</key>