Custom Packages and Visibility
This lesson provides a detailed description on how to create and successfully run a custom package in any Go IDE.
Packages are the primary means in Go for organizing and compiling code. A lot of basic information about packages has already been given in Chapter 2, most notably the Visibility rule. Now we will see concrete examples of the use of packages that you write yourself. By custom packages, we mean self-written packages or packages otherwise external to the standard library.
When writing your packages, use short, single-word, lowercase names without _ for the filename(s).
The go
command
The go
command is used as a uniform (OS independent) tool to fetch, build, and install Go packages and applications. Everything from how to get a package, how to compile, link and test it, is deduced by looking at the source code to find dependencies and determine build conditions. A single environment variable: GOPATH (which is discussed in Appendix) tells the go
command (and other related tools) where to find and install the Go packages on your system.
This command has the same syntax as the system’s PATH environment variable, so a GOPATH is a list of absolute paths, separated by colons (:) on Unix-like machines and by semicolons (;) on Windows machines.
On Unix a typical value could be:
GOPATH=/home/user/ext:/home/user/go_projects
On Windows a typical value could be:
GOPATH=d:\go_projects;e:\go_projects
Each path in the list (in this case e:\go_projects or/home/user/go_projects) specifies the location of a workspace.
A workspace contains Go source files and their associated package objects, and command executables. It has a prescribed structure of three subdirectories:
• src contains a structure of subdirectories each containing Go source files (.go)
• pkg contains installed and compiled package objects (.a)
• bin contains executables (.out or .exe)
Note: There is no specific need to have several workspaces; one workspace can often meet all needs. GOPATH should not normally contain GOROOT which is a folder where the Software Development Kit(SDK) for Go is downloaded on your systems.
Subdirectories of the src directory hold independent packages, and all source files (.go, .c, .h, and .s) in each subdirectory are elements of that subdirectory’s package.
When building a program that imports the package widget
, the go command looks for src/pkg/widget inside the
Go root, and then, if the package source isn’t found there, it searches for src/widget inside each workspace in order. Multiple workspaces can offer flexibility and convenience, but for now, we’ll concern ourselves with
only a single workspace.
Schematically:
workspace /src
/pkg1
/pkg2
...
/pkg
...
/bin
...
Example: A project containing a package
Here is a simple example as to how go
command works for building/running and how the visibility works. Create a folder e:\go_projects or $HOME/go_projects as workspace.
For Windows, set the system variable GOPATH to the value e:\go_projects and check that GOPATH\bin is added to the PATH variable.
For Linux, do the same by adding the following lines to $HOME/.profile (or equivalent):
export GOPATH=$HOME/go_projects
export PATH=$PATH:$GOPATH/bin
Suppose we want to make a Go project named book. Inside the workspace directory, create a src folder, and within it a folder book. Our base import path in Go source files will then be book/. In __book, we want to create a package pack1
. This can be downloaded from a subfolder pack1 of the folder book. This subfolder contains a program pack1.go, which indicates it belongs to package pack1
at its first line.
Remarks: It is not necessary that the subfolder and the source file have the same name as the package. For the subfolder, it is considered good practice, but in most cases, the package will be spread across more than one source file. Our project could contain more than one package, but the way of working is the same for additional packages.
By convention, there is a close relationship between subdirectories and packages: each package (all the go-files belonging to it) resides in its subdirectory, which has the same name as the package. For clarity, different packages also reside in different directories.
package pack1
var Pack1Int int = 42
var pack1Float = 3.14
func ReturnStr() string {
return "Hello main!"
}
It exports an int variable Pack1Int
, a float32 variable pack1Float
and a function ReturnStr
, which returns a string. It is a good practice to type all exported values explicitly: the API is typed. Moreover, in a real project, all the exported objects should be documented.
This program does not do anything when it is run because it does not contain a main()
function.
Building and installing the package: go install
This is done via the subcommand install of go:
go install importpath
or in this case:
go install book/pack1
from a terminal or command-line (opened in the home folder or another
folder).
This command builds and installs the package specified by importpath
and its dependencies. It writes the package
object pack1.a to the pkg\windows_amd64\book subdirectory of the workspace in which the source resides.
On Linux 64-bit this would be pkg\linux_amd64\book. If the pkg folder does not exist, it is created. If everything builds and installs correctly, then the command will produce no output.
If you are in the folder of the package (cd $GOPATH/src/book/pack1), then issue:
go install
So the resulting workspace directory tree looks like this:
src/
book/ # the sources for the project book
pack1/
pack1.go # package source
pkg/
windows_amd64/ # or linux_amd64 on a Linux 64 bit OS
book/ # contains the object
pack1.a # package object
Building and installing the command executable: go install
The go command treats code belonging to package main
as an executable command and installs the package binary
to the GOPATH’s bin subdirectory. A package is imported in another package by its importpath
, which is the pathname of the directory it is in starting after the src component. The general format of the import is:
import "path or url to the package"
like:
import "github.com/org1/pack1"
Let us create the startup program in a source file main.go; this is preferably put in a subfolder book_main of book, with the package being imported in our case via the statement:
import "book/pack1"
(Working this way the startup program is clearly put aside from the packages; it could also allow for more than 1 startup program in our book project.)
package main
import (
"fmt"
"pack1"
)
func main() {
var test1 string
test1 = pack1.ReturnStr()
fmt.Printf("ReturnStr from package1: %s\n", test1)
fmt.Printf("Integer from package1: %d\n", pack1.Pack1Int)
// fmt.Printf("Float from package1: %f\n", pack1.pack1Float)
}
Issue the command:
go install book/book_main
If it is an executable command it is written to the bin subdirectory of the workspace in which the source resides. This compiles main.go, links in the package pack1.a
and installs the executable (or binary) book_main.exe (or pack1_main on Linux) to $GOPATH/bin. If pack1.a
was not yet built, it is made with this command, so it is not necessary to build all dependent packages beforehand. The workspace directory tree now looks like this:
bin/
book_main.exe # command executable (pack1_main on Linux)
pkg/
windows_amd64/ # or linux_amd64 on a Linux 64 bit OS
book/ # contains the object files of the packages in the project book
pack1.a # package object
src/
book/ # the sources for the project book
book_main/
main.go # command source
pack1/
pack1.go # package source
Because the bin directory is in our PATH variable, we can execute the program on the command-line from any folder with book_main which produces the output:
ReturnStr from package1: Hello main!
Integer from package1: 42
Initialization of a package: init()
Program execution begins by importing the packages, initializing the main
package and invoking the function main()
. A package with no imports is initialized by assigning initial values to all its package-level variables and then calling any package-level init()
function defined in its source. A package may contain multiple init()
functions, even within a single source file. They are executed in unspecified order. It is best practice if the determination of a package’s values only depend on other values or functions found in the same package. The init()
functions cannot be called. Imported packages are initialized before the initialization of the package itself, but initialization of a package occurs only once in the execution of a program.
Now that you know how to make your package, in the next lesson, you’ll learn how to document your package.
Get hands-on with 1200+ tech skills courses.