mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-18 21:47:08 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9001304c8 | ||
|
|
4e20ea8f72 | ||
|
|
678ce0a55a | ||
|
|
8273ae66e1 | ||
|
|
4a9316a377 | ||
|
|
6321d547cf | ||
|
|
f241e21614 | ||
|
|
0eca923604 | ||
|
|
87f29cc11f | ||
|
|
90d1393137 | ||
|
|
625d431d10 |
+1
-1
@@ -6,6 +6,6 @@ export VERSION="${CIRRUS_TAG:-0}"
|
|||||||
|
|
||||||
mkdir -p .ci/pkg/
|
mkdir -p .ci/pkg/
|
||||||
cp .build/arm64-apple-macosx/debug/tart .ci/pkg/
|
cp .build/arm64-apple-macosx/debug/tart .ci/pkg/
|
||||||
pkgbuild --root .ci/pkg --version $VERSION --install-location /usr/local/bin/ --identifier com.github.cirruslabs.tart --sign "Developer ID Installer: Fedor Korotkov (9M2P8L4D89)" "./dist/Tart-$VERSION.pkg"
|
pkgbuild --root .ci/pkg --version $VERSION --install-location /usr/local/bin/ --identifier com.github.cirruslabs.tart --sign "Developer ID Installer: Cirrus Labs, Inc. (9M2P8L4D89)" "./dist/Tart-$VERSION.pkg"
|
||||||
xcrun notarytool submit "./dist/Tart-$VERSION.pkg" --keychain-profile "notarytool" --wait
|
xcrun notarytool submit "./dist/Tart-$VERSION.pkg" --keychain-profile "notarytool" --wait
|
||||||
xcrun stapler staple "./dist/Tart-$VERSION.pkg"
|
xcrun stapler staple "./dist/Tart-$VERSION.pkg"
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ task:
|
|||||||
macos_instance:
|
macos_instance:
|
||||||
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
||||||
env:
|
env:
|
||||||
MACOS_CERTIFICATE: ENCRYPTED[8a6930a8c1286e7e536ea41b7647ea40e99174ad15e9cfcc753754fea55a619b355415629dff515b54a8921643e314e5]
|
MACOS_CERTIFICATE: ENCRYPTED[552b9d275d1c2bdbc1bff778b104a8f9a53cbd0d59344d4b7f6d0ca3c811a5cefb97bef9ba0ef31c219cb07bdacdd2c2]
|
||||||
AC_PASSWORD: ENCRYPTED[4a761023e7e06fe2eb350c8b6e8e7ca961af193cb9ba47605f25f1d353abc3142606f412e405be48fd897a78787ea8c2]
|
AC_PASSWORD: ENCRYPTED[4a761023e7e06fe2eb350c8b6e8e7ca961af193cb9ba47605f25f1d353abc3142606f412e405be48fd897a78787ea8c2]
|
||||||
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
|
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
|
||||||
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
|
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
github: [cirruslabs]
|
||||||
+27
-9
@@ -1,14 +1,5 @@
|
|||||||
{
|
{
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
|
||||||
"identity" : "antlr4",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/antlr/antlr4",
|
|
||||||
"state" : {
|
|
||||||
"branch" : "4.10.1",
|
|
||||||
"revision" : "44d87bc1d130c88aa452894aa5f7e2f710f68253"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "async-http-client",
|
"identity" : "async-http-client",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -54,6 +45,15 @@
|
|||||||
"version" : "1.0.2"
|
"version" : "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-case-paths",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/pointfreeco/swift-case-paths",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "ce9c0d897db8a840c39de64caaa9b60119cf4be8",
|
||||||
|
"version" : "0.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-log",
|
"identity" : "swift-log",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -117,6 +117,15 @@
|
|||||||
"version" : "1.0.2"
|
"version" : "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-parsing",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/pointfreeco/swift-parsing",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "28d32e9ace1c4c43f5e5a177be837a202494c2d5",
|
||||||
|
"version" : "0.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swiftdate",
|
"identity" : "swiftdate",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -125,6 +134,15 @@
|
|||||||
"revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
|
"revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
|
||||||
"version" : "6.3.1"
|
"version" : "6.3.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "xctest-dynamic-overlay",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "50a70a9d3583fe228ce672e8923010c8df2deddd",
|
||||||
|
"version" : "0.2.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 2
|
"version" : 2
|
||||||
|
|||||||
+4
-3
@@ -12,10 +12,10 @@ let package = Package(
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2"),
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2"),
|
||||||
.package(url: "https://github.com/mhdhejazi/Dynamic", branch: "master"),
|
.package(url: "https://github.com/mhdhejazi/Dynamic", branch: "master"),
|
||||||
|
.package(url: "https://github.com/pointfreeco/swift-parsing", from: "0.9.2"),
|
||||||
.package(url: "https://github.com/swift-server/async-http-client", from: "1.11.4"),
|
.package(url: "https://github.com/swift-server/async-http-client", from: "1.11.4"),
|
||||||
.package(url: "https://github.com/apple/swift-algorithms", from: "1.0.0"),
|
.package(url: "https://github.com/apple/swift-algorithms", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/malcommac/SwiftDate", from: "6.3.1"),
|
.package(url: "https://github.com/malcommac/SwiftDate", from: "6.3.1")
|
||||||
.package(url: "https://github.com/antlr/antlr4", revision: "4.10.1")
|
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(name: "tart", dependencies: [
|
.executableTarget(name: "tart", dependencies: [
|
||||||
@@ -23,9 +23,10 @@ let package = Package(
|
|||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
.product(name: "AsyncHTTPClient", package: "async-http-client"),
|
.product(name: "AsyncHTTPClient", package: "async-http-client"),
|
||||||
.product(name: "Dynamic", package: "Dynamic"),
|
.product(name: "Dynamic", package: "Dynamic"),
|
||||||
|
.product(name: "Parsing", package: "swift-parsing"),
|
||||||
.product(name: "SwiftDate", package: "SwiftDate"),
|
.product(name: "SwiftDate", package: "SwiftDate"),
|
||||||
.product(name: "Antlr4", package: "Antlr4"),
|
|
||||||
]),
|
]),
|
||||||
.testTarget(name: "TartTests", dependencies: ["tart"])
|
.testTarget(name: "TartTests", dependencies: ["tart"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -238,8 +238,8 @@ This invocation calls the `tart pull` implicitly (if the image is not being pres
|
|||||||
<details>
|
<details>
|
||||||
<summary>Why Tart is free and open sourced?</summary>
|
<summary>Why Tart is free and open sourced?</summary>
|
||||||
|
|
||||||
Tart is a relatively small project, and it didn't feel right to try to monetize it.
|
Apple did all the heavy lifting with their `Virtualization.Framework` and it just felt right to develop Tart in the open.
|
||||||
Apple did all the heavy lifting with their `Virtualization.Framework`.
|
Please consider [becoming a sponsor](https://github.com/sponsors/cirruslabs) if you find Tart saving a substantial amount of money on licensing and engineering hours for your company.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -268,3 +268,21 @@ This invocation calls the `tart pull` implicitly (if the image is not being pres
|
|||||||
Tart is limited by functionality of Apple's `Virtualization.Framework`. At the moment `Virtualization.Framework`
|
Tart is limited by functionality of Apple's `Virtualization.Framework`. At the moment `Virtualization.Framework`
|
||||||
doesn't support nested virtualization.
|
doesn't support nested virtualization.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Changing the default NAT subnet</summary>
|
||||||
|
|
||||||
|
To change the default network to `192.168.77.1`:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist Shared_Net_Address -string 192.168.77.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that even through a network would normally be specified as `192.168.77.0`, the [vmnet framework](https://developer.apple.com/documentation/vmnet) seems to treat this as a starting address too and refuses to pick up such network-like values.
|
||||||
|
|
||||||
|
The default subnet mask `255.255.255.0` should suffice for most use-cases, however, you can also change it to `255.255.0.0`, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist Shared_Net_Mask -string 255.255.0.0
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -4,5 +4,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.virtualization</key>
|
<key>com.apple.security.virtualization</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.vm.networking</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ struct Clone: AsyncParsableCommand {
|
|||||||
let sourceVM = try VMStorageHelper.open(sourceName)
|
let sourceVM = try VMStorageHelper.open(sourceName)
|
||||||
|
|
||||||
let tmpVMDir = try VMDirectory.temporary()
|
let tmpVMDir = try VMDirectory.temporary()
|
||||||
|
|
||||||
|
// Lock the temporary VM directory to prevent it's garbage collection
|
||||||
|
let tmpVMDirLock = try FileLock(lockURL: tmpVMDir.baseURL)
|
||||||
|
try tmpVMDirLock.lock()
|
||||||
|
|
||||||
try await withTaskCancellationHandler(operation: {
|
try await withTaskCancellationHandler(operation: {
|
||||||
let lock = try FileLock(lockURL: Config().tartHomeDir)
|
let lock = try FileLock(lockURL: Config().tartHomeDir)
|
||||||
try lock.lock()
|
try lock.lock()
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ struct Create: AsyncParsableCommand {
|
|||||||
func run() async throws {
|
func run() async throws {
|
||||||
do {
|
do {
|
||||||
let tmpVMDir = try VMDirectory.temporary()
|
let tmpVMDir = try VMDirectory.temporary()
|
||||||
|
|
||||||
|
// Lock the temporary VM directory to prevent it's garbage collection
|
||||||
|
let tmpVMDirLock = try FileLock(lockURL: tmpVMDir.baseURL)
|
||||||
|
try tmpVMDirLock.lock()
|
||||||
|
|
||||||
try await withTaskCancellationHandler(operation: {
|
try await withTaskCancellationHandler(operation: {
|
||||||
if let fromIPSW = fromIPSW {
|
if let fromIPSW = fromIPSW {
|
||||||
if fromIPSW == "latest" {
|
if fromIPSW == "latest" {
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Rename: AsyncParsableCommand {
|
||||||
|
static var configuration = CommandConfiguration(abstract: "Rename a VM")
|
||||||
|
|
||||||
|
@Argument(help: "VM name")
|
||||||
|
var name: String
|
||||||
|
|
||||||
|
@Argument(help: "new VM name")
|
||||||
|
var newName: String
|
||||||
|
|
||||||
|
func validate() throws {
|
||||||
|
if newName.contains("/") {
|
||||||
|
throw ValidationError("<new-name> should be a local name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() async throws {
|
||||||
|
do {
|
||||||
|
let localStorage = VMStorageLocal()
|
||||||
|
|
||||||
|
if !localStorage.exists(name) {
|
||||||
|
throw ValidationError("failed to rename a non-existent VM: \(name)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if localStorage.exists(newName) {
|
||||||
|
throw ValidationError("failed to rename VM \(name), target VM \(name) already exists, delete it first!")
|
||||||
|
}
|
||||||
|
|
||||||
|
try localStorage.rename(name, newName)
|
||||||
|
|
||||||
|
Foundation.exit(0)
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
Foundation.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,15 +47,28 @@ struct Run: AsyncParsableCommand {
|
|||||||
@Option(help: ArgumentHelp("""
|
@Option(help: ArgumentHelp("""
|
||||||
Additional directory shares with an optional read-only specifier\n(e.g. --dir=\"build:~/src/build\" --dir=\"sources:~/src/sources:ro\")
|
Additional directory shares with an optional read-only specifier\n(e.g. --dir=\"build:~/src/build\" --dir=\"sources:~/src/sources:ro\")
|
||||||
""", discussion: """
|
""", discussion: """
|
||||||
|
Requires host to be macOS 13.0 (Ventura) or newer.
|
||||||
All shared directories are automatically mounted to "/Volumes/My Shared Files" directory on macOS,
|
All shared directories are automatically mounted to "/Volumes/My Shared Files" directory on macOS,
|
||||||
while on Linux you have to do it manually: "mount -t virtiofs com.apple.virtio-fs.automount /mount/point".
|
while on Linux you have to do it manually: "mount -t virtiofs com.apple.virtio-fs.automount /mount/point".
|
||||||
|
For macOS guests, they must be running macOS 13.0 (Ventura) or newer.
|
||||||
""", valueName: "name:path[:ro]"))
|
""", valueName: "name:path[:ro]"))
|
||||||
var dir: [String] = []
|
var dir: [String] = []
|
||||||
|
|
||||||
|
@Option(help: ArgumentHelp("""
|
||||||
|
Use bridged networking instead of the default shared (NAT) networking \n(e.g. --net-bridged=en0 or --net-bridged=\"Wi-Fi\")
|
||||||
|
""", discussion: """
|
||||||
|
Specify "list" as an interface name (--net-bridged=list) to list the available bridged interfaces.
|
||||||
|
""", valueName: "interface name"))
|
||||||
|
var netBridged: String?
|
||||||
|
|
||||||
func validate() throws {
|
func validate() throws {
|
||||||
if vnc && vncExperimental {
|
if vnc && vncExperimental {
|
||||||
throw ValidationError("--vnc and --vnc-experimental are mutually exclusive")
|
throw ValidationError("--vnc and --vnc-experimental are mutually exclusive")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if withSoftnet && netBridged != nil {
|
||||||
|
throw ValidationError("--with-softnet and --net-bridged are mutually exclusive")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -63,7 +76,7 @@ struct Run: AsyncParsableCommand {
|
|||||||
let vmDir = try VMStorageLocal().open(name)
|
let vmDir = try VMStorageLocal().open(name)
|
||||||
vm = try VM(
|
vm = try VM(
|
||||||
vmDir: vmDir,
|
vmDir: vmDir,
|
||||||
withSoftnet: withSoftnet,
|
network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(),
|
||||||
additionalDiskAttachments: additionalDiskAttachments(),
|
additionalDiskAttachments: additionalDiskAttachments(),
|
||||||
directoryShares: directoryShares()
|
directoryShares: directoryShares()
|
||||||
)
|
)
|
||||||
@@ -123,11 +136,53 @@ struct Run: AsyncParsableCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func userSpecifiedNetwork(vmDir: VMDirectory) throws -> Network? {
|
||||||
|
if withSoftnet {
|
||||||
|
let config = try VMConfig.init(fromURL: vmDir.configURL)
|
||||||
|
|
||||||
|
return try Softnet(vmMACAddress: config.macAddress.string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let netBridged = netBridged {
|
||||||
|
let matchingInterfaces = VZBridgedNetworkInterface.networkInterfaces.filter { interface in
|
||||||
|
interface.identifier == netBridged || interface.localizedDisplayName == netBridged
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchingInterfaces.isEmpty {
|
||||||
|
let available = bridgeInterfaces().joined(separator: ", ")
|
||||||
|
throw ValidationError("no bridge interfaces matched \"\(netBridged)\", "
|
||||||
|
+ "available interfaces: \(available)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchingInterfaces.count > 1 {
|
||||||
|
throw ValidationError("more than one bridge interface matched \"\(netBridged)\", "
|
||||||
|
+ "consider refining the search criteria")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkBridged(interface: matchingInterfaces.first!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bridgeInterfaces() -> [String] {
|
||||||
|
VZBridgedNetworkInterface.networkInterfaces.map { interface in
|
||||||
|
var bridgeDescription = interface.identifier
|
||||||
|
|
||||||
|
if let localizedDisplayName = interface.localizedDisplayName {
|
||||||
|
bridgeDescription += " (or \"\(localizedDisplayName)\")"
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridgeDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func additionalDiskAttachments() throws -> [VZDiskImageStorageDeviceAttachment] {
|
func additionalDiskAttachments() throws -> [VZDiskImageStorageDeviceAttachment] {
|
||||||
var result: [VZDiskImageStorageDeviceAttachment] = []
|
var result: [VZDiskImageStorageDeviceAttachment] = []
|
||||||
let readOnlySuffix = ":ro"
|
let readOnlySuffix = ":ro"
|
||||||
|
let expandedDiskPaths = disk.map { NSString(string:$0).expandingTildeInPath }
|
||||||
|
|
||||||
for rawDisk in disk {
|
for rawDisk in expandedDiskPaths {
|
||||||
if rawDisk.hasSuffix(readOnlySuffix) {
|
if rawDisk.hasSuffix(readOnlySuffix) {
|
||||||
result.append(try VZDiskImageStorageDeviceAttachment(
|
result.append(try VZDiskImageStorageDeviceAttachment(
|
||||||
url: URL(fileURLWithPath: String(rawDisk.prefix(rawDisk.count - readOnlySuffix.count))),
|
url: URL(fileURLWithPath: String(rawDisk.prefix(rawDisk.count - readOnlySuffix.count))),
|
||||||
@@ -166,7 +221,7 @@ struct Run: AsyncParsableCommand {
|
|||||||
|
|
||||||
let (name, path) = (String(splits[0]), String(splits[1]))
|
let (name, path) = (String(splits[0]), String(splits[1]))
|
||||||
|
|
||||||
result.append(DirectoryShare(name: name, path: URL(fileURLWithPath: path), readOnly: readOnly))
|
result.append(DirectoryShare(name: name, path: URL(fileURLWithPath: NSString(string: path).expandingTildeInPath), readOnly: readOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Foundation
|
|||||||
struct Config {
|
struct Config {
|
||||||
let tartHomeDir: URL
|
let tartHomeDir: URL
|
||||||
let tartCacheDir: URL
|
let tartCacheDir: URL
|
||||||
|
let tartTmpDir: URL
|
||||||
|
|
||||||
init() throws {
|
init() throws {
|
||||||
var tartHomeDir: URL
|
var tartHomeDir: URL
|
||||||
@@ -14,11 +15,27 @@ struct Config {
|
|||||||
.homeDirectoryForCurrentUser
|
.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent(".tart", isDirectory: true)
|
.appendingPathComponent(".tart", isDirectory: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tartHomeDir = tartHomeDir
|
self.tartHomeDir = tartHomeDir
|
||||||
tartCacheDir = tartHomeDir.appendingPathComponent("cache", isDirectory: true)
|
|
||||||
|
|
||||||
|
tartCacheDir = tartHomeDir.appendingPathComponent("cache", isDirectory: true)
|
||||||
try FileManager.default.createDirectory(at: tartCacheDir, withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: tartCacheDir, withIntermediateDirectories: true)
|
||||||
|
|
||||||
|
tartTmpDir = tartHomeDir.appendingPathComponent("tmp", isDirectory: true)
|
||||||
|
try FileManager.default.createDirectory(at: tartTmpDir, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gc() throws {
|
||||||
|
for entry in try FileManager.default.contentsOfDirectory(at: tartTmpDir,
|
||||||
|
includingPropertiesForKeys: [], options: []) {
|
||||||
|
let lock = try FileLock(lockURL: entry)
|
||||||
|
if try !lock.trylock() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
try FileManager.default.removeItem(at: entry)
|
||||||
|
|
||||||
|
try lock.unlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func jsonEncoder() -> JSONEncoder {
|
static func jsonEncoder() -> JSONEncoder {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Virtualization
|
||||||
|
|
||||||
|
protocol Network {
|
||||||
|
func attachment() -> VZNetworkDeviceAttachment
|
||||||
|
func run() throws
|
||||||
|
func stop() throws
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import Foundation
|
||||||
|
import Virtualization
|
||||||
|
|
||||||
|
class NetworkBridged: Network {
|
||||||
|
let interface: VZBridgedNetworkInterface
|
||||||
|
|
||||||
|
init(interface: VZBridgedNetworkInterface) {
|
||||||
|
self.interface = interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func attachment() -> VZNetworkDeviceAttachment {
|
||||||
|
VZBridgedNetworkDeviceAttachment(interface: interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
// no-op, only used for Softnet
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() throws {
|
||||||
|
// no-op, only used for Softnet
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Foundation
|
||||||
|
import Virtualization
|
||||||
|
|
||||||
|
class NetworkShared: Network {
|
||||||
|
func attachment() -> VZNetworkDeviceAttachment {
|
||||||
|
VZNATNetworkDeviceAttachment()
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
// no-op, only used for Softnet
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() throws {
|
||||||
|
// no-op, only used for Softnet
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import Virtualization
|
||||||
|
|
||||||
enum SoftnetError: Error {
|
enum SoftnetError: Error {
|
||||||
case InitializationFailed(why: String)
|
case InitializationFailed(why: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Softnet {
|
class Softnet: Network {
|
||||||
private let process = Process()
|
private let process = Process()
|
||||||
|
|
||||||
let vmFD: Int32
|
let vmFD: Int32
|
||||||
@@ -57,4 +58,9 @@ class Softnet {
|
|||||||
throw SoftnetError.InitializationFailed(why: "setsockopt(SO_SNDBUF) returned \(ret)")
|
throw SoftnetError.InitializationFailed(why: "setsockopt(SO_SNDBUF) returned \(ret)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func attachment() -> VZNetworkDeviceAttachment {
|
||||||
|
let fh = FileHandle.init(fileDescriptor: vmFD)
|
||||||
|
return VZFileHandleNetworkDeviceAttachment(fileHandle: fh)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
token literal names:
|
|
||||||
null
|
|
||||||
':'
|
|
||||||
'/'
|
|
||||||
'.'
|
|
||||||
'@'
|
|
||||||
'-'
|
|
||||||
'_'
|
|
||||||
null
|
|
||||||
null
|
|
||||||
|
|
||||||
token symbolic names:
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
DIGIT
|
|
||||||
LETTER
|
|
||||||
|
|
||||||
rule names:
|
|
||||||
root
|
|
||||||
host
|
|
||||||
port
|
|
||||||
namespace
|
|
||||||
namespace_component
|
|
||||||
reference
|
|
||||||
tag
|
|
||||||
separator
|
|
||||||
name
|
|
||||||
|
|
||||||
|
|
||||||
atn:
|
|
||||||
[4, 1, 8, 85, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 1, 0, 1, 0, 3, 0, 22, 8, 0, 1, 0, 1, 0, 1, 0, 3, 0, 27, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 5, 1, 34, 8, 1, 10, 1, 12, 1, 37, 9, 1, 1, 2, 4, 2, 40, 8, 2, 11, 2, 12, 2, 41, 1, 3, 1, 3, 1, 3, 5, 3, 47, 8, 3, 10, 3, 12, 3, 50, 9, 3, 1, 4, 1, 4, 3, 4, 54, 8, 4, 4, 4, 56, 8, 4, 11, 4, 12, 4, 57, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 67, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 73, 8, 6, 10, 6, 12, 6, 76, 9, 6, 1, 7, 1, 7, 1, 8, 4, 8, 81, 8, 8, 11, 8, 12, 8, 82, 1, 8, 0, 0, 9, 0, 2, 4, 6, 8, 10, 12, 14, 16, 0, 2, 2, 0, 3, 3, 5, 6, 1, 0, 7, 8, 85, 0, 18, 1, 0, 0, 0, 2, 30, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 43, 1, 0, 0, 0, 8, 55, 1, 0, 0, 0, 10, 66, 1, 0, 0, 0, 12, 68, 1, 0, 0, 0, 14, 77, 1, 0, 0, 0, 16, 80, 1, 0, 0, 0, 18, 21, 3, 2, 1, 0, 19, 20, 5, 1, 0, 0, 20, 22, 3, 4, 2, 0, 21, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, 5, 2, 0, 0, 24, 26, 3, 6, 3, 0, 25, 27, 3, 10, 5, 0, 26, 25, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 29, 5, 0, 0, 1, 29, 1, 1, 0, 0, 0, 30, 35, 3, 16, 8, 0, 31, 32, 5, 3, 0, 0, 32, 34, 3, 16, 8, 0, 33, 31, 1, 0, 0, 0, 34, 37, 1, 0, 0, 0, 35, 33, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 3, 1, 0, 0, 0, 37, 35, 1, 0, 0, 0, 38, 40, 5, 7, 0, 0, 39, 38, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 39, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 5, 1, 0, 0, 0, 43, 48, 3, 8, 4, 0, 44, 45, 5, 2, 0, 0, 45, 47, 3, 8, 4, 0, 46, 44, 1, 0, 0, 0, 47, 50, 1, 0, 0, 0, 48, 46, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 7, 1, 0, 0, 0, 50, 48, 1, 0, 0, 0, 51, 53, 3, 16, 8, 0, 52, 54, 3, 14, 7, 0, 53, 52, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 56, 1, 0, 0, 0, 55, 51, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 55, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 9, 1, 0, 0, 0, 59, 60, 5, 1, 0, 0, 60, 67, 3, 12, 6, 0, 61, 62, 5, 4, 0, 0, 62, 63, 3, 16, 8, 0, 63, 64, 5, 1, 0, 0, 64, 65, 3, 16, 8, 0, 65, 67, 1, 0, 0, 0, 66, 59, 1, 0, 0, 0, 66, 61, 1, 0, 0, 0, 67, 11, 1, 0, 0, 0, 68, 74, 3, 16, 8, 0, 69, 70, 3, 14, 7, 0, 70, 71, 3, 16, 8, 0, 71, 73, 1, 0, 0, 0, 72, 69, 1, 0, 0, 0, 73, 76, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 13, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 77, 78, 7, 0, 0, 0, 78, 15, 1, 0, 0, 0, 79, 81, 7, 1, 0, 0, 80, 79, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 17, 1, 0, 0, 0, 10, 21, 26, 35, 41, 48, 53, 57, 66, 74, 82]
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
T__0=1
|
|
||||||
T__1=2
|
|
||||||
T__2=3
|
|
||||||
T__3=4
|
|
||||||
T__4=5
|
|
||||||
T__5=6
|
|
||||||
DIGIT=7
|
|
||||||
LETTER=8
|
|
||||||
':'=1
|
|
||||||
'/'=2
|
|
||||||
'.'=3
|
|
||||||
'@'=4
|
|
||||||
'-'=5
|
|
||||||
'_'=6
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
// Generated from Reference.g4 by ANTLR 4.10.1
|
|
||||||
|
|
||||||
import Antlr4
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides an empty implementation of {@link ReferenceListener},
|
|
||||||
* which can be extended to create a listener which only needs to handle a subset
|
|
||||||
* of the available methods.
|
|
||||||
*/
|
|
||||||
open class ReferenceBaseListener: ReferenceListener {
|
|
||||||
public init() { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterRoot(_ ctx: ReferenceParser.RootContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitRoot(_ ctx: ReferenceParser.RootContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterHost(_ ctx: ReferenceParser.HostContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitHost(_ ctx: ReferenceParser.HostContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterPort(_ ctx: ReferenceParser.PortContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitPort(_ ctx: ReferenceParser.PortContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterNamespace(_ ctx: ReferenceParser.NamespaceContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitNamespace(_ ctx: ReferenceParser.NamespaceContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterNamespace_component(_ ctx: ReferenceParser.Namespace_componentContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitNamespace_component(_ ctx: ReferenceParser.Namespace_componentContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterReference(_ ctx: ReferenceParser.ReferenceContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitReference(_ ctx: ReferenceParser.ReferenceContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterTag(_ ctx: ReferenceParser.TagContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitTag(_ ctx: ReferenceParser.TagContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterSeparator(_ ctx: ReferenceParser.SeparatorContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitSeparator(_ ctx: ReferenceParser.SeparatorContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterName(_ ctx: ReferenceParser.NameContext) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitName(_ ctx: ReferenceParser.NameContext) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func enterEveryRule(_ ctx: ParserRuleContext) throws { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func exitEveryRule(_ ctx: ParserRuleContext) throws { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func visitTerminal(_ node: TerminalNode) { }
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* <p>The default implementation does nothing.</p>
|
|
||||||
*/
|
|
||||||
open func visitErrorNode(_ node: ErrorNode) { }
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
token literal names:
|
|
||||||
null
|
|
||||||
':'
|
|
||||||
'/'
|
|
||||||
'.'
|
|
||||||
'@'
|
|
||||||
'-'
|
|
||||||
'_'
|
|
||||||
null
|
|
||||||
null
|
|
||||||
|
|
||||||
token symbolic names:
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
null
|
|
||||||
DIGIT
|
|
||||||
LETTER
|
|
||||||
|
|
||||||
rule names:
|
|
||||||
T__0
|
|
||||||
T__1
|
|
||||||
T__2
|
|
||||||
T__3
|
|
||||||
T__4
|
|
||||||
T__5
|
|
||||||
DIGIT
|
|
||||||
LETTER
|
|
||||||
|
|
||||||
channel names:
|
|
||||||
DEFAULT_TOKEN_CHANNEL
|
|
||||||
HIDDEN
|
|
||||||
|
|
||||||
mode names:
|
|
||||||
DEFAULT_MODE
|
|
||||||
|
|
||||||
atn:
|
|
||||||
[4, 0, 8, 33, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 0, 0, 8, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 1, 0, 2, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 32, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 1, 17, 1, 0, 0, 0, 3, 19, 1, 0, 0, 0, 5, 21, 1, 0, 0, 0, 7, 23, 1, 0, 0, 0, 9, 25, 1, 0, 0, 0, 11, 27, 1, 0, 0, 0, 13, 29, 1, 0, 0, 0, 15, 31, 1, 0, 0, 0, 17, 18, 5, 58, 0, 0, 18, 2, 1, 0, 0, 0, 19, 20, 5, 47, 0, 0, 20, 4, 1, 0, 0, 0, 21, 22, 5, 46, 0, 0, 22, 6, 1, 0, 0, 0, 23, 24, 5, 64, 0, 0, 24, 8, 1, 0, 0, 0, 25, 26, 5, 45, 0, 0, 26, 10, 1, 0, 0, 0, 27, 28, 5, 95, 0, 0, 28, 12, 1, 0, 0, 0, 29, 30, 7, 0, 0, 0, 30, 14, 1, 0, 0, 0, 31, 32, 7, 1, 0, 0, 32, 16, 1, 0, 0, 0, 1, 0, 0]
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
// Generated from Reference.g4 by ANTLR 4.10.1
|
|
||||||
import Antlr4
|
|
||||||
|
|
||||||
open class ReferenceLexer: Lexer {
|
|
||||||
|
|
||||||
internal static var _decisionToDFA: [DFA] = {
|
|
||||||
var decisionToDFA = [DFA]()
|
|
||||||
let length = ReferenceLexer._ATN.getNumberOfDecisions()
|
|
||||||
for i in 0..<length {
|
|
||||||
decisionToDFA.append(DFA(ReferenceLexer._ATN.getDecisionState(i)!, i))
|
|
||||||
}
|
|
||||||
return decisionToDFA
|
|
||||||
}()
|
|
||||||
|
|
||||||
internal static let _sharedContextCache = PredictionContextCache()
|
|
||||||
|
|
||||||
public
|
|
||||||
static let T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, DIGIT=7, LETTER=8
|
|
||||||
|
|
||||||
public
|
|
||||||
static let channelNames: [String] = [
|
|
||||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
|
||||||
]
|
|
||||||
|
|
||||||
public
|
|
||||||
static let modeNames: [String] = [
|
|
||||||
"DEFAULT_MODE"
|
|
||||||
]
|
|
||||||
|
|
||||||
public
|
|
||||||
static let ruleNames: [String] = [
|
|
||||||
"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "DIGIT", "LETTER"
|
|
||||||
]
|
|
||||||
|
|
||||||
private static let _LITERAL_NAMES: [String?] = [
|
|
||||||
nil, "':'", "'/'", "'.'", "'@'", "'-'", "'_'"
|
|
||||||
]
|
|
||||||
private static let _SYMBOLIC_NAMES: [String?] = [
|
|
||||||
nil, nil, nil, nil, nil, nil, nil, "DIGIT", "LETTER"
|
|
||||||
]
|
|
||||||
public
|
|
||||||
static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)
|
|
||||||
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getVocabulary() -> Vocabulary {
|
|
||||||
return ReferenceLexer.VOCABULARY
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
required init(_ input: CharStream) {
|
|
||||||
RuntimeMetaData.checkVersion("4.10.1", RuntimeMetaData.VERSION)
|
|
||||||
super.init(input)
|
|
||||||
_interp = LexerATNSimulator(self, ReferenceLexer._ATN, ReferenceLexer._decisionToDFA, ReferenceLexer._sharedContextCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getGrammarFileName() -> String { return "Reference.g4" }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getRuleNames() -> [String] { return ReferenceLexer.ruleNames }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getSerializedATN() -> [Int] { return ReferenceLexer._serializedATN }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getChannelNames() -> [String] { return ReferenceLexer.channelNames }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getModeNames() -> [String] { return ReferenceLexer.modeNames }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getATN() -> ATN { return ReferenceLexer._ATN }
|
|
||||||
|
|
||||||
static let _serializedATN:[Int] = [
|
|
||||||
4,0,8,33,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,
|
|
||||||
2,7,7,7,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,6,1,6,1,7,1,
|
|
||||||
7,0,0,8,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,1,0,2,1,0,48,57,2,0,65,90,97,
|
|
||||||
122,32,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,
|
|
||||||
1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,1,17,1,0,0,0,3,19,1,0,0,0,5,21,1,0,0,
|
|
||||||
0,7,23,1,0,0,0,9,25,1,0,0,0,11,27,1,0,0,0,13,29,1,0,0,0,15,31,1,0,0,0,
|
|
||||||
17,18,5,58,0,0,18,2,1,0,0,0,19,20,5,47,0,0,20,4,1,0,0,0,21,22,5,46,0,0,
|
|
||||||
22,6,1,0,0,0,23,24,5,64,0,0,24,8,1,0,0,0,25,26,5,45,0,0,26,10,1,0,0,0,
|
|
||||||
27,28,5,95,0,0,28,12,1,0,0,0,29,30,7,0,0,0,30,14,1,0,0,0,31,32,7,1,0,0,
|
|
||||||
32,16,1,0,0,0,1,0,0
|
|
||||||
]
|
|
||||||
|
|
||||||
public
|
|
||||||
static let _ATN: ATN = try! ATNDeserializer().deserialize(_serializedATN)
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
T__0=1
|
|
||||||
T__1=2
|
|
||||||
T__2=3
|
|
||||||
T__3=4
|
|
||||||
T__4=5
|
|
||||||
T__5=6
|
|
||||||
DIGIT=7
|
|
||||||
LETTER=8
|
|
||||||
':'=1
|
|
||||||
'/'=2
|
|
||||||
'.'=3
|
|
||||||
'@'=4
|
|
||||||
'-'=5
|
|
||||||
'_'=6
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
// Generated from Reference.g4 by ANTLR 4.10.1
|
|
||||||
import Antlr4
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface defines a complete listener for a parse tree produced by
|
|
||||||
* {@link ReferenceParser}.
|
|
||||||
*/
|
|
||||||
public protocol ReferenceListener: ParseTreeListener {
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#root}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterRoot(_ ctx: ReferenceParser.RootContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#root}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitRoot(_ ctx: ReferenceParser.RootContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#host}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterHost(_ ctx: ReferenceParser.HostContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#host}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitHost(_ ctx: ReferenceParser.HostContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#port}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterPort(_ ctx: ReferenceParser.PortContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#port}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitPort(_ ctx: ReferenceParser.PortContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#namespace}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterNamespace(_ ctx: ReferenceParser.NamespaceContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#namespace}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitNamespace(_ ctx: ReferenceParser.NamespaceContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#namespace_component}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterNamespace_component(_ ctx: ReferenceParser.Namespace_componentContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#namespace_component}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitNamespace_component(_ ctx: ReferenceParser.Namespace_componentContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#reference}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterReference(_ ctx: ReferenceParser.ReferenceContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#reference}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitReference(_ ctx: ReferenceParser.ReferenceContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#tag}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterTag(_ ctx: ReferenceParser.TagContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#tag}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitTag(_ ctx: ReferenceParser.TagContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#separator}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterSeparator(_ ctx: ReferenceParser.SeparatorContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#separator}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitSeparator(_ ctx: ReferenceParser.SeparatorContext)
|
|
||||||
/**
|
|
||||||
* Enter a parse tree produced by {@link ReferenceParser#name}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func enterName(_ ctx: ReferenceParser.NameContext)
|
|
||||||
/**
|
|
||||||
* Exit a parse tree produced by {@link ReferenceParser#name}.
|
|
||||||
- Parameters:
|
|
||||||
- ctx: the parse tree
|
|
||||||
*/
|
|
||||||
func exitName(_ ctx: ReferenceParser.NameContext)
|
|
||||||
}
|
|
||||||
@@ -1,781 +0,0 @@
|
|||||||
// Generated from Reference.g4 by ANTLR 4.10.1
|
|
||||||
import Antlr4
|
|
||||||
|
|
||||||
open class ReferenceParser: Parser {
|
|
||||||
|
|
||||||
internal static var _decisionToDFA: [DFA] = {
|
|
||||||
var decisionToDFA = [DFA]()
|
|
||||||
let length = ReferenceParser._ATN.getNumberOfDecisions()
|
|
||||||
for i in 0..<length {
|
|
||||||
decisionToDFA.append(DFA(ReferenceParser._ATN.getDecisionState(i)!, i))
|
|
||||||
}
|
|
||||||
return decisionToDFA
|
|
||||||
}()
|
|
||||||
|
|
||||||
internal static let _sharedContextCache = PredictionContextCache()
|
|
||||||
|
|
||||||
public
|
|
||||||
enum Tokens: Int {
|
|
||||||
case EOF = -1, T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, T__4 = 5, T__5 = 6,
|
|
||||||
DIGIT = 7, LETTER = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
static let RULE_root = 0, RULE_host = 1, RULE_port = 2, RULE_namespace = 3,
|
|
||||||
RULE_namespace_component = 4, RULE_reference = 5, RULE_tag = 6,
|
|
||||||
RULE_separator = 7, RULE_name = 8
|
|
||||||
|
|
||||||
public
|
|
||||||
static let ruleNames: [String] = [
|
|
||||||
"root", "host", "port", "namespace", "namespace_component", "reference",
|
|
||||||
"tag", "separator", "name"
|
|
||||||
]
|
|
||||||
|
|
||||||
private static let _LITERAL_NAMES: [String?] = [
|
|
||||||
nil, "':'", "'/'", "'.'", "'@'", "'-'", "'_'"
|
|
||||||
]
|
|
||||||
private static let _SYMBOLIC_NAMES: [String?] = [
|
|
||||||
nil, nil, nil, nil, nil, nil, nil, "DIGIT", "LETTER"
|
|
||||||
]
|
|
||||||
public
|
|
||||||
static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getGrammarFileName() -> String { return "Reference.g4" }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getRuleNames() -> [String] { return ReferenceParser.ruleNames }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getSerializedATN() -> [Int] { return ReferenceParser._serializedATN }
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getATN() -> ATN { return ReferenceParser._ATN }
|
|
||||||
|
|
||||||
|
|
||||||
override open
|
|
||||||
func getVocabulary() -> Vocabulary {
|
|
||||||
return ReferenceParser.VOCABULARY
|
|
||||||
}
|
|
||||||
|
|
||||||
override public
|
|
||||||
init(_ input:TokenStream) throws {
|
|
||||||
RuntimeMetaData.checkVersion("4.10.1", RuntimeMetaData.VERSION)
|
|
||||||
try super.init(input)
|
|
||||||
_interp = ParserATNSimulator(self,ReferenceParser._ATN,ReferenceParser._decisionToDFA, ReferenceParser._sharedContextCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class RootContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func host() -> HostContext? {
|
|
||||||
return getRuleContext(HostContext.self, 0)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func namespace() -> NamespaceContext? {
|
|
||||||
return getRuleContext(NamespaceContext.self, 0)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func EOF() -> TerminalNode? {
|
|
||||||
return getToken(ReferenceParser.Tokens.EOF.rawValue, 0)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func port() -> PortContext? {
|
|
||||||
return getRuleContext(PortContext.self, 0)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func reference() -> ReferenceContext? {
|
|
||||||
return getRuleContext(ReferenceContext.self, 0)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_root
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterRoot(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitRoot(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func root() throws -> RootContext {
|
|
||||||
var _localctx: RootContext
|
|
||||||
_localctx = RootContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 0, ReferenceParser.RULE_root)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(18)
|
|
||||||
try host()
|
|
||||||
setState(21)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
if (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.T__0.rawValue
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(19)
|
|
||||||
try match(ReferenceParser.Tokens.T__0.rawValue)
|
|
||||||
setState(20)
|
|
||||||
try port()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(23)
|
|
||||||
try match(ReferenceParser.Tokens.T__1.rawValue)
|
|
||||||
setState(24)
|
|
||||||
try namespace()
|
|
||||||
setState(26)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
if (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.T__0.rawValue || _la == ReferenceParser.Tokens.T__3.rawValue
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(25)
|
|
||||||
try reference()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(28)
|
|
||||||
try match(ReferenceParser.Tokens.EOF.rawValue)
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HostContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func name() -> [NameContext] {
|
|
||||||
return getRuleContexts(NameContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func name(_ i: Int) -> NameContext? {
|
|
||||||
return getRuleContext(NameContext.self, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_host
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterHost(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitHost(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func host() throws -> HostContext {
|
|
||||||
var _localctx: HostContext
|
|
||||||
_localctx = HostContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 2, ReferenceParser.RULE_host)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(30)
|
|
||||||
try name()
|
|
||||||
setState(35)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
while (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.T__2.rawValue
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(31)
|
|
||||||
try match(ReferenceParser.Tokens.T__2.rawValue)
|
|
||||||
setState(32)
|
|
||||||
try name()
|
|
||||||
|
|
||||||
|
|
||||||
setState(37)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PortContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func DIGIT() -> [TerminalNode] {
|
|
||||||
return getTokens(ReferenceParser.Tokens.DIGIT.rawValue)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func DIGIT(_ i:Int) -> TerminalNode? {
|
|
||||||
return getToken(ReferenceParser.Tokens.DIGIT.rawValue, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_port
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterPort(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitPort(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func port() throws -> PortContext {
|
|
||||||
var _localctx: PortContext
|
|
||||||
_localctx = PortContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 4, ReferenceParser.RULE_port)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(39)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
repeat {
|
|
||||||
setState(38)
|
|
||||||
try match(ReferenceParser.Tokens.DIGIT.rawValue)
|
|
||||||
|
|
||||||
|
|
||||||
setState(41);
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
} while (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.DIGIT.rawValue
|
|
||||||
return testSet
|
|
||||||
}())
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NamespaceContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func namespace_component() -> [Namespace_componentContext] {
|
|
||||||
return getRuleContexts(Namespace_componentContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func namespace_component(_ i: Int) -> Namespace_componentContext? {
|
|
||||||
return getRuleContext(Namespace_componentContext.self, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_namespace
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterNamespace(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitNamespace(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func namespace() throws -> NamespaceContext {
|
|
||||||
var _localctx: NamespaceContext
|
|
||||||
_localctx = NamespaceContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 6, ReferenceParser.RULE_namespace)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(43)
|
|
||||||
try namespace_component()
|
|
||||||
setState(48)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
while (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.T__1.rawValue
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(44)
|
|
||||||
try match(ReferenceParser.Tokens.T__1.rawValue)
|
|
||||||
setState(45)
|
|
||||||
try namespace_component()
|
|
||||||
|
|
||||||
|
|
||||||
setState(50)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Namespace_componentContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func name() -> [NameContext] {
|
|
||||||
return getRuleContexts(NameContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func name(_ i: Int) -> NameContext? {
|
|
||||||
return getRuleContext(NameContext.self, i)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func separator() -> [SeparatorContext] {
|
|
||||||
return getRuleContexts(SeparatorContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func separator(_ i: Int) -> SeparatorContext? {
|
|
||||||
return getRuleContext(SeparatorContext.self, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_namespace_component
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterNamespace_component(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitNamespace_component(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func namespace_component() throws -> Namespace_componentContext {
|
|
||||||
var _localctx: Namespace_componentContext
|
|
||||||
_localctx = Namespace_componentContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 8, ReferenceParser.RULE_namespace_component)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(55)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
repeat {
|
|
||||||
setState(51)
|
|
||||||
try name()
|
|
||||||
setState(53)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
if (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = { () -> Bool in
|
|
||||||
let testArray: [Int] = [_la, ReferenceParser.Tokens.T__2.rawValue,ReferenceParser.Tokens.T__4.rawValue,ReferenceParser.Tokens.T__5.rawValue]
|
|
||||||
return Utils.testBitLeftShiftArray(testArray, 0)
|
|
||||||
}()
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(52)
|
|
||||||
try separator()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setState(57);
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
} while (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.DIGIT.rawValue || _la == ReferenceParser.Tokens.LETTER.rawValue
|
|
||||||
return testSet
|
|
||||||
}())
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReferenceContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func tag() -> TagContext? {
|
|
||||||
return getRuleContext(TagContext.self, 0)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func name() -> [NameContext] {
|
|
||||||
return getRuleContexts(NameContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func name(_ i: Int) -> NameContext? {
|
|
||||||
return getRuleContext(NameContext.self, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_reference
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterReference(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitReference(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func reference() throws -> ReferenceContext {
|
|
||||||
var _localctx: ReferenceContext
|
|
||||||
_localctx = ReferenceContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 10, ReferenceParser.RULE_reference)
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
setState(66)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
switch (ReferenceParser.Tokens(rawValue: try _input.LA(1))!) {
|
|
||||||
case .T__0:
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(59)
|
|
||||||
try match(ReferenceParser.Tokens.T__0.rawValue)
|
|
||||||
setState(60)
|
|
||||||
try tag()
|
|
||||||
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
case .T__3:
|
|
||||||
try enterOuterAlt(_localctx, 2)
|
|
||||||
setState(61)
|
|
||||||
try match(ReferenceParser.Tokens.T__3.rawValue)
|
|
||||||
setState(62)
|
|
||||||
try name()
|
|
||||||
setState(63)
|
|
||||||
try match(ReferenceParser.Tokens.T__0.rawValue)
|
|
||||||
setState(64)
|
|
||||||
try name()
|
|
||||||
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw ANTLRException.recognition(e: NoViableAltException(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TagContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func name() -> [NameContext] {
|
|
||||||
return getRuleContexts(NameContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func name(_ i: Int) -> NameContext? {
|
|
||||||
return getRuleContext(NameContext.self, i)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func separator() -> [SeparatorContext] {
|
|
||||||
return getRuleContexts(SeparatorContext.self)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func separator(_ i: Int) -> SeparatorContext? {
|
|
||||||
return getRuleContext(SeparatorContext.self, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_tag
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterTag(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitTag(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func tag() throws -> TagContext {
|
|
||||||
var _localctx: TagContext
|
|
||||||
_localctx = TagContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 12, ReferenceParser.RULE_tag)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(68)
|
|
||||||
try name()
|
|
||||||
setState(74)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
while (//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = { () -> Bool in
|
|
||||||
let testArray: [Int] = [_la, ReferenceParser.Tokens.T__2.rawValue,ReferenceParser.Tokens.T__4.rawValue,ReferenceParser.Tokens.T__5.rawValue]
|
|
||||||
return Utils.testBitLeftShiftArray(testArray, 0)
|
|
||||||
}()
|
|
||||||
return testSet
|
|
||||||
}()) {
|
|
||||||
setState(69)
|
|
||||||
try separator()
|
|
||||||
setState(70)
|
|
||||||
try name()
|
|
||||||
|
|
||||||
|
|
||||||
setState(76)
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SeparatorContext: ParserRuleContext {
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_separator
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterSeparator(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitSeparator(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func separator() throws -> SeparatorContext {
|
|
||||||
var _localctx: SeparatorContext
|
|
||||||
_localctx = SeparatorContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 14, ReferenceParser.RULE_separator)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(77)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
if (!(//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = { () -> Bool in
|
|
||||||
let testArray: [Int] = [_la, ReferenceParser.Tokens.T__2.rawValue,ReferenceParser.Tokens.T__4.rawValue,ReferenceParser.Tokens.T__5.rawValue]
|
|
||||||
return Utils.testBitLeftShiftArray(testArray, 0)
|
|
||||||
}()
|
|
||||||
return testSet
|
|
||||||
}())) {
|
|
||||||
try _errHandler.recoverInline(self)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_errHandler.reportMatch(self)
|
|
||||||
try consume()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NameContext: ParserRuleContext {
|
|
||||||
open
|
|
||||||
func LETTER() -> [TerminalNode] {
|
|
||||||
return getTokens(ReferenceParser.Tokens.LETTER.rawValue)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func LETTER(_ i:Int) -> TerminalNode? {
|
|
||||||
return getToken(ReferenceParser.Tokens.LETTER.rawValue, i)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func DIGIT() -> [TerminalNode] {
|
|
||||||
return getTokens(ReferenceParser.Tokens.DIGIT.rawValue)
|
|
||||||
}
|
|
||||||
open
|
|
||||||
func DIGIT(_ i:Int) -> TerminalNode? {
|
|
||||||
return getToken(ReferenceParser.Tokens.DIGIT.rawValue, i)
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func getRuleIndex() -> Int {
|
|
||||||
return ReferenceParser.RULE_name
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func enterRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.enterName(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override open
|
|
||||||
func exitRule(_ listener: ParseTreeListener) {
|
|
||||||
if let listener = listener as? ReferenceListener {
|
|
||||||
listener.exitName(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
open func name() throws -> NameContext {
|
|
||||||
var _localctx: NameContext
|
|
||||||
_localctx = NameContext(_ctx, getState())
|
|
||||||
try enterRule(_localctx, 16, ReferenceParser.RULE_name)
|
|
||||||
var _la: Int = 0
|
|
||||||
defer {
|
|
||||||
try! exitRule()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
var _alt:Int
|
|
||||||
try enterOuterAlt(_localctx, 1)
|
|
||||||
setState(80);
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_alt = 1;
|
|
||||||
repeat {
|
|
||||||
switch (_alt) {
|
|
||||||
case 1:
|
|
||||||
setState(79)
|
|
||||||
_la = try _input.LA(1)
|
|
||||||
if (!(//closure
|
|
||||||
{ () -> Bool in
|
|
||||||
let testSet: Bool = _la == ReferenceParser.Tokens.DIGIT.rawValue || _la == ReferenceParser.Tokens.LETTER.rawValue
|
|
||||||
return testSet
|
|
||||||
}())) {
|
|
||||||
try _errHandler.recoverInline(self)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_errHandler.reportMatch(self)
|
|
||||||
try consume()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw ANTLRException.recognition(e: NoViableAltException(self))
|
|
||||||
}
|
|
||||||
setState(82);
|
|
||||||
try _errHandler.sync(self)
|
|
||||||
_alt = try getInterpreter().adaptivePredict(_input,9,_ctx)
|
|
||||||
} while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER)
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ANTLRException.recognition(let re) {
|
|
||||||
_localctx.exception = re
|
|
||||||
_errHandler.reportError(self, re)
|
|
||||||
try _errHandler.recover(self, re)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _localctx
|
|
||||||
}
|
|
||||||
|
|
||||||
static let _serializedATN:[Int] = [
|
|
||||||
4,1,8,85,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,
|
|
||||||
7,2,8,7,8,1,0,1,0,1,0,3,0,22,8,0,1,0,1,0,1,0,3,0,27,8,0,1,0,1,0,1,1,1,
|
|
||||||
1,1,1,5,1,34,8,1,10,1,12,1,37,9,1,1,2,4,2,40,8,2,11,2,12,2,41,1,3,1,3,
|
|
||||||
1,3,5,3,47,8,3,10,3,12,3,50,9,3,1,4,1,4,3,4,54,8,4,4,4,56,8,4,11,4,12,
|
|
||||||
4,57,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,67,8,5,1,6,1,6,1,6,1,6,5,6,73,8,6,
|
|
||||||
10,6,12,6,76,9,6,1,7,1,7,1,8,4,8,81,8,8,11,8,12,8,82,1,8,0,0,9,0,2,4,6,
|
|
||||||
8,10,12,14,16,0,2,2,0,3,3,5,6,1,0,7,8,85,0,18,1,0,0,0,2,30,1,0,0,0,4,39,
|
|
||||||
1,0,0,0,6,43,1,0,0,0,8,55,1,0,0,0,10,66,1,0,0,0,12,68,1,0,0,0,14,77,1,
|
|
||||||
0,0,0,16,80,1,0,0,0,18,21,3,2,1,0,19,20,5,1,0,0,20,22,3,4,2,0,21,19,1,
|
|
||||||
0,0,0,21,22,1,0,0,0,22,23,1,0,0,0,23,24,5,2,0,0,24,26,3,6,3,0,25,27,3,
|
|
||||||
10,5,0,26,25,1,0,0,0,26,27,1,0,0,0,27,28,1,0,0,0,28,29,5,0,0,1,29,1,1,
|
|
||||||
0,0,0,30,35,3,16,8,0,31,32,5,3,0,0,32,34,3,16,8,0,33,31,1,0,0,0,34,37,
|
|
||||||
1,0,0,0,35,33,1,0,0,0,35,36,1,0,0,0,36,3,1,0,0,0,37,35,1,0,0,0,38,40,5,
|
|
||||||
7,0,0,39,38,1,0,0,0,40,41,1,0,0,0,41,39,1,0,0,0,41,42,1,0,0,0,42,5,1,0,
|
|
||||||
0,0,43,48,3,8,4,0,44,45,5,2,0,0,45,47,3,8,4,0,46,44,1,0,0,0,47,50,1,0,
|
|
||||||
0,0,48,46,1,0,0,0,48,49,1,0,0,0,49,7,1,0,0,0,50,48,1,0,0,0,51,53,3,16,
|
|
||||||
8,0,52,54,3,14,7,0,53,52,1,0,0,0,53,54,1,0,0,0,54,56,1,0,0,0,55,51,1,0,
|
|
||||||
0,0,56,57,1,0,0,0,57,55,1,0,0,0,57,58,1,0,0,0,58,9,1,0,0,0,59,60,5,1,0,
|
|
||||||
0,60,67,3,12,6,0,61,62,5,4,0,0,62,63,3,16,8,0,63,64,5,1,0,0,64,65,3,16,
|
|
||||||
8,0,65,67,1,0,0,0,66,59,1,0,0,0,66,61,1,0,0,0,67,11,1,0,0,0,68,74,3,16,
|
|
||||||
8,0,69,70,3,14,7,0,70,71,3,16,8,0,71,73,1,0,0,0,72,69,1,0,0,0,73,76,1,
|
|
||||||
0,0,0,74,72,1,0,0,0,74,75,1,0,0,0,75,13,1,0,0,0,76,74,1,0,0,0,77,78,7,
|
|
||||||
0,0,0,78,15,1,0,0,0,79,81,7,1,0,0,80,79,1,0,0,0,81,82,1,0,0,0,82,80,1,
|
|
||||||
0,0,0,82,83,1,0,0,0,83,17,1,0,0,0,10,21,26,35,41,48,53,57,66,74,82
|
|
||||||
]
|
|
||||||
|
|
||||||
public
|
|
||||||
static let _ATN = try! ATNDeserializer().deserialize(_serializedATN)
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
all: clean
|
|
||||||
antlr -o Generated -Dlanguage=Swift Reference.g4
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf Generated
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
grammar Reference;
|
|
||||||
|
|
||||||
root: host (':' port)? '/' namespace reference? EOF;
|
|
||||||
host: name ('.' name)*;
|
|
||||||
port: DIGIT+;
|
|
||||||
namespace: namespace_component ('/' namespace_component)*;
|
|
||||||
namespace_component: (name separator?)+;
|
|
||||||
reference: (':' tag) | ('@' name ':' name);
|
|
||||||
tag: name (separator name)*;
|
|
||||||
separator: '.' | '-' | '_';
|
|
||||||
name: (LETTER | DIGIT)+;
|
|
||||||
DIGIT: [0-9];
|
|
||||||
LETTER: [A-Za-z];
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Antlr4
|
import Parsing
|
||||||
|
|
||||||
struct Reference: Comparable, Hashable, CustomStringConvertible {
|
struct Reference: Comparable, Hashable, CustomStringConvertible {
|
||||||
enum ReferenceType: Comparable {
|
enum ReferenceType: Comparable {
|
||||||
@@ -46,39 +46,6 @@ struct Reference: Comparable, Hashable, CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReferenceCollector: ReferenceBaseListener {
|
|
||||||
var host: String? = nil
|
|
||||||
var port: String? = nil
|
|
||||||
var namespace: String? = nil
|
|
||||||
var reference: String? = nil
|
|
||||||
|
|
||||||
override func exitHost(_ ctx: ReferenceParser.HostContext) {
|
|
||||||
host = ctx.getText()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func exitPort(_ ctx: ReferenceParser.PortContext) {
|
|
||||||
port = ctx.getText()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func exitNamespace(_ ctx: ReferenceParser.NamespaceContext) {
|
|
||||||
namespace = ctx.getText()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func exitReference(_ ctx: ReferenceParser.ReferenceContext) {
|
|
||||||
reference = ctx.getText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ErrorCollector: BaseErrorListener {
|
|
||||||
var error: String? = nil
|
|
||||||
|
|
||||||
override func syntaxError<T>(_ recognizer: Recognizer<T>, _ offendingSymbol: AnyObject?, _ line: Int, _ charPositionInLine: Int, _ msg: String, _ e: AnyObject?) {
|
|
||||||
if error == nil {
|
|
||||||
error = "\(msg) (character \(charPositionInLine + 1))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RemoteName: Comparable, Hashable, CustomStringConvertible {
|
struct RemoteName: Comparable, Hashable, CustomStringConvertible {
|
||||||
var host: String
|
var host: String
|
||||||
var namespace: String
|
var namespace: String
|
||||||
@@ -91,40 +58,51 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(_ name: String) throws {
|
init(_ name: String) throws {
|
||||||
let errorCollector = ErrorCollector()
|
let csNormal = [
|
||||||
let inputStream = ANTLRInputStream(Array(name.unicodeScalars), name.count)
|
UInt8(ascii: "a")...UInt8(ascii: "z"),
|
||||||
let lexer = ReferenceLexer(inputStream)
|
UInt8(ascii: "A")...UInt8(ascii: "Z"),
|
||||||
lexer.removeErrorListeners()
|
UInt8(ascii: "0")...UInt8(ascii: "9"),
|
||||||
lexer.addErrorListener(errorCollector)
|
].asCharacterSet().union(CharacterSet(charactersIn: "_-."))
|
||||||
|
|
||||||
let tokenStream = CommonTokenStream(lexer)
|
let csHex = [
|
||||||
let parser = try ReferenceParser(tokenStream)
|
UInt8(ascii: "a")...UInt8(ascii: "f"),
|
||||||
parser.removeErrorListeners()
|
UInt8(ascii: "0")...UInt8(ascii: "9"),
|
||||||
parser.addErrorListener(errorCollector)
|
].asCharacterSet()
|
||||||
|
|
||||||
let referenceCollector = ReferenceCollector()
|
let parser = Parse {
|
||||||
try ParseTreeWalker().walk(referenceCollector, try parser.root())
|
Consumed {
|
||||||
|
csNormal
|
||||||
if let error = errorCollector.error {
|
Optionally {
|
||||||
throw RuntimeError("failed to parse remote name: \(error)")
|
":"
|
||||||
}
|
Digits()
|
||||||
|
}
|
||||||
host = referenceCollector.host!
|
|
||||||
if let port = referenceCollector.port {
|
|
||||||
host += ":" + port
|
|
||||||
}
|
|
||||||
namespace = referenceCollector.namespace!
|
|
||||||
if let reference = referenceCollector.reference {
|
|
||||||
if reference.starts(with: "@sha256:") {
|
|
||||||
self.reference = Reference(digest: String(reference.dropFirst(1)))
|
|
||||||
} else if reference.starts(with: ":") {
|
|
||||||
self.reference = Reference(tag: String(reference.dropFirst(1)))
|
|
||||||
} else {
|
|
||||||
throw RuntimeError("failed to parse remote name: unknown reference format")
|
|
||||||
}
|
}
|
||||||
} else {
|
"/"
|
||||||
self.reference = Reference(tag: "latest")
|
csNormal.union(CharacterSet(charactersIn: "/"))
|
||||||
|
Optionally {
|
||||||
|
OneOf {
|
||||||
|
Parse {
|
||||||
|
":"
|
||||||
|
csNormal.map {
|
||||||
|
Reference(tag: String($0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parse {
|
||||||
|
"@sha256:"
|
||||||
|
csHex.map {
|
||||||
|
Reference(digest: "sha256:" + String($0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
End()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result = try parser.parse(name)
|
||||||
|
|
||||||
|
host = String(result.0)
|
||||||
|
namespace = String(result.1)
|
||||||
|
reference = result.2 ?? Reference(tag: "latest")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func <(lhs: RemoteName, rhs: RemoteName) -> Bool {
|
static func <(lhs: RemoteName, rhs: RemoteName) -> Bool {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ struct Root: AsyncParsableCommand {
|
|||||||
Pull.self,
|
Pull.self,
|
||||||
Push.self,
|
Push.self,
|
||||||
Prune.self,
|
Prune.self,
|
||||||
|
Rename.self,
|
||||||
Delete.self,
|
Delete.self,
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -36,6 +37,9 @@ struct Root: AsyncParsableCommand {
|
|||||||
do {
|
do {
|
||||||
var command = try parseAsRoot()
|
var command = try parseAsRoot()
|
||||||
|
|
||||||
|
// Run garbage-collection before each command (shouldn't take too long)
|
||||||
|
try Config().gc()
|
||||||
|
|
||||||
if var asyncCommand = command as? AsyncParsableCommand {
|
if var asyncCommand = command as? AsyncParsableCommand {
|
||||||
try await asyncCommand.run()
|
try await asyncCommand.run()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+11
-27
@@ -34,10 +34,10 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
// VM's config
|
// VM's config
|
||||||
var config: VMConfig
|
var config: VMConfig
|
||||||
|
|
||||||
var softnet: Softnet? = nil
|
var network: Network
|
||||||
|
|
||||||
init(vmDir: VMDirectory,
|
init(vmDir: VMDirectory,
|
||||||
withSoftnet: Bool = false,
|
network: Network = NetworkShared(),
|
||||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
|
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
|
||||||
directoryShares: [DirectoryShare] = []
|
directoryShares: [DirectoryShare] = []
|
||||||
) throws {
|
) throws {
|
||||||
@@ -49,13 +49,10 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the virtual machine and its configuration
|
// Initialize the virtual machine and its configuration
|
||||||
if withSoftnet {
|
self.network = network
|
||||||
softnet = try Softnet(vmMACAddress: config.macAddress.string)
|
|
||||||
}
|
|
||||||
|
|
||||||
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL,
|
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL,
|
||||||
nvramURL: vmDir.nvramURL, vmConfig: config,
|
nvramURL: vmDir.nvramURL, vmConfig: config,
|
||||||
softnet: softnet, additionalDiskAttachments: additionalDiskAttachments,
|
network: network, additionalDiskAttachments: additionalDiskAttachments,
|
||||||
directoryShares: directoryShares)
|
directoryShares: directoryShares)
|
||||||
virtualMachine = VZVirtualMachine(configuration: configuration)
|
virtualMachine = VZVirtualMachine(configuration: configuration)
|
||||||
|
|
||||||
@@ -114,7 +111,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
vmDir: VMDirectory,
|
vmDir: VMDirectory,
|
||||||
ipswURL: URL?,
|
ipswURL: URL?,
|
||||||
diskSizeGB: UInt16,
|
diskSizeGB: UInt16,
|
||||||
withSoftnet: Bool = false,
|
network: Network = NetworkShared(),
|
||||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = []
|
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = []
|
||||||
) async throws {
|
) async throws {
|
||||||
let ipswURL = ipswURL != nil ? ipswURL! : try await VM.retrieveLatestIPSW();
|
let ipswURL = ipswURL != nil ? ipswURL! : try await VM.retrieveLatestIPSW();
|
||||||
@@ -149,12 +146,9 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
try config.save(toURL: vmDir.configURL)
|
try config.save(toURL: vmDir.configURL)
|
||||||
|
|
||||||
// Initialize the virtual machine and its configuration
|
// Initialize the virtual machine and its configuration
|
||||||
if withSoftnet {
|
self.network = network
|
||||||
softnet = try Softnet(vmMACAddress: config.macAddress.string)
|
|
||||||
}
|
|
||||||
|
|
||||||
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL,
|
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL,
|
||||||
vmConfig: config, softnet: softnet,
|
vmConfig: config, network: network,
|
||||||
additionalDiskAttachments: additionalDiskAttachments,
|
additionalDiskAttachments: additionalDiskAttachments,
|
||||||
directoryShares: [])
|
directoryShares: [])
|
||||||
virtualMachine = VZVirtualMachine(configuration: configuration)
|
virtualMachine = VZVirtualMachine(configuration: configuration)
|
||||||
@@ -193,9 +187,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run(_ recovery: Bool) async throws {
|
func run(_ recovery: Bool) async throws {
|
||||||
if let softnet = softnet {
|
try network.run()
|
||||||
try softnet.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
Task {
|
Task {
|
||||||
@@ -225,16 +217,14 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let softnet = softnet {
|
try network.stop()
|
||||||
try softnet.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func craftConfiguration(
|
static func craftConfiguration(
|
||||||
diskURL: URL,
|
diskURL: URL,
|
||||||
nvramURL: URL,
|
nvramURL: URL,
|
||||||
vmConfig: VMConfig,
|
vmConfig: VMConfig,
|
||||||
softnet: Softnet? = nil,
|
network: Network = NetworkShared(),
|
||||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment],
|
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment],
|
||||||
directoryShares: [DirectoryShare]
|
directoryShares: [DirectoryShare]
|
||||||
) throws -> VZVirtualMachineConfiguration {
|
) throws -> VZVirtualMachineConfiguration {
|
||||||
@@ -268,13 +258,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
|
|
||||||
// Networking
|
// Networking
|
||||||
let vio = VZVirtioNetworkDeviceConfiguration()
|
let vio = VZVirtioNetworkDeviceConfiguration()
|
||||||
|
vio.attachment = network.attachment()
|
||||||
if let softnet = softnet {
|
|
||||||
let fh = FileHandle.init(fileDescriptor: softnet.vmFD)
|
|
||||||
vio.attachment = VZFileHandleNetworkDeviceAttachment(fileHandle: fh)
|
|
||||||
} else {
|
|
||||||
vio.attachment = VZNATNetworkDeviceAttachment()
|
|
||||||
}
|
|
||||||
vio.macAddress = vmConfig.macAddress
|
vio.macAddress = vmConfig.macAddress
|
||||||
configuration.networkDevices = [vio]
|
configuration.networkDevices = [vio]
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct VMDirectory: Prunable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func temporary() throws -> VMDirectory {
|
static func temporary() throws -> VMDirectory {
|
||||||
let tmpDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
let tmpDir = try Config().tartTmpDir.appendingPathComponent(UUID().uuidString)
|
||||||
try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: false)
|
try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: false)
|
||||||
|
|
||||||
return VMDirectory(baseURL: tmpDir)
|
return VMDirectory(baseURL: tmpDir)
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class VMStorageLocal {
|
|||||||
_ = try FileManager.default.replaceItemAt(vmURL(name), withItemAt: from.baseURL)
|
_ = try FileManager.default.replaceItemAt(vmURL(name), withItemAt: from.baseURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rename(_ name: String, _ newName: String) throws {
|
||||||
|
_ = try FileManager.default.replaceItemAt(vmURL(newName), withItemAt: vmURL(name))
|
||||||
|
}
|
||||||
|
|
||||||
func delete(_ name: String) throws {
|
func delete(_ name: String) throws {
|
||||||
try FileManager.default.removeItem(at: vmURL(name))
|
try FileManager.default.removeItem(at: vmURL(name))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,14 +150,20 @@ class VMStorageOCI: PrunableStorage {
|
|||||||
if !exists(digestName) {
|
if !exists(digestName) {
|
||||||
let tmpVMDir = try VMDirectory.temporary()
|
let tmpVMDir = try VMDirectory.temporary()
|
||||||
|
|
||||||
|
// Lock the temporary VM directory to prevent it's garbage collection
|
||||||
|
let tmpVMDirLock = try FileLock(lockURL: tmpVMDir.baseURL)
|
||||||
|
try tmpVMDirLock.lock()
|
||||||
|
|
||||||
// Try to reclaim some cache space if we know the VM size in advance
|
// Try to reclaim some cache space if we know the VM size in advance
|
||||||
if let uncompressedDiskSize = manifest.uncompressedDiskSize() {
|
if let uncompressedDiskSize = manifest.uncompressedDiskSize() {
|
||||||
let requiredCapacityBytes = UInt64(uncompressedDiskSize + 128 * 1024 * 1024)
|
let requiredCapacityBytes = UInt64(uncompressedDiskSize + 128 * 1024 * 1024)
|
||||||
|
|
||||||
let attrs = try Config().tartCacheDir.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
|
let attrs = try Config().tartCacheDir.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey, .volumeAvailableCapacityKey])
|
||||||
let availableCapacityBytes = UInt64(attrs.volumeAvailableCapacityForImportantUsage!)
|
let availableCapacityBytes = max(UInt64(attrs.volumeAvailableCapacityForImportantUsage!), UInt64(attrs.volumeAvailableCapacity!))
|
||||||
|
|
||||||
if availableCapacityBytes < requiredCapacityBytes {
|
// There is a suspicious that occasionally capacity is returned as zero which can't be true.
|
||||||
|
// Let's validate to avoid unnecessary pruning.
|
||||||
|
if 0 < availableCapacityBytes && availableCapacityBytes < requiredCapacityBytes {
|
||||||
try Prune.pruneReclaim(reclaimBytes: requiredCapacityBytes - availableCapacityBytes)
|
try Prune.pruneReclaim(reclaimBytes: requiredCapacityBytes - availableCapacityBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,4 @@ final class RemoteNameTests: XCTestCase {
|
|||||||
// Port must be specified when ":" is used
|
// Port must be specified when ":" is used
|
||||||
XCTAssertEqual(try? RemoteName("127.0.0.1:/a/b").host, nil)
|
XCTAssertEqual(try? RemoteName("127.0.0.1:/a/b").host, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNoPathTraversal() throws {
|
|
||||||
XCTAssertEqual(try? RemoteName("ghcr.io/a/../b/c:latest"), nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ apple_id {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sign {
|
sign {
|
||||||
application_identity = "Developer ID Application: Fedor Korotkov"
|
application_identity = "Developer ID Application: Cirrus Labs, Inc."
|
||||||
entitlements_file = "Resources/tart.entitlements"
|
entitlements_file = "Resources/tart.entitlements"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user