Go Modules

From NovaOrdis Knowledge Base
Revision as of 20:52, 26 September 2023 by Ovidiu (talk | contribs) (→‎Overview)
Jump to navigation Jump to search

External

Internal

Overview

A module is a collection of related packages that are stored together under the same filesystem tree, released, versioned and distributed together. Modules have been introduced in Go 1.11. Packages, a lower-level namespacing, encapsulation and code sharing mechanism, are published as part of modules.

A module is stored as a directory that contains a go.mod file and subdirectories that contain the source code for the module's packages. The go.mod stores metadata such as dependencies needed by the module, required Go version, etc. If any of the subdirectories contains a go.mod file, that subdirectory represents an embedded module.

Modules may be downloaded directly from version control repositories, or from module proxy servers.

Module Path (Module Name)

The module path and module name are used interchangeably. The module path is specified when the module is initialized with:

go mod init <module-path>

For more details on go.mod initialization, see go.mod Initialization below.

The module path serves as import path prefix for all the packages in the module. The module path reflects the published location, it indicates where the go tool command should look to download it. For example, to download the module golang.org/x/tools, the go tool should go to the repository indicated by https://golang.org/x/tools. Packages in the standard library do not have a module path prefix.

Package Import Path

For packages distributed as part of a module, the package's import path is the concatenation of the module's module path and the package's subdirectory within the module. The Multi-Package Module Layout example, provided below, contains packages with import paths example.com/generic/a and example.com/generic/other/b. It is possible to declare a module that contains a single package, with identical module path and package import path. Single-Package Module is such an example.

go.mod

go.mod declares the module path, which is the import path prefix for all packages within the module. It also tracks the modules that provide dependencies to this module.

One way to create it is with go mod init:

go mod init <module-path>

go.mod stays with the code, including in the source repository.

Example

module example.com/hello

go 1.21.0

require rsc.io/quote v1.5.2

require (
	golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
	rsc.io/sampler v1.3.0 // indirect
)

Declaring Modules

This section assumes we want to write an example.com/generic module, which consists of two packages a and b. The a package lives in an a directory in the module root directory, which gives it a example.com/generic/a import path. The b package lives in an other/b directory in the module root directory, which gives it a example.com/generic/other/b import path. There is also a main package that provides the command line binary.

Initialize go.mod

In the module root directory, initialize the go.mod file.

go mod init <module-path>
go mod init example.com/generic

Module Layout

This section needs refactoring after reading "Managing module source" https://go.dev/doc/modules/managing-source

The module root directory contains the go.mod file and the package directories.

Single-Package Module

Simple, one-package modules, where the module path and the the package import path are the same, are supported. In the example below, the single-package module contains just a single a package. The module path example.com/a is the same as the package import path. The filesystem layout is:

.
├─ go.mod
└─ a.go

The source file declares inclusion in the a package:

  
package a

...

go.mod declares the module path and not much else:

  
module example.com/a
go 1.21.0

Multi-Package Module Layout

.
├── go.mod # Declares a module named example.com/generic
│
├── a
│   └─ a.go 
│
├── other
│    └─ b
│       └─ b.go
│
└── generic-cmd.go # part of the "main" package

Publishing Modules

This section needs refactoring after reading "Publishing a module" https://go.dev/doc/modules/publishing

Publishing a module involves compiling, building and publishing the object files corresponding to all or some of the module's packages, or, if the module contains a main package, building and publishing the executable file.

Publishing Modules Locally

Local module publishing is done with go install, configured to support modules. For details on compiling and installing packages without module support see:

Publishing Packages Locally

We assume that go install is configured in module-aware mode.

Single Package with No Internal Dependencies

To compile and publish a single package within a module, execute:

go install <package-import-path>

where the package import path is concatenation of the module path and the package's relative import path within the module, as explained in Package Import Path.

go install example.com/generic/other/b

The command compiles and stores the object file in the build cache, but it does not write in any user-level directory. Why?

Packages with Internal Dependencies

If a module package example.com/generic/a depends on the package example.com/generic/other/b , the command

go install example.com/generic/a

will correctly locate and compile the source files for both a and other/b packages, and if deeper dependencies exist, all source files in the transitive dependency tree. If any of the dependencies fails to compile, the installation process will fail. However, only the object file.

The command compiles and stores the object file in the build cache, but it does not write in any user-level directory. Why?

Executables

An executable is produced when the entire module is built with go install and the linker detects a main package among the packages being compiled.

go install example.com/generic

Assuming the example above has a generic-cmd.go source code that belongs to the main package and has a main() function, installing the module produces an executable and writes it in ~/go/bin by default. If the environment variable GOBIN is set, the value takes precedence.

The name of the executable in this case is generic (Why? It is not given by the source file name.)

Publishing Modules Remotely

The typical way to publish a module is to share the source code in a version-controlled repository, with a module path that reflects its published location. More details required here.

Consuming Modules

A package published as part of module can be consumed from another consumer module by importing it from the consumer module source code with the import keyword followed by the package import path, which includes the module path. In simple cases, the module and package import paths can be the same:

package main

import (
  ...
  "example.com/a"
)

The go.mod of the consumer module must be updated with dependency tracking information. If the dependency is published and accessible via network, go.mod can be automatically updated with (after declaring imports appropriately in source files):

go mod tidy

However, for a local, unpublished modules whose code is available only on the local filesystem as a known relative path, the consumer module's go.mod can be edited manually as follows:

module example.com/consumer

[...]

require example.com/a v0.0.0-unpublished
replace example.com/a v0.0.0-unpublished => ../a  // relative path from the consumer module directory to the dependency module directory

Importing Packages from Remote Modules

Importing Precompiled Packages

Importing Packages from GitHub

  • Where does go mod store the precompiled packages?
  • Same question for GoLand. Is the same place?
  • What does actually happen there? Are we downloading sources or binaries?

Managing Dependencies

TODO: https://go.dev/doc/modules/managing-dependencies

Developing and Publishing Modules

TODO: https://go.dev/doc/modules/developing

Module Version

Modules evolve by publishing new versions.

TODO:

Other Module Subjects

Module Proxy Server

Module Cache

Is the module cache the same as build cache?

Build Cache

Configuring Modules in GoLand

GoLand

TODO