Project Layout

Just as $PYTHONPATH controls where the Python interpreter will look for source files, $GOPATH controls where the Go compiler and tools will look for source code.

Hello World

Let us consider a modified version of the standard “Hello World” program. Our example will consist of a library package and an application package. The library exports one function, which returns the string “Hello world, the time is: ” plus the current time. The application package calls the library function, and prints the hello message to the console.

Python

In Python we might have a file layout like this:

$PYTHONPATH/
   hello.py
   greeter.py

The library package is contained in greeter.py:

The application is contained in hello.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/usr/bin/env python

import time


def greeting():
    '''Returns a pleasant, semi-useful greeting.'''
    return "Hello world, the time is: " + time.ctime()


def main():
    print greeting()


if __name__ == '__main__':
    main()

We run the Python application like this:

$ python hello.py
Hello world, the time is: Mon Jul  8 19:16:40 2013

Go

The “standard” layout for Go code is more complex than that for Python code. The disadvantage is that setup is slightly more work. The upside is a well-structured codebase that encourages modular code and keeps things orderly as a project grows in size.

Typically Go code is developed using distributed version control systems like Git or Mercurial. Therefore import paths are conventionally named based on the Github etc URL. The Go toolchain is aware of this, and can gracefully handle automatic dependency installation if your project conforms to the convention.

Imagine for a moment we have created a Github repository named hello for our Hello World example. We would create a file layout like this:

$GOPATH/
   src/
      github.com/
         jmcvetta/
            hello/
               hello.go
               greeter/
                  greeter.go

The library is located in greeter.go. Note the package name, greeter, is the same as the name of the containing folder. This is mandatory. The package imports time from the standard library.

The application is in hello.go. This file declares its package as main, and defines a main() function. This tells the compiler to generate an executable from the file. The package imports fmt from the standard library, and our greeter package specified by its path.

Functions imported from another package are always namespaced with the package name. In this case, we call greeter.Greeting(). Go intentionally has no equivalent of Python’s from some_package import *.

We can build the application with the go build command:

$ go build -v
github.com/jmcvetta/hello/greeter
github.com/jmcvetta/hello

$ ls
greeter/  hello*  hello.go

$ ./hello
Hello world, the time is: 2013-07-08 19:49:39.946836748 -0700 PDT

Dependencies

The go tool can automatically install dependencies. They are installed in the same URL-derived folder heirarchy alongside your code.

Let’s embelish our Greeting() function by making it return the current PATH as well. Although this could be done using nothing but the standard library, for purpose of instruction we will use the popular env package.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package greeter

import (
	"github.com/darkhelmet/env"
	"time"
)

// Greeting returns a pleasant, semi-useful greeting.
func Greeting() string {
	msg := "Hello world, the time is "
	msg += time.Now().String()
	msg += " and your PATH is "
	msg += env.String("PATH")
	return msg
}

We can automatically install our new dependency with the go get command:

$ go get -v .
github.com/darkhelmet/env (download)
github.com/darkhelmet/env
github.com/jmcvetta/hello/greeter
github.com/jmcvetta/hello

Now we the env package installed, and our file system looks like:

$GOPATH/
   src/
      github.com/
         darkhelmet/
            env/
               env.go
               env_test.go
               format.bash
               LICENSE.md
               README.md
         jmcvetta/
            hello/
               hello.go
               greeter/
                  greeter.go

We can try out our new Hello World with the go run command, which build the application then runs it.

$ go run hello.go
Hello world, the time is 2013-07-08 20:36:31.496236239 -0700 PDT and your PATH is /home/jason/.rvm/gems/ruby-1.9.2-p320/bin:/home/jason/.rvm/gems/ruby-1.9.2-p320@global/bin:/home/jason/.rvm/rubies/ruby-1.9.2-p320/bin:/home/jason/.rvm/bin:/usr/local/heroku/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/jason/opt/bin:/home/jason/.scripts:/home/jason/opt/go/bin:/home/jason/work/go/bin:/home/jason/.rvm/bin