imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
package imports
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-08-02 11:04:42 -06:00
|
|
|
"context"
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
"encoding/json"
|
2019-08-02 12:05:22 -06:00
|
|
|
"fmt"
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/tools/internal/gopathwalk"
|
|
|
|
"golang.org/x/tools/internal/module"
|
|
|
|
)
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
// ModuleResolver implements resolver for modules using the go command as little
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
// as feasible.
|
2019-07-03 13:23:05 -06:00
|
|
|
type ModuleResolver struct {
|
2019-08-20 13:39:18 -06:00
|
|
|
env *ProcessEnv
|
|
|
|
moduleCacheDir string
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
Initialized bool
|
|
|
|
Main *ModuleJSON
|
|
|
|
ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
|
|
|
|
ModsByDir []*ModuleJSON // ...or Dir.
|
2019-07-16 15:37:05 -06:00
|
|
|
|
2019-08-02 12:05:22 -06:00
|
|
|
// moduleCacheInfo stores information about the module cache.
|
|
|
|
moduleCacheInfo *moduleCacheInfo
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
type ModuleJSON struct {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
Path string // module path
|
|
|
|
Version string // module version
|
|
|
|
Versions []string // available module versions (with -versions)
|
2019-07-03 13:23:05 -06:00
|
|
|
Replace *ModuleJSON // replaced by this module
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
Time *time.Time // time version was created
|
2019-07-03 13:23:05 -06:00
|
|
|
Update *ModuleJSON // available update, if any (with -u)
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
Main bool // is this the main module?
|
|
|
|
Indirect bool // is this module only an indirect dependency of main module?
|
|
|
|
Dir string // directory holding files for this module, if any
|
|
|
|
GoMod string // path to go.mod file for this module, if any
|
2019-07-03 13:23:05 -06:00
|
|
|
Error *ModuleErrorJSON // error loading module
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
type ModuleErrorJSON struct {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
Err string // the error itself
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
func (r *ModuleResolver) init() error {
|
|
|
|
if r.Initialized {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
stdout, err := r.env.invokeGo("list", "-m", "-json", "...")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for dec := json.NewDecoder(stdout); dec.More(); {
|
2019-07-03 13:23:05 -06:00
|
|
|
mod := &ModuleJSON{}
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if err := dec.Decode(mod); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if mod.Dir == "" {
|
2019-05-06 13:37:46 -06:00
|
|
|
if r.env.Debug {
|
2019-06-28 14:21:07 -06:00
|
|
|
r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
// Can't do anything with a module that's not downloaded.
|
|
|
|
continue
|
|
|
|
}
|
2019-07-03 13:23:05 -06:00
|
|
|
r.ModsByModPath = append(r.ModsByModPath, mod)
|
|
|
|
r.ModsByDir = append(r.ModsByDir, mod)
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if mod.Main {
|
2019-07-03 13:23:05 -06:00
|
|
|
r.Main = mod
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
sort.Slice(r.ModsByModPath, func(i, j int) bool {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
count := func(x int) int {
|
2019-07-03 13:23:05 -06:00
|
|
|
return strings.Count(r.ModsByModPath[x].Path, "/")
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
return count(j) < count(i) // descending order
|
|
|
|
})
|
2019-07-03 13:23:05 -06:00
|
|
|
sort.Slice(r.ModsByDir, func(i, j int) bool {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
count := func(x int) int {
|
2019-07-03 13:23:05 -06:00
|
|
|
return strings.Count(r.ModsByDir[x].Dir, "/")
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
return count(j) < count(i) // descending order
|
|
|
|
})
|
|
|
|
|
2019-08-02 12:05:22 -06:00
|
|
|
if r.moduleCacheInfo == nil {
|
|
|
|
r.moduleCacheInfo = &moduleCacheInfo{
|
|
|
|
modCacheDirInfo: make(map[string]*directoryPackageInfo),
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 15:37:05 -06:00
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
r.Initialized = true
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// findPackage returns the module and directory that contains the package at
|
|
|
|
// the given import path, or returns nil, "" if no module is in scope.
|
2019-07-03 13:23:05 -06:00
|
|
|
func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
|
2019-09-10 14:48:19 -06:00
|
|
|
// This can't find packages in the stdlib, but that's harmless for all
|
|
|
|
// the existing code paths.
|
2019-07-03 13:23:05 -06:00
|
|
|
for _, m := range r.ModsByModPath {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if !strings.HasPrefix(importPath, m.Path) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pathInModule := importPath[len(m.Path):]
|
|
|
|
pkgDir := filepath.Join(m.Dir, pathInModule)
|
2019-08-20 13:39:18 -06:00
|
|
|
if r.dirIsNestedModule(pkgDir, m) {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-07-19 13:21:23 -06:00
|
|
|
if info, ok := r.moduleCacheInfo.Load(pkgDir); ok {
|
|
|
|
if packageScanned, err := info.reachedStatus(directoryScanned); packageScanned {
|
|
|
|
if err != nil {
|
|
|
|
// There was some error with scanning this directory.
|
|
|
|
// It does not contain a valid package.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return m, pkgDir
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
pkgFiles, err := ioutil.ReadDir(pkgDir)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// A module only contains a package if it has buildable go
|
|
|
|
// files in that directory. If not, it could be provided by an
|
|
|
|
// outer module. See #29736.
|
|
|
|
for _, fi := range pkgFiles {
|
|
|
|
if ok, _ := r.env.buildContext().MatchFile(pkgDir, fi.Name()); ok {
|
|
|
|
return m, pkgDir
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// findModuleByDir returns the module that contains dir, or nil if no such
|
|
|
|
// module is in scope.
|
2019-07-03 13:23:05 -06:00
|
|
|
func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
// This is quite tricky and may not be correct. dir could be:
|
|
|
|
// - a package in the main module.
|
|
|
|
// - a replace target underneath the main module's directory.
|
|
|
|
// - a nested module in the above.
|
|
|
|
// - a replace target somewhere totally random.
|
|
|
|
// - a nested module in the above.
|
|
|
|
// - in the mod cache.
|
|
|
|
// - in /vendor/ in -mod=vendor mode.
|
|
|
|
// - nested module? Dunno.
|
|
|
|
// Rumor has it that replace targets cannot contain other replace targets.
|
2019-07-03 13:23:05 -06:00
|
|
|
for _, m := range r.ModsByDir {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if !strings.HasPrefix(dir, m.Dir) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-20 13:39:18 -06:00
|
|
|
if r.dirIsNestedModule(dir, m) {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
|
|
|
// mod, not actually in mod.
|
2019-08-20 13:39:18 -06:00
|
|
|
func (r *ModuleResolver) dirIsNestedModule(dir string, mod *ModuleJSON) bool {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if !strings.HasPrefix(dir, mod.Dir) {
|
|
|
|
return false
|
|
|
|
}
|
2019-08-20 13:39:18 -06:00
|
|
|
if r.dirInModuleCache(dir) {
|
|
|
|
// Nested modules in the module cache are pruned,
|
|
|
|
// so it cannot be a nested module.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
mf := r.findModFile(dir)
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if mf == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return filepath.Dir(mf) != mod.Dir
|
|
|
|
}
|
|
|
|
|
2019-08-20 13:39:18 -06:00
|
|
|
func (r *ModuleResolver) findModFile(dir string) string {
|
|
|
|
if r.dirInModuleCache(dir) {
|
|
|
|
matches := modCacheRegexp.FindStringSubmatch(dir)
|
|
|
|
index := strings.Index(dir, matches[1]+"@"+matches[2])
|
|
|
|
return filepath.Join(dir[:index], matches[1]+"@"+matches[2], "go.mod")
|
|
|
|
}
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
for {
|
|
|
|
f := filepath.Join(dir, "go.mod")
|
|
|
|
info, err := os.Stat(f)
|
|
|
|
if err == nil && !info.IsDir() {
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
d := filepath.Dir(dir)
|
|
|
|
if len(d) >= len(dir) {
|
|
|
|
return "" // reached top of file system, no go.mod
|
|
|
|
}
|
|
|
|
dir = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 13:39:18 -06:00
|
|
|
func (r *ModuleResolver) dirInModuleCache(dir string) bool {
|
|
|
|
if r.moduleCacheDir == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return strings.HasPrefix(dir, r.moduleCacheDir)
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if err := r.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
names := map[string]string{}
|
|
|
|
for _, path := range importPaths {
|
|
|
|
_, packageDir := r.findPackage(path)
|
|
|
|
if packageDir == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name, err := packageDirToName(packageDir)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
names[path] = name
|
|
|
|
}
|
|
|
|
return names, nil
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:05 -06:00
|
|
|
func (r *ModuleResolver) scan(_ references) ([]*pkg, error) {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if err := r.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk GOROOT, GOPATH/pkg/mod, and the main module.
|
|
|
|
roots := []gopathwalk.Root{
|
|
|
|
{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
|
2019-03-15 13:21:23 -06:00
|
|
|
}
|
2019-07-03 13:23:05 -06:00
|
|
|
if r.Main != nil {
|
|
|
|
roots = append(roots, gopathwalk.Root{r.Main.Dir, gopathwalk.RootCurrentModule})
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
2019-08-20 13:39:18 -06:00
|
|
|
if r.moduleCacheDir == "" {
|
|
|
|
r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod")
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
2019-08-20 13:39:18 -06:00
|
|
|
roots = append(roots, gopathwalk.Root{r.moduleCacheDir, gopathwalk.RootModuleCache})
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
|
|
|
|
// Walk replace targets, just in case they're not in any of the above.
|
2019-07-03 13:23:05 -06:00
|
|
|
for _, mod := range r.ModsByModPath {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if mod.Replace != nil {
|
|
|
|
roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var result []*pkg
|
|
|
|
dupCheck := make(map[string]bool)
|
|
|
|
var mu sync.Mutex
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
// Packages in the module cache are immutable. If we have
|
|
|
|
// already seen this package on a previous scan of the module
|
|
|
|
// cache, return that result.
|
|
|
|
skip := func(root gopathwalk.Root, dir string) bool {
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
2019-07-19 10:41:29 -06:00
|
|
|
// If we have already processed this directory on this walk, skip it.
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
if _, dup := dupCheck[dir]; dup {
|
2019-07-19 10:41:29 -06:00
|
|
|
return true
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
// If we have saved this directory information, skip it.
|
|
|
|
info, ok := r.moduleCacheInfo.Load(dir)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// This directory can be skipped as long as we have already scanned it.
|
|
|
|
// Packages with errors will continue to have errors, so there is no need
|
|
|
|
// to rescan them.
|
|
|
|
packageScanned, _ := info.reachedStatus(directoryScanned)
|
|
|
|
return packageScanned
|
|
|
|
}
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
add := func(root gopathwalk.Root, dir string) {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
if _, dup := dupCheck[dir]; dup {
|
2019-07-16 15:37:05 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
info, err := r.scanDirForPackage(root, dir)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if root.Type == gopathwalk.RootModuleCache {
|
|
|
|
// Save this package information in the cache and return.
|
|
|
|
// Packages from the module cache are added after Walk.
|
|
|
|
r.moduleCacheInfo.Store(dir, info)
|
|
|
|
return
|
2019-08-02 12:05:22 -06:00
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
// Skip this package if there was an error loading package info.
|
|
|
|
if info.err != nil {
|
2019-08-02 12:05:22 -06:00
|
|
|
return
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
|
|
|
|
2019-08-02 12:05:22 -06:00
|
|
|
// The rest of this function canonicalizes the packages using the results
|
|
|
|
// of initializing the resolver from 'go list -m'.
|
2019-07-19 10:41:29 -06:00
|
|
|
res, err := r.canonicalize(info.nonCanonicalImportPath, info.dir, info.needsReplace)
|
|
|
|
if err != nil {
|
2019-08-02 12:05:22 -06:00
|
|
|
return
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
}
|
2019-08-02 12:05:22 -06:00
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
result = append(result, res)
|
|
|
|
}
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
gopathwalk.WalkSkip(roots, add, skip, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true})
|
|
|
|
|
|
|
|
// Add the packages from the modules in the mod cache that were skipped.
|
|
|
|
for _, dir := range r.moduleCacheInfo.Keys() {
|
|
|
|
info, ok := r.moduleCacheInfo.Load(dir)
|
|
|
|
if !ok {
|
|
|
|
continue
|
2019-07-16 15:37:05 -06:00
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
// Skip this directory if we were not able to get the package information successfully.
|
|
|
|
if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
|
|
|
|
continue
|
2019-07-16 15:37:05 -06:00
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
res, err := r.canonicalize(info.nonCanonicalImportPath, info.dir, info.needsReplace)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-07-16 15:37:05 -06:00
|
|
|
result = append(result, res)
|
2019-07-19 10:41:29 -06:00
|
|
|
}
|
|
|
|
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2019-07-19 10:41:29 -06:00
|
|
|
// canonicalize gets the result of canonicalizing the packages using the results
|
|
|
|
// of initializing the resolver from 'go list -m'.
|
|
|
|
func (r *ModuleResolver) canonicalize(importPath, dir string, needsReplace bool) (res *pkg, err error) {
|
|
|
|
// Check if the directory is underneath a module that's in scope.
|
|
|
|
if mod := r.findModuleByDir(dir); mod != nil {
|
|
|
|
// It is. If dir is the target of a replace directive,
|
|
|
|
// our guessed import path is wrong. Use the real one.
|
|
|
|
if mod.Dir == dir {
|
|
|
|
importPath = mod.Path
|
|
|
|
} else {
|
|
|
|
dirInMod := dir[len(mod.Dir)+len("/"):]
|
|
|
|
importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod))
|
|
|
|
}
|
|
|
|
} else if needsReplace {
|
|
|
|
return nil, fmt.Errorf("needed this package to be in scope: %s", dir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We may have discovered a package that has a different version
|
|
|
|
// in scope already. Canonicalize to that one if possible.
|
|
|
|
if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" {
|
|
|
|
dir = canonicalDir
|
|
|
|
}
|
|
|
|
return &pkg{
|
|
|
|
importPathShort: VendorlessPath(importPath),
|
|
|
|
dir: dir,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-08-02 11:04:42 -06:00
|
|
|
func (r *ModuleResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
|
|
|
if err := r.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return loadExportsFromFiles(ctx, r.env, expectPackage, pkg.dir)
|
|
|
|
}
|
|
|
|
|
2019-08-02 12:05:22 -06:00
|
|
|
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) (directoryPackageInfo, error) {
|
|
|
|
subdir := ""
|
|
|
|
if dir != root.Path {
|
|
|
|
subdir = dir[len(root.Path)+len("/"):]
|
|
|
|
}
|
|
|
|
importPath := filepath.ToSlash(subdir)
|
|
|
|
if strings.HasPrefix(importPath, "vendor/") {
|
|
|
|
// Ignore vendor dirs. If -mod=vendor is on, then things
|
|
|
|
// should mostly just work, but when it's not vendor/
|
|
|
|
// is a mess. There's no easy way to tell if it's on.
|
|
|
|
// We can still find things in the mod cache and
|
|
|
|
// map them into /vendor when -mod=vendor is on.
|
|
|
|
return directoryPackageInfo{}, fmt.Errorf("vendor directory")
|
|
|
|
}
|
|
|
|
switch root.Type {
|
|
|
|
case gopathwalk.RootCurrentModule:
|
|
|
|
importPath = path.Join(r.Main.Path, filepath.ToSlash(subdir))
|
|
|
|
case gopathwalk.RootModuleCache:
|
|
|
|
matches := modCacheRegexp.FindStringSubmatch(subdir)
|
2019-09-04 12:24:10 -06:00
|
|
|
if len(matches) == 0 {
|
|
|
|
return directoryPackageInfo{
|
|
|
|
status: directoryScanned,
|
|
|
|
err: fmt.Errorf("invalid module cache path: %v", subdir),
|
|
|
|
}, nil
|
|
|
|
}
|
2019-08-02 12:05:22 -06:00
|
|
|
modPath, err := module.DecodePath(filepath.ToSlash(matches[1]))
|
|
|
|
if err != nil {
|
|
|
|
if r.env.Debug {
|
|
|
|
r.env.Logf("decoding module cache path %q: %v", subdir, err)
|
|
|
|
}
|
|
|
|
return directoryPackageInfo{
|
|
|
|
status: directoryScanned,
|
|
|
|
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
importPath = path.Join(modPath, filepath.ToSlash(matches[3]))
|
|
|
|
case gopathwalk.RootGOROOT:
|
|
|
|
importPath = subdir
|
|
|
|
}
|
|
|
|
|
2019-09-10 14:48:19 -06:00
|
|
|
result := directoryPackageInfo{
|
|
|
|
status: directoryScanned,
|
|
|
|
dir: dir,
|
|
|
|
nonCanonicalImportPath: importPath,
|
|
|
|
needsReplace: false,
|
|
|
|
}
|
|
|
|
if root.Type == gopathwalk.RootGOROOT {
|
|
|
|
// stdlib packages are always in scope, despite the confusing go.mod
|
|
|
|
return result, nil
|
|
|
|
}
|
2019-08-02 12:05:22 -06:00
|
|
|
// Check that this package is not obviously impossible to import.
|
2019-08-20 13:39:18 -06:00
|
|
|
modFile := r.findModFile(dir)
|
2019-08-02 12:05:22 -06:00
|
|
|
|
|
|
|
modBytes, err := ioutil.ReadFile(modFile)
|
|
|
|
if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) {
|
|
|
|
// The module's declared path does not match
|
|
|
|
// its expected path. It probably needs a
|
|
|
|
// replace directive we don't have.
|
2019-09-10 14:48:19 -06:00
|
|
|
result.needsReplace = true
|
2019-08-02 12:05:22 -06:00
|
|
|
}
|
|
|
|
|
2019-09-10 14:48:19 -06:00
|
|
|
return result, nil
|
2019-08-02 12:05:22 -06:00
|
|
|
}
|
|
|
|
|
imports: stop using go/packages for modules
go/packages needs to call `go list` multiple times, which causes
redundant work and slows down goimports. If we reimplement `go list` in
memory, we can reuse state, saving time. `go list` also does work we
don't really need, like adding stuff to go.mod, and skipping that saves
more time.
We start with `go list -m`, which does MVS and such. The remaining work
is mostly mapping import paths and directories through the in-scope
modules to make sure we're giving the right answers. Unfortunately this
is quite subtle, and I don't know where all the traps are. I did my
best.
cmd/go already has tests for `go list`, of course, and packagestest is
not well suited to tests of this complexity. So I ripped off the script
tests in cmd/go that seemed relevant and made sure that our logic
returns the right stuff in each case. I'm sure that there are more cases
to cover, but this hit all the stuff I knew about and quite a bit I
didn't.
Since we may want to use the go/packages code path in the future, e.g.
for Bazel, I left that in place. It won't be used unless the magic env
var is set.
Files in internal and imports/testdata/mod were copied verbatim from
cmd/go.
Change-Id: I1248d99c400c1a0c7ef180d4460b9b8a3db0246b
Reviewed-on: https://go-review.googlesource.com/c/158097
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-01-16 14:24:49 -07:00
|
|
|
// modCacheRegexp splits a path in a module cache into module, module version, and package.
|
|
|
|
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
|
|
|
|
|
|
|
|
var (
|
|
|
|
slashSlash = []byte("//")
|
|
|
|
moduleStr = []byte("module")
|
|
|
|
)
|
|
|
|
|
|
|
|
// modulePath returns the module path from the gomod file text.
|
|
|
|
// If it cannot find a module path, it returns an empty string.
|
|
|
|
// It is tolerant of unrelated problems in the go.mod file.
|
|
|
|
//
|
|
|
|
// Copied from cmd/go/internal/modfile.
|
|
|
|
func modulePath(mod []byte) string {
|
|
|
|
for len(mod) > 0 {
|
|
|
|
line := mod
|
|
|
|
mod = nil
|
|
|
|
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
|
|
|
line, mod = line[:i], line[i+1:]
|
|
|
|
}
|
|
|
|
if i := bytes.Index(line, slashSlash); i >= 0 {
|
|
|
|
line = line[:i]
|
|
|
|
}
|
|
|
|
line = bytes.TrimSpace(line)
|
|
|
|
if !bytes.HasPrefix(line, moduleStr) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
line = line[len(moduleStr):]
|
|
|
|
n := len(line)
|
|
|
|
line = bytes.TrimSpace(line)
|
|
|
|
if len(line) == n || len(line) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if line[0] == '"' || line[0] == '`' {
|
|
|
|
p, err := strconv.Unquote(string(line))
|
|
|
|
if err != nil {
|
|
|
|
return "" // malformed quoted string or multiline module path
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(line)
|
|
|
|
}
|
|
|
|
return "" // missing module path
|
|
|
|
}
|