Hey, guys. I’m trying out pants for our monorepo, ...
# general
n
Hey, guys. I’m trying out pants for our monorepo, and I’m having a lot of trouble figuring out how to get our Go project to build — was hoping someone here could give me some guidance.
tailor
generated a bunch of build files, but when I to run
./pants list ::
I get an error complaining that one of our dependencies does not exist — it’s something that we’ve vendored and linked through go.mod, and when I looked in the chroot for that step I found that only my mod and sum files were there. No sources or anything.
I think the mental model is part of what’s giving me trouble —
tailor
has tried to generate a build file for every directory, specifying individual `go_package`s and `go_binary`s. I don’t understand why it does this: from my perspective, building go binaries is just
go build
, so I assumed that the build step for a Go project would just be • specify input files • specify output file(s) • write a command that runs
go install
to install deps, then
go build
, and writes to the output location(s) And in theory, that should give you a hermetic build, as long as the Go toolchain is included as part of the sandbox. I don’t see why having a build-system-level abstraction for each package and for each third-party dependency is necessary — but when I tried Bazel I noticed that it had a very similar model, so I’m guessing there’s a reason for it I just haven’t grokked yet.
I also figure that I could do exactly what I described above with a shell rule, but again, there’s presumably some reason that the standard tooling for Go in these build systems doesn’t work that way, and I don’t want to blow past something important somebody else already figured out…
h
Hi! Sorry for the trouble. Which version of Pants is this? I'm no Go expert but I'm pretty sure we changed this to be more ergonomic recently. @fast-nail-55400 and @hundreds-father-404 would know far more than I do though.
n
@happy-kitchen-89482 thanks for getting back to me on a Saturday 😅 it should be whichever the most recent release is -- I just installed it today
h
Try 2.10.0rc1, not 2.9. We redesigned how we analyze third-party dependencies in 2.10 to remove a gotcha some people were running into, which it sounds like you might be hitting Also welcome! Glad you're here, and we're definitely eager to hear your feedback on Go support 🙂
I don’t see why having a build-system-level abstraction for each package and for each third-party dependency is necessary
So with both Pants and Bazel, a "target" is how you give decentralized metadata for parts of your code. That metadata includes things like which packages/dirs depend on which others, and also optional metadata like the timeout for tests. A key point of build systems is to keep up as your codebase scales, and to do that, we generally believe metadata should live near the code it describes, rather than a centralized file. Hence, BUILD files. But Pants tries hard to make that boilerplate more minimal, such as inferring your dependencies for you. It also generates the
go_third_party_package
targets for you, rather than having to write them to a BUILD file or use a BUILD file generator We actually used to generate the
go_package
targets for you in Pants 2.8, rather than having one BUILD file per directory. But after a lot of discussion, we changed it in Pants 2.9 to be explicit. https://blog.pantsbuild.org/pants-2-9/ Does that make sense?
f
I get an error complaining that one of our dependencies does not exist — it’s something that we’ve vendored and linked through go.mod, and when I looked in the chroot for that step I found that only my mod and sum files were there.
vendoring isn’t supported yet. https://github.com/pantsbuild/pants/issues/12768
As for why Pants doesn’t just use
go build
, it is because
go build
is designed as a CLI tool for end users and makes many assumptions consistent with that design but that conflict with the needs of Pants. For example,
go build
does not provide any insight into the separate build cache that it maintains. Thus, Pants uses
go tool compile
and
go tool link
to invoke the Go compiler and linker directly. This allows Pants to specify where to output the “package archive” for a compiled package and then store that package archive in the Pants cache (which includes any configured remote cache which is something
go
does not support. Bazel rules_go had to make similar choices. Note that Bazel vendors or reimplements some of the
go
source. See https://github.com/bazelbuild/rules_go/tree/master/go/tools/builders.)
It would be great if the logic behind
go build
were fully available as a library, but that is not the case. For example, Pants originally used
go list
to analyze Go packages but that caused performance problems for Pants due to
go list
assuming that transitive dependencies would always be available. Pants ended up incorporating an adapted version the package analysis code from
go
to side step that issue. (Note that Bazel also made a similar choice, for what I will presume are similar reasons.)
n
@fast-nail-55400 @hundreds-father-404 Thanks guys — that does help a bit. I think I understood less about Go’s compilation process than I realized! So if I can restate a little bit, it sounds like you’re saying that: • Pants (and Bazel et all) use package-level metadata to populate Go’s build cache, whereupon •
go build
itself is smart enough to recognize the presence of already-compiled copies of matching packages in that cache and not recompile them at all I knew Go did build caching at the package level and that you can save and restore that cache from the filesystem (we do this in our current CI), but it had never occurred to me to populate the cache selectively. which it sounds like is what you’re saying Pants does.
f
Pants doesn’t actually use Go’s build cache at all. Pants invokes the Go compiler and directs the output such that Pants can capture that output directly into the Pants cache. Later when linking a binary, Pants provides
go tool link
with all of the package archives that are already stored in the Pants cache.
n
wild. I didn’t even know
go tool link
was a thing
I assume that’s substantially faster than building a whole binary with just
go build
even with a hot Go build cache, because it doesn’t have to do any checking that the cache is up to date?
f
Pants is actually somewhat slower than
go build
since
go build
is very optimized for its use case. But then again we haven’t micro-optimized the Go backend in Pants at all. The main reason for not using
go build
is really around the fact that Pants would not be able to leverage its own cache at all, which is part of how Pants tracks dependencies and is able to invalidate build artifacts only when they change.
If Pants had no insight into compiler output (which would be the case with using
go build
), Pants would have no way to provide the developer experience that users have to come to expect from other Pants language backends. And if the Pants Go backend cannot provide that experience, no particular reason to have a Go backend any more.
n
well, that depends on what you mean by insight, right? The way I see it, thinking of a particular go binary as a function of the sources and the go.mod/sum files still has some value in preventing unnecessary recompilation. Or is that what you mean by “no need for a backend” — at that point, the use case is well served by a simple genrule?
I’m imagining a hybrid system where a build run with
go build
is “sandboxed” by Pants, but access to the Go filesystem-based build cache is considered an allowed exception, like (I think?) the toolchain itself
which would allow a user to populate a less-granular build cache based on eg the hashes of the mod and sum files, and still get the DX benefits of pants
in that: • changes to the source would motivate a recompilation of the binary • changes to the dep files would download new deps, but the binaries of unchanged deps would still exist in the build cache • builds remain hermetic (as long as you don’t jam a bunch of unrelated stuff into the build cache to use it as an escape hatch)
f
Or is that what you mean by “no need for a backend” — at that point, the use case is well served by a simple genrule?
I meant that no real need to implement the Go backend at all if Pants can’t provide the expected developer experience. The user is free to use
go build
without Pants.
h
One example of "expected developer experience" here would be support for remote caching and remote execution, neither of which the standard
go
tooling can provide, but Pants can.
n
@happy-kitchen-89482 got it. pants could still provide remote caching and remote execution for a use of go build by simply caching the input source and compiled binary, and running the go build command remotely, though, right? Sorry if it seems like I'm being obtuse here; I'm sincerely trying to figure out if pants can work for my team and I'm not very used to systems like it (or bazel, which we're also experimenting with). If we can effectively deduplicate our Go builds using a genrule and this provides better performance than the "standard" Go experience that would work for me -- but I'm guessing there's a reason people don't usually use it that way. Also we have vendored deps so if I'm going to use pants I think I'll have to go off the beaten path either way 😅
h
I think we plan to support vendored deps soon, for some value of "soon"
And Pants's caching is much more fine-grained than caching the output of an entire linked binary
the latter would not be very useful in practice, as any change to any input file would invalidate the whole thing