Compare commits

...

4 Commits

Author SHA1 Message Date
JP Simard 348de24fe2
Bump SourceKitten 2022-07-26 13:59:17 -04:00
JP Simard 81f209114c
Credit Keith in the changelog too 2022-07-26 12:11:31 -04:00
JP Simard 6dc8da85ba
Use dlopen/dlsym to resolve the `_mh_execute_header` pointer
https://stackoverflow.com/a/49438718/373262

Co-authored-by: Keith Smiley <keithbsmiley@gmail.com>
2022-07-26 12:10:11 -04:00
JP Simard 7574bed430
Add support for native custom rules
With this change, it's now possible to add native custom rules when
building SwiftLint via Bazel (possible as of
https://github.com/realm/SwiftLint/pull/4038).

First, add a local bazel repository where custom rules will be defined:

```python
local_repository(
    name = "swiftlint_extra_rules",
    path = "swiftlint_extra_rules",
)
```

Then in the extra rules directory, add an empty `WORKSPACE` and a
`BUILD` file with the following contents:

```python
filegroup(
    name = "extra_rules",
    srcs = glob(["*.swift"]),
    visibility = ["//visibility:public"],
)
```

To add a rule (for example, `MyPrivateRule`) add the following two
files:

```swift
// ExtraRules.swift
func extraRules() -> [Rule.Type] {
    [
        MyPrivateRule.self,
    ]
}
```

```swift
// MyPrivateRule.swift
import SourceKittenFramework

struct MyPrivateRule: ConfigurationProviderRule {
    var configuration = SeverityConfiguration(.error)

    init() {}

    static let description = RuleDescription(
        identifier: "my_private_rule",
        name: "My Private Rule",
        description: "This is my private rule.",
        kind: .idiomatic
    )

    func validate(file: SwiftLintFile) -> [StyleViolation] {
        // ...
    }
}
```

Then you can reference the rule in your configuration or source files
as though they were built in to the official SwiftLint repo.

Recent changes to the linter cache made it correctly invalidate previous
results when custom native rules are edited.
2022-07-26 08:06:23 -04:00
9 changed files with 101 additions and 15 deletions

5
BUILD
View File

@ -6,7 +6,10 @@ load(
swift_library(
name = "SwiftLintFramework",
srcs = glob(["Source/SwiftLintFramework/**/*.swift"]),
srcs = glob(
["Source/SwiftLintFramework/**/*.swift"],
exclude = ["Source/SwiftLintFramework/Rules/ExcludedFromBazel/ExtraRules.swift"],
) + ["@swiftlint_extra_rules//:extra_rules"],
module_name = "SwiftLintFramework",
visibility = ["//visibility:public"],
deps = [

View File

@ -19,6 +19,12 @@
* Support for building SwiftLint with bazel.
[JP Simard](https://github.com/jpsim)
* Support for writing custom private native rules when building with
bazel.
[JP Simard](https://github.com/jpsim)
[Keith Smiley](https://github.com/keith)
[#3516](https://github.com/realm/SwiftLint/issues/3516)
#### Bug Fixes
* None.

View File

@ -63,9 +63,8 @@ extension Configuration {
.map { rule in [type(of: rule).description.identifier, rule.cacheDescription] }
.sorted { $0[0] < $1[0] }
let jsonObject: [Any] = [rootDirectory, cacheRulesDescriptions]
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject),
let jsonString = String(data: jsonData, encoding: .utf8) {
return jsonString.sha256()
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject) {
return jsonData.sha256().toHexString()
}
queuedFatalError("Could not serialize configuration for cache")
}
@ -81,7 +80,14 @@ extension Configuration {
baseURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
#endif
}
let folder = baseURL.appendingPathComponent("SwiftLint/\(Version.current.value)")
let versionedDirectory = [
"SwiftLint",
Version.current.value,
ExecutableInfo.buildID
].compactMap({ $0 }).joined(separator: "/")
let folder = baseURL.appendingPathComponent(versionedDirectory)
do {
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)

View File

@ -1,14 +1,24 @@
#if canImport(CommonCrypto)
import CommonCrypto
import Foundation
extension Data {
internal func sha256() -> Data {
withUnsafeBytes { bytes in
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
_ = CC_SHA256(bytes.baseAddress, CC_LONG(count), &hash)
return Data(hash)
}
}
internal func toHexString() -> String {
reduce(into: "") { $0.append(String(format: "%02x", $1)) }
}
}
extension String {
internal func sha256() -> String {
let theData = data(using: .utf8)!
return theData.withUnsafeBytes { bytes in
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
_ = CC_SHA256(bytes.baseAddress, CC_LONG(theData.count), &hash)
return hash.reduce(into: "") { $0.append(String(format: "%02x", $1)) }
}
data(using: .utf8)!.sha256().toHexString()
}
}
#endif

View File

@ -0,0 +1,38 @@
#if os(macOS)
import Foundation
import MachO
enum ExecutableInfo {
static let buildID: String? = {
if let handle = dlopen(nil, RTLD_LAZY) {
defer { dlclose(handle) }
if let ptr = dlsym(handle, MH_EXECUTE_SYM) {
return getUUID(pointer: ptr)?.uuidString
}
}
return nil
}()
private static func getUUID(pointer: UnsafeRawPointer) -> UUID? {
var offset: UInt64 = 0
let header = pointer.bindMemory(to: mach_header_64.self, capacity: 1)
offset += UInt64(MemoryLayout<mach_header_64>.size)
for _ in 0..<header.pointee.ncmds {
let loadCommand = pointer.load(fromByteOffset: Int(offset), as: load_command.self)
if loadCommand.cmd == LC_UUID {
let uuidCommand = pointer.load(fromByteOffset: Int(offset), as: uuid_command.self)
return UUID(uuid: uuidCommand.uuid)
}
offset += UInt64(loadCommand.cmdsize)
}
return nil
}
}
#else
enum ExecutableInfo {
static let buildID: String? = nil
}
#endif

View File

@ -216,4 +216,4 @@ public let primaryRuleList = RuleList(rules: [
XCTFailMessageRule.self,
XCTSpecificMatcherRule.self,
YodaConditionRule.self
])
] + extraRules())

View File

@ -0,0 +1,6 @@
// DO NOT EDIT
/// This is an extension point for custom native rules for Bazel.
///
/// - returns: Extra rules that are compiled with Bazel.
func extraRules() -> [Rule.Type] { [] }

View File

@ -2,6 +2,20 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@com_github_keith_swift_syntax_bazel//:deps.bzl", "swift_syntax_deps")
load("@com_github_jpsim_sourcekitten//bazel:repos.bzl", "sourcekitten_repos")
def _default_extra_swift_sources_impl(ctx):
ctx.file("WORKSPACE", "")
ctx.file("empty.swift", "func extraRules() -> [Rule.Type] { [] }")
ctx.file("BUILD.bazel", """
filegroup(
name = "extra_rules",
srcs = ["empty.swift"],
visibility = ["//visibility:public"],
)""")
_default_extra_swift_sources = repository_rule(
implementation = _default_extra_swift_sources_impl,
)
def swiftlint_deps():
"""Fetches SwiftLint dependencies"""
if not native.existing_rule("build_bazel_rules_apple"):
@ -10,5 +24,8 @@ def swiftlint_deps():
if not native.existing_rule("build_bazel_rules_swift"):
fail("error: this depends on rules_swift but that wasn't setup")
if not native.existing_rule("swiftlint_extra_rules"):
_default_extra_swift_sources(name = "swiftlint_extra_rules")
swift_syntax_deps()
sourcekitten_repos()

View File

@ -4,9 +4,9 @@ def swiftlint_repos():
"""Fetches SwiftLint repositories"""
http_archive(
name = "com_github_jpsim_sourcekitten",
sha256 = "f3e17da70aa039a54df45d648c20c84fb13f4f70df33df88bcf69b06f8714304",
strip_prefix = "SourceKitten-24fc942861f1446c2aefb19ebc94a471b2abea0f",
url = "https://github.com/jpsim/SourceKitten/archive/24fc942861f1446c2aefb19ebc94a471b2abea0f.tar.gz",
sha256 = "2e79dde69433880bcf366b5328bde08a86eb446ccfde1e455702d62c436c07b9",
strip_prefix = "SourceKitten-d5b430416ad2df92adfe7649fea949d10357d2d4",
url = "https://github.com/jpsim/SourceKitten/archive/d5b430416ad2df92adfe7649fea949d10357d2d4.tar.gz",
)
http_archive(