Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upcmd/go: clarify best practice for tool dependencies #25922
Comments
gopherbot
added this to the vgo milestone
Jun 16, 2018
myitcv
added
the
NeedsDecision
label
Jun 16, 2018
myitcv
modified the milestones:
vgo,
Go1.11
Jun 16, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bcmills
Jun 19, 2018
Member
Let's take a step back: what do you mean by “tool dependency”?
The stringer example suggests that you mean “a tool required for use with go generate”, but note that generate is intended to be run by the author of the module, not its users: a tool needed to run generate shouldn't be a hard requirement for the module or its tests.
|
Let's take a step back: what do you mean by “tool dependency”? The |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Jun 19, 2018
Member
Let's take a step back: what do you mean by “tool dependency”?
Roughly:
- I'm writing a module; or, more generally, me and my team are writing a module
- we want to use a tool (e.g.
stringer) whilst writing that code - we want to reproducibly re-generate code, or put another way, we want the whole team to consistently generate the same code at any given point in time
- hence I want to ensure that everyone is using the same version of any tool we require
but note that generate is intended to be run by the author of our module, not its users: a tool needed to run generate shouldn't be a hard requirement for the module or its tests.
Correct, this would only be a dependency for the authors of the module (as you point out, the users of our module probably don't care whether or not we've used stringer or other tools).
As I understand it, these tool dependencies do not introduce any additional requirements for users of our module: they (the tool dependencies) can only ever be referenced by running vgo install or vgo run in the context of our module. So as a user of our module, I would, by definition be in a different context (specifically the module that references our module) when running vgo install or vgo run.
I'm using the term "context" here to mean "the go.mod that vgo uses." I'm sure there must be a better, more precise term
Roughly:
Correct, this would only be a dependency for the authors of the module (as you point out, the users of our module probably don't care whether or not we've used As I understand it, these tool dependencies do not introduce any additional requirements for users of our module: they (the tool dependencies) can only ever be referenced by running I'm using the term "context" here to mean "the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jimmyfrasche
Jun 19, 2018
Member
I haven't played around with vgo much yet or looked under the hood.
I know a lot of package managers handle this by sorting dependencies into buckets: regular dependencies, test dependencies, dev dependencies. That seems like overkill and there can also be dependencies by build tag, like, say, importing a module for internal use but only on windows, and dependencies may only be for a certain package in a module that you may not be using.
Could vgo record all the dependencies for a project—for all build tags, for all packages, for tests, for development—but lazily fetch them as needed? (This may require more invasive integration but it would be an optimization as the semantics are the same for lazy-fetch and fetch-everything-upfront)
|
I haven't played around with vgo much yet or looked under the hood. I know a lot of package managers handle this by sorting dependencies into buckets: regular dependencies, test dependencies, dev dependencies. That seems like overkill and there can also be dependencies by build tag, like, say, importing a module for internal use but only on windows, and dependencies may only be for a certain package in a module that you may not be using. Could vgo record all the dependencies for a project—for all build tags, for all packages, for tests, for development—but lazily fetch them as needed? (This may require more invasive integration but it would be an optimization as the semantics are the same for lazy-fetch and fetch-everything-upfront) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Jun 20, 2018
Member
Could vgo record all the dependencies for a project—for all build tags, for all packages, for tests, for development—but lazily fetch them as needed?
My understanding is that this is exactly what vgo does today, for any dependency, test/tool or otherwise.
And, to your earlier point, I agree it means the delineation between the buckets is unnecessary (for anyone wanting to know why a dependency exists, vgo can tell us that answer).
Just to clarify one point above. My proposal for vgo install -tool golang.org/x/tools/cmd/stringer would, however, add a section to go.mod (or equivalent) where the tool packages are listed. This is simply to avoid the need for the tools.go file referenced in my example.
My understanding is that this is exactly what And, to your earlier point, I agree it means the delineation between the buckets is unnecessary (for anyone wanting to know why a dependency exists, Just to clarify one point above. My proposal for |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bcmills
Jun 20, 2018
Member
A couple of observations:
-
Some tools required by Go builds are not themselves built using the
gotool. (For example, Google's protocol compiler is written in C++; only the Go code generator plugin is written in Go.) So even if we add support for tooling to thegotool, it will be at best a partial solution. -
If you are assuming the use of the
gotool anyway, then you can write a Go program that invokes it with appropriate arguments. -
Because a command-line tool is a top-level package, its versions are not context-sensitive: if you're not mixing in other packages to customize the tool, you don't need to resolve version constraints from those packages.
To me, those suggest a fairly straightforward solution: go run pkg@version. In combination with //go:generate, that would allow you to express the dependency (at the exact version used) directly in the source file:
//go:generate go run golang.org/x/tools/cmd/stringer@v1.11.0 -type=Pill
In contrast, if you are building a custom version of the tool, then you should be able to customize it using blank-imports as you describe.
|
A couple of observations:
To me, those suggest a fairly straightforward solution:
In contrast, if you are building a custom version of the tool, then you should be able to customize it using blank-imports as you describe. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jimmyfrasche
Jun 20, 2018
Member
@bcmills that sounds good but it would be awkward to write that each time and easy for things to go out of sync and you have some code using 1.11.0 and some using 1.12.3 but probably want .
go generate let's you define aliases with //go:generate -command alias the-command (I've not used it personally and unsure of the scope: file? package? (I assume file.))
Perhaps a more vgo-aware variant would be helpful, like
//go:generate -require golang.org/x/tools/cmd/stringer@v1.11.0
which is module scoped. It falls somewhere between an ephemeral go install -tool and a fictitious blank import.
There are a some details to iron out in such an approach, of course.
|
@bcmills that sounds good but it would be awkward to write that each time and easy for things to go out of sync and you have some code using 1.11.0 and some using 1.12.3 but probably want . go generate let's you define aliases with Perhaps a more vgo-aware variant would be helpful, like
which is module scoped. It falls somewhere between an ephemeral There are a some details to iron out in such an approach, of course. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Jun 21, 2018
Member
Some tools required by Go builds are not themselves built using the go tool.
Completely. As you say, this can either be solved entirely orthogonally, or with Go wrappers (wrappers that could even take care of installing the right version).
Because a command-line tool is a top-level package, its versions are not context-sensitive: if you're not mixing in other packages to customize the tool, you don't need to resolve version constraints from those packages.
I'm not entirely sure I follow your point here. The point I was trying to make is that where (i.e. in what directory) you run vgo install golang.org/x/tools/cmd/stringer is relevant because that will determine which go.mod you reference and therefore which version of the golang.org/x/tools module you use.
To me, those suggest a fairly straightforward solution:
go run pkg@version.
As @jimmyfrasche pointed out this has the major downside of littering the required version across all the places you require go:generate directives, which seems to be somewhat counter to having go.mod be the single point of reference for versions.
Just one point to note about the "proposal":
vgo install -tool golang.org/x/tools/cmd/stringer
vgo run golang.org/x/tools/cmd/stringer
Despite the explanation in #25416, I think vgo run golang.org/x/tools/cmd/stringer could actually cache the linked binary, because the cache can very precisely be trimmed given we know exactly what version is being referenced at a given point in time.
Completely. As you say, this can either be solved entirely orthogonally, or with Go wrappers (wrappers that could even take care of installing the right version).
I'm not entirely sure I follow your point here. The point I was trying to make is that where (i.e. in what directory) you run
As @jimmyfrasche pointed out this has the major downside of littering the required version across all the places you require Just one point to note about the "proposal":
Despite the explanation in #25416, I think |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jimmyfrasche
Jun 21, 2018
Member
Running a tool at a specific version depending on the module would have to go through vgo.
Running stringer lets $SHELL pick which stringer to run, so it would need to be something like go run-dev-tool stringer so that vgo could dispatch the correct version of the command.
Something like that could be stuffed into go run but that doesn't seem to fit with the spirit of that command. Arguably go tool would be a better fit but that could cause namespacing issues so it seems like a separate command built to purpose would be best.
go generate could handle this transparently but not all tools are necessarily for code generation. You may also want a specific version of, say, a linter.
An IDE/editor could use tool dependencies to figure out if this is a tool dependency or a regular program and run it through vgo if need be. From the command line, though, it would be up to the user to know whether to run stringer vs go run-dev-tool stringer so there would still be potential skew with the version documented. I don't think there's much to do about that, though.
A tool module could have multiple commands. Look up would be slow if these weren't included somewhere in the metadata.
A tool module could have multiple dependencies, some shared with the module under development. Unlike with other dependencies, you probably don't want those to be solved together: using foobar shouldn't affect your version baz or the version of baz used by other tools you depend on. They should all be siloed.
This is all starting to sound mostly unrelated to vgo but perhaps it's something that can be built on top of vgo.
Going back on what I said about buckets, let's say there was as separate tool godev and a separate file dev.mod.
godev install foo updates dev.mod recording the version and the exposed commands. Instead of installing the command globally it installs the binaries outside of $PATH but somewhere it can easily grab the correct version. It updates dev.mod with the vgo style import and version path but also records the names of the commands. For the most part it's a wrapper around go get that does some extra work.
godev run foo invokes the appropriate foo from dev.mod.
Is there anything that would need to change in vgo to allow something like this?
A downside of not having it integrated in vgo itself would be that go:generate directives would need to explicitly prefix godev run.
|
Running a tool at a specific version depending on the module would have to go through vgo. Running stringer lets $SHELL pick which stringer to run, so it would need to be something like Something like that could be stuffed into
An IDE/editor could use tool dependencies to figure out if this is a tool dependency or a regular program and run it through vgo if need be. From the command line, though, it would be up to the user to know whether to run A tool module could have multiple commands. Look up would be slow if these weren't included somewhere in the metadata. A tool module could have multiple dependencies, some shared with the module under development. Unlike with other dependencies, you probably don't want those to be solved together: using foobar shouldn't affect your version baz or the version of baz used by other tools you depend on. They should all be siloed. This is all starting to sound mostly unrelated to vgo but perhaps it's something that can be built on top of vgo. Going back on what I said about buckets, let's say there was as separate tool
Is there anything that would need to change in vgo to allow something like this? A downside of not having it integrated in vgo itself would be that |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rsc
Jul 6, 2018
Contributor
What is the decision to be made here?
I think the tools.go file is in fact the best practice for tool dependencies, certainly for Go 1.11.
I like it because it does not introduce new mechanisms.
It simply reuses existing ones.
|
What is the decision to be made here? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Jul 6, 2018
Member
I think the tools.go file is in fact the best practice for tool dependencies, certainly for Go 1.11.
Indeed, we don't need to "solve" this now.
What is the decision to be made here?
For Go 1.11
- is the
vgo mod -syncbehaviour observed in the description a bug or not? It feels like it is.
For later:
- is there a better way to ensure the fake
tools.gois valid? Because it will never compile. We could do something withvgo listand a bit of shell, but it feels like we're side stepping the issue - is there a way to reduce the "boilerplate" setup described in the issue, to make the experience for module maintainers nicer?
Indeed, we don't need to "solve" this now.
For Go 1.11
For later:
|
rsc
modified the milestones:
vgo,
Go1.11
Jul 12, 2018
rsc
added
the
modules
label
Jul 12, 2018
rsc
changed the title from
x/vgo: clarify best practice for tool dependencies
to
cmd/go: clarify best practice for tool dependencies
Jul 12, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Aug 14, 2018
Member
Adding another comment to this thread to give the equivalent, and current, go commands
This is a go-based rewrite of #25922 (comment)
cd $(mktemp -d)
mkdir hello
cd hello
go mod init example.com/hello
# Either rely on GOBIN=GOPATH/bin or set it explicitly
export GOBIN=$PWD/bin
# add a dependency on golang.org/x/tools/cmd/stringer
# the build constraint ensures this file is ignored
cat <<EOD > tools.go
// +build tools
package tools
import (
_ "golang.org/x/tools/cmd/stringer"
)
EOD
go install golang.org/x/tools/cmd/stringer
cat go.modresults in:
module example.com/hello
require golang.org/x/tools v0.0.0-20180813205110-a434f64ace81 // indirect
|
Adding another comment to this thread to give the equivalent, and current, This is a cd $(mktemp -d)
mkdir hello
cd hello
go mod init example.com/hello
# Either rely on GOBIN=GOPATH/bin or set it explicitly
export GOBIN=$PWD/bin
# add a dependency on golang.org/x/tools/cmd/stringer
# the build constraint ensures this file is ignored
cat <<EOD > tools.go
// +build tools
package tools
import (
_ "golang.org/x/tools/cmd/stringer"
)
EOD
go install golang.org/x/tools/cmd/stringer
cat go.modresults in:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
joeshaw
Aug 15, 2018
Contributor
Similarly, converting an existing project from dep to the modules system and running go mod vendor results in the stringer tool and its immediate dependencies being removed from the vendor directory.
Sorry, this is actually working. My tools.go file was missing the package directive at the top of the file and because it's never built the compiler never got a chance to complain. This, ironically, is one of @myitcv's concerns in his initial comment.
|
Sorry, this is actually working. My |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rsc
Aug 17, 2018
Contributor
Best practice remains creating the tools.go file.
It's true that go mod init does not auto-create a tools.go from dep's config,
but I think doing so is getting a bit beyond scope.
|
Best practice remains creating the tools.go file. |
rsc
modified the milestones:
Go1.11,
Go1.12
Aug 17, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
AlekSi
Aug 21, 2018
Contributor
Probably it's worth to highlight why tools.go should contain some build tag like tools (but not ignore): to avoid side-effects of init() functions in tool dependencies.
|
Probably it's worth to highlight why |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
thepudds
Aug 24, 2018
A related comment: as far as I am aware, creating a tools.go as described above in #25922 (comment) seems to work with go mod vendor for Go-based tool dependencies.
In addition, there is also this separate comment in #26244 (comment) :
vgo vendor (now vgo mod -vendor or go mod -vendor) does not copy entire repositories. By design, it copies only the directories needed to satisfy imports in builds of the top-level module. If your module is not importing github.com/antlr/grammars-v4 then that directory will not be copied. And if there are directories with only non-Go files, there is no way to copy those directories into vendor. That's not what vendor is for. Vendor is only about preserving the code needed to build the main module
...which on the one hand seems to say go mod vendor is not intended to cover all use cases around non-Go pieces of a repository...
...but that comment also seems to suggest that if there is a piece of Go code that will be vendored, the operation seems to function at the granularity of directories (e.g., it "copies only the directories needed to satisfy imports in builds of the top-level module").
In practice, this currently seems to mean that non-Go tool dependencies (such as a shell script) are copied into the vendor directory by go mod vendor if they are in the same directory as a vendored piece of Go code. So a tools.go-based approach could in theory be extended to pulling in non-Go based tool dependencies (which would happen naturally if the non-Go tools are intermixed at the directory level with Go code, or perhaps even by adding something like a vendorme.go to a particular directory if needed).
However, as far as I can see, the go mod vendor documentation currently does not say it operates at the directory level, so perhaps that is behavior that should not be relied upon, or perhaps this would otherwise be considered an undesirable approach.
thepudds
commented
Aug 24, 2018
|
A related comment: as far as I am aware, creating a In addition, there is also this separate comment in #26244 (comment) :
...which on the one hand seems to say ...but that comment also seems to suggest that if there is a piece of Go code that will be vendored, the operation seems to function at the granularity of directories (e.g., it "copies only the directories needed to satisfy imports in builds of the top-level module"). In practice, this currently seems to mean that non-Go tool dependencies (such as a shell script) are copied into the vendor directory by However, as far as I can see, the |
mark-rushakoff
referenced this issue
Aug 27, 2018
Open
Develop a new method to handle binary dependencies for generated files #704
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
caarlos0
Aug 29, 2018
I've tried the solution proposed by @myitcv, it more or less works, seems like stringer can't solve the dependencies in the package that has the //go:generate:
~/Code/goreleaser/goreleaser vgo*
λ go list -f "{{.Target}}" golang.org/x/tools/cmd/stringer
/Users/carlos/Code/goreleaser/goreleaser/bin/stringer
~/Code/goreleaser/goreleaser vgo*
λ go generate ./...
stringer: checking package: artifact.go:11:2: could not import github.com/apex/log (type-checking package "github.com/apex/log" failed (/Users/carlos/go/pkg/mod/github.com/apex/log@v0.0.0-20180702155952-941dea75d3eb/stack.go:3:8: could not import github.com/pkg/errors (cannot find package "github.com/pkg/errors" in any of:
/usr/local/Cellar/go/1.11/libexec/src/github.com/pkg/errors (from $GOROOT)
/Users/carlos/go/src/github.com/pkg/errors (from $GOPATH))))
internal/artifact/artifact.go:15: running "stringer": exit status 1
Not sure if it is a bug in stringer or somewhere else, happy to provide more info if needed though.
Not sure if useful, but:
- PR migrating to vgo: goreleaser/goreleaser#779
- Specific type that with the
go generate:
https://github.com/goreleaser/goreleaser/blob/master/internal/artifact/artifact.go#L15
caarlos0
commented
Aug 29, 2018
|
I've tried the solution proposed by @myitcv, it more or less works, seems like
Not sure if it is a bug in stringer or somewhere else, happy to provide more info if needed though. Not sure if useful, but:
|
mark-rushakoff
referenced this issue
Aug 29, 2018
Merged
feat: switch to using go modules for building #735
jonstaryuk
referenced this issue
Aug 30, 2018
Closed
pre-modules `go get` replacement needed, as it won't work outside of existsing module with modules enabled #27380
bcmills
referenced this issue
Sep 6, 2018
Open
cmd/go: allow installing programs to GOBIN when outside a module in module mode #24250
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
myitcv
Sep 7, 2018
Member
@caarlos0 - this is because stringer requires code to type-check in order to work. And in your situation it can't type check because it can't resolve an import. stringer can't resolve an import because it doesn't "know" about modules.
stringer is being fixed to work with modules; this work is being tracked under the umbrella of #24661
|
@caarlos0 - this is because
|
komuw
referenced this issue
Sep 12, 2018
Open
cmd/go: go get should not add a dependency to go.mod #27643
myitcv
referenced this issue
Sep 13, 2018
Open
cmd/go: support module-local install/run of tool dependencies #27653
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rogpeppe
Sep 13, 2018
Contributor
I'm fairly sure I'm not in favour of mixing tool dependencies in with all the other project dependencies. I think it's good to have reproducible tool behaviour ("I used vX.Y.Z of tool T" should mean something), and attaching tools to arbitrary projects' go.mod files will mean that the tool binaries will be built with arbitrary (although notionally compatible) dependencies. In my view, when we use a tool to build parts of a project, we don't necessarily wish that tool to be part of the project, merely that it's a way of creating parts of that project.
Part of the promise of MVS is that of reliable and predictable results. That predictability applies to everything below a given module root, but it doesn't necessarily apply when a module is included inside a module root.
If there's anywhere that we want to be predictable, it's the building of binaries, so when building external tool binaries, I think we should respect exclude and replace clauses in their top level module, and use exactly the versions that are implied by that top level module, not the top level module that we're building the tool for.
If we do this, it means that when someone raises an issue about the behaviour of a tool, they can say "this went wrong with v3.1.4" and the tool maintainer can know exactly what binary they're using.
So I propose that we have another file, say go.tools, that lists required Go-implemented tool packages and their versions.
For example:
golang.org/x/tools/cmd/stringer v1.0.3
gopkg.in/httprequest.v1/cmd/httprequest-generate-client v1.2.0
I don't think it should be in go.mod itself, because these tools are explicitly not governed by the dependencies in go.mod.
One problem with this is that it doesn't work well when the tool doesn't have a go.mod file. In that case, we could fall back to using the local go.mod file ( the tools.go approach above could work). If a go.tools entry doesn't have a go.mod file and it's not in the dependencies, then the build of that tool would fail. This is only necessary while there are Go projects that don't contain a go.mod file.
Another question is what to do about dependency updates? Currently the go tool manages dependency updates very nicely. One possible idea is that we could treat go get of a main package as an explicit request to add the command to the go.tools file.
Then go get golang.org/x/tools/cmd/stringer@latest would update the version in go.tools to the latest version. It could also make the binary available for use by go run too.
Thoughts?
|
I'm fairly sure I'm not in favour of mixing tool dependencies in with all the other project dependencies. I think it's good to have reproducible tool behaviour ("I used vX.Y.Z of tool T" should mean something), and attaching tools to arbitrary projects' go.mod files will mean that the tool binaries will be built with arbitrary (although notionally compatible) dependencies. In my view, when we use a tool to build parts of a project, we don't necessarily wish that tool to be part of the project, merely that it's a way of creating parts of that project. Part of the promise of MVS is that of reliable and predictable results. That predictability applies to everything below a given module root, but it doesn't necessarily apply when a module is included inside a module root. If there's anywhere that we want to be predictable, it's the building of binaries, so when building external tool binaries, I think we should respect exclude and replace clauses in their top level module, and use exactly the versions that are implied by that top level module, not the top level module that we're building the tool for. If we do this, it means that when someone raises an issue about the behaviour of a tool, they can say "this went wrong with v3.1.4" and the tool maintainer can know exactly what binary they're using. So I propose that we have another file, say For example:
I don't think it should be in One problem with this is that it doesn't work well when the tool doesn't have a Another question is what to do about dependency updates? Currently the go tool manages dependency updates very nicely. One possible idea is that we could treat Then Thoughts? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bcmills
Sep 13, 2018
Member
when building external tool binaries, I think we should respect exclude and replace clauses in their top level module, and use exactly the versions that are implied by that top level module, not the top level module that we're building the tool for.
I can see the value in that, but I think there is also substantial value in being able to, say, fix a bug in the dependencies of a tool and apply that fix immediately (e.g., using a local replace directive).
If a tool vendor wants builds to be perfectly reproducible, they need the user to replicate their configuration exactly: not just the same dependencies, but also the same version of the compiler, same experiment flags, same gcc, etc. I don't think that merely pinning the go.mod will really solve that: it's a distribution problem, not (just) a reproducibility problem.
At any rate, it's easy enough for users to include the actual configuration when reporting problems by attaching the output of go env and goversion -m.
I can see the value in that, but I think there is also substantial value in being able to, say, fix a bug in the dependencies of a tool and apply that fix immediately (e.g., using a local If a tool vendor wants builds to be perfectly reproducible, they need the user to replicate their configuration exactly: not just the same dependencies, but also the same version of the compiler, same experiment flags, same At any rate, it's easy enough for users to include the actual configuration when reporting problems by attaching the output of |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rogpeppe
Sep 13, 2018
Contributor
I can see the value in that, but I think there is also substantial value in being able to, say, fix a bug in the dependencies of a tool and apply that fix immediately (e.g., using a local replace directive).
I can see the value in that too. I think there's value in allowing both approaches. For example, the go.tools file could allow us to specify that a given tool is governed by our local go.mod file.
I'm also concerned about tool dependencies leaking through into our production code dependencies. We already have that kind of bleed-through from external test dependencies, but the tools are even less part of the code, and some can potentially have large and tricky dependency graphs. Isolating those from the main project dependencies seems like it would be a good thing to me. I'd like to be able to use the latest version of a tool without necessarily pulling all my dependencies forward at the same rate.
As someone that struggles daily with near-intractable diamond-dependency issues, keeping some isolation between components that are potentially entirely independent seems like something that's good to aim for. There's no need for a separate binary to use all the same versions of all its dependencies as the main code, and there are significant advantages in allowing them to be different.
I can see the value in that too. I think there's value in allowing both approaches. For example, the I'm also concerned about tool dependencies leaking through into our production code dependencies. We already have that kind of bleed-through from external test dependencies, but the tools are even less part of the code, and some can potentially have large and tricky dependency graphs. Isolating those from the main project dependencies seems like it would be a good thing to me. I'd like to be able to use the latest version of a tool without necessarily pulling all my dependencies forward at the same rate. As someone that struggles daily with near-intractable diamond-dependency issues, keeping some isolation between components that are potentially entirely independent seems like something that's good to aim for. There's no need for a separate binary to use all the same versions of all its dependencies as the main code, and there are significant advantages in allowing them to be different. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bcmills
Sep 13, 2018
Member
I'm also concerned about tool dependencies leaking through into our production code dependencies.
That's fair, but that seems like a better role for go list -deps than the go.mod file itself: the go.mod file is pretty much always an approximation (due to the granularity of modules being larger than packages).
I'd like to be able to use the latest version of a tool without necessarily pulling all my dependencies forward at the same rate.
IMO that's the more interesting angle, but there is also an empirical question buried in there: how much harm does/will it cause to have tools pull your other dependencies forward, compared to the rate at which you would normally upgrade those dependencies anyway?
That's fair, but that seems like a better role for
IMO that's the more interesting angle, but there is also an empirical question buried in there: how much harm does/will it cause to have tools pull your other dependencies forward, compared to the rate at which you would normally upgrade those dependencies anyway? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rogpeppe
Sep 13, 2018
Contributor
IMO that's the more interesting angle, but there is also an empirical question buried in there: how much harm does/will it cause to have tools pull your other dependencies forward, compared to the rate at which you would normally upgrade those dependencies anyway?
FWIW it applies the other way too: I'd like to be able to continue to use an old and stable version of a tool while I roll dependencies forward on my project willy-nilly.
FWIW it applies the other way too: I'd like to be able to continue to use an old and stable version of a tool while I roll dependencies forward on my project willy-nilly. |
myitcv commentedJun 16, 2018
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version)?Does this issue reproduce with the latest release?
Yes; and latest
vgocommit (per above)What operating system and processor architecture are you using (
go env)?What did you do?
Off the back of #25624 (comment), I'd like to confirm that the following represents the "best practice" advice when adding and installing tool dependencies:
The
go.modand.Targetforstringerlook fine:The issue however is that running
vgo mod -syncthen removes our module requirement ongolang.org/x/tools- I suspect this is a bug:If we assume this is a bug and ignore it for now, I also wonder whether we can improve this workflow for adding tool dependencies somehow. The following steps feel a bit "boilerplate" and unnecessary:
tools.gofilemainpackage, so I'm not sure we can ever safely verifytools.gois "good"?)GOBINto an appropriate locationvgo install toolPATHincludesGOBINI wonder whether we could in fact obviate all of this by having something like:
Thoughts?
vgo run toolis possible as a result of #22726, but because of #25416 it effectively requires a link step each time.What did you expect to see?
With respect to what I think is a bug with
vgo mod -syncgo.modunchanged by thevgo mod -syncWhat did you see instead?
The
golang.org/x/toolsrequirement removed./cc @rsc @bcmills