This document describes the developments and breaking changes in v17, and how to migrate. You can find the complete changelog here.
The plugin API is a powerful new way to extend Please to support new languages and technologies. It offers rule authors greater flexibility in how they configure, distribute, and version their rules. In v17, Plugins are becoming the default, replacing all the built-in language rules.
The language plugins are hosted in the please-build github organisation, where we better collaborate with external contributors. If you have a plugin you'd like to contribute, please get in touch!
To migrate from the built-in rules, simply run plz init plugin [plugin name]
.
The Golang plugin has remained mostly compatible with the builtin rules, however there are a lot of new features available!
Previously, the Go rules would let you import a package using two different import paths,
example.com/owner/repo/package
, and
example.com/owner/repo/package/package
. This was a bug that has now been resolved. To
help migration, use the LegacyImports
config option, under
[Plugin "go"]
in your .plzconfig
file.
One of the major shortcomings of using Please when compared to the standard go tooling is the toil involved in
managing third party dependencies. The go_module()
rule has served us well, but it has
some major shortcomings.
Cyclic dependencies between modules are hard to represent as we compile the entire module in one go. It's possible to work around this by downloading the module with one rule and compiling it in parts, however this requires a lot of toil, and can get complicated quickly:
go_mod_download(
name = "go-opentelemetry_download",
module = "go.opentelemetry.io/otel",
version = "v1.11.1",
)
go_module(
name = "go-opentelemetry",
download = ":go-opentelemetry_download",
install = [
".",
"baggage",
"internal",
"internal/baggage",
"internal/attribute",
"internal/global",
"propagation",
"semconv/internal",
"semconv/v1.10.0",
],
module = "go.opentelemetry.io/otel",
deps = [
":go-opentelemetry.trace",
":logr",
":stdr",
],
)
# split off due to go-opentelemetry's circular dependency with go-opentelemetry.trace
go_module(
name = "go-opentelemetry_1",
download = ":go-opentelemetry_download",
install = [
"attribute",
"codes",
],
module = "go.opentelemetry.io/otel",
)
Additionally, dependencies between modules must be explicitly defined, along with the packages that we need from that module. This results in a slow, iterative cycle of building, and waiting for the next compiler error to figure out which package or module we're missing. We also often end up relying on wildcards, compiling more of t he module than we actually need because figuring out the set of packages we actually need is so hard.
Compiling large modules with a single rule like this can also take a long time. Some modules such and the AWS SDK can take upwards for 4 minutes to compile. This happens in a single build task so often can't take advantage of multicore systems.
In the Go plugin, we have introduced a brand new build rule called go_repo()
, which
uses a wildly different paradigm:
go_repo(module="github.com/google/go-cmp", version="v0.5.9")
go_repo(module="github.com/stretchr/testify", version="v1.8.0")
go_repo(module="go.opentelemetry.io/otel/trace", version="v1.11.1")
go_repo(module="go.opentelemetry.io/otel", version="v1.11.1")
go_repo(module="github.com/go-logr/stdr", version="v1.2.2")
go_repo(module="github.com/pmezard/go-difflib", version="v1.0.0")
go_repo(module="github.com/stretchr/objx", version="v0.4.0")
go_repo(module="gopkg.in/yaml.v3", version="v3.0.1")
go_repo(module="gopkg.in/check.v1", version="v0.0.0-20161208181325-20d25e280405")
go_repo(module="github.com/go-logr/logr", version="v1.2.3")
go_repo(module="github.com/davecgh/go-spew", version="v1.1.1")
By treating third party modules as subrepos, we can generate individual build
targets for each package! This eliminates all these problems with the existing
go_module()
rules:
go_repo()
are the module name and version.
go_library()
and
go_binary()
) to compile the packages in the subrepo!
Labels follow the following format:
///third_party/go/github.com_module_name//package/name
.
To depend on github.com/stretchr/testify/assert
, use
///third_party/go/github.com_stretchr_testify//assert
.
As these build labels are quite cumbersome, you may pass a name and install list to
go_repo()
:
go_repo(
name = "testify",
install = ["assert", "require"],
module = "github.com/stretchr/testify",
version = "v1.8.0",
)
You may then use //third_party/go:testify
as an alias for
///third_party/go/github.com_stretchr_testify//assert
and
///third_party/go/github.com_stretchr_testify//require
.
In go v1.20, they stopped distributing the SDK in binary form with the SDK. The Go rules depend on these
binaries. The simplest way to work with go v1.20 is to use the go_toolchain()
rule,
which now compiles the SDK for you.
Under the hood, the go_toolchain()
rule is using
go_stdlib()
, which compiles the standard library as a normal build target. This detail
lets us support additional features such as cross compilation, build modes, and race detection by allowing
Please to re-build the standard library as needed. For example,
plz build -o plugin.go.race:true //src:main
, will build that target with race
detection enabled. There's also go.buildmode
for different build modes. Both of these
can also be configured in [Plugin "go"]
in your .plzconfig
.
The Go packages driver is an experimental tool that provides a bridge between go tooling and build systems like Please. Tools like gopls, gosec, and many of the linters included in golangci-lint will use this.
In v17 we have implemented a new approach to the proto rules. The original proto rules supported a fixed set of languages, and extending them with new plugins proved quite difficult. The new proto rules provide an sdk for adding new protoc plugins for new languages and targets e.g. gRPC gateways.
So far, Golang and Python are supported, however there are plans to support C++ and Java in the future.
The new proto rules don't currently support the same set of languages. For a drop in replacement, you may use
///proto//build_defs:legacy
. These rules use the original configuration under
[Proto]
, so there's no need to update your build file.
Due to poor adoption, persistent workers have been removed as of v17. Users migrating to the Java plugin should
configure JavacTool
instead of JavacWorker
.
The plz generate
subcommand can be used to link generated sources into the source tree
which can help with integrating with tooling and IDEs, however if you're using glob in that directory, this may
cause build failures. In v17, glob will omit symlinks by default. If you want to include symlinks, pass
glob(include_symlinks = True, ...)
.
Glob is often used to find files on disk to reduce toil in keeping build rules up to date, however it's often
easy to get the glob pattern wrong. In this case, it used to silently return an empty list which can lead to
confusing errors. In v17, glob will now throw an exception. If you want to allow glob to match empty files,
pass glob(allow_empty = True, ...)
.