...
 
Commits (16)
Subproject commit f1771b5bfe51d13392aded398012f291534adf89
Subproject commit 0f49f75f602539077f4bdeb4c5f37f999bab61f2
......@@ -23,21 +23,32 @@ public func collectSources(sourceDescriptions: [String], taskForCalculatingPath
for unPrefixedDescription in sourceDescriptions {
let description = (task?.importedPath ?? Path()) + unPrefixedDescription
if unPrefixedDescription.hasSuffix("**.swift") {
let basepath = description.dirname()
do {
let iterator = try FS.iterateItems(path: basepath, recursive: true)
for file in iterator {
if file.path.components.last!.hasSuffix(".swift") {
sources.append(file.path)
}
}
} catch {
fatalError("Error: \(error) for '\(basepath)'")
}
sources.append(contentsOf: findSources(basepath: description.dirname(),extension: ".swift"))
}
else if unPrefixedDescription.hasSuffix("**.c") {
sources.append(contentsOf: findSources(basepath: description.dirname(),extension: ".c"))
}
else if unPrefixedDescription.hasSuffix("**.h") {
sources.append(contentsOf: findSources(basepath: description.dirname(),extension: ".h"))
}
else {
sources.append(description)
}
}
return sources
}
private func findSources(basepath: Path, extension: String) -> [Path] {
var sources: [Path] = []
do {
let iterator = try FS.iterateItems(path: basepath, recursive: true)
for file in iterator {
if file.path.components.last!.hasSuffix(`extension`) {
sources.append(file.path)
}
}
} catch {
fatalError("Error: \(error) for '\(basepath)'")
}
return sources
}
\ No newline at end of file
......@@ -14,7 +14,7 @@
import atfoundation
enum PackageError: ErrorProtocol {
enum PackageError: Error {
case NonVectorImport
case ParserFailed
case NonPackage
......@@ -22,12 +22,20 @@ enum PackageError: ErrorProtocol {
case RequiredOverlayNotPresent([String])
}
private extension Task {
fileprivate func merge(_ lhs: ParseValue, _ rhs: ParseValue) -> ParseValue {
switch(lhs, rhs) {
case (.Vector(let l), .Vector(let r)):
return .Vector(l + r)
default:
fatalError("Can't merge \(lhs) and \(rhs)")
}
}
fileprivate extension Task {
/**Apply the overlay to the receiver
- warning: an overlay may itself apply another overlay. In this case, the overlay for the task should be recalculated.
- return: whether the overlay applied another overlay */
@warn_unused_result
private func applyOverlay(name: String, overlay: [String: ParseValue]) -> Bool {
fileprivate func applyOverlay(name: String, overlay: [String: ParseValue], globalOverlays: [String]) -> Bool {
precondition(!appliedOverlays.contains(name), "Already applied overlay named \(name)")
for (optionName, optionValue) in overlay {
switch(optionValue) {
......@@ -65,6 +73,21 @@ private extension Task {
}
self[optionName] = ParseValue.BoolLiteral(b)
case ParseValue.Map(let m):
precondition(optionName == "overlays", "Don't support map merging for key \(optionName)")
for key in m.keys {
if globalOverlays.contains(key) {
for (key, value) in m[key]!.map! {
if let existingValue = self[key] {
self[key] = merge(existingValue, value)
}
else {
self[key] = value
}
}
}
}
default:
fatalError("Canot overlay value \(optionValue); please file a bug")
......@@ -167,7 +190,7 @@ final public class Package {
- parameter overlay: A list of overlays to apply globally to all tasks in the package.
- parameter focusOnTask: The user has "selected" the particular task. We provide more diagnostics for this task.
*/
public convenience init(filepath: Path, overlay: [String], focusOnTask: String?) throws {
public convenience init(filepath: Path, overlay: [String], focusOnTask: String?, softFail: Bool = true) throws {
//todo: why doesn't this throw?
guard let parser = try Parser(filepath: filepath) else {
......@@ -176,15 +199,16 @@ final public class Package {
let result = try parser.parse()
let basepath = filepath.dirname()
try self.init(type: result, overlay: overlay, pathOnDisk:basepath, focusOnTask: focusOnTask)
try self.init(type: result, overlay: overlay, pathOnDisk:basepath, focusOnTask: focusOnTask, softFail: softFail)
}
/**
- parameter overlay: The names of things to overlay.
- parameter pathOnDisk: The path to the file on disk. This does not include the file name.
- parameter focusOnTask: The user has "selected" the particular task. We provide more diagnostics for this task.
- parameter softFail: Don't fail hard on certain kinds of errors that may occur if dependencies are not fetched
*/
public init(type: ParseType, overlay requestedGlobalOverlays: [String], pathOnDisk: Path, focusOnTask: String?) throws {
public init(type: ParseType, overlay requestedGlobalOverlays: [String], pathOnDisk: Path, focusOnTask: String?, softFail: Bool = false) throws {
//warn on unknown keys
for (k,_) in type.properties {
if !Key.allKeys.map({$0.rawValue}).contains(k) {
......@@ -224,7 +248,7 @@ final public class Package {
for importFile in imports {
guard let importFileString = importFile.string else { fatalError("Non-string import \(importFile)")}
let adjustedImportPath = (pathOnDisk + importFileString).dirname()
let remotePackage = try Package(filepath: pathOnDisk + importFileString, overlay: requestedGlobalOverlays, focusOnTask: nil)
let remotePackage = try Package(filepath: pathOnDisk + importFileString, overlay: requestedGlobalOverlays, focusOnTask: nil, softFail: softFail)
remotePackage.adjustedImportPath = adjustedImportPath
remotePackages.append(remotePackage)
}
......@@ -289,7 +313,7 @@ final public class Package {
// import the atbuild file if it is there
let adjustedImportPath = (pathOnDisk + importFileString).dirname()
do {
let remotePackage = try Package(filepath: pathOnDisk + importFileString, overlay: requestedGlobalOverlays, focusOnTask: nil)
let remotePackage = try Package(filepath: pathOnDisk + importFileString, overlay: requestedGlobalOverlays, focusOnTask: nil, softFail: softFail)
remotePackage.adjustedImportPath = adjustedImportPath
remotePackages.append(remotePackage)
} catch {
......@@ -349,22 +373,30 @@ final public class Package {
for overlayName in task.overlay {
if task.appliedOverlays.contains(overlayName) { continue }
guard let overlay = declaredOverlays[overlayName] else {
fatalError("Can't find overlay named \(overlayName) in \(declaredOverlays)")
if softFail {
let proposedWarning = "Warning: Can't find overlay named \(overlayName) in \(declaredOverlays); run atpm fetch"
if !warnings.contains(proposedWarning) { warnings.append(proposedWarning) }
continue
}
else {
fatalError("Can't find overlay named \(overlayName) in \(declaredOverlays)")
}
}
again = again || task.applyOverlay(name: overlayName, overlay: overlay)
again = again || task.applyOverlay(name: overlayName, overlay: overlay, globalOverlays: requestedGlobalOverlays)
}
for overlayName in requestedGlobalOverlays {
if task.appliedOverlays.contains(overlayName) { continue }
guard let overlay = declaredOverlays[overlayName] else {
if focusOnTask == task.unqualifiedName || focusOnTask == task.qualifiedName {
let proposedWarning = "Warning: Can't apply overlay \(overlayName) to task \(task.qualifiedName)"
if !warnings.contains(proposedWarning) { warnings.append(proposedWarning) }
if !overlayName.hasPrefix("at.") && !overlayName.hasPrefix("atbuild.") {
let proposedWarning = "Warning: Can't apply overlay \(overlayName) to task \(task.qualifiedName)"
if !warnings.contains(proposedWarning) { warnings.append(proposedWarning) }
}
}
continue
}
again = again || task.applyOverlay(name: overlayName, overlay: overlay)
again = again || task.applyOverlay(name: overlayName, overlay: overlay, globalOverlays: requestedGlobalOverlays)
usedGlobalOverlays.append(overlayName)
}
}
......@@ -377,7 +409,7 @@ final public class Package {
//warn about unused global overlays
for requestedOverlay in requestedGlobalOverlays {
if !usedGlobalOverlays.contains(requestedOverlay) {
if !requestedOverlay.hasPrefix("atbuild.") {
if !requestedOverlay.hasPrefix("atbuild.") && !requestedOverlay.hasPrefix("at.") {
print("Warning: overlay \(requestedOverlay) had no effect on package \(name)")
}
}
......
......@@ -14,7 +14,7 @@
import atfoundation
public enum ParseError: ErrorProtocol {
public enum ParseError: Error {
case InvalidPackageFile
case ExpectedTokenType(TokenType, Token?)
case InvalidTokenForValueType(Token?)
......@@ -111,7 +111,7 @@ final public class Parser {
private func parseKeyValuePairs() throws -> [String:ParseValue] {
var pairs: [String:ParseValue] = [:]
while let token = next() where token.type != .CloseParen && token.type != .CloseBrace {
while let token = next(), token.type != .CloseParen && token.type != .CloseBrace {
lexer.stall()
let key = try parseKey()
......@@ -152,24 +152,24 @@ final public class Parser {
}
private func parseVector() throws -> ParseValue {
if let token = next() where token.type != .OpenBracket { throw ParseError.ExpectedTokenType(.OpenBracket, token) }
if let token = next(),token.type != .OpenBracket { throw ParseError.ExpectedTokenType(.OpenBracket, token) }
var items: [ParseValue] = []
while let token = next() where token.type != .CloseBracket {
while let token = next(), token.type != .CloseBracket {
lexer.stall()
items.append(try parseValue())
}
lexer.stall()
if let token = next() where token.type != .CloseBracket { throw ParseError.ExpectedTokenType(.CloseBracket, token) }
if let token = next(), token.type != .CloseBracket { throw ParseError.ExpectedTokenType(.CloseBracket, token) }
return .Vector(items)
}
private func parseMap() throws -> ParseValue {
if let token = next() where token.type != .OpenBrace { throw ParseError.ExpectedTokenType(.OpenBrace, token) }
if let token = next(), token.type != .OpenBrace { throw ParseError.ExpectedTokenType(.OpenBrace, token) }
let items = try parseKeyValuePairs()
if let token = next() where token.type != .CloseBrace { throw ParseError.ExpectedTokenType(.CloseBrace, token) }
if let token = next(), token.type != .CloseBrace { throw ParseError.ExpectedTokenType(.CloseBrace, token) }
return .Map(items)
}
......
......@@ -94,7 +94,7 @@ final public class Lexer {
scanner.stall()
while let info = scanner.next() where isWhitespace(c: info.character) {}
while let info = scanner.next(), isWhitespace(c: info.character) {}
scanner.stall()
guard let next = scanner.next() else { return Token(type: .EOF) }
......@@ -104,7 +104,7 @@ final public class Lexer {
}
else if isValidIdentifierSignalCharacter(c: next.character) {
var content = String(next.character!)
while let info = scanner.next() where isValidIdenitifierCharacter(c: info.character) {
while let info = scanner.next(), isValidIdenitifierCharacter(c: info.character) {
content.append(info.character!)
}
scanner.stall()
......@@ -137,10 +137,10 @@ final public class Lexer {
let line = scanner.peek()!.line
var comment = ""
while let info = scanner.next() where info.character == ";" {}
while let info = scanner.next(), info.character == ";" {}
scanner.stall()
while let info = scanner.next() where info.character != "\n" {
while let info = scanner.next(), info.character != "\n" {
comment.append(info.character!)
}
......@@ -148,7 +148,7 @@ final public class Lexer {
}
else if next.character == "\"" {
var content = ""
while let info = scanner.next() where info.character != "\"" {
while let info = scanner.next(), info.character != "\"" {
if info.character == "\\" {
let escaped = scanner.next()
let char = escaped?.character
......
(package
:name "multi-overlays"
;;in this test we verify the overlay of a custom overlay export
:overlays {
:link { ;;exporting link flags
:overlays {
:atbuild.platform.vax { ;;custom link flags for VAX platform
:link-options ["-DUSEVAX"]
}
}
}
}
:tasks {
:build {
:link-options ["-DUSENORMAL"]
:use-overlays ["link"] ;;get VAX settings if on VAX
:tool "foo"
}
}
)
;; End of the sample.
\ No newline at end of file
......@@ -16,7 +16,7 @@
import atfoundation
extension String : ErrorProtocol {}
extension String : Error {}
enum test {
static func assert(_ condition: Bool, file: String = #file, functionName: String = #function, line: Int = #line) throws {
......
......@@ -31,6 +31,7 @@ class PackageTests: Test {
PackageTests.testOnlyPlatforms,
PackageTests.testUseBinary,
PackageTests.testBinaryManifest,
PackageTests.testMultiOverlays,
]
......@@ -282,4 +283,14 @@ class PackageTests: Test {
}
}
static func testMultiOverlays() throws {
let filepath = Path("tests/collateral/multi-overlays.atpkg")
let p = try Package(filepath: filepath, overlay: ["atbuild.platform.vax"], focusOnTask: nil)
guard let task = p.tasks["build"] else { fatalError("No task")}
guard let linkOptions = task["link-options"]?.vector else { fatalError("No link options")}
print(linkOptions)
try test.assert(linkOptions.count == 2)
}
}