Open Source im professionellen Einsatz
Linux-Magazin 04/2015
© vvvita, 123RF.com

© vvvita, 123RF.com

Programmieren mit Go

Gut ausgeführt

2009 legte die von Google initiierte Programmiersprache Go einen wilden Start hin. Spätestens seit 2012, dem Erscheinungsjahr von Version 1.0, gilt Go als stabil. Sie bringt recht erbauliche Features mit, die Entwicklern lästige Routinearbeiten abnehmen. Ist Go inzwischen im Mainstream angekommen?

1124

Mit Go kam 2009 eine auf den ersten Blick simple Sprache zur Welt [1], die wie ein modernes C wirkte. Neben ihrem Markenzeichen CSP (Communicating Sequential Processes, [2]) als Lösung für Nebenläufigkeit, schlugen auch Einflüsse von Limbo [3] und Inferno [4] durch. Die Entwicklung fing damals gerade erst an und Änderungen im Wochenrhythmus führten mitunter dazu, dass sich eben noch lauffähiger Code nicht mehr kompilieren ließ.

Erst 2012 wurde Go 1.0 mit dem Versprechen veröffentlicht, dass der geschriebene Code auch mit zukünftigen Versionen der 1.x-Reihe funktioniert. Seitdem liegt der Fokus der Go-Entwickler auf den Tools, Compilern und der Runtime.

Das Buildsystem

Im Jahr 2009 brachte Go noch kein eigenes Buildsystem mit. Vielmehr mussten Entwickler die Compiler und Linker manuell aufrufen oder für größere Projekte Makefiles schreiben, ähnlich wie in C. Da Go es jedoch erleichtern sollte, komplexe und verteilte Projekte zu entwickeln, musste eine bessere Lösung her. Das »go« -Tool lieferte ein einfach zu bedienendes Buildsystem ohne Konfiguration. Mit Hilfe seiner diversen Funktionen lassen sich Projekte bauen und mit diesen arbeiten. Die wichtigsten Funktionen zählt Tabelle 1 auf.

Tabelle 1

Wichtige Go-Tool-Funktionen

Funktion

Effekt

go build

Damit kann ein Entwickler Pakete bauen, um zu schauen, ob der Compiler zufrieden ist (für die Bibliotheken), oder um schlicht ausführbare Dateien zu erzeugen.

go install

Verhält sich ähnlich wie »go build« , speichert aber das Ergebnis auch für Bibliotheken, damit spätere Kompiliervorgänge schneller ablaufen.

go get

Der Befehl lädt sowohl die Abhängigkeiten des aktuellen Projekts als auch allgemein Pakete aus dem Internet herunter.

Wie erwähnt brauchen all diese Funktionen keine Konfiguration, ein Entwickler muss also weder Makefiles schreiben noch Abhängigkeiten explizit auflisten, noch auf den Quelltext verlinken. Das ermöglichen im Kern zwei simple Prinzipien: Die »GOPATH« -Umgebungsvariable (Abbildung 1), die sich ähnlich wie »PATH« verhält und einen oder mehrere Workspaces angibt sowie die Paketpfade. »GOPATH« ist ein wichtiges Konzept. Die Variable bestimmt, wo Go die Quellen eines Pakets findet und wo es Objektdateien speichert. Jeder Workspace besteht dabei aus drei Ordnern:

Abbildung 1: Die GOPATH-Variable und ihre Pfade in einem Beispielprojekt.
  • »src/« speichert Quellcode
  • »pkg/« speichert Objektdateien
  • »bin/« speichert kompilierte, ausführbare Dateien

Führt der Entwickler also »go build« aus, sucht das Buildskript die Quelltexte in »$GOPATH/src/« , und »go install« schreibt Dateien nach »$GOPATH/pkg/« sowie »GOPATH/bin/« .

Alle Quelltexte sind in Pakete unterteilt. Jeder Ordner und Unterordner in »$GOPATH/src/« stellt ein einzelnes Paket dar, jeder Quelltext muss sich in einem solchen Paket befinden, damit Go ihn findet. Aber anders als zum Beispiel Workspaces in Java bezieht sich ein Workspace in Go nicht ausschließlich auf ein einzelnes Projekt. Vielmehr referenziert es eine Sammlung von Go-Paketen und -Projekten, sozusagen die eigene Kopie eines Teils des Go-Universums. Daher finden sich meist verschiedene Projekte in einem einzelnen Workspace.

Jedes Paket lässt sich über einen individuellen Pfad erreichen. Während Pakete in der Standardbibliothek nur sehr kurze Pfadnamen tragen, etwa »fmt« oder »net/http« , sollten eigene Pakete eindeutige Namen besitzen, die nicht mit den Namen der Pakete anderer Entwickler kollidieren. Daher verwenden Programmierer häufig den Pfad, über den sie das Paket im Internet erreichen, etwa auf Github [5]. Ein Pfad heißt dann zum Beispiel »github.com/Benutzer/example« . Dieser Pfad ist eindeutig, da niemand anderes den Benutzernamen verwenden kann. Zugleich erlaubt es diese Art der Benennung später, die Pakete automatisch aus dem Internet zu beziehen.

In Aktion

Die folgenden Listings verdeutlichen die Dateistruktur und zeigen die Tools in Aktion. Nach Vorbild von Listing 1 erstellt ein Entwickler die Grundstruktur für ein Paket. Der Pfad lautet »github.com/Benutzer/hallo« , in der Annahme, dass das Paket dereinst auf Github gehostet werden wird. Listing 2 zeigt den Quelltext des eigentlichen Programms, das, weil es in der Datei »$GOPATH/src/github.com/Benutzer/hallo« steckt, den Namen »hallo.go« trägt. Es könnte aber auch anders heißen, etwa »main.go« .

Listing 2

hallo.go

01 package main
02
03 import "fmt"
04
05 func main() {
06   fmt.Println("Hallo, Benutzer.")
07 }

Listing 1

Pfad anlegen

01 $ export GOPATH=~/go/
02 $ mkdir -p $GOPATH/src/github.com/Benutzer/hallo

Listing 3 kompiliert das Projekt. Hierzu übergibt der Entwickler den Pfad zum Paket und erhält als Dank eine ausführbare Datei im aktuellen Verzeichnis. Die heißt nach dem Ordner mit dem Quelltext, in diesem Fall also »hallo« . Wer das Programm ausführt, den begrüßt es auf etwas steife Art und Weise.

Listing 3

go build

01 $ go build github.com/Benutzer/hallo
02 $ ./hallo
03 Hallo, Benutzer.

Auch Listing 4 kompiliert das Projekt, allerdings über »go install« und nicht »go build« . Das Endresultat landet im selbst festgelegten Ordner »$GOPATH/bin/« . Davon abgesehen verhält sich das Programm identisch.

Listing 4

go install

01 $ go install github.com/Benutzer/hallo
02 $ $GOPATH/bin/hallo
03 Hallo, Benutzer.

Listing 5 zeigt eine etwas veränderte Version des Programms, die eine externe Bibliothek verwendet, um Parameter auf der Kommandozeile zu übergeben. Go bringt hierzu zwar auch das Paket »flag« in der Standardbibliothek mit, dieses implementiert aber die Flag-Regeln von Plan 9, nicht die von Gnu. Der Versuch, es zu kompilieren, schlägt fehl, wie Listing 6 veranschaulicht.

Listing 6

Vergessene Abhängigkeiten

01 $ go build github.com/user/hallo
02 github.com/user/hallo/main.go:5:3: cannot find package "github.com/ogier/pflag" in any of:
03         /usr/lib/go/src/github.com/ogier/pflag (from $GOROOT)
04         /home/dominikh/go/src/github.com/ogier/pflag (from $GOPATH)

Listing 5

hallo.go (Version 2)

01 package main
02
03 import (
04   "fmt"
05   "github.com/ogier/pflag"
06 )
07
08 func main() {
09   name := pflag.String("name", "Unbekannt", "Dein Name")
10   pflag.Parse()
11   fmt.Println("Hallo,", *name)
12 }

Das liegt daran, dass der Nutzer das Paket »pflag« einfach noch nicht heruntergeladen hat. Bei dieser Aufgabe hilft »go get« . Es holt Pakete aus dem Netz und installiert sie, was rekursiv geschieht, wodurch es auch alle Abhängigkeiten einspielt. Ruft der Entwickler, wie es Listing 7 zeigt, zuerst »go get« für das eigene Paket auf, beschafft er auch alle Abhängigkeiten, im konkreten Fall »pflag« .

Listing 7

go get

01 $ go get github.com/Benutzer/hallo
02 $ go build github.com/Benutzer/hallo
03 dominikh-pc ./junk/src $ ./hallo
04 Hallo, Unbekannt
05 dominikh-pc ./junk/src $ ./hallo --name="lieber Leser"
06 Hallo, lieber Leser
07

Dieser Ablauf skaliert problemlos für Hunderte von Paketen und Abhängigkeiten. Der Entwickler muss nie sagen, wie genau der Buildprozess ablaufen soll, welches Versionskontrollsystem er verwendet oder wie er Abhängigkeiten herunterladen möchte.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 3 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.