Custom Packages: Folder Structure, go install and go test
This lesson covers important concepts like folder structure of a custom package, testing the executable and installation under workspace.
For demonstration, we take a simple package uc
which has a function UpperCase
to turn a string into uppercase letters. This is just for demonstration purposes (it wraps the same functionality from package “strings”), but the same techniques can be applied to more complex packages.
Folder-structure for custom packages
The following structure is considered best practice and imposed by the go tool
(where uc
stands for a general package name; the names in bold are folders; italicized is the executable):
go_projects (a workspace in $GOPATH)
src/uppercase
/uc (contains go code for package uc)
uc.go
uc_test.go
/uc_main
ucmain.go (main program for using package uc)
pkg/windows_amd64 (the actual name depends on your operating system/architecture)
/uppercase
uc.a (object file of package)
bin (contains the final executable files)
uc_main.exe
Building the package uc
in the uc folder
The functionality is implemented in uc.go, belonging to the package uc
:
package uc
import "strings"
func UpperCase(str string) string {
return strings.ToUpper(str)
}
From the app’s folder ($GOPATH/src/uppercase), build and install the package locally with the command:
go install uppercase/uc
This copies the package archive uc.a
to pkg/os_arch/uppercase.
Testing the uc package: go test
Go can test our package uc
. To do that, we write 1 (or more) test source files whose names end with _test.go, and that import the package testing
. They must contain functions named TestXXX
with signature func (t*testing.T)
. The test framework invoked by go test
runs each such function. If the function calls a failure function such as t.Error
or t.Fail
, the test is considered to have failed. Add a test to the uc
package by creating the file $GOPATH/src/uppercase/uc/uc_test.go containing the following Go
code:
package uc
import "testing"
type ucTest struct {
in, out string
}
var ucTests = []ucTest {
ucTest{"abc", "ABC"},
ucTest{"Go", "GO"},
ucTest{"Antwerp", "ANTWERP"},
}
func TestUC(t *testing.T) {
for _, ut := range ucTests {
uc := UpperCase(ut.in)
if uc != ut.out {
t.Errorf("UpperCase(%s) = %s, must be
%s.", ut.in, uc, ut.out)
}
}
}
Go to the package folder $GOPATH/src/uppercase and test it with:
go test uppercase/uc
which produces as output:
ok uppercase/uc 0.155s
or with more verbosity (-v):
go test –v uppercase/uc
which produces as output:
=== RUN TestUC
--- PASS: TestUC (0.00 seconds
PASS
ok uppercase/uc 0.091s
The command go test ./...
will run all test code from the packages in and beneath the current directory.
Building the executable:
Then we make our main starting program which uses the uc
package as ucmain.go in folder uppercase/uc_main:
package main
import (
"fmt"
"uppercase/uc"
)
func main() {
str1 := " USING package uc!"
fmt.Println(uc.UpperCase(str1))
}
Then issue the following command in the package folder:
go install uppercase/uc_main
which puts the executable uc_main.exe in the bin folder. Running uc_main gives as output:
USING PACKAGE UC!
If the go command has no path-parameter, it operates on the current directory only.
cd /path/to/package
go build # build package to local directory
go install # install package to $GOPATH/bin or $GOBIN
go test # test package in local directory
Installing under $GOROOT:
If we want the package to be used from any Go-program on the system, it must be installed under $GOROOT.
To do this, set GOPATH = $GOROOT
in .profile and .bashrc; then go install uppercase
will:
- Copy the source code to $GOROOT/src/pkg/os_arch/uppercase
- Copy the package archive to $GOROOT/pkg/os_arch/uppercase
The package can then be imported in any Go-source as:
import uc
Alternatives for setting up your Go environment
- Put every project inside its own workspace: GOPATH=/path/to/proj1:/path/to/proj2:…
- Separate your own projects from 3rd party projects:
/home/user/goprojects
/own
/bin
/pkg
/src
/3rdparty
/bin
/pkg
/src
This can be accomplished with GOPATH=$HOME/projects/3rdparty: $HOME/projects/own.
OS-dependent code
Your program should rarely be written differently according to the operating system on which it is going to run; in the vast majority of cases, the language and standard library handle most portability issues. You could have an excellent reason to write platform-specific code such as assembly language support. In that case, it is reasonable to follow this convention:
prog1.go
prog1_linux.go
prog1_darwin.go
prog1_windows.go
prog1.go defines the common code interface independent from operating systems, put the OS-specific code in its own Go-file named prog1_os.go.
That’s it about the custom packages. In the next lesson, you’ll learn the distribution of Go code.
Get hands-on with 1400+ tech skills courses.