Hello.
I’m getting rather confused by Go modules and cross-compilation. I think that I have fundamentally misunderstood some module-related concept in the way that I’ve structured my project.
Scenario: My project consists of some individual executables that share some common code. The project is (or should be) a coherent, self-contained whole: the executables don’t make much sense individually, and I want to work on / version the supporting code and executables together.
In GOPATH times I had something like:
$ find . -type f
./src/cmd/foo/main.go
./src/cmd/bar/main.go
./src/qux/qux.go
$ cat src/qux/qux.go
package qux
const Qux = `qux`
$ src/cmd/foo/main.go # bar/main.go is essentially the same
package main
import "qux"
func main() {
println(`foo: ` + qux.Qux)
}and the following works as expected, following my understanding that cross-compilation just means setting GOOS and/or GOARCH:
$ uname -sm
Darwin x86_64
$ GOPATH=$PWD go install ./...
$ GOOS=linux GOARCH=arm GOPATH=$PWD go install ./...
$ find bin -type f
bin/linux_arm/foo
bin/linux_arm/bar
bin/foo
bin/barI’ve tried to migrate this to modules. At a logical level, these binaries plus support code are one unit: if Go doesn’t require me to spread them across separate modules then I’d rather not.
First I removed the src directory, then added a go.mod:
$ find . -type f
./cmd/foo/main.go
./cmd/bar/main.go
./qux/qux.go
$ go mod init foo.com/foobarThis baffles me:
$ go build foo.com/foobar
can't load package: package foo.com/foobar: cannot find module providing package foo.com/foobar
$ head -1 go.mod
module foo.com/foobar
$ go list -m
foo.com/foobar # but this finds it perfectly well?
although this seems to work:
But: now I want to cross-compile:
$ GOOS=linux GOBIN=/some/path go install ./...
go install: cannot install cross-compiled binaries when GOBIN is set
go install: cannot install cross-compiled binaries when GOBIN is set(Why not? If I set GOBIN then I’m saying “please put the binaries over there”. I know that I don’t want them appearing on my Mac’s $PATH: that’s what I’m setting GOBIN.
I want to cross-compile some binaries and have them appear in some
suitable output directory, so that I can then put them into some disk
image or whatever. In any case, if this runs in CI then,
cross-compilation or not, I don’t want them appear on the CI box’s PATH at all.)
The Internet says that I should use go build instead of go install, and use -o to write the results somewhere:
$ GOOS=linux go build -o /some/path ./...
go build: cannot write multiple packages to non-directory /some/pathThis seems to work, but I’ve never had to create the output directory before, and it doesn't do the handy creation of per-OS/ARCH subdirectories for me:
So:
I don’t understand why go build foo.com/foobar fails to find the module if it doesn’t contain some top-level .go file, but go list -m finds it perfectly fine.
[How] can I have a module X that provides packages X/Y and X/Z, with no source files in the top-level package X? Or am I not supposed to do this under Go modules?
Go modules appear to break my assumption that “I just set GOOS and/or GOARCH
to cross-compile, and everything else just works”. The is (was?) one of
the biggest benefits of Go for me. I need to cross-compile for Linux
(ARM, AArch64 and AMD64) and Windows (AMD64) all the time.
Cross-compiled go install to some non-global output directory worked fine in GOPATH days: creating $GOPATH/bin/$GOOS-$GOARCH/*, which seemed perfectly sensible to me. I don’t understand why it’s broken under modules, particularly when I set GOBIN, which I take to mean “Put the binaries here please: I know what I’m doing; please don’t argue”. Or at least have some sort of -yes-really flag to stop the toolchain arguing.
At the moment I can see two solutions:
Split up my project into individual modules for the shared code (or even every package therein), and each executable. Since none of those components, in this context, makes sense individually, this seems like the tail wagging the dog.
Revert to using GOPATH and hope that it never gets removed. History teaches me that this is unwise.
but I can’t help thinking that I’m doing something fundamentally wrong. I’ve read the docs, help pages, Wiki et cetera, and found nothing that has helped me. Can someone please put me right?
Many thanks,
Geoff.
This baffles me:
$ go build foo.com/foobar can't load package: package foo.com/foobar: cannot find module providing package foo.com/foobar
$ go list -m foo.com/foobar # but this finds it perfectly well?$ head -1 go.mod module foo.com/foobar
[... larger rant on the tooling ...]
So:
I don’t understand why
go build foo.com/foobarfails to find the module if it doesn’t contain some top-level.gofile, butgo list -mfinds it perfectly fine.
[How] can I have a module
Xthat provides packagesX/YandX/Z, with no source files in the top-level packageX? Or am I not supposed to do this under Go modules?
Go modules appear to break my assumption that “I just set
GOOSand/orGOARCHto cross-compile, and everything else just works”. The is (was?) one of the biggest benefits of Go for me. I need to cross-compile for Linux (ARM, AArch64 and AMD64) and Windows (AMD64) all the time.
Cross-compiled
go installto some non-global output directory worked fine inGOPATHdays: creating$GOPATH/bin/$GOOS-$GOARCH/*, which seemed perfectly sensible to me. I don’t understand why it’s broken under modules, particularly when I setGOBIN, which I take to mean “Put the binaries here please: I know what I’m doing; please don’t argue”. Or at least have some sort of-yes-reallyflag to stop the toolchain arguing.
At the moment I can see two solutions:
Split up my project into individual modules for the shared code (or even every package therein), and each executable. Since none of those components, in this context, makes sense individually, this seems like the tail wagging the dog.
Revert to using
GOPATHand hope that it never gets removed. History teaches me that this is unwise.but I can’t help thinking that I’m doing something fundamentally wrong. I’ve read the docs, help pages, Wiki et cetera, and found nothing that has helped me. Can someone please put me right?
Hello.
> [...]
So:
I don’t understand why
go build foo.com/foobarfails to find the module if it doesn’t contain some top-level.gofile, butgo list -mfinds it perfectly fine.
[How] can I have a module
Xthat provides packagesX/YandX/Z, with no source files in the top-level packageX? Or am I not supposed to do this under Go modules?
Go modules appear to break my assumption that “I just set GOOS and/or GOARCH
to cross-compile, and everything else just works”. The is (was?) one of
the biggest benefits of Go for me. I need to cross-compile for Linux
(ARM, AArch64 and AMD64) and Windows (AMD64) all the time.