This is an example repo that integrates SwiftLint with Bazel.
- Install Bazel or Bazelisk (recommended): https://github.com/bazelbuild/bazelisk#installation
- Install Xcode 15 or later: https://developer.apple.com/xcode/
- You must be running macOS 13 or later
Please see SwiftLint's bazel instructions if these instructions become out of date.
Make sure that you're using bzlmod by adding this line to your
.bazelrc
:
common --enable_bzlmod
Then put this in your MODULE.bazel
:
bazel_dep(name = "swiftlint", version = "0.54.0", repo_name = "SwiftLint")
Then you can run SwiftLint with this command:
bazel run -c opt @SwiftLint//:swiftlint -- --help
You can wire up SwiftLint to use some custom, private, native SwiftLint
rules by adding this to your MODULE.bazel
:
extra_rules = use_extension("@SwiftLint//bazel:extensions.bzl", "extra_rules")
extra_rules.setup(srcs = "@extended-swiftlint//swiftlint_extra_rules:extra_rules")
Finally, you'll need to tell Bazel what source files you want to compile
as part of SwiftLint by placing BUILD
configuration into directory with
custom rules sources.
During build, Bazel includes custom rules through func extraRules() -> [Rule.Type]
,
which should exist in sources. Easiest way is to manually write this function in
separate source file, but for example below this function is autogenerated.
⚠️ Starting from SwiftLint 0.52.0 extra rules reside in separate module. This requirespublic
keyword forfunc extraRules()
.
Example of swiftlint_extra_rules/BUILD
configuration to include custom
SwiftLint rules sources as well as autogenerated extra_rules.swift
:
filegroup(
name = "extra_rules",
srcs = glob(["**/*.swift"]) + ["extra_rules.swift"],
visibility = ["//visibility:public"],
)
genrule(
name = "extra_rules_func",
srcs = glob(["*.swift"]),
outs = ["extra_rules.swift"],
cmd = """
set -euo pipefail
echo "public func extraRules() -> [any Rule.Type] {" >> $(OUTS)
echo " [" >> $(OUTS)
for file in $(SRCS); do
filename=$$(basename -- "$$file")
echo " $${filename%.*}.self," >> $(OUTS)
done
echo " ]" >> $(OUTS)
echo "}" >> $(OUTS)
""",
)
Then any Swift sources you add in the swiftlint_extra_rules
directory
will be compiled as part of the SwiftLintFramework
module just as if
those files had been in the official upstream SwiftLint git repository.
This means you have access to all the internal APIs in that module. These internal APIs can and will change over time, so you may need to adjust the custom code you write accordingly whenever you update the version of SwiftLint you target.
Then build and run SwiftLint and you should see your rule being applied:
$ bazel run -c opt @SwiftLint//:swiftlint
Linting Swift files in current working directory
Linting 'ForbiddenVarRule.swift' (2/3)
Linting 'file.swift' (1/3)
Linting 'ExtraRules.swift' (3/3)
file.swift:1:14: warning: Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals. (colon)
file.swift:1:5: warning: Forbidden Var Violation: Can't name a variable 'forbidden' (forbidden_var)
Done linting! Found 2 violations, 0 serious in 3 files.
You can validate the triggering, non-triggering and correction examples in your rule's description by running SwiftLint's "extra rules" tests:
bazel test --test_output=streamed @SwiftLint//Tests:ExtraRulesTests
You can use the excellent rules_xcodeproj project to generate an Xcode project giving you Xcode's IDE functionality to help you develop your rules.
You can add it to the bottom of your MODULE.bazel
file:
bazel_dep(name = "rules_xcodeproj", version = "1.4.0")
And define a BUILD
file with this configuration:
load("@rules_xcodeproj//xcodeproj:defs.bzl", "xcodeproj")
xcodeproj(
name = "swiftlint_xcodeproj",
project_name = "SwiftLint",
top_level_targets = [
"@SwiftLint//:swiftlint",
"@SwiftLint//Tests:ExtraRulesTests",
],
)
Generate and open the Xcode project with this command:
bazel run :swiftlint_xcodeproj && xed .
At which point you should have access to most Xcode features you'd want when developing.