如何快速构建/安装交叉编译的嵌套包?

I have a repository with a group of nested go packages organized as follows:

$GOPATH/src/
  - mypackage/common/utils.go
  - mypackage/app1/main.go
  - mypackage/app2/main.go
  ...

It compiles to a handful of binaries. For releasing, I'm cross-compiling for a multitude of platforms/archs (I deploy repeatedly with a different GOOS and GOARCH). I'm trying to write the compilation results to a directory of my choice, but I'm fighting with the toolchain.

I can:

  1. Combine GOBIN and go install when compiling for my own architecture (i.e. not cross compiling):

    # build + output all binaries in /somedir/bin
    # this works great when compiling for my local architecture.
    GOBIN=/somedir/bin go install mypackage/app1 mypackage/app2 mypackage/app3
    

    But unfortunately, GOBIN conflicts with cross-compilation e.g:

    # Throws: "cannot install cross-compiled binaries when GOBIN is set"
    GOBIN=/somedir/bin GOARCH=amdm64 GOOS=darwin go install mypackage/app1 mypackage/app2
    
  2. Use go build with GOOS=X and GOARCH=Y for each subpackage

    # cross compile one of the binaries
    cd /output/darwin-386-bin/ && \
    GOOS=darwin GOARCH=386 go build mypackage/app1
    
    # but if given multiple binaries, there is no output. (as documented)
    GOOS=darwin GOARCH=386 go build mypackage/app1 mypackage/app2
    

    When go build is given multiple packages, it no longer emits binaries -- it just checks that the code compiles. To emit all the binaries, it seems I have to run go build once for each subpackage, so it takes longer, esp when building with -a.

    Another possible issue with using go build like this, is that it's potentially mixing binaries and intermediary results across multiple architectures in the same workspace. But maybe that's just a matter of taste. Hopefully the toolchain keeps cached results separate for different architectures.

How is this handled in practice? Should I treat each subpackage as an individual package, despite their shared common code (would it then be safe to build them all in parallel)?

Related articles:

What I'm about to suggest feels like a hack at best, but it might work for you if you're cross-compiling stuff in a container or an isolated build environment.

You can drop the GOBIN from the install command:

# I'm not on this platform, so this is a cross compile
GOOS=darwin GOARCH=amd64 go install mypackage/app1 mypackage/app2

Assuming your package is in $GOPATH/src/mypackage the above command will install the two binaries to:

$GOPATH/bin/darwin_amd64/{app1, app2}

and the .a compile dependency files in:

$GOPATH/pkg/darwin_amd64/mypackage/{app1,app2,common}

If you're running this in a for loop for all the platforms you plan on supporting, one nuisance with this process is that when you pass GOOS=x GOARCH=y matching your local architecture (i.e. not cross compiling), then the executables will be placed directly in $GOPATH/bin/ and not $GOPATH/bin/x_y/.

(packages on the other hand always end up in $GOPATH/pkg/x_y/, cross compilation or not).

To determine your local architecture, you can follow steps in this answer: https://stackoverflow.com/a/35669816/5556676 .

Simulating GOBIN=/foo/bin GOOS=x GOARCH=y go install mypackage/app{1,2,3}

Changing $GOPATH has little effect on where go install writes output. With some go commands, like go get, you tweak where packages are installed by adding a new component at the front of $GOPATH. But in go 1.9, go install will always copy binaries in the bin/ folder that's a sibling of the src/ which contains the packages to install.

Instead of using GOBIN=/foo/bin, you can pretend your source files are in /foo/src/mypackage (you may use a symlink), and then do GOPATH=/foo GOOS=x GOARCH=y go install mypackage/app{1,2,3}. This will put binaries in /foo/bin, because of the behavior I describe in the previous paragraph.

It's probably simpler to just grab the binaries from where you expect them to be though, than mucking with copying source trees around.