Skip to content

proposal: cmd/go: module semantics in $GOPATH/src #26377

Closed
@bcmills

Description

@bcmills

(Caveat: I'm not sure whether this is actually a good idea, and I'm really not sure whether it's feasible in time for the 1.11 experiment.)

In this golang-dev message, @rsc explains why GO111MODULE=auto doesn't work inside $GOPATH/src:

in auto mode we're trying not to break the existing meaning of working in the non-module-aware GOPATH/src/A: it's no good to redefine what the B half of it means.

I think there is a way to make modules work within $GOPATH/src without changing the existing meaning of $GOPATH/src.

When GO111MODULE=on or GO111MODULE=auto,

  1. When the user runs go get mod/pkg@version within $GOPATH/src, update the contents of $GOPATH/src to match the versions implied by that dependency.

    • Download (to $GOPATH/src) any modules needed to satisfy the (transitive) imports of mod/pkg, just like the old go get but taking versions into account.
      • If any of those packages have been modified, go get should fail (the same as it does today if you run go get -u pkg with modified dependencies).
    • If any other modules in the build list are present in $GOPATH/src, update their contents too — even if they are not needed to satisfy imports.
    • Leave the $GOPATH/src contents unchanged for any module that was removed from the build list entirely.
  2. If a build within a module in $GOPATH/src imports a package from a module that is not present in $GOPATH/src, copy that module into $GOPATH/src (as go get would do).

  3. Whenever any package within any module in $GOPATH/src is built, update its go.mod file to reflect the actual contents of $GOPATH/src. That way, any future go commands within that module will produce the same contents as the current build.

    ⚠ This is the difficult part of this proposal!

    • For each module involved in the build, compare the contents of that module's go.mod file with the contents of its (transitive) dependencies in $GOPATH/src, ignoring any modules that are not present (e.g., because they were not needed to satisfy transitive imports).
    • Keep the original require directives from that module's go.mod file. Add replace directives for any (transitive) dependencies that do not match the versions implied by that module's go.mod. If the source code stored in $GOPATH still matches a committed (and non-excluded) version, use that version as the replacement; otherwise, use the local filesystem path.
    • Since replace directives do not affect other modules, ignore changes to them when comparing the contents of dependencies, including via go.sum checksums.
  4. If a build within a module in $GOPATH/src imports a package that is present in $GOPATH/src but does not have an associated module, add require and replace directives (pointing into $GOPATH) to the go.mod files for all affected modules, as if there were a top-level go.mod for $GOPATH/src itself.

    module github.com/bcmills/module-with-unsatisfied-imports
    require GOPATH/src v0.0.0
    replace GOPATH/src => /home/bcmills/src
    

I believe that those steps maintain the essential properties of both $GOPATH and modules. Namely:

  1. The source files in $GOPATH/src are exactly the files used for builds within $GOPATH/src.
  2. All source files used for builds within $GOPATH/src are present in $GOPATH/src.
  3. The go.mod files within $GOPATH/src accurately describe the sources used for builds.
  4. The go.mod files within $GOPATH/src include requirements for all packages imported during builds.

(CC @FiloSottile @myitcv @natefinch)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions