Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ let package = Package(
.target(
name: "SwiftLintCore",
dependencies: [
.product(name: "CryptoSwift", package: "CryptoSwift", condition: .when(platforms: [.linux])),
.product(name: "CryptoSwift", package: "CryptoSwift", condition: .when(platforms: [.linux, .windows])),
.target(name: "DyldWarningWorkaround", condition: .when(platforms: [.macOS])),
.product(name: "SourceKittenFramework", package: "SourceKitten"),
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private extension String {
var strippingURLs: String {
let range = fullNSRange
// Workaround for Linux until NSDataDetector is available
#if os(Linux)
#if os(Linux) || os(Windows)
// Regex pattern from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
let pattern = "(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)" +
"(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*" +
Expand Down
2 changes: 1 addition & 1 deletion Source/SwiftLintFramework/Configuration+CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private func fileCount(from envVar: String) throws(SwiftLintError) -> Int {
return count
}

#if os(Linux)
#if os(Linux) || os(Windows)
private func autoreleasepool<T>(block: () -> T) -> T { block() }
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import SourceKittenFramework
import FoundationNetworking
#endif

#if os(Windows)
import func WinSDK.Sleep
#endif

internal extension Configuration.FileGraph.FilePath {
// MARK: - Properties: Remote Cache
/// This should never be touched.
Expand Down Expand Up @@ -83,7 +87,11 @@ internal extension Configuration.FileGraph.FilePath {
while true {
if taskDone { break }
if Date().timeIntervalSince(startDate) > timeout { task.cancel(); break }
#if os(Windows)
Sleep(50)
#else
usleep(50_000) // Sleep for 50 ms
#endif
}

// Handle wrong data
Expand Down
29 changes: 29 additions & 0 deletions Source/SwiftLintFramework/Helpers/Glob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import func Musl.glob
#endif
#endif

#if os(Windows)
import WinSDK
#endif

// Adapted from https://gist.github.com/efirestone/ce01ae109e08772647eb061b3bb387c3

struct Glob {
Expand All @@ -20,6 +24,28 @@ struct Glob {

return expandGlobstar(pattern: pattern)
.reduce(into: [String]()) { paths, pattern in
#if os(Windows)
URL(fileURLWithPath: pattern).withUnsafeFileSystemRepresentation {
var ffd = WIN32_FIND_DATAW()

let hDirectory: HANDLE = String(cString: $0!).withCString(encodedAs: UTF16.self) {
FindFirstFileW($0, &ffd)
Copy link
Contributor

@roman-bcny roman-bcny Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to work for globs that include directories

SwiftLintPackageTests.xctest	21520	CreateFile	C:\workspace\SwiftLint\Tests\FileSystemAccessTests\Resources\ProjectMock\Level1\*\*.swift	NAME INVALID	Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a

testExcludeByPrefixGlobExcludePaths runs this for Level1/*/*.swift, and on Windows only Level1/*.swift would work but Level1/*/*.swift gets treated as Level1/* dir with the *.swift glob, which can't find the dir and fails.

From a cursory research it seems Windows doesn't expose this as an api and we'll have to manually split the string and query recursively to handle such cases.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be tougher than it seems as this is an area that has caused a lot of headaches in the past (even in the Unix case). See #6342 as an example where I try to improve performance (again). It also lists (some) issues related to that topic.

On the other hand, getting it to work at all on Windows should be doable. Performance is another topic then ...

}
if hDirectory == INVALID_HANDLE_VALUE { return }
defer { FindClose(hDirectory) }

repeat {
let path: String = withUnsafePointer(to: &ffd.cFileName) {
$0.withMemoryRebound(to: UInt16.self,
capacity: MemoryLayout.size(ofValue: $0) / MemoryLayout<WCHAR>.size) {
String(decodingCString: $0, as: UTF16.self)
}
}
if path == "." || path == ".." { continue }
paths.append(path)
} while FindNextFileW(hDirectory, &ffd)
}
#else
var globResult = glob_t()
defer { globfree(&globResult) }

Expand All @@ -31,6 +57,7 @@ struct Glob {
if glob(pattern, flags, nil, &globResult) == 0 {
paths.append(contentsOf: populateFiles(globResult: globResult))
}
#endif
}
.unique
.sorted()
Expand Down Expand Up @@ -92,6 +119,7 @@ struct Glob {
return isDirectory && isDirectoryBool.boolValue
}

#if !os(Windows)
private static func populateFiles(globResult: glob_t) -> [String] {
#if os(Linux)
let matchCount = globResult.gl_pathc
Expand All @@ -102,4 +130,5 @@ struct Glob {
globResult.gl_pathv[index].flatMap { String(validatingUTF8: $0) }
}
}
#endif
}
2 changes: 1 addition & 1 deletion Source/SwiftLintFramework/LintOrAnalyzeCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ private actor CorrectionsBuilder {
}

private func memoryUsage() -> String? {
#if os(Linux)
#if os(Linux) || os(Windows)
return nil
#else
var info = mach_task_basic_info()
Expand Down
2 changes: 1 addition & 1 deletion Source/SwiftLintFramework/ProgressBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ actor ProgressBar {
}
}

#if os(Linux)
#if os(Linux) || os(Windows)
// swiftlint:disable:next identifier_name
private let NSEC_PER_SEC = 1_000_000_000
#endif
Expand Down
11 changes: 11 additions & 0 deletions Source/swiftlint/Commands/Rules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import ArgumentParser
import Foundation
import SwiftLintFramework
import SwiftyTextTable
#if os(Windows)
import WinSDK
#endif

private typealias SortedRules = [(String, any Rule.Type)]

Expand Down Expand Up @@ -141,12 +144,20 @@ private extension TextTable {

private struct Terminal {
static func currentWidth() -> Int {
#if os(Windows)
var csbi = CONSOLE_SCREEN_BUFFER_INFO()
guard GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) else {
return 80
}
return Int(csbi.srWindow.Right - csbi.srWindow.Left) + 1
#else
var size = winsize()
#if os(Linux)
_ = ioctl(CInt(STDOUT_FILENO), UInt(TIOCGWINSZ), &size)
#else
_ = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)
#endif
return Int(size.ws_col)
#endif
}
}