On Tue, Jan 19, 2021 at 7:02 AM Jay Conrod <jaycon...@google.com> wrote: > > > Interesting - is the difference the absolute paths vs relative? > > It looks like the bug has to do with whether the directory is below the main module root directory or not. If it is, the go command takes a path that assumes it's part of the main module, which it's not. > > > I hoped maybe `-modfile` would do the same trick, but alas not: > > -modfile lets you change the effective content of go.mod but not the module root directory. Unfortunately it doesn't look like that can be used to work around the issue. > > > It seems that is because the "main" (top-level dir) go.mod has > > `replace` directives with relative paths, which kubernetes really > > does. > > You may need to copy those over to the tmp go.mod and adjust the paths. Sorry this has gotten pretty involved.
I don't think I follow what you mean. If you look at https://github.com/thockin/go-list-modules, I generated this file in /tmp: ``` module tmp go 1.15 require ( example.com/m v0.0.0 example.com/m/submod v0.0.0 example.com/other1 v0.0.0 example.com/other2 v0.0.0 ) replace ( example.com/m v0.0.0 => /tmp/gomodhack/2/go-list-modules example.com/m/submod v0.0.0 => /tmp/gomodhack/2/go-list-modules/submod example.com/other1 v0.0.0 => /tmp/gomodhack/2/go-list-modules/staging/src/example.com/other1 example.com/other2 v0.0.0 => /tmp/gomodhack/2/go-list-modules/staging/src/example.com/other2 ) ``` Some queries pass: ``` $ (cd /tmp/gomodhack/2/hack; go list /tmp/gomodhack/2/go-list-modules/submod/used) example.com/m/submod/used ``` and some fail: ``` $ (cd /tmp/gomodhack/2/hack; go list /tmp/gomodhack/2/go-list-modules/staging/src/example.com/other1/used) go: finding module for package example.com/m/staging/src/example.com/other1/used cannot find module providing package example.com/m/staging/src/example.com/other1/used: unrecognized import path "example.com/m/staging/src/example.com/other1/used": reading https://example.com/m/staging/src/example.com/other1/used?go-get=1: 404 Not Found ``` I can't see what's different between them. If I remove the `example.com/m` lines from the tmp go.mod, it works. > > Yeah, I noticed. When GO111MODULE=off, everything I am doing is much > > faster. I'm wary of depending on that forever, though. > > Module-aware mode is quite a bit more complicated than GOPATH mode, so to some degree it's not surprising it's slower... it's surprising that it's a LOT slower though. I expect there's some optimization work for us to do in the next development cycle. > > We would eventually like to deprecate GOPATH mode though, so it's a good idea not to depend on it in new tooling today. 'go list' should be fine to get package dependency info in either module mode or GOPATH mode. go/packages is useful if you need additional information on top of that (parsed syntax trees, type info). That's what I expected, hence trying to get ahead of it. The issue in https://github.com/golang/go/issues/43733 SOUNDS like it would alleviate the main problem I have? > > I want to run a slow codegen process only if the packages it depends > > on have ACTUALLY changed (mtime is a good enough proxy) and I don't > > know a priori which packages need codegen. I want to scan the file > > tree, find the files that need codegen, check their deps, and only > > then run the codegen. > > How much dependency info do you need? If the codegen is only within packages with files that have changed, 'go list' might be overkill (it always loads dependencies, even if they aren't printed). If you need dependencies or reverse dependencies, 'go list' or go/packages are probably the right tools. We collect 1-level of deps, which is (in practice) sufficient, but is a compromise. > On Fri, Jan 15, 2021 at 6:43 PM Tim Hockin <thoc...@google.com> wrote: >> >> On Fri, Jan 15, 2021 at 2:17 PM Jay Conrod <jaycon...@google.com> wrote: >> > >> > I was initially going to suggest adding the module subdirectories as requirements to the main go.mod, then replacing those with the subdirectories. >> > >> > module example.com/m >> > >> > go 1.15 >> > >> > require ( >> > example.com/other1 v0.0.0 >> > example.com/other2 v0.0.0 >> > example.com/m/submod v0.0.0 >> > ) >> > >> > replace ( >> > example.com/other1 => ./staging/src/example.com/other1 >> > example.com/other2 => ./staging/src/example.com/other2 >> > example.com/m/submod v0.0.0 => ./submod >> > ) >> > >> > >> > I think you might have tried this already. It gives the same "main module ... does not contain package" error. I believe that's a bug. I've opened #43733 to track it. >> >> Interesting. If that's a bug, then maybe I'll be able to do what I >> need once fixed. >> >> > In general, it should be possible to give 'go list' an absolute or relative path (starting with ./ or ../) to any directory containing a package which is part of any module in the build list. For example, some tools list directories in the module cache to find out what package a .go file belongs to. >> > >> > As a workaround, you could put a go.mod in an otherwise empty directory (in /tmp or something), then require the relevant modules from the repo and replace them with absolute paths. Then you can run 'go list' in that directory with absolute paths of package directories. >> >> Interesting - is the difference the absolute paths vs relative? >> >> I hoped maybe `-modfile` would do the same trick, but alas not: >> >> ``` >> $ (cd /tmp/gomodhack/; go list /tmp/go-list-modules/submod/used/) >> example.com/m/submod/used >> >> $ go list --modfile /tmp/gomodhack/go.mod /tmp/go-list-modules/submod/used/ >> main module (tmp) does not contain package tmp/submod/used >> ``` >> >> It also fails some cases: >> >> ``` >> (cd /tmp/gomodhack/; go list /tmp/go-list-modules/submod/used/) >> example.com/m/submod/used >> thockin@thockin-glaptop4 go-list-modules main /$ (cd /tmp/gomodhack/; >> go list /tmp/go-list-modules/staging/src/example.com/other1/used/) >> go: finding module for package example.com/m/staging/src/example.com/other1/used >> cannot find module providing package >> example.com/m/staging/src/example.com/other1/used: unrecognized import >> path "example.com/m/staging/src/example.com/other1/used": reading >> https://example.com/m/staging/src/example.com/other1/used?go-get=1: >> 404 Not Found >> ``` >> >> It seems that is because the "main" (top-level dir) go.mod has >> `replace` directives with relative paths, which kubernetes really >> does. >> >> > Incidentally, golang.org/x/tools/go/packages will call 'go list' under the hood in module mode. go/build might do the same, depending on how it's invoked. 'go list' may be the best thing to use if it gives the information you need. >> >> Yeah, I noticed. When GO111MODULE=off, everything I am doing is much >> faster. I'm wary of depending on that forever, though. >> >> Stepping back, I fear I am pushing the square peg into a round hole. >> Let me restate what I am trying to do. >> >> I want to run a slow codegen process only if the packages it depends >> on have ACTUALLY changed (mtime is a good enough proxy) and I don't >> know a priori which packages need codegen. I want to scan the file >> tree, find the files that need codegen, check their deps, and only >> then run the codegen. >> >> We do this today with `go list` and GO111MODULE=off, but I was advised >> at some point that x/tools/go/packages was the future-safe approach. >> >> If there's a better way, I am all ears. >> >> Tim >> GO111MODULE=off >> > On Fri, Jan 15, 2021 at 11:59 AM 'Tim Hockin' via golang-nuts < golang-nuts@googlegroups.com> wrote: >> >> >> >> Hi. This isn't exactly burning urgent, but it is a long-term issue >> >> for Kubernetes. If there's anything I can do to catalyze the >> >> discussion - tests to run, info to dig up, etc - please let me know. >> >> >> >> On Wed, Dec 23, 2020 at 10:48 AM Tim Hockin <thoc...@google.com> wrote: >> >> > >> >> > Hi Paul! >> >> > >> >> > On Wed, Dec 23, 2020 at 4:23 AM Paul Jolly <p...@myitcv.io> wrote: >> >> > > >> >> > > > I just can't figure out how to do this. Maybe it can't be done in `go >> >> > > > list` ? Or maybe we're just missing some detail of go modules.. >> >> > > >> >> > > go list operates in the context of a single module (in the mode you >> >> > > are interested in), so you cannot do this with a single command across >> >> > > multiple modules. >> >> > >> >> > This might be a real problem for us. For this post I am reducing it >> >> > to `go list`, but in actuality we have a small program that we wrote >> >> > which does what we need in terms of `go/build`. It works great when >> >> > `GO111MODULE=off` but is more than 100x slower normally. I thought it >> >> > was finally time to rewrite it in terms of `go/packages` and get rid >> >> > of GO111MODULE=off. That didn't pan out, hence this post. >> >> > >> >> > More inline and below >> >> > >> >> > > > First I do a `find` for any file that has a specific comment tag, >> >> > > > indicating that the package needs codegen. The results span several >> >> > > > of the in-repo submodules. >> >> > > >> >> > > Just to check, I'm assuming the results of this find command are being >> >> > > translated to a list of packages? Because the transitive dependencies >> >> > > of a list of packages within a module can be done via a single go list >> >> > > command. >> >> > >> >> > The trick is "within a module". I'll update >> >> > https://github.com/thockin/go-list-modules to reflect the process >> >> > more. I've added a >> >> > get_codegen_deps.sh that models the behavior. Note that I really want >> >> > files, not packages, so I can express the dep-graph. >> >> > >> >> > What do you mean by "translated to a list of packages" - which specific syntax? >> >> > >> >> > What I end up with is something like `go list ./path/to/dir1 >> >> > ./path/to/dir2 ./path/to/dir3`. Any of those dirs might be in >> >> > different modules. So `go list` tells me "main module ( example.com/m) >> >> > does not contain package example.com/m/path/to/dir1" and so on. >> >> > Setting `GO111MODULE=off` does work, but I fear the future of that. >> >> > >> >> > > > For each target package, I want to get the list of all deps and >> >> > > > extract the GoFiles. Then I can use that to determine if the codegen >> >> > > > needs to run. >> >> > > >> >> > > FWIW I wrote a tool to do just this: >> >> > > https://pkg.go.dev/myitcv.io@v0.0.0-20201125173645-a7167afc9e13/cmd/gogenerate >> >> > > which might work in your situation. >> >> > >> >> > I will take a look - it seems I will need to restructure a bunch of >> >> > tooling to prove it works for us or doesn't :) >> >> > >> >> > > > Where it breaks down is that I can't seem to `go list` all at once: >> >> > > > >> >> > > > ``` >> >> > > > # This works within the "root" module >> >> > > > $ go list -f '{{.GoFiles}}' ./subdir >> >> > > > [file.go] >> >> > > >> >> > > This will work. >> >> > > >> >> > > > # This does not work across modules >> >> > > > $ go list -f '{{.GoFiles}}' ./submod/used ./submod/unused >> >> > > > main module (example.com/m) does not contain package example.com/m/submod/used >> >> > > > main module (example.com/m) does not contain package example.com/m/submod/unused >> >> > > >> >> > > Per above, this will not work across module boundaries. >> >> > >> >> > It works with `GO111MODULE=off` which means that introducing modules >> >> > is a breaking change. Can I depend on GO111MODULE=off to work the >> >> > same way forever? >> >> > >> >> > > > # Nor does this work, even with module replacements >> >> > > > $ go list -f '{{.GoFiles}}' ./staging/src/ example.com/other1/used >> >> > > > ./staging/src/example.com/other1/unused >> >> > > > main module (example.com/m) does not contain package >> >> > > > example.com/m/staging/src/example.com/other1/used >> >> > > > main module (example.com/m) does not contain package >> >> > > > example.com/m/staging/src/example.com/other1/unused >> >> > > > ``` >> >> > > >> >> > > With replace directives in place this should work, but you won't be >> >> > > able to use the relative path to the modules (which is in fact >> >> > > interpreted as a directory): it will need to be the full >> >> > > module/package path. >> >> > >> >> > Given a "./path/to/pkg" - how do I convert that to a module/package >> >> > path? I can run `(cd $dir && go list -m)` but that is super slow. >> >> > Running JUST that for each directory that needs codegen in kubernetes >> >> > takes 20+ seconds. Is there a better way, short of writing my own >> >> > directory-climb and parsing go.mod? >> >> > >> >> > > > I can run `go list` multiple times, but that's INCREDIBLY slow - most >> >> > > > of these submodules have common deps that are large. This re-parses >> >> > > > everything over and over. It takes almost 60 seconds just to do `cd >> >> > > > $dir; go list` (on the real kubernetes repo). >> >> > > >> >> > > Do you have a repro of this taking 60 seconds? Because that really >> >> > > shouldn't be the case with a populated local module cache. >> >> > >> >> > github.com/kubernetes/kubernetes >> >> > >> >> > ``` >> >> > $ time \ >> >> > find . -type f -name \*.go \ >> >> > | xargs grep -l "^// *+k8s:" \ >> >> > | xargs -n 1 dirname \ >> >> > | sort \ >> >> > | uniq \ >> >> > | while read X; do \ >> >> > (cd $X; go list -f '{{.Deps}}'); \ >> >> > done \ >> >> > > /dev/null >> >> > >> >> > real 0m50.488s >> >> > user 0m46.686s >> >> > sys 0m18.416s >> >> > ``` >> >> > >> >> > Just running that inner `go list` with GO111MODULE=off cuts the run >> >> > time in half. >> >> > >> >> > Compare to: >> >> > >> >> > ``` >> >> > time \ >> >> > ( \ >> >> > export GO111MODULE=off; \ >> >> > find . -type f -name \*.go \ >> >> > | xargs grep -l "^// *+k8s:" \ >> >> > | xargs -n 1 dirname \ >> >> > | sort \ >> >> > | uniq \ >> >> > | xargs go list -e -f '{{.Deps}}' \ >> >> > ) \ >> >> > > /dev/null >> >> > >> >> > real 0m1.323s >> >> > user 0m1.174s >> >> > sys 0m0.567s >> >> > ``` >> >> > >> >> > The model repo doesn't show so significantly because it is small. >> >> > Kubernetes is not small. >> >> > >> >> > I'm happy to hear better approaches - I really don't like relying on >> >> > GO111MODULE=off forever - it seems like the sort of thing that will >> >> > eventually get removed. >> >> >> >> -- >> >> You received this message because you are subscribed to the Google Groups "golang-nuts" group. >> >> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. >> >> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAO_Rewa6rMW79iBHj2Jz6HfJ-tCFLFhNAhYiwDh%3DNy6M35Y91Q%40mail.gmail.com . -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAO_Rewa8sbKOUXbHThYp%2BidAv9XN2YA7LuGLZ1qFcTqxvFJtRg%40mail.gmail.com.