Anpassen von Go-Binärdateien mit Build-Tags

Einführung

In Go ist ein Build-Tag, oder eine Build-Einschränkung, eine Kennung, die einem Codeabschnitt hinzugefügt wird und bestimmt, wann die Datei beim build-Prozess in ein Paket eingebunden werden sollte. Dies ermöglicht es Ihnen, verschiedene Versionen Ihrer Go-Anwendung aus dem gleichen Quellcode zu erstellen und schnell und organisiert zwischen ihnen umzuschalten. Viele Entwickler verwenden Build-Tags, um den Workflow beim Bau跨-Plattform-kompatibler Anwendungen zu verbessern, wie z.B. Programme, die Änderungen am Code erforderlich haben, um Unterschiede zwischen verschiedenen Betriebssystemen zu berücksichtigen. Build-Tags werden auch für Integrations-Tests verwendet, um schnell zwischen dem integrierten Code und dem Code mit einem Mock-Service oder Stub umzuschalten und für unterschiedliche Ebenen von FunktionalitätsSets innerhalb einer Anwendung.

Lassen Sie uns das Problem unterschiedlicher Kundenfunktionssätze als Beispiel nehmen. Bei der Entwicklung einiger Anwendungen möchten Sie möglicherweise steuern, welche Funktionen in das Binary aufgenommen werden sollen, wie bei einer Anwendung, die die Ebenen Free, Pro und Enterprise anbietet. Wenn der Kunde sein Abonnementlevel in diesen Anwendungen erhöht, werden weitere Funktionen freigeschaltet und verfügbar. Um dieses Problem zu lösen, könnten Sie separate Projekte pflegen und versuchen, sie durch die Verwendung von import-Anweisungen synchron zu halten. Während dieser Ansatz funktionieren würde, würde er mit der Zeit langweilig und fehleranfällig werden. Ein alternativer Ansatz wäre die Verwendung von Build-Tags.

In diesem Artikel werden Sie Build-Tags in Go verwenden, um verschiedene ausführbare Binärdateien zu erstellen, die die Funktionssätze Free, Pro und Enterprise einer Beispielanwendung bieten. Jede wird einen anderen Satz an Funktionen bereitstellen, wobei die Free-Version die Standardauswahl ist.

Voraussetzungen

Um dem Beispiel in diesem Artikel zu folgen, benötigen Sie:

Erstellung der kostenlosen Version

Lassen Sie uns damit beginnen, die kostenlose Version der Anwendung zu erstellen, da sie Standard sein wird, wenn Sie go build ohne jegliche Build-Tags ausführen. Später werden wir Build-Tags verwenden, um ausgewählte andere Teile zu unserem Programm hinzuzufügen.

Erstellen Sie im Verzeichnis src einen Ordner mit dem Namen Ihrer Anwendung. Dieser Tutorial verwendet app:

  1. mkdir app

Wechseln Sie in diesen Ordner:

  1. cd app

Als nächstes erstellen Sie eine neue Textdatei in Ihrem bevorzugten Texteditor namens main.go:

  1. nano main.go

Nun definieren wir die kostenlose Version der Anwendung. Fügen Sie die folgenden Inhalte in main.go ein:

main.go
package main

import "fmt"

var features = []string{
  "Free Feature #1",
  "Free Feature #2",
}

func main() {
  for _, f := range features {
    fmt.Println(">", f)
  }
}

In dieser Datei haben wir ein Programm erstellt, das eine slice namens features deklariert, die zwei strings enthält, die die Funktionen unserer kostenlosen Anwendung darstellen. Die main()-Funktion in der Anwendung verwendet eine for-Schleife, um durch die features-Slice zu range und alle verfügbaren Funktionen auf dem Bildschirm anzuzeigen.

Datei speichern und beenden. Nun, da diese Datei gespeichert ist, müssen wir sie für den Rest des Artikels nicht mehr bearbeiten. Stattdessen werden wir Build-Tags verwenden, um die Eigenschaften der daraus zu bauenden Binäre zu ändern.

Programm bauen und ausführen:

  1. go build
  2. ./app

Du erhälst die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2

Das Programm hat unsere beiden kostenlosen Features ausgegeben, was die kostenlose Version unserer App abgeschlossen hat.

Bis jetzt hast du eine Anwendung erstellt, die über ein sehr grundlegendes Feature-Satz verfügt. Nächstes Mal baut du eine Methode auf, um weitere Features in der Anwendung während des Buildvorganges hinzuzufügen.

Kostenpflichtige Features hinzufügen mit go build

Bisher haben wir die Änderung von main.go vermieden, um ein typisches Produktionsumfeld zu simulieren, in dem Code ohne Änderungen und möglicherweise Breakage des Hauptcodes hinzugefügt werden muss. Da wir die Datei main.go nicht bearbeiten können, werden wir eine andere Mechanik brauchen, um weitere Features in den features-Slice während des Buildvorganges einzufügen.

Lege eine neue Datei namens pro.go an, die eine init()-Funktion verwendet, um weitere Features zum features-Slice hinzuzufügen:

  1. nano pro.go

Sobald der Editor die Datei geöffnet hat, füge die folgenden Zeilen hinzu:

pro.go
package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

In diesem Code haben wir init() verwendet, um Code vor der main()-Funktion unserer Anwendung auszuführen, gefolgt von append(), um die Pro-Funktionen zum features-Slice hinzuzufügen. Speichern Sie die Datei und beenden Sie sie.

Kompilieren und führen Sie die Anwendung mit go build aus:

  1. go build

Da es nun zwei Dateien in unserem aktuellen Verzeichnis gibt (pro.go und main.go), wird go build eine Binärdatei aus beiden erstellen. Führen Sie dieses Binary aus:

  1. ./app

Dies gibt Ihnen die folgende Funktionsauswahl:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

Die Anwendung enthält nun sowohl die Pro- als auch die Free-Funktionen. Dies ist jedoch nicht wünschenswert: Da es keine Unterscheidung zwischen den Versionen gibt, enthält die Free-Version nun Funktionen, die nur in der Pro-Version verfügbar sein sollen. Um dies zu beheben, könnten Sie mehr Code hinzufügen, um die verschiedenen Ebenen der Anwendung zu verwalten, oder Sie könnten Build-Tags verwenden, um der Go-Werkzeugkette mitzuteilen, welche .go-Dateien erstellt und welche ignoriert werden sollen. Lassen Sie uns in der nächsten Schritt Build-Tags hinzufügen.

Build-Tags hinzufügen

Sie können nun Build-Tags verwenden, um die Pro-Version Ihrer Anwendung von der Free-Version zu unterscheiden.

Beginnen wir damit, zu untersuchen, wie ein Build-Tag aussieht:

// +build tag_name

Durch das Setzen dieser Codezeile als erste Zeile Ihres Pakets und das Ersetzen von tag_name durch den Namen Ihres Build-Tags, markieren Sie dieses Paket als Code, der选择性 in die endgültige Binärdatei aufgenommen werden kann. Lassen Sie uns das in Aktion sehen, indem wir einen Build-Tag zum pro.go-File hinzufügen, um dem go build-Befehl mitzuteilen, es zu ignorieren, es sei denn, der Tag ist angegeben. Öffnen Sie die Datei in Ihrem Texteditor:

  1. nano pro.go

Dann fügen Sie die folgende hervorgehobene Zeile hinzu:

pro.go
// +build pro

package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

Am Anfang des pro.go-Files haben wir // +build pro gefolgt von einer leeren neuen Zeile hinzugefügt. Diese abschließende Neue Zeile ist erforderlich, sonst interpretiert Go dies als Kommentar. Build-Tag-Deklarationen müssen auch am Anfang einer .go-Datei stehen. Nichts, nicht einmal Kommentare, dürfen über den Build-Tags stehen.

Die +build-Deklaration teilt dem go build-Befehl mit, dass dies kein Kommentar ist, sondern ein Build-Tag. Der zweite Teil ist der pro-Tag. Durch Hinzufügen dieses Tags am Anfang des pro.go-Files wird der go build-Befehl nur das pro.go-File einbeziehen, wenn der pro-Tag vorhanden ist.

Kompilieren und führen Sie die Anwendung erneut aus:

  1. go build
  2. ./app

Sie erhalten die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2

Da das pro.go-File erfordert, dass ein pro-Tag vorhanden ist, wird die Datei ignoriert und die Anwendung wird ohne sie kompiliert.

Wenn Sie den go build-Befehl ausführen, können wir die -tags-Flag verwenden, um bedingentlich Code in die kompilierte Quelle aufzunehmen, indem wir das Tag selbst als Argument hinzufügen. Lassen Sie uns das für den pro-Tag tun:

  1. go build -tags pro

Dies wird die folgende Ausgabe erzeugen:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

Nun erhalten wir die zusätzlichen Funktionen nur, wenn wir die Anwendung mit dem pro-Build-Tag erstellen.

Das ist in Ordnung, wenn es nur zwei Versionen gibt, aber die Dinge werden komplizierter, wenn Sie weitere Tags hinzufügen. Um die Enterprise-Version unserer App im nächsten Schritt hinzuzufügen, verwenden wir mehrere Build-Tags, die mit boolscher Logik verbunden sind.

Build-Tag-Boolsche Logik

Wenn es mehrere Build-Tags in einem Go-Paket gibt, interagieren die Tags miteinander mittels boolscher Logik. Um dies zu demonstrieren, fügen wir die Enterprise-Ebene unserer Anwendung hinzu, indem wir sowohl das pro-Tag als auch das enterprise-Tag verwenden.

Um eine Enterprise-Binärdatei zu erstellen, müssen wir sowohl die Standardfunktionen, die Pro-Ebene-Funktionen als auch eine neue Reihe von Funktionen für Enterprise einschließen. Öffnen Sie zuerst einen Editor und erstellen Sie eine neue Datei enterprise.go, die die neuen Enterprise-Funktionen hinzufügt:

  1. nano enterprise.go

Der Inhalt von enterprise.go wird fast identisch zu pro.go sein, aber enthält neue Funktionen. Fügen Sie die folgenden Zeilen in die Datei ein:

enterprise.go
package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Speichern Sie die Datei und beenden Sie den Editor.

Derzeit hat die Datei enterprise.go keine Build-Tags, und wie du gelernt hast, als du pro.go hinzugefügt hast, bedeutet das, dass diese Funktionen zur Free-Version hinzugefügt werden, wenn go.build ausgeführt wird. Für pro.go hast du // +build pro und eine neue Zeile am Anfang der Datei hinzugefügt, um go build mitzuteilen, dass es nur dann eingebunden werden soll, wenn -tags pro verwendet wird. In dieser Situation musstest du nur ein Build-Tag hinzufügen, um das Ziel zu erreichen. Bei dem Hinzufügen der neuen Enterprise-Funktionen musst du jedoch zunächst auch die Pro-Funktionen haben.

Lass uns zunächst den Support für das pro-Build-Tag in enterprise.go hinzufügen. Öffne die Datei mit deinem Texteditor:

  1. nano enterprise.go

Füge dann vor der Deklaration package main das Build-Tag ein und stelle sicher, dass nach dem Build-Tag eine neue Zeile folgt:

enterprise.go
// +build pro

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Speichere und verlasse die Datei.

Kompiliere und führe die Anwendung ohne Tags aus:

  1. go build
  2. ./app

Du erhältst die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2

Die Enterprise-Funktionen erscheinen nicht mehr in der Free-Version. Jetzt fügen wir das pro-Build-Tag hinzu und bauen und führen die Anwendung erneut aus:

  1. go build -tags pro
  2. ./app

Du erhältst die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

Das ist immer noch nicht genau das, was wir benötigen: Die Enterprise-Funktionen erscheinen jetzt, wenn wir versuchen, die Pro-Version zu erstellen. Um dies zu lösen, benötigen wir ein weiteres Build-Tag. Im Gegensatz zum pro-Tag müssen wir jetzt sicherstellen, dass sowohl die pro– als auch die enterprise-Funktionen verfügbar sind.

Das Go-Build-System berücksichtigt diese Situation, indem es die Verwendung grundlegender Boolean-Logik im Build-Tags-System ermöglicht.

Lassen Sie uns enterprise.go noch einmal öffnen:

  1. nano enterprise.go

Fügen Sie einen weiteren Build-Tag, enterprise, in der gleichen Zeile wie das pro-Tag ein:

enterprise.go
// +build pro enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Speichern Sie die Datei und schließen Sie sie.

Nun compilieren und starten wir die Anwendung mit dem neuen enterprise-Build-Tag.

  1. go build -tags enterprise
  2. ./app

Das gibt folgendes:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2

Nun haben wir die Pro-Funktionen verloren. Das liegt daran, dass wir, wenn wir mehrere Build-Tags in der gleichen Zeile in einer .go-Datei setzen, go build diese als ODER-Logik interpretiert. Mit der Zeile // +build pro enterprise wird die Datei enterprise.go erstellt, wenn entweder das pro-Build-Tag oder das enterprise-Build-Tag vorhanden ist. Wir müssen die Build-Tags korrekt einrichten, um beide zu erfordern und stattdessen UND-Logik zu verwenden.

Anstatt beide Tags in derselben Zeile zu setzen, interpretiert go build diese Tags mit UND-Logik, wenn wir sie in separate Zeilen setzen.

Öffnen Sie enterprise.go noch einmal und teilen Sie die Build-Tags in mehrere Zeilen auf.

enterprise.go
// +build pro
// +build enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Nun compilieren und starten Sie die Anwendung mit dem neuen enterprise-Build-Tag.

  1. go build -tags enterprise
  2. ./app

Sie erhalten die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2

Noch nicht ganz perfekt: Da eine UND-Aussage erfordert, dass beide Elemente als wahr betrachtet werden, müssen wir beide pro– und enterprise-Build-Tags verwenden.

Lassen Sie uns noch einmal versuchen:

  1. go build -tags "enterprise pro"
  2. ./app

Sie erhalten die folgende Ausgabe:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

Jetzt kann unsere Anwendung aus dem gleichen Quellbaum in mehrere Wege gebaut werden, um die Funktionen der Anwendung entsprechend freizuschalten.

In diesem Beispiel haben wir einen neuen // +build-Tag verwendet, um eine UND-Logik zu signalisieren, aber es gibt alternative Möglichkeiten, Boolean-Logik mit Build-Tags darzustellen. Die folgende Tabelle enthält einige Beispiele für andere syntaktische Formatierungen für Build-Tags sowie deren Boolean-Äquivalent:

Build Tag Syntax Build Tag Sample Boolean Statement
Space-separated elements // +build pro enterprise pro OR enterprise
Comma-separated elements // +build pro,enterprise pro AND enterprise
Exclamation point elements // +build !pro NOT pro

Schlussfolgerung

In dieser Anleitung haben Sie Build-Tags verwendet, um zu steuern, welcher Teil Ihres Codes in das Binary kompiliert wird. Zuerst haben Sie Build-Tags deklariert und sie mit go build verwendet, dann haben Sie mehrere Tags mit Boolean-Logik kombiniert. Sie haben dann ein Programm gebaut, das die unterschiedlichen Feature-Sets einer Free-, Pro- und Enterprise-Version darstellt und die mächtige Kontrollmöglichkeit zeigt, die Build-Tags über Ihr Projekt geben können.

Wenn Sie mehr über Build-Tags erfahren möchten, schauen Sie sich die Golang-Dokumentation zu diesem Thema an oder erkunden Sie unsere How To Code in Go-Reihe weiter.

Source:
https://www.digitalocean.com/community/tutorials/customizing-go-binaries-with-build-tags