Awesome
sbt-bazel
This plugin is considered experimental. Expect frequent breaking changes.
sbt-bazel is an sbt plugin that converts sbt projects to Bazel workspaces. This readme assumes you have basic knowledge of how a Bazel workspace is organized.
This plugin will generate BUILD files for each project along with a WORKSPACE files that handles importing rules_scala and downloading binary artifacts from Maven repos.
Typical Usage
The readme-example
project in the plugin's test directory is a minimal usage example. This section will walk through the settings used.
First you must install the plugin in your project. Do this by adding the following to your project's project/plugins.sbt
:
addSbtPlugin("com.stripe" %% "sbt-bazel" % "0.0.1")
In your project's build.sbt
file, you must now set the version of rules_scala
. This is done by setting bazelScalaRulesVersion
to the SHA of the rules_scala
commit you'd like to use. For example, to set the version to 0eab80ff0696d419aa54c2ab4b847ce9bdcbb379 add the following to the top of your build file:
ThisBuild / bazelScalaRulesVersion := "0eab80ff0696d419aa54c2ab4b847ce9bdcbb379"
A common sbt pattern is to set up a root project that does not contain any source files itself, but is instead an aggregation all of a repo's sub-projects. This is the pattern the example project follows.
In order to generate one WORKSPACE
file, enable workspace file generation with bazelWorkspaceGenerate
for the root project. Because the root project is just an empty aggregation, it also makes sense to turn off BUILD
file generation with bazelBuildGenerate
. Putting all this together, the root target will look as follows:
lazy val root = project.
in(file(".")).
aggregate(core, example).
settings(
bazelWorkspaceGenerate := true,
bazelBuildGenerate := false,
)
You can now run the bazelGenerate
task against the root project. The result will be BUILD
files generated for all the aggregated projects. These will appear at the base directory of each of the projects. A WORKSPACE
file will also be generated at the root directory of the root project, which will handle downloading binary dependencies and rules_scala.
See the *.expect
files for examples of what the generated files will look like.
Overriding Behavior
For more complicated projects, you may need to customize the behavior of the plugin. Several settings are available to you.
Toggling File Generation
bazelWorkspaceGenerate
: When set totrue
, aWORKSPACE
file is generated for the current project at the project's base directory. Otherwise, no file is generated. As shown above, this will typically be set totrue
for the root aggregate project.bazelBuildGenerate
: When set tofalse
, noBUILD
file is generated for the current project. Otherwise, aBUILD
file is generated for the current project at the project's base directory. As shown above, this will typically be set tofalse
for the root aggregate project.
Customizing Workspace and Build File Generation
bazelCustomWorkspace
: This allows customizing how theWORKSPACE
file is generated using a DSL. By default, theWORKSPACE
file is made up of two sections:- A
WorkspacePrelude
, which contains the code to load and set uprules_scala
- And a
MavenBindings
section, which contains code to load and bind any dependency artifacts.
- A
So the default setting is: bazelCustomWorkspace := WorkspacePrelude +: MavenBindings
, where the +:
operator concatenates sections together.
The BazelString
operand allows you add arbitrary text to the WORKSPACE
file. The following would completely replace the generated WORKSPACE
contents with the string passed to BazelString
: bazelCustomWorkspace := BazelString("# Custom workspace file")
bazelCustomBuild
: This allows customizing how theBUILD
file is generated using a DSL. By default, theWORKSPACE
file is made up of two sections:- A
BuildPrelude
, which contains the code to load rules fromrules_scala
- And a
BuildTargets
section, which contains the generatedscala_library
target andscala_binary
target, if applicable.
- A
The +:
operator concatenates sections and BazelString
allows you to specify arbitrary strings.
Customizing Dependency Generation
The plugin will add dependencies to targets based on sbt's dependsOn
and libraryDependencies
setting. If this needs to be overridden for some reason, this can be customized with the bazelRuleDeps
setting. The default setting is bazelRuleDeps := Deps(Compile)
, which means each target will include dependencies on any internal project dependencies and all dependencies in Keys.externalDependencyClasspath
.
Each operand of the DSL specifies a set of dependencies, and these sets can be used in an expression with the normal set operators: +
, -
, ∪
, and ∩
.
The operands are:
Deps(config: Configuration)
: This denotes the set of all internal and external dependencies for a given config.ModuleDep(moduleId: ModuleID)
: This denotes the dependency referred to by the givenModuleId
. For example,ModuleDep("io.circe" %% "circe-parser" % "0.9.3")
.StringDep(dep: String)
: This denotes the dependency referred to by the given string. For example,StringDep("//core")
.BazelDep(path: String, name: String)
: This denotes the dependency referred to by the combination of the given path and target name. For example,BazelDep("//core", "core")
denotes the Bazel dependency//core:core
.EmptyDep
: This denotes the empty set.
Putting this together, the following would remove the circe-parser
0.9.2 dependency and substitute it for version 0.9.3:
bazelRuleDeps := Deps(Compile) -
ModuleDep("io.circe" %% "circe-parser" % "0.9.2") +
ModuleDep("io.circe" %% "circe-parser" % "0.9.3")
Customizing Maven Dependency Resolution in WORKSPACE
You can also customize which dependencies are loaded in the WORKSPACE
file with bazelMavenDeps
. The default is bazelMavenDeps := AllExternalDeps(Compile)
The operators and operands in the Customizing Dependency Generation section can be used here.
scala_rules Version
bazelScalaRulesVersion
: Set the version ofscala_rules
to a specific SHA.
Limitations
The plugin has the following limitations:
- All dependencies in the
Keys.externalDependencyClasspath
are added as compile time dependencies, even if they are only needed at runtime. - Any additional resolvers added to the
Resolver
setting are used as mirrors in theWORKSPACE
file for all dependencies. - Only Maven resolvers are supported.
Contributing
Contributions are welcome. If you have a large contribution in mind, please open an issue to discuss the change first.
To update the test expectations:
- Change the version in
version.sbt
to end in-SNAPSHOT
. - Install nix.
- Run
scripts/update_test_output.sh
from the project root.