Praktischer Einstieg in Go Modules

Seit der Version 1.11 bietet Go mit den Modulen einen neuen Weg für das Dependency Management an. Musste es zunächst noch über ein Flag explizit aktiviert werden, ist das seit der Version 1.13 standardmäßig der Fall. Es empfiehlt sich also, zukünftige Projekte direkt unter Verwendung der Module zu starten. Dem interessierten Leser sei empfohlen, sich die offiziellen Dokumente (aus dem Blog und von GitHub) zu den Go Modules durchzulesen, aus welchen ich auch die Informationen für diesen Artikel herangezogen haben.

Mit Erscheinen der Module wird nun auch (endlich) der $GOPATH obsolet. Mussten zuvor noch alle Go-Projekte innerhalb dieses Pfades angelegt werden, kann das nun auch außerhalb davon geschehen. Das ist aktuell sogar Pflicht, denn Projekte, die weiterhin innerhalb des $GOPATH angelegt werden, fallen auf die ursprüngliche Funktionsweise zurück, selbst wenn die für Module notwendige go.mod Datei im Project-Root gefunden wird.

Strukturierung von Projekten

Wie bereits erwähnt ist es nun endlich möglich, Projekte außerhalb des $GOPATH zu erstellen. Ich hoffe, dass ich mir somit nie wieder "How to Write Go Code" durchlesen muss. Wie neue Projekte strukturiert werden, ist in der offiziellen Dokumentation nur einen kleinen Abschnitt gewidmet. Zusammengefasst gilt folgendes:

  • Ein (Git-)Repository beinhaltet meistens ein Modul. Mehr ist möglich, macht die Dinge aber nur komplizierter.
  • Ein Modul beinhaltet ein oder mehrere Packages.
  • Jedes Package beinhaltet ein oder mehrere Source Files, jeweils in einem einzelnen Ordner.

Zudem ist es wichtig, dass Module nach Semantic Versioning ausgezeichnet werden.

Start eines Projektes

Ähnlich wie man es auch von npm-init kennt, kann man mittels go mod init github.com/my/repo ein neues Projekt anlegen. Go erstellt nun die Datei go.mod in der, ähnlich zur package.json, die Abhängigkeiten verwaltet werden.

Beispiel:

$ go mod init github.com/timoisik/article
go: creating new go.mod: module github.com/timoisik/article

$ cat go.mod
module github.com/timoisik/article  

go 1.13

Nun können, wie gewohnt, Source-Files angelegt werden. Wir bleiben beim üblichen, kurzen, dafür aber übersichtlichen Hello World. Hierfür wird die Datei main.go mit folgendem Inhalt erstellt:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World")
}

Mit go run main.go kann nun das Programm gestartet werden. Die Ausgabe sollte nicht überraschen. 🤓

Hinzufügen einer Abhängigkeit

Um uns zu ersparen, jedes mal "Hello World" selbst zu tippen, verwenden wir das package "rsc.io/quote" welches wir den imports hinzufügen. Die main.go sieht also wie folgt aus. In der Ausgabe verwenden wir nun das Modul quote, um "Hello World" auszugeben.

package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}

Go erkennt durch das erneute Ausführen mittels go run main.go, dass eine Abhängigkeit zu einem Modul besteht, welche noch nicht geladen wurde und lädt dieses automatisch herunter. Auch wurde die Datei go.sum angelegt, welche die Hashes der Module enthält, so dass in zukünftigen Builds sichergestellt wird, dass genau diese Modul-Versionen wiederverwendet werden. Die Datei go.mod wurde ebenfalls um das benötigte Modul inklusive der verwendeten Version erweitert.

Bestimmte Version einer Abhängigkeit laden

In dem ersten Beispiel wurde von rsc.io/quote die letzte als stabil markierte Version geladen, da nicht definiert wurde, welche Version verwendet werden sollte. Soll jedoch eine bestimmte Version genutzt werden, kann entweder direkt die Datei go.mod angepasst werden oder man lädt das Modul über den Befehl go get rsc.io/quote@v1.5.1 herunter, wobei die Version mit @vMAJOR.MINOR.PATH angegeben wird.

Entfernen eines Modules

Um ein Modul zu entfernen, kann einfach der Befehl go mod tidy verwendet werden. Go prüft dabei, welche Module noch in Verwendung sind und entfernt jene, die nirgendwo mehr im Code eingesetzt werden.

Auflisten der Abhängigkeiten

Die direkten Abhängigkeiten können, wie oben gezeigt, in der Datei go.mod betrachtet und auch angepasst werden. Möchte man allerdings alle Abhängigkeiten auflisten, also auch die indirekten Abhängigkeiten, kann der Befehl go list -m all verwendet werden. Dabei sorgt das Flag -m dafür, dass anstelle von Packages Module in der Ausgabe dargestellt werden.

Schlusswort

Go Modules bieten noch weitere Möglichkeiten, wie z. B. der Einsatz zwei unterschiedlicher Versionen einer Dependency in einem Modul. Dies ist besonders in großen Projekten hilfreich, wo ein Update einer Version nicht auf einmal möglich ist.

Go bringt mit den Modulen letztlich eine elegantere Möglichkeit der Verwaltung von Abhängigkeiten. Auch das Projekte nun nicht mehr alle unter dem $GOPATH erstellt werden müssen passt sicherlich besser in den Workflow vieler Entwickler.

Die Entwicklung an dem Feature für versionierte Module befindet sich aktuell noch in der Entwicklung. Laut Dokumentation ist es jedoch geplant, die Arbeit an dieser Funktion mit der Version 1.14 abzuschließen.

Show Comments