go labs

Next Steps in Go: Code Organization

My first steps in learning Go were simple “scripts”; programs compiled and immediately executed by the go command-line tool. This was a great way to quickly get started; kind of similar to using a REPL. However, I soon wanted to learn how to structure larger programs, create reusable libraries, and use third-party code. In this post, we’ll look at how Go code is organized into packages, the various commands to build and install them, and how to integrate third-party libraries.

Packages

All Go programs and libraries are defined in packages. Packages are named after the final part in their directory path. An import declaration is used to load a package.


package main

import (
  "net/http"
  "fmt"
  "io/ioutil"
)

func main() {
  resp, err := http.Get("http://www.google.com")
  if err != nil {
    panic(err)
  }

  body, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    panic(err)
  }
  fmt.Println(string(body))
}

The entry point for every Go program is the main function in the main package. This program imports three packages from the Go standard library. A package’s name is used to access its exported interface. In this example, http is used to access the package’s Get function.

Creating a Custom Package

The first step in creating a custom package is to create a workspace. A workspace is a directory hierarchy containing three subdirectories:

  • src – Go source code organized into packages
  • pkg – OS and architecture specific compilation artifacts
  • bin – executable Go programs

When you import a custom package, Go looks for its definition in each workspace listed in the GOPATH environment variable.

Let’s create a workspace and set the GOPATH.


% mkdir -p ~/Projects/golang/src

% export GOPATH=~/Projects/golang/

Our custom package will be defined in a src subdirectory.

~/Projects/golang/src/foo/foo.go


package foo

import (
  "fmt"
)

func Bar() {
  fmt.Println("bar")
}

% tree golang
golang
└── src
    └── foo
        └── foo.go

A Go package is named after its directory. Within its directory, it can be implemented in any number of arbitrarily named files. In this example, I chose to name our custom package’s only file after the package.

Importing a Custom Package

With this directory structure in place and the GOPATH set, we can now create a Go program that can import and use our custom package. This program will also be defined in a src subdirectory.

~/Projects/golang/src/fooer/fooer.go


package main

import (
  "foo"
)

func main() {
  foo.Bar()
}

% tree golang
golang
└── src
    ├── foo
    │   └── foo.go
    └── fooer
        └── fooer.go

Our next step is to build and install this program.

Building Go Code

Build Go code using the go command-line tool’s build command.

Building a program creates an executable file in the program’s directory. The executable file is named after its directory.


% pwd
/Users/jared/Projects/golang/src/fooer

% go build

% ls
fooer fooer.go

% ./fooer
bar

Building a custom package results in no build artifacts.

Installing Go Code

Install Go code using the go command-line tool’s install command.

Programs are installed in the workspace’s bin directory.


% pwd
/Users/jared/Projects/golang/src/fooer

% go install

% ls ../../bin
fooer

Custom packages are installed in an OS and architecture specific subdirectory in the workspace’s pkg directory.


% pwd
/Users/jared/Projects/golang/src/foo

% go install

% ls ../../pkg/darwin_amd64
foo.a

Add $GOPATH/bin to your PATH to make executing Go programs easier.

Integrating Third-Party Go Code

Integrate third-party Go code using the go command-line tool’s get command. Third-party code is downloaded and installed in the first workspace listed in the GOPATH.


% pwd
/Users/jared/Projects/golang/src

% go get -v code.google.com/p/freetype-go/freetype
code.google.com/p/freetype-go (download)
code.google.com/p/freetype-go/freetype/raster
code.google.com/p/freetype-go/freetype/truetype
code.google.com/p/freetype-go/freetype

% ls
code.google.com foo           fooer

% ls ../pkg/darwin_amd64/
code.google.com foo.a

Moving Beyond Scripts

Every programming language has its own way of organizing code. Knowing where a language is expecting code is a must when moving beyond simple scripts.