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 @@
import Foundation
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 tool: String = "atllbuild"
public var importedPath: String ///the directory at which the task was imported. This includes a trailing /.
......@@ -28,11 +38,13 @@ final public class Task {
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 }
self.importedPath = importedPath.pathWithTrailingSlash
self.kvp = kvp
self.key = name
self.unqualifiedName = unqualifiedName
self.package = package
self.allKeys = [String](kvp.keys)
self.tool = kvp["tool"]?.string ?? self.tool
if let ol = kvp["overlay"] {
......@@ -102,13 +114,13 @@ final public class Task {
case ParseValue.StringLiteral(let str):
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)
case ParseValue.BoolLiteral(let b):
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)
......@@ -131,6 +143,10 @@ final public class Package {
// The optional properties. All optional properties must have a default value.
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] = [:]
var overlays: [String: [String: ParseValue]] = [:]
......@@ -146,7 +162,7 @@ final public class Package {
guard let nextTask = tasks[depName] else { fatalError("Can't find so-called task \(depName)")}
let nextGraph = prunedDependencyGraph(nextTask)
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 }
pruned.append(nextItem)
}
......@@ -188,9 +204,10 @@ final public class Package {
if let value = type.properties["version"]?.string { self.version = value }
if let parsedTasks = type.properties["tasks"]?.map {
for (key, value) in parsedTasks {
if let task = Task(value: value, name: key, importedPath: pathOnDisk) {
self.tasks[key] = task
for (name, value) in parsedTasks {
if let task = Task(value: value, unqualifiedName: name, package: self, importedPath: pathOnDisk) {
self.tasks[task.unqualifiedName] = task
self.tasks[task.qualifiedName] = task
}
}
}
......@@ -255,7 +272,7 @@ final public class Package {
if task.appliedOverlays.contains(overlayName) { continue }
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
}
again = again || task.applyOverlay(overlayName, overlay: overlay)
......@@ -276,8 +293,7 @@ final public class Package {
for remotePackage in remotePackages {
for (_, task) in remotePackage.tasks {
task.importedPath = remotePackage.adjustedImportPath
task.key = "\(remotePackage.name).\(task.key)"
self.tasks[task.key] = task
self.tasks[task.qualifiedName] = 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 {
PackageTests.testBasic,
PackageTests.testImport,
PackageTests.testOverlays,
PackageTests.testExportedOverlays
PackageTests.testExportedOverlays,
PackageTests.testChainedImports
]
let filename = __FILE__
......@@ -40,9 +41,9 @@ class PackageTests: Test {
try test.assert(package.name == "basic")
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 {
try test.assert(key == "build")
try test.assert(key == "build" || key == "basic.build")
try test.assert(task.tool == "lldb-build")
try test.assert(task["name"]?.string == "json-swift")
try test.assert(task["output-type"]?.string == "lib")
......@@ -137,4 +138,28 @@ class PackageTests: Test {
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