Joe Jenne

Go Packaging

Understanding GOPATH and modules

This is my understanding of go packaging from playing around trying to do what I wanted, not what other tutorials wanted me to do. More info: module reference

To get a simple go sandbox, let's demo in a container

> docker run -it golang

First, what is our GOPATH? If it isn't set, go defaults to ~/go.

> echo $GOPATH
/go

This is the location that packages and executables are installed to - usually containing bin/, pkg/ and src/.

Go searches each directory listed in GOPATH to find source code, but new packages are always downloaded into the first directory in the list.

Lets make a new go module. We can write it to any in the list of $GOPATH/src directories.

> mkdir /go/src/foo
> cd /go/src/foo
> go mod init jenne.uk/foo  # creates go.mod

The go module is named with a domain to distinguish modules that may have the same name, for a repo on github it might be called github.com/<user>/<repo>.

Let's look at what a dummy package might look like. Say we've just written the following three go files.

> tree
/go/src/foo
├── go.mod
├── a.go
├── b.go
└── bar
    └── c.go

> cat a.go
package main

import (
    "fmt"
    "jenne.uk/foo/bar"
)

func main() {
    fmt.Println("a")
    printName()
    bar.PrintName()
}

> cat b.go
package main

import "fmt"

func printName() {
    fmt.Println("b")
}

> cat bar/c.go
package bar

import "fmt"

func PrintName() {
    fmt.Println("c")
}

First we see that every go file needs to begin with a package statement. All root level go files are part of the main package (if creating an executable rather than a library), one of which defines the main() function where execution starts.

A package is a directory containing go files. All files in that directory have the same package <name> statement, the directory name doesn't matter.

Second we see that any function defined in a package (directory) is available implicitly across the package. Function printName() defined in b.go can be called in a.go.

Third we see that to import another package (even if defined in the same module), we use the module prefix jenne.uk/foo in front of the name defined in our package statement: package bar. Note, across pacakges, that we can only import/export names that are capitalised - like PrintName() in bar.

So let's run this executable by passing all the files in the main package:

> go run a.go b.go
b
c

we could equally build and run the executable:

> go build
> ./foo
a
b
c

or install the executable to $GOPATH/bin:

> go install
> ls /go/bin
foo