proposal: cmd/go: build: add -buildmode=nolink flag
My understanding of the "go build" process is it does something like this:
- Figure out all of the dependencies of the package.
- For each dependency, determine whether it has changed since the last time it was built, by checking the
.afile or equivalent in$GOPATH/pkg. - Compile them, in parallel if possible
- Generate the new artifact (a
.afile or similar) we checked in #2 that is now out of date. - Link them all into a single binary.
I use vim to edit Go files. The most popular plugin, vim-go, recommends checking compilation by running :GoBuild. I do this frequently to ensure I have syntax correct, imports correct, a working Go program.
:GoBuild changes directory to the directory containing the file you are editing, then runs
go build -tags '' . errors
It builds the "errors" package because vim-go desperately does not want to build any final artifacts, and attempting to build multiple packages turns off the binary building behavior.
However, I notice that this never takes advantage of build caching. That is, if I run :GoBuild and then run :GoBuild again immediately without making changes, it takes 4 seconds on the package I am compiling. If I run go build . to compile an artifact, it is much faster, about 600ms on the second run.
Is there a way to make "go build" take advantage of whatever intermediate build steps exist - the .a files from above, or their equivalents - even if it does not produce a final binary?
Alternatively, can you work with the vim-go maintainers to recommend a different tool for checking a package (and/or test package) contains valid Go syntax and identifiers?
See also fatih/vim-go#2245.
Another update, the slowness only reproduces when compiling a main package, otherwise it's fast.
This is taking advantage of build caching. But the cache does not cache executables, so the link is happening (for package main), and that's what must be taking the time. I feel like there's another open issue for a 'build without link' flag but I am not sure exactly where it is, so I retitled this one. That's really what's needed here.
Ah, thanks Russ!
Hi, Is this possibly be accepted, I am making a patch to implement this.
I'm not sure that this is really relevant anymore thanks to the proliferation of gopls; you don't need to manually compile your code anymore to know if it compiles if gopls is checking that, and can run :GoBuild when you actually want the binary.
I'm not sure that this is really relevant anymore thanks to the proliferation of
gopls; you don't need to manually compile your code anymore to know if it compiles ifgoplsis checking that, and can run:GoBuildwhen you actually want the binary.
Thank you @zikaeroh . I'm cursor how gopls do that,does it invoke go tool compile directly ?
It uses x/tools/go/packages (https://pkg.go.dev/golang.org/x/tools/go/packages), which internally calls the Go toolchain for info (without actually "building" binaries or anything).
Seems x/tools/go/packages can't find compile errors.
I propose we allow -buildmode=archive for main packages.
- For non-main packages (the default)
-buildmode=archivealready means "to compile the package". - For non-main packages it generates the .a and then throw it away (the default) or optionally keep it (with the
-oflag).
We can just extend that to main packages as well, to mean "compile but not link", and optionally generate the .a file.
As a compiler/linker developer, from time to time I want the main.a file. Currently I have to do go build -x -work and dig it out from the work directory. It would be nice if I can just do go build -buildmode=archive -o main.a.
The original motivation was editor integration (which is probably better covered by gopls) and a note on the lack of caching of linked executables (which cmd/go does now cache). Is this issue still relevant?
I still want my use case above. It is mainly for toolchain developers and it has a relatively simple workaround, though.