Learning Go - Packages and Dependency Management
I’m writing this post to summarize my understanding of Package and Dependency management in Go.
As of writing this post Go modules have been introduced. This post however covers package and dependency management in Go that existed prior to modules
Contents
- Go Workspace
- Importing external packages & commands
- File and Folder structure for a project in Go
- FAQ
- References
Go Workspace
As a Node.js developer moving to Go, I was looking for the equivalents of npm
and package.json
and npm init
to initialize a project. It took me a while to get a hang of the notion of workspace in Go and the resulting differences in initializing a project in Go compared to Node.js.
Starting a project in Go
The greatest shift for me as a developer coming from Node.js was the following statement from Go Docs
Go programmers typically keep all their Go code in a single workspace
Creating a project in Node.js
-
Create a new folder -
mkdir myApp
-
Initialize
package.json
to track dependencies -npm init -y
-
Initialize Git repository
cd myApp; git init
Creating a project in Go
-
Identify the Go workspace from
GOPATH
environment variablego env GOPATH
(The default value of GOPATH is$HOME/go
. Suppose for a usernamedemo
this is/home/demo/go
) -
Move to $GOPATH -
cd /home/demo/go
-
Move to
src
folder -cd src
-
Inside
src
folder there are multiple folders each corresponding to domains hosting Go code (eg.github.com
,golang.org
). Suppose the new project we create is going to be hosted ongithub.com
under accountdemouser
. Move to the foldergithub.com/demouser
- cdgithub.com/demouser
-
The earlier step where we create a namespace using
github.com/<username>
can be skipped and the project folder can be directly created inside$GOPATH/src/
. Creating the unique namespace would help to share the package in the future -
Create a folder for the project
myApp
-mkdir myApp
-
Initialize Git repository for the project -
cd myApp; git init
Compiling a project in Go
-
Move to the project’s folder
cd $GOPATH/github.com/demouser/myApp
-
go install
The above command will
-
Create package archives for non-executable packages in
myApp
and store them in$GOPATH/pkg
-
Create binary for executable packages (
package main
) inmyApp
and store them in$GOPATH/bin
Executing a project in Go
Assuming that $GOPATH/bin
is added to $PATH (for Unix systems) the project can be executed from anywhere on the system with the following command myApp
(This also assumes that myApp has a single executable package main
and not multiple commands)
Go workspace - Folder structure and responsibilities
The long winded path to creating a project folder in Go has its reasons and becomes palatable after understanding the folder structure at $GOPATH
and the responsibilities of each of the folders
bin/
pkg/
|--github.com
|--username1
src/
|--github.com
|--username1
|--project11
|--project11
|--username2
|--project21
|--username3
|--project31
|--golang.org
|--gopkg.in
-
src
- This folder houses the code for all your projects and external dependencies that you load from remote repositories (usinggo get
) -
pkg
- This folder stores package archives after compiling (usinggo install
for non-executable packages). This is to avoid re-compilation of packages each time a program using these packages is compiled -
bin
- This folder stores the binary for executable packages (usinggo install
for executable packages)
Both src
and pkg
folders use the unique namespace <remote-URL>/folder-name/
(eg. github.com/username
) to store code and archive files.
From Go Docs
anyone should be able to publish their code on any server, in contrast to central registries such as Perl’s CPAN, Java’s Maven, or Node’s NPM. Placing domain names at the start of the go get import space reused an existing decentralized system and avoided needing to solve anew the problems of deciding who can use which names
Importing external packages & commands
Fetching an external package
An external package can be installed using the go get
command
On running go get gopkg.in/yaml.v2
the source code for the package will be downloaded at $GOPATH/src/gopkg.in/yaml.v2
Importing an external package in a Go project
To import the above package in a Go file import gopkg.in/yaml.v2
.
When importing a package Go first looks for the package at $GOROOT/src
followed by $GOPATH/src
. Since yaml.v2
is an external package Go finds it at $GOPATH/src
Importing a nested package
A nested package can be imported by providing its relative path eg import github.com/demouser/pkg/nestedpkg
after it has been go get
from the remote repository go get github.com/demouser/pkg
Compiling external packages
When go install
is run, the external package is compiled and stored as an archive in $GOPATH/pkg
with the appropriate namespace (gopkg.in/
)
File and Folder structure for a project in Go
The file and folder structure for a project in Go depends upon the number of executable and non-executable packages
One executable package
The project can have multiple Go files with package main
One non-executable package
The project can have multiple Go files with package <package-name>
n Executable packages
-
Create a
cmd
folder inside the project folder -
Create a separate folder for each executable package inside
cmd
folder
When go install
is run on this project the binary for the executable packages will take the name of the last enclosing folder and not the filename housing the main()
function. Eg. $GOPATH/src/github.com/demouser/myApp/cmd/test
would be compiled to test
n Non-executable packages
Each non-executable package should be declared in its own folder (else it returns a package can't be loaded error
on go install
)
When go install
is run, each of the packages are compiled and stored as archive files at $GOPATH/pkg/
FAQ
What is the difference between go build
and go install
?
For non-executable packages
go build
compiles the package and discards the resultgo install
compiles the package and places the archive at$GOPATH/pkg
For executable packages
go build
compiles the package and places the resulting binary in the current directorygo install
compiles the package and places the binary at$GOPATH/bin