How to Use External Swift Packages in Bazel
NOTE: This article is about using rules_spm
to leverage Swift packages in Bazel. The rules_spm
project is being sunset. It has been superseded by swift_bazel. I have written a blog post on how to get started.
If you build Swift software in Bazel, you probably use the excellent rules_swift rules to develop modules and executables for your project. While working on your project, you almost certainly have wanted to import one of the many third-party Swift packages that exist. Unfortunately, rules_swift
does not provide a means to integrate these packages into your project quickly. So, I created rules_spm to address this very issue.
Table of Contents
What is rules_spm
?
The rules_spm
project contains Bazel rules and macros that allow a project to depend upon external Swift packages. It provides a means to declare dependent packages, much like Swift Package Manager (SPM). Under the covers, it downloads the external packages, builds them using SPM, and makes the artifacts available as Bazel binary targets for execution or Bazel library targets suitable for consumption by rules_swift
rules.
A Simple Example
This article will describe how to import the Logging
module from Apple’s swift-log package into a Swift binary built using Bazel. You can find the code for this example here.
1. Download and Configure rules_spm
First, we need to add rules_spm
to the WORKSPACE
file. The following code downloads the rules and configures them.
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "cgrindel_rules_spm",
sha256 = "fab28a41793744f3944ad2606cdd9c0f8e2f4861dd29fb1d61aa4263c7a1400a",
strip_prefix = "rules_spm-0.6.0",
urls = ["https://github.com/cgrindel/rules_spm/archive/v0.6.0.tar.gz"],
)
load(
"@cgrindel_rules_spm//spm:deps.bzl",
"spm_rules_dependencies",
)
spm_rules_dependencies()
load(
"@build_bazel_rules_swift//swift:repositories.bzl",
"swift_rules_dependencies",
)
swift_rules_dependencies()
load(
"@build_bazel_rules_swift//swift:extras.bzl",
"swift_rules_extra_dependencies",
)
swift_rules_extra_dependencies()
NOTE: At the time of writing, the latest version of rules_spm was 0.6.0. Before using rules_spm
in your project, check out the README.md for the most up-to-date version and initialization code.
NOTE: The rules_spm
rules use rules_swift
under the covers. If your project requires a specific version of rules_spm
, add the appropriate rules_swift
declarations before running spm_rules_dependencies
.
2. Declare the Dependent Swift Packages
Next, we need to declare the Swift packages that we want to use in our project. We do that by adding a spm_repositories collection to the WORKSPACE
file. Then, we add an spm_pkg entry for each of the Swift packages that we want to import. Much like the SPM package dependency syntax, spm_pkg
accepts a URL, a minimum version, and, optionally, a name. The one additional piece of information required is the list of product names that our project will use.
load("@cgrindel_rules_spm//spm:spm.bzl", "spm_pkg", "spm_repositories")
spm_repositories(
name = "swift_pkgs",
dependencies = [
spm_pkg(
"https://github.com/apple/swift-log.git",
from_version = "1.0.0",
products = ["Logging"],
),
],
)
For our example, the previous code will download the latest 1.x.x
version of apple/swift-log and generate Bazel build files that define the SPM targets associated with the Logging
library product.
3. Import the Logging module in the Swift Binary
Now, it’s time to use the Logging
module in the project’s Swift code. Add a dependency to the swift_binary
declaration in the BUILD file.
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_binary")
swift_binary(
name = "simple",
srcs = ["main.swift"],
visibility = ["//swift:__subpackages__"],
deps = [
"@swift_pkgs//swift-log:Logging",
],
)
The format for the dependency target is
@collection-name//swift-package-name:SwiftModuleName
The collection-name
is the name that you provide to the spm_repositories
declaration. The swift-package-name
is the name of the Swift package. Finally, the SwiftModuleName
is the name of the Swift module.
NOTE: The Swift package name is defined in the package’s Package.swift
. While the package name is often identical to the repository name. There are occasions when they are different.
Finally, import the module and use it in the Swift code.
import Logging
let logger = Logger(label: "com.example.main")
logger.info("Hello World!")