diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f93f4b8..57e10e7 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,7 +1,35 @@ on: push jobs: test: - runs-on: macos-latest + strategy: + matrix: + os: [macos-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - run: swift test + + windows-test: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: seanmiddleditch/gha-setup-vsdevenv@master + - name: Install swift-5.5 + run: | + Install-Binary -Url "https://download.swift.org/swift-5.5-release/windows10/swift-5.5-RELEASE/swift-5.5-RELEASE-windows10.exe" -Name "installer.exe" -ArgumentList ("-q") + - name: Set Environment Variables + run: | + echo "SDKROOT=C:\Library\Developer\Platforms\Windows.platform\Developer\SDKs\Windows.sdk" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "DEVELOPER_DIR=C:\Library\Developer" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: Adjust Paths + run: | + echo "C:\Library\Swift-development\bin;C:\Library\icu-67\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Install Supporting Files + run: | + Copy-Item "$env:SDKROOT\usr\share\ucrt.modulemap" -destination "$env:UniversalCRTSdkDir\Include\$env:UCRTVersion\ucrt\module.modulemap" + Copy-Item "$env:SDKROOT\usr\share\visualc.modulemap" -destination "$env:VCToolsInstallDir\include\module.modulemap" + Copy-Item "$env:SDKROOT\usr\share\visualc.apinotes" -destination "$env:VCToolsInstallDir\include\visualc.apinotes" + Copy-Item "$env:SDKROOT\usr\share\winsdk.modulemap" -destination "$env:UniversalCRTSdkDir\Include\$env:UCRTVersion\um\module.modulemap" + - name: Test + run: swift test diff --git a/Sources/PathKit.swift b/Sources/PathKit.swift index 3cf558f..7cec113 100644 --- a/Sources/PathKit.swift +++ b/Sources/PathKit.swift @@ -4,6 +4,7 @@ import Glibc let system_glob = Glibc.glob +#elseif os(Windows) #else import Darwin @@ -16,13 +17,17 @@ import Foundation /// Represents a filesystem path. public struct Path { /// The character used by the OS to separate two path elements +#if os(Windows) + public static let separator = "\\" +#else public static let separator = "/" +#endif /// The underlying string representation internal let path: String internal static let fileManager = FileManager.default - + internal let fileSystemInfo: FileSystemInfo // MARK: Init @@ -35,7 +40,7 @@ public struct Path { public init(_ path: String) { self.init(path, fileSystemInfo: DefaultFileSystemInfo()) } - + internal init(_ path: String, fileSystemInfo: FileSystemInfo) { self.path = path self.fileSystemInfo = fileSystemInfo @@ -44,7 +49,7 @@ public struct Path { internal init(fileSystemInfo: FileSystemInfo) { self.init("", fileSystemInfo: fileSystemInfo) } - + /// Create a Path by joining multiple path components together public init(components: S) where S.Iterator.Element == String { let path: String @@ -585,6 +590,7 @@ extension Path { // MARK: Globbing +#if !os(Windows) extension Path { public static func glob(_ pattern: String) -> [Path] { var gt = glob_t() @@ -632,6 +638,7 @@ extension Path { return fnmatch(cPattern, cPath, 0) == 0 } } +#endif // MARK: SequenceType diff --git a/Tests/PathKitTests/PathKitSpec.swift b/Tests/PathKitTests/PathKitSpec.swift index 7abab91..4d59b84 100644 --- a/Tests/PathKitTests/PathKitSpec.swift +++ b/Tests/PathKitTests/PathKitSpec.swift @@ -17,7 +17,11 @@ describe("PathKit") { } $0.it("provides the system separator") { + #if os(Windows) + try expect(Path.separator) == "\\" + #else try expect(Path.separator) == "/" + #endif } $0.it("returns the current working directory") { @@ -96,9 +100,13 @@ describe("PathKit") { let path = Path("~") $0.it("can be converted to an absolute path") { + #if os(Windows) + throw skip() + #endif + #if os(Linux) if NSUserName() == "root" { - try expect(path.absolute()) == "/root" + try expect(path.absolute()) == "/root" } else { try expect(path.absolute()) == "/home/" + NSUserName() @@ -142,23 +150,23 @@ describe("PathKit") { $0.it("can be abbreviated") { let home = Path.home.string - + try expect(Path("\(home)/foo/bar").abbreviate()) == Path("~/foo/bar") try expect(Path("\(home)").abbreviate()) == Path("~") try expect(Path("\(home)/").abbreviate()) == Path("~") try expect(Path("\(home)/backups\(home)").abbreviate()) == Path("~/backups\(home)") try expect(Path("\(home)/backups\(home)/foo/bar").abbreviate()) == Path("~/backups\(home)/foo/bar") - + #if os(Linux) try expect(Path("\(home.uppercased())").abbreviate()) == Path("\(home.uppercased())") #else try expect(Path("\(home.uppercased())").abbreviate()) == Path("~") #endif } - + struct FakeFSInfo: FileSystemInfo { let caseSensitive: Bool - + func isFSCaseSensitiveAt(path: Path) -> Bool { return caseSensitive } @@ -168,27 +176,35 @@ describe("PathKit") { let home = Path.home.string let fakeFSInfo = FakeFSInfo(caseSensitive: true) let path = Path("\(home.uppercased())", fileSystemInfo: fakeFSInfo) - + try expect(path.abbreviate().string) == home.uppercased() } - + $0.it("can abbreviate paths on a case insensitive fs") { let home = Path.home.string let fakeFSInfo = FakeFSInfo(caseSensitive: false) let path = Path("\(home.uppercased())", fileSystemInfo: fakeFSInfo) - + try expect(path.abbreviate()) == Path("~") } $0.describe("symlinking") { $0.it("can create a symlink with a relative destination") { - let path = fixtures + "symlinks/file" + #if os(Windows) + throw skip() + #endif + + let path = fixtures + "symlinks" + "file" let resolvedPath = try path.symlinkDestination() try expect(resolvedPath.normalize()) == fixtures + "file" } $0.it("can create a symlink with an absolute destination") { - let path = fixtures + "symlinks/swift" + #if os(Windows) + throw skip() + #endif + + let path = fixtures + "symlinks" + "swift" let resolvedPath = try path.symlinkDestination() try expect(resolvedPath) == Path("/usr/bin/swift") } @@ -197,9 +213,9 @@ describe("PathKit") { #if os(Linux) throw skip() #else - let path = fixtures + "symlinks/same-dir" + let path = fixtures + "symlinks" + "same-dir" let resolvedPath = try path.symlinkDestination() - try expect(resolvedPath.normalize()) == fixtures + "symlinks/file" + try expect(resolvedPath.normalize()) == fixtures + "symlinks" + "file" #endif } } @@ -240,29 +256,29 @@ describe("PathKit") { $0.describe("file info") { $0.it("can test if a path is a directory") { try expect((fixtures + "directory").isDirectory).to.beTrue() - try expect((fixtures + "symlinks/directory").isDirectory).to.beTrue() + try expect((fixtures + "symlinks" + "directory").isDirectory).to.beTrue() } $0.it("can test if a path is a symlink") { - try expect((fixtures + "file/file").isSymlink).to.beFalse() - try expect((fixtures + "symlinks/file").isSymlink).to.beTrue() + try expect((fixtures + "file" + "file").isSymlink).to.beFalse() + try expect((fixtures + "symlinks" + "file").isSymlink).to.beTrue() } $0.it("can test if a path is a file") { try expect((fixtures + "file").isFile).to.beTrue() - try expect((fixtures + "symlinks/file").isFile).to.beTrue() + try expect((fixtures + "symlinks" + "file").isFile).to.beTrue() } $0.it("can test if a path is executable") { - try expect((fixtures + "permissions/executable").isExecutable).to.beTrue() + try expect((fixtures + "permissions" + "executable").isExecutable).to.beTrue() } $0.it("can test if a path is readable") { - try expect((fixtures + "permissions/readable").isReadable).to.beTrue() + try expect((fixtures + "permissions" + "readable").isReadable).to.beTrue() } $0.it("can test if a path is writable") { - try expect((fixtures + "permissions/writable").isWritable).to.beTrue() + try expect((fixtures + "permissions" + "writable").isWritable).to.beTrue() } // fatal error: isDeletableFile(atPath:) is not yet implemented @@ -270,7 +286,7 @@ describe("PathKit") { #if os(Linux) throw skip() #else - try expect((fixtures + "permissions/deletable").isDeletable).to.beTrue() + try expect((fixtures + "permissions" + "deletable").isDeletable).to.beTrue() #endif } } @@ -462,6 +478,13 @@ describe("PathKit") { } $0.it("can be appended to") { + #if os(Windows) + try expect(Path("a\\b")) == "a" + "b" + try expect(Path("a\\b")) == "a\\" + "b" + + throw skip() + #endif + // Trivial cases. try expect(Path("a/b")) == "a" + "b" try expect(Path("a/b")) == "a/" + "b" @@ -490,37 +513,54 @@ describe("PathKit") { try expect(Path("a/c")) == "a/b" + "../c" try expect(Path("a/b/d/e")) == "a/b/c" + "../d/e" try expect(Path("../../a")) == ".." + "../a" + #endif } $0.describe("glob") { $0.it("Path static glob") { + #if os(Windows) + throw skip() + #else let pattern = (fixtures + "permissions/*able").description let paths = Path.glob(pattern) let results = try (fixtures + "permissions").children().map { $0.absolute() }.sorted(by: <) try expect(paths) == results.sorted(by: <) + #endif } $0.it("can glob inside a directory") { + #if os(Windows) + throw skip() + #else let paths = fixtures.glob("permissions/*able") let results = try (fixtures + "permissions").children().map { $0.absolute() }.sorted(by: <) try expect(paths) == results.sorted(by: <) + #endif } } $0.describe("#match") { $0.it("can match pattern against relative path") { + #if os(Windows) + throw skip() + #else try expect(Path("test.txt").match("test.txt")).to.beTrue() try expect(Path("test.txt").match("*.txt")).to.beTrue() try expect(Path("test.txt").match("*")).to.beTrue() try expect(Path("test.txt").match("test.md")).to.beFalse() + #endif } $0.it("can match pattern against absolute path") { + #if os(Windows) + throw skip() + #else try expect(Path("/home/kyle/test.txt").match("*.txt")).to.beTrue() try expect(Path("/home/kyle/test.txt").match("/home/*.txt")).to.beTrue() try expect(Path("/home/kyle/test.txt").match("*.md")).to.beFalse() + #endif } } }