- Why use Gazelle and Go?
- Why split the implementation between Go and Starlark?
- Is the same build file generation logic used for the Go/Gazelle and Starlark implementations?
- Does this replace rules_spm?
- Can I migrate from rules_spm to
rules_swift_package_manager
? - Can I just manage my external Swift packages and not generate Bazel build files for my project?
- After running
//:swift_update_pkgs
, I see a.build
directory. What is it? Do I need it? - Does the Gazelle plugin run Swift package manager with every execution?
- Can I store the Swift dependency files in a sub-package (i.e., not in the root of the workspace)?
- How do I handle the error
Unable to resolve byName reference XXX in @swiftpkg_yyy.
?
The Gazelle framework provides lots of great features for generating Bazel build and Starlark files. Right now, the best way to leverage the framework is to write the plugin in Go.
In addition, adoption of the Gazelle ecosystem has started to take off. There are lots of useful plugins for other languages. Letting Gazelle generate and maintain Bazel build files is a real game changer for developer productivity.
As mentioned previously, the easiest way to implement a Gazelle plugin is to write it in Go. This works great for generating build files in the primary workspace. However, there is a chicken-and-egg problem when it comes time to generate build files in a repository rule. The repository rule needs to generate files during the loading phase. The Go toolchain and the Gazelle framework defined in the workspace are not available to the repository rule during this phase. So, one needs to either perform some gymnastics to build the Gazelle plugin (see below) or use a language/runtime that is guaranteed to be available during the loading phase. Since Starlark is available during the loading phase, the build file generation logic for the repository rules is implemented in Starlark.
In short, they assume that if you are using the Gazelle plugin for Go, then you must have a Go toolchain installed on the host system. In essence, they shell out and run Go from the system.
No. The Gazelle plugin inspects the Swift source files and the directory structure to determine the
placement and content of the Bazel build files. The repository rules leverage information about the
Swift packages (e.g., dump and describe JSON). However, both implementations use the
module_index.json
to resolve module references to Bazel targets for the external dependencies.
Does this replace rules_spm?
Yes. There are some limitations with the rules_spm implementation. After receiving feedback and suggestions from the community, we opted to create a clean sheet implementation which includes new features and improvements:
- Bazel build file generation for the primary workspace.
- Build the external dependencies with rules_swift.
- Pin the exact versions for the direct and transitive dependencies.
Can I migrate from rules_spm to rules_swift_package_manager
?
Absolutely. A migration guide from rules_spm is on the roadmap.
Yes. Just omit the //:update_build_files
target that is mentioned in the quickstart.
The //:swift_update_pkgs
target runs the Gazelle plugin in update-repos
mode. This mode
resolves the external dependencies listed in your Package.swift
by running Swift package manager
commands. These commands result in a .build
directory being created. The directory is a side
effect of running the Swift package manager commands. It can be ignored and should not be checked
into source control. It is not used by the Gazelle plugin or the Starlark repository rules.
No. The Gazelle plugin only executes the Swift package manager when run in update-repos
mode. This
mode only needs to be run when modifying your external dependencies (e.g., add/remove a dependency,
update the version selection for a dependency). The update
mode for the Gazelle plugin generates
Bazel build files for your project. It uses information written to the swift_deps_index.json
and
the source files that exist in your project to generate the Bazel build files.
Yes. The vapor example demonstrates storing the Swift dependency files in a sub-package called
swift
.
This can happen with some Swift packages (e.g. firebase-ios-sdk
). rules_xcodeproj removes the
sandboxed
spawn
strategy
in their default build configuration due to slow performance of the MacOS sandbox. The above bazelrc
stanza adds it back. An issue
exists tracking the work to allow these Swift packages to be built using the local
spawn strategy.
tl;dr A transitive dependency uses a by-name product reference format that was deprecated in Swift 5.2. You need to patch the Swift package to use a more explicit product reference.
Patch the Swift package to use a fully-qualified product reference.
Let's look at an example. At the time of this writing, the pusher-websocket-swift Package.swift
refers to the TweetNacl
product in the tweetnacl-swiftwrap
package using just the name of the
product.
// Parts of the definition have been omitted for brevity.
let package = Package(
name: "PusherSwift",
dependencies: [
.package(url: "https://github.com/pusher/NWWebSocket.git", .upToNextMajor(from: "0.5.4")),
.package(url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap", .upToNextMajor(from: "1.0.0")),
],
targets: [
.target(
name: "PusherSwift",
dependencies: [
"NWWebSocket",
"TweetNacl", // <=== byName reference to product in tweetnacl-swiftwrap
],
path: "Sources"
),
],
)
To make this work with rules_swift_package_manager
, you need to patch this file so that the
reference is the following:
.target(
name: "PusherSwift",
dependencies: [
"NWWebSocket",
.product(name: "TweetNacl", package: "tweetnacl-swiftwrap"), // <=== Explicit ref
],
path: "Sources"
),
This specific case is the reason that previous versions of rules_swift_package_manager
pre-processed the transitive Swift dependencies using the Gazelle plugin’s update-repos
action and
stored the results in a JSON file that was checked into source control.
Here is a quick explainer of why rules_swift_package_manager
cannot resolve this dependency with
the new architecture:
- Each Swift package is declared as an external Bazel repository.
- During the loading phase, there is no mechanism for one external repository to peek into another external repository. In other words, a Swift package cannot query another Swift package to determine all of the available products.
- So, when
rules_swift_package_manager
generates aBUILD.bazel
file for a Swift package external repo, it only has the information that is returned by callingswift package description
andswift package dump-package
. The output from these calls only provides the identity and the type of the Swift package (e.g., remote, file system). - As a result,
byName
lookups can only be resolved to the targets/products in the current package and to products in Swift packages where the identity exactly matches the product name.