Compare commits

...
11 Commits
Author SHA1 Message Date
Fedor Korotkov f9001304c8 Fixed packaging 2022-09-14 10:06:22 -04:00
Nikolay Edigaryev 4e20ea8f72 tart run: introduce --net-bridged (#245)
* tart run: introduce --net-bridged

* tart.entitlements: add com.apple.vm.networking
2022-09-14 17:53:04 +04:00
Nikolay EdigaryevandPete Goldsmith 678ce0a55a Introduce "tart rename" command to rename VMs (#246)
* Introduce "tart rename" command to rename VMs

* Remove unused SystemConfiguration import

* Update Sources/tart/Commands/Rename.swift

Co-authored-by: Pete Goldsmith <peter.n.goldsmith@gmail.com>

Co-authored-by: Pete Goldsmith <peter.n.goldsmith@gmail.com>
2022-09-14 17:44:26 +04:00
Fedor Korotkov 8273ae66e1 Update Developer Certificates (#242)
* Update Developer Certificates

Now the signature will state `Cirrus Labs, Inc.` and not `Fedor Korotkov`.

* Updated identity
2022-09-13 09:36:35 -04:00
Fedor Korotkov 4a9316a377 [skip ci] Add sponsorship option (#241) 2022-09-13 16:30:26 +04:00
Fedor Korotkov 6321d547cf Improve available capacity checking (#240)
* Improve available capacity checking

* Fixed expression
2022-09-12 21:55:25 +04:00
Nikolay Edigaryev f241e21614 Self-hosted temporary directory (#238) 2022-09-12 20:56:52 +04:00
Nikolay Edigaryev 0eca923604 Document NAT subnet change procedure (#236) 2022-09-08 16:26:40 +04:00
Pete Goldsmith 87f29cc11f Clarify requirements for dir argument (#231) 2022-09-07 09:02:46 -04:00
Pete Goldsmith 90d1393137 Handle tilde in path for directory share (#233)
* Expand Tilde in path

* Expand tilde in disk paths
2022-09-07 08:15:29 -04:00
Nikolay Edigaryev 625d431d10 Revert "Improve RemoteName parser (#225)" (#230)
This reverts commit ae7018c31f.
2022-09-06 22:13:30 +04:00
34 changed files with 308 additions and 1388 deletions
+1 -1
View File
@@ -6,6 +6,6 @@ export VERSION="${CIRRUS_TAG:-0}"
mkdir -p .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 stapler staple "./dist/Tart-$VERSION.pkg"
+1 -1
View File
@@ -22,7 +22,7 @@ task:
macos_instance:
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
env:
MACOS_CERTIFICATE: ENCRYPTED[8a6930a8c1286e7e536ea41b7647ea40e99174ad15e9cfcc753754fea55a619b355415629dff515b54a8921643e314e5]
MACOS_CERTIFICATE: ENCRYPTED[552b9d275d1c2bdbc1bff778b104a8f9a53cbd0d59344d4b7f6d0ca3c811a5cefb97bef9ba0ef31c219cb07bdacdd2c2]
AC_PASSWORD: ENCRYPTED[4a761023e7e06fe2eb350c8b6e8e7ca961af193cb9ba47605f25f1d353abc3142606f412e405be48fd897a78787ea8c2]
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
+1
View File
@@ -0,0 +1 @@
github: [cirruslabs]
+27 -9
View File
@@ -1,14 +1,5 @@
{
"pins" : [
{
"identity" : "antlr4",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antlr/antlr4",
"state" : {
"branch" : "4.10.1",
"revision" : "44d87bc1d130c88aa452894aa5f7e2f710f68253"
}
},
{
"identity" : "async-http-client",
"kind" : "remoteSourceControl",
@@ -54,6 +45,15 @@
"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",
"kind" : "remoteSourceControl",
@@ -117,6 +117,15 @@
"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",
"kind" : "remoteSourceControl",
@@ -125,6 +134,15 @@
"revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
"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
+4 -3
View File
@@ -12,10 +12,10 @@ let package = Package(
dependencies: [
.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/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/apple/swift-algorithms", from: "1.0.0"),
.package(url: "https://github.com/malcommac/SwiftDate", from: "6.3.1"),
.package(url: "https://github.com/antlr/antlr4", revision: "4.10.1")
.package(url: "https://github.com/malcommac/SwiftDate", from: "6.3.1")
],
targets: [
.executableTarget(name: "tart", dependencies: [
@@ -23,9 +23,10 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Dynamic", package: "Dynamic"),
.product(name: "Parsing", package: "swift-parsing"),
.product(name: "SwiftDate", package: "SwiftDate"),
.product(name: "Antlr4", package: "Antlr4"),
]),
.testTarget(name: "TartTests", dependencies: ["tart"])
]
)
+20 -2
View File
@@ -238,8 +238,8 @@ This invocation calls the `tart pull` implicitly (if the image is not being pres
<details>
<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`.
Apple did all the heavy lifting with their `Virtualization.Framework` and it just felt right to develop Tart in the open.
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>
@@ -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`
doesn't support nested virtualization.
</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>
+3 -1
View File
@@ -4,5 +4,7 @@
<dict>
<key>com.apple.security.virtualization</key>
<true/>
<key>com.apple.vm.networking</key>
<true/>
</dict>
</plist>
</plist>
+5
View File
@@ -34,6 +34,11 @@ struct Clone: AsyncParsableCommand {
let sourceVM = try VMStorageHelper.open(sourceName)
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: {
let lock = try FileLock(lockURL: Config().tartHomeDir)
try lock.lock()
+5
View File
@@ -27,6 +27,11 @@ struct Create: AsyncParsableCommand {
func run() async throws {
do {
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: {
if let fromIPSW = fromIPSW {
if fromIPSW == "latest" {
+40
View File
@@ -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)
}
}
}
+58 -3
View File
@@ -47,15 +47,28 @@ struct Run: AsyncParsableCommand {
@Option(help: ArgumentHelp("""
Additional directory shares with an optional read-only specifier\n(e.g. --dir=\"build:~/src/build\" --dir=\"sources:~/src/sources:ro\")
""", 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,
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]"))
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 {
if vnc && vncExperimental {
throw ValidationError("--vnc and --vnc-experimental are mutually exclusive")
}
if withSoftnet && netBridged != nil {
throw ValidationError("--with-softnet and --net-bridged are mutually exclusive")
}
}
@MainActor
@@ -63,7 +76,7 @@ struct Run: AsyncParsableCommand {
let vmDir = try VMStorageLocal().open(name)
vm = try VM(
vmDir: vmDir,
withSoftnet: withSoftnet,
network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(),
additionalDiskAttachments: additionalDiskAttachments(),
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] {
var result: [VZDiskImageStorageDeviceAttachment] = []
let readOnlySuffix = ":ro"
let expandedDiskPaths = disk.map { NSString(string:$0).expandingTildeInPath }
for rawDisk in disk {
for rawDisk in expandedDiskPaths {
if rawDisk.hasSuffix(readOnlySuffix) {
result.append(try VZDiskImageStorageDeviceAttachment(
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]))
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
+19 -2
View File
@@ -3,6 +3,7 @@ import Foundation
struct Config {
let tartHomeDir: URL
let tartCacheDir: URL
let tartTmpDir: URL
init() throws {
var tartHomeDir: URL
@@ -14,11 +15,27 @@ struct Config {
.homeDirectoryForCurrentUser
.appendingPathComponent(".tart", isDirectory: true)
}
self.tartHomeDir = tartHomeDir
tartCacheDir = tartHomeDir.appendingPathComponent("cache", isDirectory: true)
tartCacheDir = tartHomeDir.appendingPathComponent("cache", isDirectory: 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 {
+7
View File
@@ -0,0 +1,7 @@
import Virtualization
protocol Network {
func attachment() -> VZNetworkDeviceAttachment
func run() throws
func stop() throws
}
+22
View File
@@ -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
}
}
+16
View File
@@ -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 Virtualization
enum SoftnetError: Error {
case InitializationFailed(why: String)
}
class Softnet {
class Softnet: Network {
private let process = Process()
let vmFD: Int32
@@ -57,4 +58,9 @@ class Softnet {
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)
}
-5
View File
@@ -1,5 +0,0 @@
all: clean
antlr -o Generated -Dlanguage=Swift Reference.g4
clean:
rm -rf Generated
-13
View File
@@ -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];
+42 -64
View File
@@ -1,5 +1,5 @@
import Foundation
import Antlr4
import Parsing
struct Reference: Comparable, Hashable, CustomStringConvertible {
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 {
var host: String
var namespace: String
@@ -91,40 +58,51 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
}
init(_ name: String) throws {
let errorCollector = ErrorCollector()
let inputStream = ANTLRInputStream(Array(name.unicodeScalars), name.count)
let lexer = ReferenceLexer(inputStream)
lexer.removeErrorListeners()
lexer.addErrorListener(errorCollector)
let csNormal = [
UInt8(ascii: "a")...UInt8(ascii: "z"),
UInt8(ascii: "A")...UInt8(ascii: "Z"),
UInt8(ascii: "0")...UInt8(ascii: "9"),
].asCharacterSet().union(CharacterSet(charactersIn: "_-."))
let tokenStream = CommonTokenStream(lexer)
let parser = try ReferenceParser(tokenStream)
parser.removeErrorListeners()
parser.addErrorListener(errorCollector)
let csHex = [
UInt8(ascii: "a")...UInt8(ascii: "f"),
UInt8(ascii: "0")...UInt8(ascii: "9"),
].asCharacterSet()
let referenceCollector = ReferenceCollector()
try ParseTreeWalker().walk(referenceCollector, try parser.root())
if let error = errorCollector.error {
throw RuntimeError("failed to parse remote name: \(error)")
}
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")
let parser = Parse {
Consumed {
csNormal
Optionally {
":"
Digits()
}
}
} 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 {
+4
View File
@@ -17,6 +17,7 @@ struct Root: AsyncParsableCommand {
Pull.self,
Push.self,
Prune.self,
Rename.self,
Delete.self,
])
@@ -36,6 +37,9 @@ struct Root: AsyncParsableCommand {
do {
var command = try parseAsRoot()
// Run garbage-collection before each command (shouldn't take too long)
try Config().gc()
if var asyncCommand = command as? AsyncParsableCommand {
try await asyncCommand.run()
} else {
+11 -27
View File
@@ -34,10 +34,10 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
// VM's config
var config: VMConfig
var softnet: Softnet? = nil
var network: Network
init(vmDir: VMDirectory,
withSoftnet: Bool = false,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
directoryShares: [DirectoryShare] = []
) throws {
@@ -49,13 +49,10 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
// Initialize the virtual machine and its configuration
if withSoftnet {
softnet = try Softnet(vmMACAddress: config.macAddress.string)
}
self.network = network
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL,
nvramURL: vmDir.nvramURL, vmConfig: config,
softnet: softnet, additionalDiskAttachments: additionalDiskAttachments,
network: network, additionalDiskAttachments: additionalDiskAttachments,
directoryShares: directoryShares)
virtualMachine = VZVirtualMachine(configuration: configuration)
@@ -114,7 +111,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
vmDir: VMDirectory,
ipswURL: URL?,
diskSizeGB: UInt16,
withSoftnet: Bool = false,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = []
) async throws {
let ipswURL = ipswURL != nil ? ipswURL! : try await VM.retrieveLatestIPSW();
@@ -149,12 +146,9 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
try config.save(toURL: vmDir.configURL)
// Initialize the virtual machine and its configuration
if withSoftnet {
softnet = try Softnet(vmMACAddress: config.macAddress.string)
}
self.network = network
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL,
vmConfig: config, softnet: softnet,
vmConfig: config, network: network,
additionalDiskAttachments: additionalDiskAttachments,
directoryShares: [])
virtualMachine = VZVirtualMachine(configuration: configuration)
@@ -193,9 +187,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
func run(_ recovery: Bool) async throws {
if let softnet = softnet {
try softnet.run()
}
try network.run()
DispatchQueue.main.sync {
Task {
@@ -225,16 +217,14 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
}
if let softnet = softnet {
try softnet.stop();
}
try network.stop()
}
static func craftConfiguration(
diskURL: URL,
nvramURL: URL,
vmConfig: VMConfig,
softnet: Softnet? = nil,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment],
directoryShares: [DirectoryShare]
) throws -> VZVirtualMachineConfiguration {
@@ -268,13 +258,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
// Networking
let vio = VZVirtioNetworkDeviceConfiguration()
if let softnet = softnet {
let fh = FileHandle.init(fileDescriptor: softnet.vmFD)
vio.attachment = VZFileHandleNetworkDeviceAttachment(fileHandle: fh)
} else {
vio.attachment = VZNATNetworkDeviceAttachment()
}
vio.attachment = network.attachment()
vio.macAddress = vmConfig.macAddress
configuration.networkDevices = [vio]
+1 -1
View File
@@ -29,7 +29,7 @@ struct VMDirectory: Prunable {
}
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)
return VMDirectory(baseURL: tmpDir)
+4
View File
@@ -32,6 +32,10 @@ class VMStorageLocal {
_ = 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 {
try FileManager.default.removeItem(at: vmURL(name))
}
+9 -3
View File
@@ -150,14 +150,20 @@ class VMStorageOCI: PrunableStorage {
if !exists(digestName) {
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
if let uncompressedDiskSize = manifest.uncompressedDiskSize() {
let requiredCapacityBytes = UInt64(uncompressedDiskSize + 128 * 1024 * 1024)
let attrs = try Config().tartCacheDir.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
let availableCapacityBytes = UInt64(attrs.volumeAvailableCapacityForImportantUsage!)
let attrs = try Config().tartCacheDir.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey, .volumeAvailableCapacityKey])
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)
}
}
-4
View File
@@ -44,8 +44,4 @@ final class RemoteNameTests: XCTestCase {
// Port must be specified when ":" is used
XCTAssertEqual(try? RemoteName("127.0.0.1:/a/b").host, nil)
}
func testNoPathTraversal() throws {
XCTAssertEqual(try? RemoteName("ghcr.io/a/../b/c:latest"), nil)
}
}
+1 -1
View File
@@ -7,6 +7,6 @@ apple_id {
}
sign {
application_identity = "Developer ID Application: Fedor Korotkov"
application_identity = "Developer ID Application: Cirrus Labs, Inc."
entitlements_file = "Resources/tart.entitlements"
}