Personnaliser les binaires Go avec des tags de build

Introduction

En Go, une tag de construction, ou une contrainte de construction, est un identificateur ajouté au code pour déterminer quand le fichier doit être inclus dans un paquet pendant le processus de build. Cela permet de construire différentes versions de votre application Go à partir du même code source et de basculer entre elles de manière rapide et organisée. De nombreux développeurs utilisent les tags de construction pour améliorer le workflow de la construction d’applications compatible avec plusieurs plateformes, telles que des programmes nécessitant des modifications de code pour prendre en compte les variances entre différents systèmes d’exploitation. Les tags de construction sont également utilisés pour les tests d’intégration, permettant de basculer rapidement entre le code intégré et le code avec un service mock ou stub, ainsi que pour différents niveaux de jeux de fonctionnalités dans une application.

Prenons par exemple le problème des ensembles de fonctionnalités clients différents. Lorsque vous écrivez certaines applications, vous pouvez vouloir contrôler les fonctionnalités à inclure dans le binaire, comme une application qui offre des niveaux Gratuit, Pro et Entreprise. Lorsque le client augmente son niveau d’abonnement dans ces applications, plus de fonctionnalités deviennent déverrouillées et disponibles. Pour résoudre ce problème, vous pourriez maintenir des projets séparés et essayer de les synchroniser les uns avec les autres grâce à l’utilisation d’instructions import. Bien que cette approche fonctionne, elle deviendrait fastidieuse et sujette aux erreurs au fil du temps. Une approche alternative serait d’utiliser des balises de construction.

Dans cet article, vous utiliserez des balises de construction en Go pour générer différents binaires exécutables qui offrent les ensembles de fonctionnalités Gratuit, Pro et Entreprise d’une application échantillon. Chacun disposera d’un ensemble de fonctionnalités différent, avec la version Gratuite par défaut.

Prérequis

Pour suivre l’exemple de cet article, vous aurez besoin de :

Construire la version gratuite

Commençons par construire la version gratuite de l’application, car elle sera la valeur par défaut lorsque vous exécutez go build sans aucun tag de construction. Plus tard, nous utiliserons des tags de construction pour ajouter sélectivement d’autres parties à notre programme.

Dans le répertoire src, créez un dossier portant le nom de votre application. Ce tutoriel utilisera app :

  1. mkdir app

Accédez à ce dossier :

  1. cd app

Ensuite, créez un nouveau fichier texte dans votre éditeur de texte préféré nommé main.go :

  1. nano main.go

Maintenant, définissons la version gratuite de l’application. Ajoutez le contenu suivant à main.go :

main.go
package main

import "fmt"

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

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

Dans ce fichier, nous avons créé un programme qui déclare une tranche nommée features, qui contient deux chaînes qui représentent les fonctionnalités de notre application gratuite. La fonction main() de l’application utilise une boucle for pour range à travers la tranche features et imprimer toutes les fonctionnalités disponibles à l’écran.

Enregistrez et quittez le fichier. Maintenant que ce fichier est enregistré, vous n’aurez pas à le modifier pour le reste de l’article. Au lieu de cela, vous utiliserez des balises de construction pour modifier les caractéristiques des binaires que vous construirez à partir de celui-ci.

Construisez et exécutez le programme :

  1. go build
  2. ./app

Vous recevrez l’output suivant :

Output
> Free Feature #1 > Free Feature #2

Le programme a imprimé nos deux caractéristiques gratuites, achevant la version gratuite de notre application.

Jusqu’à présent, vous avez créé une application avec un ensemble de caractéristiques très basique. La prochaine fois, vous construirez une méthode pour ajouter d’autres caractéristiques dans l’application à la phase de construction.

Ajout des Fonctionnalités Pro Avec go build

Jusqu’à maintenant, nous avons évité de modifier le fichier main.go, simulant un environnement de production commun où le code doit être ajouté sans modifier et éventuellement casser le code principal. Comme nous ne pouvons pas éditer le fichier main.go, nous aurons besoin d’utiliser un autre mécanisme pour injecter d’autres caractéristiques dans la slice features en utilisant des balises de construction.

Créons un nouveau fichier appelé pro.go qui utilisera une fonction init() pour ajouter d’autres caractéristiques à la slice features :

  1. nano pro.go

Une fois que l’éditeur a ouvert le fichier, ajoutez les lignes suivantes :

pro.go
package main

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

Dans ce code, nous avons utilisé init() pour exécuter du code avant la fonction main() de notre application, suivi de append() pour ajouter les fonctionnalités Pro au slice features. Enregistrez et quittez le fichier.

Compilez et exécutez l’application en utilisant go build :

  1. go build

Puisque nous avons maintenant deux fichiers dans notre répertoire actuel (pro.go et main.go), go build créera un binaire à partir des deux. Exécutez ce binaire :

  1. ./app

Cela vous donnera le jeu de fonctionnalités suivant :

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

L’application inclut maintenant à la fois les fonctionnalités Pro et Gratuites. Cependant, ce n’est pas souhaitable : comme il n’y a pas de distinction entre les versions, la version Gratuite inclut maintenant les fonctionnalités qui sont censées être uniquement disponibles dans la version Pro. Pour corriger cela, vous pourriez inclure plus de code pour gérer les différents niveaux de l’application, ou vous pourriez utiliser des tags de construction pour indiquer à la chaîne d’outils Go quels fichiers .go doivent être compilés et quels ignorer. Ajoutons des tags de construction à l’étape suivante.

Ajout de Tags de Construction

Vous pouvez maintenant utiliser des tags de construction pour distinguer la version Pro de votre application de la version Gratuite.

Commençons par examiner à quoi ressemble un tag de construction :

// +build tag_name

En plaçant cette ligne de code en tant que première ligne de votre paquet et en remplaçant tag_name par le nom de votre balise de construction, vous étiquettez ce paquet comme du code pouvant être inclusionnellement inclus dans le binaire final. Voyons cela en action en ajoutant une balise de construction au fichier pro.go pour indiquer au commande go build de l’ignorer除非 la balise est spécifiée. Ouvrez le fichier dans votre éditeur de texte :

  1. nano pro.go

Ensuite, ajoutez la ligne suivante mise en avant :

pro.go
// +build pro

package main

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

Au début du fichier pro.go, nous avons ajouté // +build pro suivi d’une nouvelle ligne vide. Cette nouvelle ligne est obligatoire, sinon Go interprète cela comme un commentaire. Les déclarations d’étiquettes de construction doivent également se trouver au sommet d’un fichier .go. Rien, pas même les commentaires, ne doit se trouver au-dessus des étiquettes de construction.

La déclaration +build indique au commande go build que cela n’est pas un commentaire, mais plutôt une étiquette de construction. La deuxième partie est l’étiquette pro. En ajoutant cette étiquette en haut du fichier pro.go, le commande go build ne chargera maintenant le fichier pro.go que si l’étiquette pro est présente.

Compilez et exécutez l’application à nouveau :

  1. go build
  2. ./app

Vous recevrez la sortie suivante :

Output
> Free Feature #1 > Free Feature #2

Comme le fichier pro.go nécessite l’étiquette pro pour être présente, le fichier est ignoré et l’application est compilée sans lui.

Lors de l’exécution de la commande go build, nous pouvons utiliser le flag -tags pour inclure conditionnellement du code dans la source compilée en ajoutant l’étiquette elle-même en argument. Faisons-le pour la balise pro :

  1. go build -tags pro

Cela affichera ce qui suit :

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

Maintenant, nous n’obtenons les fonctionnalités supplémentaires que lorsque nous construisons l’application en utilisant l’attribut pro.

Cela est acceptable si il n’y a que deux versions, mais les choses se compliquent quand vous ajoutez d’autres balises. Pour ajouter la version Entreprise de notre application au prochain étape, nous utiliserons plusieurs balises de construction jointes par une logique booléenne.

Logique booléenne des balises de construction

Lorsqu’il y a plusieurs balises de construction dans un paquet Go, les balises interagissent l’une avec l’autre en utilisant une logique booléenne. Pour la démontrer, nous ajouterons le niveau Entreprise de notre application en utilisant à la fois l’attribut pro et l’attribut enterprise.

Pour construire un binaire Entreprise, nous aurons besoin d’inclure les fonctionnalités par défaut, les fonctionnalités de niveau Pro et une nouvelle série de fonctionnalités pour l’Entreprise. Tout d’abord, ouvrez un éditeur et créez un nouveau fichier, enterprise.go, qui ajoutera les nouvelles fonctionnalités Entreprise :

  1. nano enterprise.go

Le contenu de enterprise.go sera presque identique à pro.go mais contiendra de nouvelles fonctionnalités. Ajoutez les lignes suivantes au fichier :

enterprise.go
package main

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

Enregistrez et quittez le fichier.

Actuellement, le fichier enterprise.go n’a pas de balises de construction, et comme vous avez appris lorsque vous avez ajouté pro.go, cela signifie que ces fonctionnalités seront ajoutées à la version gratuite lorsqu’elle est exécutée avec go build. Pour pro.go, vous avez ajouté // +build pro et une ligne de fin au début du fichier pour dire à go build que ce ne soit qu’inclus quand -tags pro est utilisé. En ce qui concerne cette situation, vous n’avez nécessairement besoin d’une seule balise de construction. Cependant, lorsque vous ajoutez les nouvelles fonctionnalités d’entreprise, vous devez d’abord également avoir les fonctionnalités Pro.

Ouvrez le fichier avec votre éditeur de texte :

  1. nano enterprise.go

Pour commencer, ajoutez la balise de construction avant la déclaration package main et assurez-vous d’inclure une nouvelle ligne après la balise de construction :

enterprise.go
// +build pro

package main

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

Sauvegardez et fermez le fichier.

Compiler et exécuter l’application sans aucune étiquette :

  1. go build
  2. ./app

Vous recevrez les informations suivantes :

Output
> Free Feature #1 > Free Feature #2

Les fonctionnalités d’entreprise ne sont plus affichées dans la version gratuite. Maintenant, ajoutez la balise pro et compilez et exécutez l’application de nouveau :

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

Vous recevrez les informations suivantes :

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

Ceci n’est toujours pas exactement ce que nous avons besoin : Les fonctionnalités d’entreprise sont maintenant affichées lorsque nous essayons de construire la version Pro. Pour résoudre ce problème, nous devons maintenant utiliser une autre balise de construction. Contrairement aux balises pro, toutefois, nous avons besoin de faire fonctionner certaines opérations booléennes simples dans le système de construction Go.

Le système de construction Go permet d’utiliser un peu de logique booléenne dans le système des balises de construction. Pour spécifier que les deux fonctionnalités pro et enterprise soient disponibles, ajoutez une autre balise de construction qui combine les deux avec des « && » ou des « || ». Dans ce cas, si vous voulez inclure les fonctionnalités pro et enterprise, ajoutez une balise de construction qui combine les deux avec des « && ». Si vous voulez seulement inclure les fonctionnalités enterprise, ajoutez une balise de construction qui combine les deux avec des « || ».

Ouvrons à nouveau enterprise.go :

  1. nano enterprise.go

Ajoutez un autre tag de construction, enterprise, sur la même ligne que le tag pro :

enterprise.go
// +build pro enterprise

package main

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

Enregistrez et fermez le fichier.

Maintenant, compilez et exécutez l’application avec le nouveau tag de construction enterprise.

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

Cela donnera ce qui suit :

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

A présent, nous avons perdu les fonctionnalités Pro. Cela est dû au fait que lorsque nous plaçons plusieurs tags de construction sur la même ligne dans un fichier .go, go build les interprète en utilisant la logique OU. Avec l’ajout de la ligne // +build pro enterprise, le fichier enterprise.go sera compilé si soit le tag de construction pro ou le tag de construction enterprise est présent. Nous devons configurer les tags de construction correctement pour exiger les deux et utiliser en lieu et place la logique ET au lieu de l’opérateur OU.

Au lieu de placer les deux tags sur la même ligne, si nous les plaçons sur des lignes distinctes, alors go build interprétera ces tags en utilisant la logique ET.

Ouvrez à nouveau enterprise.go et séparons les tags de construction sur plusieurs lignes.

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

package main

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

Maintenant, compilez et exécutez l’application avec le nouveau tag de construction enterprise.

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

Vous recevrez l’output suivant :

Output
> Free Feature #1 > Free Feature #2

Ce n’est toujours pas ce que nous voulons : comme une déclaration ET exige que les deux éléments soient considérés comme vrai, nous devons utiliser les tags de construction pro et enterprise à la fois.

Essayons à nouveau :

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

Vous recevrez la sortie suivante :

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

Maintenant, notre application peut être construite à partir de la même arborescence de source de plusieurs manières, en déverrouillant les fonctionnalités de l’application en conséquence.

Dans cet exemple, nous avons utilisé une nouvelle balise // +build pour signifier une logique ET, mais il existe des moyens alternatifs de représenter la logique booléenne avec des balises de construction. Le tableau suivant contient quelques exemples d’autres formats syntaxiques pour les balises de construction, ainsi que leur équivalent booléen :

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

Conclusion

Dans ce tutoriel, vous avez utilisé des balises de construction pour permettre de contrôler quel partie de votre code est compilée dans le binaire. Tout d’abord, vous avez déclaré des balises de construction et les avez utilisées avec go build, puis vous avez combiné plusieurs balises avec une logique booléenne. Ensuite, vous avez construit un programme qui représentait les différents ensembles de fonctionnalités d’une version Gratuite, Pro et Enterprise, montrant le niveau de contrôle puissant que les balises de construction peuvent vous donner sur votre projet.

Si vous souhaitez en savoir plus sur les balises de construction, consultez la documentation Golang sur le sujet, ou continuez à explorer notre série Comment coder en Go.

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