Introduction to the

In version 1.13, the Go authors added a new way to manage the libraries that Go projects depend on, called the Go module. The Go module was added to meet growing demand, to make it easier for developers to maintain the various versions they rely on, and to add more flexibility to the way developers organize projects on their computers. Go modules typically consist of a project or library that contains a series of Go packages, which are then distributed together. The Go module solved many of the problems with GOPATH, where the original system allowed users to place project code in a directory of their choice and specify dependent versions for each module.

In this tutorial, you will create your own public Go module and add a package for your new module. In addition, you will add someone else’s public module to your project and a specific version of that module to your project.

The premise condition

To follow this tutorial, you will need.

  • To install Go 1.16 or later, you can follow our series on How to Install and Set up a Native Programming environment for Go.
  • Familiar with software packages written with Go. For more information, follow the tutorial How to Write packages in Go.

Create a new module

At first glance, the Go module is similar to the Go package. A module has some Go code files that implement the functionality of the package, but it has two additional important files at the root: the go.mod file and the go.sum file. These files contain information that the GO tool uses to track your module configuration and are usually maintained by the tool, so you don’t need to maintain them.

The first thing to do is decide which directory the module will be in. With the introduction of the Go module, it is possible for Go projects to be located anywhere in the file system, not just in a specific directory defined by Go. You probably already have a directory for your projects, but in this tutorial you will create a directory called projects, and the new module will be called MyModule. You can create projects directories either from the IDE or from the command line.

If you use the command line, first create the projects directory and navigate to it.

mkdir projects
cd projects
Copy the code

Next, you will create the module directory itself. Typically, the top-level directory name for a module is the same as the module name, which makes things easier to track. In your projects directory, run the following command to create the MyModule directory.

mkdir mymodule
Copy the code

Once you have created the module directory, the directory structure will look like this.

└ ─ ─ the projects └ ─ ─ mymoduleCopy the code

The next step is to create a go.mod file in the MyModule directory to define the Go module itself. To do this, you use the Go tool’s mod init command and supply it with the name of the module, in this case myModule. Now create the module by running go Mod init in the myModule directory and providing it with the name of the module, myModule.

go mod init mymodule
Copy the code

This command returns the following output when the module is created.

Outputgo: creating new go.mod: module mymodule
Copy the code

With the creation of the module, your directory structure now looks like this.

├ ─ ├ ─ garbage ├ ─ garbageCopy the code

Now that you’ve created a module, let’s take a look inside the go.mod file and see what the go mod init command does.

To understandgo.modfile

The go.mod file is a very important part of running commands with the Go tool. It is a file that contains the module name and the versions of other modules on which your own module depends. It can also contain other directives, such as REPLACE, which is useful for developing multiple modules at the same time.

In the MyModule directory, use Nano, or your favorite text editor, to open the go.mod file.

nano go.mod
Copy the code

It doesn’t matter that the content will be similar.

projects/mymodule/go.mod

module mymodule

go 1.16
Copy the code

The first line, the module directive, tells Go the name of your module so that when it looks for the import path in the package, it knows not to look for MyModule elsewhere. The myModule value comes from the argument you passed to Go Mod init.

module mymodule
Copy the code

At this point, the only other line in the file, the GO directive, tells the language version for which the GO module is targeted. In this case, because the module was created with Go 1.16, the Go directive says: 1.16.

go 1.16
Copy the code

This file will be expanded as more information is added to the module, but it’s a good idea to look at it for now and see how it changes as further dependencies are added.

Now you’ve created a go module with Go Mod init and looked at the contents of the initial go.mod file, but your module hasn’t done anything yet. Now it’s time to take your module a step further and add some code.

Add Go code to your module

To ensure that the module is created correctly, and to add code so that you can run your first Go module, you will create a main.go file in the myModule directory. The main.go file is commonly used in go programs to indicate the starting point of the program. The name of the file is not as important as the main function inside it, but matching the two makes it easier to find. In this tutorial, the main function prints Hello, Modules! .

To create this file, use Nano, or your favorite text editor, to open the main.go file.

nano main.go
Copy the code

In the main.go file, add the following code to define your main package, import the FMT package, and then print Hello, Modules! The message.

projects/mymodule/main.go

package main

import "fmt"

func main(a) {
    fmt.Println("Hello, Modules!")}Copy the code

In Go, each directory is treated as its own package, and each file has its own package declaration line. In the main.go file you just created, the package is named main. Normally, you can name packages any way you want, but the main package is special in Go. When Go sees a package named main, it knows that the package should be considered a binary and compiled into an executable, not a library intended to be used by other programs.

After defining the package, the import declaration says to import the FMT package so that you can use its Println function to “Hello, Modules! The information is printed to the screen.

Finally, the main function is defined. The main function is another special case in Go, related to the main package. When Go sees a function named main in a package named main, it knows that main is the first function it should run. This is called the entry point of the program.

Once you have created the main.go file, the module’s directory structure will look something like this.

├ ─ ├ ─ unregulated, unregulated, unregulated, unregulated, unregulatedCopy the code

If you’re familiar with Go and GOPATH, running code in modules is similar to running code in GOPATH’s directory. (Don’t worry if you’re not familiar with GOPATH, because using modules will replace its usage).

There are two common ways to run executable programs in Go: build binaries with Go Build or run files with Go Run. In this tutorial, you will use Go Run to run modules directly instead of building binaries, which must be run separately.

Use go Run to run the main.go file you created.

go run main.go
Copy the code

Running this command prints Hello, Modules! As defined in the code. The text.

OutputHello, Modules!
Copy the code

In this section, you add a main.go file to your module, which has an initial main function that prints Hello, Modules! . At this point, your program hasn’t benefited from being a Go module — it could be a file anywhere on your computer, using Go Run. The first real benefit of the Go module is the ability to add dependencies to your project in any directory, not just the GOPATH directory structure. You can also add packages to your modules. In the next section, you will extend your module by creating an additional package within the module.

Add packages to your module

Similar to standard Go packages, a module can contain any number of packages and subpackages, or none at all. In this example, you will create a package called mypackage in the myModule directory.

Create the new package by running the mkdir command in the myModule directory with the parameter mypackage.

mkdir mypackage
Copy the code

This will create a new directory, mypackage, as a subpackage of the MyModule directory.

├ ─ unregulated, unregulated, unregulated, unregulated, unregulated, unregulated, unregulated, unregulatedCopy the code

Use the CD command to change the directory to your new mypackage directory, and then use Nano, or your favorite text editor, to create a mypackage.go file. This file can have any name, but using the same name as the package makes it easier to find the package’s main file.

cd mypackage
nano mypackage.go
Copy the code

In the mypackage.go file, add a function called PrintHello that prints the message Hello, Modules! When called. This is mypackage speaking! .

projects/mymodule/mypackage/mypackage.go

package mypackage

import "fmt"

func PrintHello(a) {
    fmt.Println("Hello, Modules! This is mypackage speaking!")}Copy the code

Since you want the PrintHello function to be available from another package, the capital P in the function name is important. Uppercase letters mean that the function is exported and available to any external program. For more information about Package Visibility in Go, Understanding Package Visibility in Go includes more details.

Now that you have created the Mypackage package with the export function, you will need to import it from the MyModule package to use it. This is similar to how you import other packages, such as the FMT package before, except this time you add your module name at the beginning of the import path. Open your main.go file from the MyModule directory and add the call to PrintHello by adding the highlighted line below.

projects/mymodule/main.go


package main

import (
    "fmt"

    "mymodule/mypackage"
)

func main(a) {
    fmt.Println("Hello, Modules!")

    mypackage.PrintHello()
}
Copy the code

If you take a closer look at the import statement, you’ll see that the new import starts with myModule, which is the same module name you set in the go.mod file. This is followed by the path separator and the package you want to import, in this case mypackage.

"mymodule/mypackage"
Copy the code

In the future, if you add packages to mypackage, you will add them to the end of the import path in a similar way. For example, if you are with another mypackage extrapackage package, you import the path will be mymodule/mypackage/extrapackage.

Mymodule runs your update module in the go Run and main.go directories as before.

go run main.go
Copy the code

When you run the module again, you’ll see the previous Hello, Modules! Info and the new mypackage’sPrintHello function prints new info.

OutputHello, Modules!
Hello, Modules! This is mypackage speaking!
Copy the code

Now you have added a new package for your initial module, creating a directory called mypackage and a PrintHello function. As your module expands, it may be useful to start using other people’s modules in your own. In the next section, you will add a remote module as a dependency for your module.

Add a remote module as a dependency

The Go module is distributed through a version control library, usually a Git library. When you want to add a new module as a dependency for your own module, you can use the repository path as a way of referring to the module you want to use. When Go sees the import path of these modules, it can infer from this repository path where it can be found remotely.

In this example, you will add a dependency github.com/spf13/cobra library dependency to your module. Cobra is a popular library for creating console applications, but we won’t cover that in this tutorial.

Just like when you created the MyModule module, you will use the Go tool again. This time, however, you will run the Go Get command from the MyModule directory. Run Go Get and provide the modules you want to add. In this case, you get github.com/spf13/cobra.

go get github.com/spf13/cobra
Copy the code

When you run this command, the GO tool will find the Cobra repository from the path you specify and determine which version of Cobra is the latest by looking at the repository branches and labels. It will then download the version and add the module name and version to the go.mod file to record the version it has selected for future reference.

Now, open the go.mod file in the MyModule directory and see how the Go tool updates the go.mod file when you add new dependencies. The examples below may change depending on which version of Cobra has been released or which version of the Go tool you are using, but the overall structure of the change should be similar.

projects/mymodule/go.mod

module mymodule

go 1.16

require (
    github.com/inconshreveable/mousetrap v1. 0. 0 // indirect
    github.com/spf13/cobra v12.1. // indirect
    github.com/spf13/pflag v1. 0. 5 // indirect
)
Copy the code

Added a new section that uses the require directive. This directive tells Go which module you want, such as github.com/spf13/cobra, and the version of the module you are adding. Sometimes the require directive also includes a // indirect comment. This comment indicates that the module is not referenced directly in any of the module’s source files when the require directive is added. Some additional require lines have also been added to the file. These rows are other modules that Cobra depends on, and the Go tool thinks they should be referenced as well.

You may also notice that after running the go run command, a new file, go.sum, is created in the myModule directory. This is another important file for the Go module, containing information that Go uses to record specific hash values and dependency versions. This ensures consistency of dependencies, even if they are installed on different machines.

Once you download the dependencies, you will update your main.go file with some minimal Cobra code to use the new dependencies. Update the main.go file in the myModule directory with the Cobra code below to use the new dependency.

projects/mymodule/main.go

package main

import (
    "fmt"

    "github.com/spf13/cobra"

    "mymodule/mypackage"
)

func main(a) {
    cmd := &cobra.Command{
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Hello, Modules!")

            mypackage.PrintHello()
        },
    }

    fmt.Println("Calling cmd.Execute()!")
    cmd.Execute()
}
Copy the code

This code creates a cobra.mand structure with a Run function containing your existing “Hello “statement, which is then executed by calling cmd.execute (). Now run the updated code.

go run main.go
Copy the code

You should see the output below, which looks similar to what you saw earlier. This time, however, it uses your new dependencies, such as Calling cmd.execute ()! Shown in line.

OutputCalling cmd.Execute()!
Hello, Modules!
Hello, Modules! This is mypackage speaking!
Copy the code

Use Go Get to add the latest version of remote dependencies, such as github.com/sp13/cobra here, to make it easier to keep your dependencies up to date with the latest bug fixes. However, sometimes you might prefer to use a specific version of a module, a repository label, or a repository branch. In the next section, you’ll reference these versions using Go Get when you want this option.

Use a specific version of the module

Because Go modules are released from a version control repository, they can use version control functions such as tagging, branching, and even submission. You can reference these in your dependencies by using the @ symbol at the end of the module path with the version you want to use. Earlier, when you installed the latest version of Cobra, you were taking advantage of this capability, but you didn’t need to explicitly add it to your commands. The GO tool knows that if a specific version is not provided using @, it should use the special version Latest. Latest versions are not actually in the repository, as my-Tag or My-Branch might be. It’s built into the Go tool as an accessibility tool, so you don’t have to search for the latest version yourself.

For example, when you initially add your dependencies, you can use the following command to get the same result.

go get github.com/spf13/cobra@latest
Copy the code

Now, imagine that there is a module you use that is currently under development. In this case, call it your_domain/ Sammy /awesome. The awesome module is adding a new feature that works in a new feature called new-feature. To make this branch a dependency of your own module, you need to provide go Get with the module path, followed by the @ symbol, and finally the name of the branch.

go get your_domain/sammy/awesome@new-feature
Copy the code

Running this command will cause go to connect to your_domain/ Sammy /awesome repository, download the new-feature branch with the branch’s current latest commit, and add this information to the go.mod file.

However, branching isn’t the only way you can use the @ option. This syntax can be used for tags or even specific commits to version libraries. For example, sometimes the latest version of the library you are using may have a broken commit. In this case, it may be useful to refer to the commit before the broken commit.

Taking your module’s Cobra dependency as an example, let’s say you need to refer to submitted 07445EA github.com/spf13/cobra because it has some changes you need, and you can’t use another version for some reason. In this case, you can provide the submitted hash after the @ sign, just as you would for branches or labels. Run the go get command in your myModule directory and enter the module and version to download the new version.

go get github.com/spf13/cobra@07445ea
Copy the code

If you open your module’s go.mod file again, you’ll see that Go Get has updated the require line for github.com/spf13/cobra to reference the commit you specified.

projects/mymodule/go.mod

module mymodule

go 1.16

require (
    github.com/inconshreveable/mousetrap v1. 0. 0 // indirect
    github.com/spf13/cobra v11.2.0.20210209210842- 07445.ea179fc // indirect
    github.com/spf13/pflag v1. 0. 5 // indirect
)
Copy the code

Because commit is a specific point in time, unlike a label or branch, Go includes additional information in the require directive to ensure that it uses the correct version in the future. If you take a closer look at the version, you’ll see that it does include the commit hash you provided. Ea179fc v1.1.2-0.20210209210842-07445.

The Go module also uses this functionality to support releasing different versions of modules. When a new version of the Go module is released, a new label is added to the version library with the version number as the label. If you want to use a particular version, you can look at the list of tags in the version library to find the version you are looking for. If you already know the version, you probably don’t need to search in the tag, because the name of the version tag is consistent.

Taking Cobra as an example, let’s say you want to use Cobra version 1.1.1. You can check out the Cobra repository and see that it has a label called V1.1.1, among other labels. To use the version of this tag, you can use the @ symbol in the Go Get command just as you would use non-version tags or branches. Now update your module to use Cobra 1.1.1 by running the Go Get command. V1.1.1 is used as the version.

Go get github.com/spf13/[email protected]Copy the code

Now if you open your module’s go.mod file, you’ll see that Go Get has updated the require line for github.com/spf13/cobra to reference the version you provided.

projects/mymodule/go.mod

module mymodule

go 1.16

require (
    github.com/inconshreveable/mousetrap v1. 0. 0 // indirect
    github.com/spf13/cobra v11.1. // indirect
    github.com/spf13/pflag v1. 0. 5 // indirect
)
Copy the code

Finally, if you are using a specific version of the library, such as 07445EA Commit or previous v1.1.1, but you decide to start using the latest version, you can do so by using the special Latest version. To update your module to the latest version of Cobra, run Go Get again and enter the module’s path and latest version.

go get github.com/spf13/cobra@latest
Copy the code

Once this command is complete, the go.mod file is updated to look just like it did before you referred to that particular version of Cobra. Depending on your Go version and the current latest Cobra version, your output may be slightly different, but you should still see the github.com/spf13/cobra line in the Require section updated again with the latest version.

module mymodule

go 1.16

require (
    github.com/inconshreveable/mousetrap v1. 0. 0 // indirect
    github.com/spf13/cobra v12.1. // indirect
    github.com/spf13/pflag v1. 0. 5 // indirect
)
Copy the code

The go get command is a powerful tool that you can use to manage dependencies in the go.mod file without having to edit it manually. As you can see in this section, using the @ character in a module name lets you use a specific version for a module, from a release version to a specific repository commit. It can even be used to go back to the latest version of your dependency. Using a combination of these options will enable you to ensure the stability of your program in the future.

conclusion

In this tutorial, you create a Go module with a subpackage and use that package in your module. You also added another module as a dependency to your module and explored how to reference the module version in various ways.

For more information about the Go module, the Go project has a series of blog posts on how the Go tool interacts with and understands modules. The Go project also provides a very detailed technical reference for the Go module in the Go Module Reference.

This tutorial is also part of the DigitalOcean How to Code in Go series. The series covers many Go topics, from first installing Go to how to use the language itself.