Search code examples
linuxgowebkitcross-platformgccgo

How to compile cross-platform Go language project on Linux?


I am trying to set up my Go compiler on Linux which could compile project for any other architecture or platform. I am using default packages from official Ubuntu 14.04 repositories and I am using 64 bit system. This configuration allows me to compile only for Linux and only for 64 bit system. At least I would like to compile for 32 bit Linux or even for 32 bit Windows systems. Is it possible to do somehow?

One additional thing is I want to use two Go bindings: https://github.com/mattn/go-gtk and https://github.com/mattn/go-webkit

I was testing with go-webkit example code:

package main

import (
    "os"
    "github.com/mattn/go-gtk/gtk"
    "github.com/mattn/go-webkit/webkit"
)

const HTML_STRING = `
<doctype html>
<meta charset="utf-8"/>
<style>
div { font-size: 5em }
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function() {
    $('#hello1').slideDown('slow', function() {
        $('#hello2').fadeIn()
    })
})
</script>
<div id="hello1" style="display: none">Hello</div>
<div id="hello2" style="display: none">世界</div>
</div>
`

const MAP_EMBED = `
<style> *{ margin : 0; padding : 0; } </style>
<iframe width="100%" height="100%" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.co.jp/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=osaka&amp;aq=&amp;sll=34.885931,-115.180664&amp;sspn=29.912003,39.506836&amp;brcurrent=3,0x6000e86b2acc70d7:0xa399ff48811f596d,0&amp;ie=UTF8&amp;hq=&amp;hnear=%E5%A4%A7%E9%98%AA%E5%BA%9C%E5%A4%A7%E9%98%AA%E5%B8%82&amp;ll=34.693738,135.502165&amp;spn=0.471406,0.617294&amp;z=11&amp;output=embed"></iframe>
`

func main() {
    gtk.Init(nil)
    window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
    window.SetTitle("webkit")
    window.Connect("destroy", gtk.MainQuit)

    vbox := gtk.NewVBox(false, 1)

    entry := gtk.NewEntry()
    entry.SetText("http://golang.org/")
    vbox.PackStart(entry, false, false, 0)

    swin := gtk.NewScrolledWindow(nil, nil)
    swin.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    swin.SetShadowType(gtk.SHADOW_IN)

    webview := webkit.NewWebView()
    webview.Connect("load-committed", func() {
        entry.SetText(webview.GetUri())
    })
    swin.Add(webview)

    vbox.Add(swin)

    entry.Connect("activate", func() {
        webview.LoadUri(entry.GetText())
    })
    button := gtk.NewButtonWithLabel("load String")
    button.Clicked(func() {
        webview.LoadString("hello Go GTK!", "text/plain", "utf-8", ".")
    })
    vbox.PackStart(button, false, false, 0)

    button = gtk.NewButtonWithLabel("load HTML String")
    button.Clicked(func() {
        webview.LoadHtmlString(HTML_STRING, ".")
    })
    vbox.PackStart(button, false, false, 0)

    button = gtk.NewButtonWithLabel("Google Maps")
    button.Clicked(func() {
        webview.LoadHtmlString(MAP_EMBED, ".")
    })
    vbox.PackStart(button, false, false, 0)

    window.Add(vbox)
    window.SetSizeRequest(600, 600)
    window.ShowAll()

    proxy := os.Getenv("HTTP_PROXY")
    if len(proxy) > 0 {
        soup_uri := webkit.SoupUri(proxy)
        webkit.GetDefaultSession().Set("proxy-uri", soup_uri)
        soup_uri.Free()
    }
    entry.Emit("activate")
    gtk.Main()
}

It works fine if I compile it with

go build

If I try to compile it with other settings:

GOOS=windows GOARCH=386 go build
GOARCH=386 go build

I get this error:

webview.go:5:2: no buildable Go source files in /home/yeeapple/Documents/Coding/Go/Source/src/github.com/mattn/go-gtk/gtk

webview.go:6:2: no buildable Go source files in /home/yeeapple/Documents/Coding/Go/Source/src/github.com/mattn/go-webkit/webkit

Another thing I saw is that in GOPATH directory and pkg folder is only "linux_amd64" folder with *.a files.

For example, I can compile Go files for other systems if it does not have additional imports. Cross-platform compiling works fine with:

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

Versions:

$ go version
go version go1.2.1 linux/amd64

$ gccgo --version
gccgo (Ubuntu 4.9-20140406-0ubuntu1) 4.9.0 20140405 (experimental) [trunk revision 209157]
Copyright (C) 2014 Free Software Foundation, Inc.

Solution

  • With Go 1.2, the cgo feature is disabled when cross compiling code. This means that any source file containing import "C" will not be compiled, which leaves many packages unusable. So while your simple "hello world" program compiles, an equivalent one using cgo will fail:

    package main
    
    /*
    #include <stdlib.h>
    #include <stdio.h>
    */
    import "C"
    import "unsafe"
    
    func main() {
            hello := C.CString("Hello world")
            defer C.free(unsafe.Pointer(hello))
            C.puts(hello)
    }
    

    On an amd64 Linux system, you will get a build failure if you try to compile for x86:

    $ GOARCH=386 go build hello.go
    can't load package: no buildable Go source files in /...
    

    To get this program to compile, you will also need to set the environment variable CGO_ENABLED=1 to manually enable cgo. This will cause it to attempt to compile the program, but it will still fail if you don't have an x86 compiler installed. Since you said you are using Ubuntu, you can do this by installing the gcc-multilib package:

    $ sudo apt-get install gcc-multilib
    

    With that installed, you should be able to compile the program:

    $ GOARCH=386 CGO_ENABLED=1 go build hello.go
    $ file hello
    hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=..., not stripped
    

    While Ubuntu comes with a Windows cross compile tool chain (in the mingw32 package), you'll still have trouble compiling Windows binaries:

    $ GOOS=windows GOARCH=386 CGO_ENABLED=1 go build hello.go 
    go build runtime/cgo: cannot use cgo when compiling for a different operating system
    

    This check is no longer present in GO 1.3, so you may have some luck with that release.

    Now having the cross-compiler tool chain available is only part of the battle: you will also need versions of the your dependencies compiled for the target architecture(s) available. This isn't going to be trivial for something as big as WebKit, but you could probably point Go at an appropriate copy using the CFLAGS, LDFLAGS and PKG_CONFIG_PATH environment variables.