Easier cross-compiling with gccgo

682 views
Skip to first unread message

Ed Swierk

unread,
May 14, 2014, 5:41:48 PM5/14/14
to golan...@googlegroups.com
I'd like to be able to build go packages with a cross-compiling gccgo toolchain, without having to modify the package build scripts, or install wrapper scripts or target-specific versions of the go tool.

Ideally cross-compiling would work just like it does with the Linux kernel and other non-go projects, where the value of CROSS_COMPILE environment variable gets tacked onto the front of any invocation of gcc, g++, ld, and so on. If you're building for, say, a MIPS target, and your build machine has cross compilers installed as mips-linux-gnu-gcc, mips-linux-gnu-g++, and so on, you run make CROSS_COMPILE=mips-linux-gnu-. To build with the normal compilers, you just leave CROSS_COMPILE unset or empty.

The following patch is my attempt to add this capability to go. With this patch, setting CROSS_COMPILE to xxx-yyy-zzz- causes go build to:

- use gccgo rather than gc toolchain (similar to passing -compiler gccgo, except it doesn't whine when the architecture is something other than x86 or arm)
- set GOARCH=xxx
- set GOOS=yyy
- enable cgo (and pass actual arch and os to cgo via GOARCH and GOOS)
- prepend xxx-yyy-zzz- default compiler names, e.g. xxx-yyy-zzz-gccgo instead of gccgo

You can still override these parameters individually by setting GOARCH, GOOS, CGO_ENABLED, CC, and so on. But my goal is to make cross-compiling as transparent as possible, requiring only the CROSS_COMPILE environment variable to be set.

When CROSS_COMPILE is empty or unset, go build behaves as usual.

The attached patch should work with the recent default branch of the golang source tree. I have also backported the changes to the Debian 1.2.1-2 version of golang.


--Ed

golang-gccgo-cross-19905.patch

Ed Swierk

unread,
Jun 25, 2014, 7:08:23 PM6/25/14
to golan...@googlegroups.com
Some discussion on this topic has taken place in the code review for my patch: https://codereview.appspot.com/103480044.

The main points of contention are:

(1) Why do we need another environment variable to tweak the build process for cross-compiling; what about all the existing ones like CC, CXX, and so on?

(2) Does the new environment variable cover a broad enough range of use cases, or is it too tightly bound to a specific build process?

To clarify the first point, here's an example to make the use case more concrete. With this patch, I can use a single go front-end to build gopacket/afpacket using either gc for the native architecture or with gccgo for any target architecture. Assuming gcc for the MIPS target is installed as mips-linux-gnu-gcc (likewise for g++, gccgo, and so on), I set CROSS_COMPILE=mips-linux-gnu- to enable cross-compiling.

$ go build -x
WORK=/tmp/go-build209857879
mkdir -p $WORK/code.google.com/p/
cd /bobo/gopacket
/usr/lib/go/pkg/tool/linux_amd64/6g -o $WORK/code.google.com/p/gopacket.a -trimpath $WORK -p code.google.com/p/gopacket -complete -D _/bobo/gopacket -I $WORK -pack ./base.go ./decode.go ./doc.go ./flows.go ./layerclass.go ./layertype.go ./packet.go ./parser.go ./writer.go
mkdir -p $WORK/_/bobo/gopacket/afpacket/_obj/
mkdir -p $WORK/_/bobo/gopacket/
cd /bobo/gopacket/afpacket
CGO_LDFLAGS="-g" "-O2" GOARCH=amd64 GOOS=linux /usr/lib/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/bobo/gopacket/afpacket/_obj/ -- -I $WORK/_/bobo/gopacket/afpacket/_obj/ afpacket.go header.go options.go
/usr/lib/go/pkg/tool/linux_amd64/6c -F -V -w -trimpath $WORK -I $WORK/_/bobo/gopacket/afpacket/_obj/ -I /usr/lib/go/pkg/linux_amd64 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.6 -D GOOS_linux -D GOARCH_amd64 $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -print-libgcc-file-name
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.o -c $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o -c $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o -g -O2
/usr/lib/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/bobo/gopacket/afpacket/_obj/ -dynimport $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_.o -dynout $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_import.c
/usr/lib/go/pkg/tool/linux_amd64/6c -F -V -w -trimpath $WORK -I $WORK/_/bobo/gopacket/afpacket/_obj/ -I /usr/lib/go/pkg/linux_amd64 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_import.6 -D GOOS_linux -D GOARCH_amd64 $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_import.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -o $WORK/_/bobo/gopacket/afpacket/_obj/_all.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o -g -O2 -Wl,-r -nostdlib /usr/lib/gcc/x86_64-linux-gnu/4.9/libgcc.a
/usr/lib/go/pkg/tool/linux_amd64/6g -o $WORK/_/bobo/gopacket/afpacket.a -trimpath $WORK -p _/bobo/gopacket/afpacket -D _/bobo/gopacket/afpacket -I $WORK -I /foo/go/pkg/linux_amd64 -pack $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_gotypes.go $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo1.go $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo1.go $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo1.go
pack r $WORK/_/bobo/gopacket/afpacket.a $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_import.6 $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.6 $WORK/_/bobo/gopacket/afpacket/_obj/_all.o # internal

$ CROSS_COMPILE=mips-linux-gnu- go build -x
WORK=/tmp/go-build899865184
mkdir -p $WORK/code.google.com/p/
cd /bobo/gopacket
mips-linux-gnu-gccgo -I $WORK -c -g -fgo-pkgpath=code.google.com/p/gopacket -fgo-relative-import-path=_/bobo/gopacket -o $WORK/code.google.com/p/gopacket/_obj/gopacket.o ./base.go ./decode.go ./doc.go ./flows.go ./layerclass.go ./layertype.go ./packet.go ./parser.go ./writer.go
mkdir -p $WORK/_/bobo/gopacket/afpacket/_obj/
mkdir -p $WORK/_/bobo/gopacket/
cd /bobo/gopacket/afpacket
CGO_LDFLAGS="-g" "-O2" GOARCH=mips GOOS=linux /usr/lib/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/bobo/gopacket/afpacket/_obj/ -gccgo -gccgopkgpath=_/bobo/gopacket/afpacket -- -I $WORK/_/bobo/gopacket/afpacket/_obj/ afpacket.go header.go options.go
mips-linux-gnu-gcc -Wall -g -I $WORK/_/bobo/gopacket/afpacket/_obj/ -I /usr/lib/go/pkg/linux_mips -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.o -D GOOS_linux -D GOARCH_mips -D "GOPKGPATH=\"__bobo_gopacket_afpacket\"" -c $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -print-libgcc-file-name
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.o -c $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o -c $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -I $WORK/_/bobo/gopacket/afpacket/_obj/ -g -O2 -o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o -c $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.c
mips-linux-gnu-gcc -I . -fPIC -pthread -fmessage-length=0 -o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_main.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o -g -O2
mips-linux-gnu-gccgo -I $WORK -I /foo/go/pkg/gccgo_linux_mips -c -g -fgo-pkgpath=_/bobo/gopacket/afpacket -fgo-relative-import-path=_/bobo/gopacket/afpacket -o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_gotypes.go $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo1.go $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo1.go $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo1.go
ar cru $WORK/_/bobo/gopacket/libafpacket.a $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_defun.o $WORK/_/bobo/gopacket/afpacket/_obj/_cgo_export.o $WORK/_/bobo/gopacket/afpacket/_obj/afpacket.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/header.cgo2.o $WORK/_/bobo/gopacket/afpacket/_obj/options.cgo2.o

It's possible to achieve something similar with mainline go, but with many more steps, including rebuilding go itself, modifying the PATH, installing symlinks or wrapper scripts, and passing several environment variables to go build. So my patch can be viewed as an optimization for the use case I just described.

The second point of discussion is whether my patch is the right way to achieve this optimization. I can't say I understand all the design decisions behind the existing environment variables (GOARCH, GOOS, CGO_ENABLED, CC, CXX, and so on), go build flags (-compiler, -gccgoflags, etc.) and other mechanisms. Nor am I an expert on other cross-compilation use cases.

Would this patch help or hinder other ways of building Go packages? Is there a more general solution that would cover more use cases?

--Ed

Ian Lance Taylor

unread,
Jun 25, 2014, 8:35:36 PM6/25/14
to Ed Swierk, golang-dev
Yes.

> (2) Does the new environment variable cover a broad enough range of use
> cases, or is it too tightly bound to a specific build process?

Yes.

You can cross-compile using the gc compiler too. For gc you do it by
setting GOARCH and GOOS to the cross-compilation target.

Also, while setting the prefix for a GCC cross-compiler is the default
behaviour, there are many other ways to install a cross-compiling GCC
under different names.

So I would prefer setting CC and CXX and GOARCH and GOOS and using
-compiler=gccgo. It's harder to get started, but I think it's
entirely doable. More a matter of documentation than anything else.
I think it's OK for cross-compiling, a very unusual case, to be harder
to get started with, as long as it's easy to use.

If that doesn't work, we should make it work.

Ian
> --
> You received this message because you are subscribed to the Google Groups
> "golang-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-dev+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages