11. Tooling¶
We become what we behold. We shape our tools, and thereafter our tools shape us. — Marshall McLuhan
Good support for lots of useful tools is another strength of Go. Apart from the built-in tools, there any many other community-built tools. This chapter covers the built-in Go tools and few other external tools.
The built-in Go tools can access through the go
command. When you
install the Go compiler (gc
); the go
tool is available in the
path. The go
tool has many commands. You can use the go
tool to
compile Go programs, run test cases, and format source files among other
things.
11.1. Getting Help¶
The go tool is self-documented. You can get help about any commands
easily. To see the list of all commands, you can run the "help"
command. For example, to see help for build
command, you can run
like this:
go help build
The help command also provides help for specific topics like “buildmode”, “cache”, “filetype”, and “environment” among other topics. To see help for a specific topic, you can run the command like this:
go help environment
11.2. Basic Information¶
11.2.1. Version¶
When reporting bugs, it is essential to specify the Go version number and environment details. The Go tool gives access to this information through the following commands.
To get version information, run this command:
go version
The output should look something like this:
go version go1.20.4 linux/amd64
As you can see, it shows the version number followed by operating system and CPU architecture.
11.2.2. Environment¶
To get environment variables, you can run this command:
go env
The output should display all the environment variables used by the Go tool when running different commands.
A typical output will look like this:
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/baiju/.cache/go-build"
GOENV="/home/baiju/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/baiju/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/baiju/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.4"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0
-fdebug-prefix-map=/tmp/go-build1378738152=/tmp/go-build
-gno-record-gcc-switches"
You can temporarily set environment variable with -w
option. This is
an example to set GOPATH in Windows system.
C:\> go env -w GOPATH=C:\mygo
Note: To set an environment variable permenantly in Windows, see the first chapter which explains about Windows installation.
11.2.3. List¶
The list command provides meta information about packages. Running without any arguments shows the current packages import path. The -f helps to extract more information, and it can specify a format. The text/template package syntax can be used to specify the format.
The struct used to format has many attributes, here is a subset:
Dir – directory containing package sources
ImportPath – import path of package in dir
ImportComment – path in import comment on package statement
Name – package name
Doc – package documentation string
Target – install path
GoFiles – list of
.go
source files
Here is an example usage:
$ go list -f '{{.GoFiles}}' text/template
[doc.go exec.go funcs.go helper.go option.go template.go]
11.3. Building and Running¶
To compile a program, you can use the build
command. To compile a
package, first change to the directory where the program is located and
run the build
command:
go build
You can also compile Go programs without changing the directory. To do
that, you are required to specify the package location in the command
line. For example, to compile github.com/baijum/introduction
package
run the command given below:
go build github.com/baijum/introduction
If you want to set the executable binary file name, use the -o
option:
go build -o myprog
If you want to build and run at once, you can use the "run"
command:
go run program.go
You can specify more that one Go source file in the command line arguments:
go run file1.go file2.go file3.go
Of course, when you specify more than one file names, only one “main” function should be defined among all of the files.
11.3.1. Conditional Compilation¶
Sometimes you need to write code specific to a particular operating system. In some other case the code for a particular CPU architecture. It could be code optimized for that particular combination. The Go build tool supports conditional compilation using build constraints. The Build constraint is also known as build tag. There is another approach for conditional compilation using a naming convention for files names. This section is going to discuss both these approaches.
The build tag should be given as comments at the top of the source code. The build tag comment should start like this:
// +build
The comment should be before package documentation and there should be a line in between.
The space is OR and comma is AND. The exclamation character is stands for negation.
Here is an example:
// +build linux,386
In the above example, the file will compile on 32-bit x86 Linux system.
// +build linux darwin
The above one compiles on Linux or Darwin (Mac OS).
// +build !linux
The above runs on anything that is not Linux.
The other approach uses file naming convention for conditional compilation. The files are ignore if it doesn’t match the target OS and CPU architecture, if any.
This compiles only on Linux:
stat_linux.go
This one on 64 bit ARM linux:
os_linux_arm64.go
11.4. Running Test¶
The Go tool has a built-in test runner. To run tests for the current package, run this command:
go test
To demonstrate the remaining commands, consider packages organized like this:
.
|-- hello.go
|-- hello_test.go
|-- sub1
| |-- sub1.go
| `-- sub1_test.go
`-- sub2
|-- sub2.go
`-- sub2_test.go
If you run go test
from the top-level directory, it’s going to run
tests in that directory, and not any sub directories. You can specify
directories as command line arguments to go test
command to run
tests under multiple packages simultaneously. In the above listed case,
you can run all tests like this:
go test . ./sub1 ./sub2
Instead of listing each packages separates, you can use the ellipsis syntax:
go test ./...
The above command run tests under current directory and its child directories.
By default go test
shows very few details about the tests.
$ go test ./...
ok _/home/baiju/code/mypkg 0.001s
ok _/home/baiju/code/mypkg/sub1 0.001s
--- FAIL: TestSub (0.00s)
FAIL
FAIL _/home/baiju/code/mypkg/sub2 0.003s
In the above results, it shows the name of failed test. But details
about other passing tests are not available. If you want to see verbose
results, use the -v
option.
$ go test ./... -v
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok _/home/baiju/code/mypkg 0.001s
=== RUN TestSub
--- PASS: TestSub (0.00s)
PASS
ok _/home/baiju/code/mypkg/sub1 0.001s
=== RUN TestSub
--- FAIL: TestSub (0.00s)
FAIL
FAIL _/home/baiju/code/mypkg/sub2 0.002s
If you need to filter tests based on the name, you can use the -run
option.
$ go test ./... -v -run Sub
testing: warning: no tests to run
PASS
ok _/home/baiju/code/mypkg 0.001s [no tests to run]
=== RUN TestSub
--- PASS: TestSub (0.00s)
PASS
ok _/home/baiju/code/mypkg/sub1 0.001s
=== RUN TestSub
--- FAIL: TestSub (0.00s)
FAIL
FAIL _/home/baiju/code/mypkg/sub2 0.002s
As you can see above, the TestHello
test was skipped as it doesn’t
match “Sub” pattern.
The chapter on testing has more details about writing test cases.
11.5. Formatting Code¶
Go has a recommended source code formatting. To format any Go source file to conform to that format, it’s just a matter of running one command. Normally you can integrate this command with your text editor or IDE. But if you really want to invoke this program from command line, this is how you do it:
go fmt myprogram.go
In the above command, the source file name is explicitly specified. You can also give package name:
go fmt github.com/baijum/introduction
The command will format source files and write it back to the same file. Also it will list the files that is formatted.
11.6. Generating Code¶
If you have use case to generate Go code from a grammar, you may consider the go generate. In fact, you can add any command to be run before compiling the code. You can add a special comment in your Go code with this syntax:
//go:generate command arguments
For example, if you want to use peg (https://github.com/pointlander/peg), a Parsing Expression Grammar implementation, you can add the command like this:
//go:generate peg -output=parser.peg.go grammar.peg
When you build the program, the parser will be generated and will be part of the code that compiles.
11.7. Embedding Code¶
Go programs are normally distributed as a single binary. What if your program need some files to run. Go has a feature to embed files in the binary. You can embed any type of files, including text files and binary files. Some of the commonly embedded files are SQL, HTML, CSS, JavaScript, and images. You can embed individual files or diectories including nested sub-directories.
You need to import the embed package and use the //go:embed
compiler directive to embed. Here is an example to embed an SQL file:
import _ "embed"
//go:embed database-schema.sql
var dbSchema string
As you can see, the “embed” package is imported with a blank identifier as it is not directly used in the code. This is required to initialize the package to embed files. The variable must be at package level and not at function or method level.
The variable could be slice of bytes. This is useful for binary files. Here is an example:
import _ "embed"
//go:embed logo.jpg
var logo []byte
If you need an entire directory, you can use the embed.FS
as the
type:
import "embed"
//go:embed static
var content embed.FS
11.8. Displaying Documentation¶
Go has good support for writing documentation along with source code. You can write documentation for packages, functions and custom defined types. The Go tool can be used to display those documentation.
To see the documentation for the current packages, run this command:
go doc
To see documentation for a specific package:
go doc strings
The above command shows documentation for the “strings” package.
go doc strings
If you want to see documentation for a particular function within that package:
go doc strings.ToLower
or a type:
go doc strings.Reader
Or a method:
go doc strings.Reader.Size
11.9. Find Suspicious Code Using Vet¶
There is a handy tool named vet to find suspicious code in your program. Your program might compile and run. But some of the results may not be desired output.
Consider this program:
1package main
2
3import (
4 "fmt"
5)
6
7func main() {
8 v := 1
9 fmt.Printf("%#v %s\n", v)
10}
If you compile and run it. It’s going to be give some output. But if you
observe the code, there is an unnecessary %s
format string.
If you run vet
command, you can see the issue:
$ go vet susp.go
# command-line-arguments
./susp.go:9: Printf format %s reads arg #2,
but call has only 1 arg
Note: The vet command is automatically run along with the test command.
11.10. Exercises¶
Exercise 1: Create a program with function to return “Hello, world!” and write test and run it.
hello.go:
package hello
// SayHello returns a "Hello word!" message
func SayHello() string {
return "Hello, world!"
}
hello_test.go:
package hello
import "testing"
func TestSayHello(t *testing.T) {
out := SayHello()
if out != "Hello, world!" {
t.Error("Incorrect message", out)
}
}
To run the test:
go test . -v
11.10.1. Additional Exercises¶
Answers to these additional exercises are given in the Appendix A.
Problem 1: Write a program with exported type and methods with
documentation strings. Then print the documentation using the go doc
command.
11.11. Summary¶
This chapter introduced the Go tool. It explained all the major Go commands in detail and provided practical examples for each command. It covered how to build and run programs, run tests, format code, and display documentation. It also mentioned a few other useful tools.