Commit 8c5cbcb6 authored by Drew's avatar Drew

Flatten import hierarchy

We import all packages in a flat hierarchy, with the package name, a
period, and the task.

Using a flat heirarchy eliminates the case where a/b/c != a/d/c.

As a side effect, this closes #4
parent 00150eb9
Pipeline #878 passed with stage
...@@ -14,7 +14,17 @@ ...@@ -14,7 +14,17 @@
import Foundation import Foundation
final public class Task { final public class Task {
public var key: String = "" ///The unqualified name of the task, not including its package name
public let unqualifiedName: String
///The qualified name of the task, including its package name
public var qualifiedName: String {
return package.name + "." + unqualifiedName
}
///The package for the task
let package: Package
public var dependencies: [String] = [] public var dependencies: [String] = []
public var tool: String = "atllbuild" public var tool: String = "atllbuild"
public var importedPath: String ///the directory at which the task was imported. This includes a trailing /. public var importedPath: String ///the directory at which the task was imported. This includes a trailing /.
...@@ -28,11 +38,13 @@ final public class Task { ...@@ -28,11 +38,13 @@ final public class Task {
private var kvp: [String:ParseValue] private var kvp: [String:ParseValue]
init?(value: ParseValue, name: String, importedPath: String) { init?(value: ParseValue, unqualifiedName: String, package: Package, importedPath: String) {
precondition(!unqualifiedName.characters.contains("."), "Task \(unqualifiedName) may not contain a period.")
guard let kvp = value.map else { return nil } guard let kvp = value.map else { return nil }
self.importedPath = importedPath.pathWithTrailingSlash self.importedPath = importedPath.pathWithTrailingSlash
self.kvp = kvp self.kvp = kvp
self.key = name self.unqualifiedName = unqualifiedName
self.package = package
self.allKeys = [String](kvp.keys) self.allKeys = [String](kvp.keys)
self.tool = kvp["tool"]?.string ?? self.tool self.tool = kvp["tool"]?.string ?? self.tool
if let ol = kvp["overlay"] { if let ol = kvp["overlay"] {
...@@ -102,13 +114,13 @@ final public class Task { ...@@ -102,13 +114,13 @@ final public class Task {
case ParseValue.StringLiteral(let str): case ParseValue.StringLiteral(let str):
if let existingValue = self[optionName] { if let existingValue = self[optionName] {
fatalError("Can't overlay on \(self.key)[\(optionName)] which already has a value \(existingValue)") fatalError("Can't overlay on \(self.qualifiedName)[\(optionName)] which already has a value \(existingValue)")
} }
self.kvp[optionName] = ParseValue.StringLiteral(str) self.kvp[optionName] = ParseValue.StringLiteral(str)
case ParseValue.BoolLiteral(let b): case ParseValue.BoolLiteral(let b):
if let existingValue = self[optionName] { if let existingValue = self[optionName] {
fatalError("Can't overlay on \(self.key)[\(optionName)] which already has a value \(existingValue)") fatalError("Can't overlay on \(self.qualifiedName)[\(optionName)] which already has a value \(existingValue)")
} }
self.kvp[optionName] = ParseValue.BoolLiteral(b) self.kvp[optionName] = ParseValue.BoolLiteral(b)
...@@ -131,6 +143,10 @@ final public class Package { ...@@ -131,6 +143,10 @@ final public class Package {
// The optional properties. All optional properties must have a default value. // The optional properties. All optional properties must have a default value.
public var version: String = "" public var version: String = ""
/**The tasks for the package. For tasks in this package, they are indexed
both by qualified and unqualified name. For tasks in another package, they
appear only by qualified name. */
public var tasks: [String:Task] = [:] public var tasks: [String:Task] = [:]
var overlays: [String: [String: ParseValue]] = [:] var overlays: [String: [String: ParseValue]] = [:]
...@@ -146,7 +162,7 @@ final public class Package { ...@@ -146,7 +162,7 @@ final public class Package {
guard let nextTask = tasks[depName] else { fatalError("Can't find so-called task \(depName)")} guard let nextTask = tasks[depName] else { fatalError("Can't find so-called task \(depName)")}
let nextGraph = prunedDependencyGraph(nextTask) let nextGraph = prunedDependencyGraph(nextTask)
for nextItem in nextGraph { for nextItem in nextGraph {
let filteredTasks = pruned.filter() {$0.key == nextItem.key} let filteredTasks = pruned.filter() {$0.qualifiedName == nextItem.qualifiedName}
if filteredTasks.count >= 1 { continue } if filteredTasks.count >= 1 { continue }
pruned.append(nextItem) pruned.append(nextItem)
} }
...@@ -188,9 +204,10 @@ final public class Package { ...@@ -188,9 +204,10 @@ final public class Package {
if let value = type.properties["version"]?.string { self.version = value } if let value = type.properties["version"]?.string { self.version = value }
if let parsedTasks = type.properties["tasks"]?.map { if let parsedTasks = type.properties["tasks"]?.map {
for (key, value) in parsedTasks { for (name, value) in parsedTasks {
if let task = Task(value: value, name: key, importedPath: pathOnDisk) { if let task = Task(value: value, unqualifiedName: name, package: self, importedPath: pathOnDisk) {
self.tasks[key] = task self.tasks[task.unqualifiedName] = task
self.tasks[task.qualifiedName] = task
} }
} }
} }
...@@ -255,7 +272,7 @@ final public class Package { ...@@ -255,7 +272,7 @@ final public class Package {
if task.appliedOverlays.contains(overlayName) { continue } if task.appliedOverlays.contains(overlayName) { continue }
guard let overlay = declaredOverlays[overlayName] else { guard let overlay = declaredOverlays[overlayName] else {
print("Warning: Can't apply overlay \(overlayName) to task \(task.key)") print("Warning: Can't apply overlay \(overlayName) to task \(task.qualifiedName)")
continue continue
} }
again = again || task.applyOverlay(overlayName, overlay: overlay) again = again || task.applyOverlay(overlayName, overlay: overlay)
...@@ -276,8 +293,7 @@ final public class Package { ...@@ -276,8 +293,7 @@ final public class Package {
for remotePackage in remotePackages { for remotePackage in remotePackages {
for (_, task) in remotePackage.tasks { for (_, task) in remotePackage.tasks {
task.importedPath = remotePackage.adjustedImportPath task.importedPath = remotePackage.adjustedImportPath
task.key = "\(remotePackage.name).\(task.key)" self.tasks[task.qualifiedName] = task
self.tasks[task.key] = task
} }
} }
......
(package
:name "a"
:import ["b.atpkg"]
:tasks {
:default {
:dependencies ["b.default"]
:name "a_default"
}
}
)
\ No newline at end of file
(package
:name "b"
:import ["c.atpkg"]
:tasks {
:default {
:dependencies ["c.default"]
:name "b_default"
}
}
)
\ No newline at end of file
(package
:name "c"
:tasks {
:default {
:name "c_default"
}
}
)
\ No newline at end of file
...@@ -22,7 +22,8 @@ class PackageTests: Test { ...@@ -22,7 +22,8 @@ class PackageTests: Test {
PackageTests.testBasic, PackageTests.testBasic,
PackageTests.testImport, PackageTests.testImport,
PackageTests.testOverlays, PackageTests.testOverlays,
PackageTests.testExportedOverlays PackageTests.testExportedOverlays,
PackageTests.testChainedImports
] ]
let filename = __FILE__ let filename = __FILE__
...@@ -40,9 +41,9 @@ class PackageTests: Test { ...@@ -40,9 +41,9 @@ class PackageTests: Test {
try test.assert(package.name == "basic") try test.assert(package.name == "basic")
try test.assert(package.version == "0.1.0-dev") try test.assert(package.version == "0.1.0-dev")
try test.assert(package.tasks.count == 1) try test.assert(package.tasks.count == 2) //indexed twice, by qualified and unqualified name
for (key, task) in package.tasks { for (key, task) in package.tasks {
try test.assert(key == "build") try test.assert(key == "build" || key == "basic.build")
try test.assert(task.tool == "lldb-build") try test.assert(task.tool == "lldb-build")
try test.assert(task["name"]?.string == "json-swift") try test.assert(task["name"]?.string == "json-swift")
try test.assert(task["output-type"]?.string == "lib") try test.assert(task["output-type"]?.string == "lib")
...@@ -137,4 +138,28 @@ class PackageTests: Test { ...@@ -137,4 +138,28 @@ class PackageTests: Test {
try test.assert(compileOptions2[5].string == "MOST_AWESOME") try test.assert(compileOptions2[5].string == "MOST_AWESOME")
} }
static func testChainedImports () throws {
let filepath = "./tests/collateral/chained_imports/a.atpkg"
guard let package = Package(filepath: filepath, overlay: []) else { print("error"); try test.assert(false); return }
guard let a_default_unqualified = package.tasks["default"] else {
fatalError("No default task")
}
try test.assert(a_default_unqualified["name"]?.string == "a_default")
guard let a_default_qualified = package.tasks["a.default"] else {
fatalError("No default task (qualified)")
}
try test.assert(a_default_qualified["name"]?.string == "a_default")
guard let b_default_qualified = package.tasks["b.default"] else {
fatalError("No default task in b")
}
try test.assert(b_default_qualified["name"]?.string == "b_default")
guard let c_default_qualified = package.tasks["c.default"] else {
fatalError("No default task in c")
}
try test.assert(c_default_qualified["name"]?.string == "c_default")
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment