Mes options de construction préférées pour Go

L’une des parties les plus gratifiantes de l’apprentissage d’un nouveau langage de programmation est enfin d’exécuter un exécutable et d’obtenir la sortie souhaitée. Lorsque j’ai découvert le langage de programmation Go, j’ai commencé par lire quelques exemples de programmes pour me familiariser avec la syntaxe, puis j’ai écrit de petits programmes de test. Au fil du temps, cette approche m’a aidé à me familiariser avec la compilation et la construction du programme.
Les options de construction disponibles pour Go offrent des moyens de mieux contrôler le processus de construction. Ils peuvent également fournir des informations supplémentaires pour aider à diviser le processus en parties plus petites. Dans cet article, je vais démontrer certaines des options que j’ai utilisées. Remarque : j’utilise les termes construire et compiler signifier la même chose.
Contents
- Premiers pas avec Go
- Compilation et exécution de base des programmes Go
- Sous la capuche
- Simuler la compilation sans produire l’exécutable
- Enregistrer les répertoires temporaires
- Options de compilation alternatives
- Options de compilation croisée
- Afficher les instructions d’assemblage sous-jacentes
- Supprimer les fichiers binaires pour réduire leur taille
- Conclusion
Premiers pas avec Go
J’utilise Go version 1.16.7; cependant, la commande donnée ici devrait également fonctionner sur les versions les plus récentes. Si vous n’avez pas installé Go, vous pouvez le télécharger depuis le Aller sur le site et suivez les instructions d’installation. Vérifiez la version que vous avez installée en ouvrant une commande d’invite et en tapant :
$ go version
La réponse devrait ressembler à ceci, selon votre version.
go version go1.16.7 linux/amd64
$
Compilation et exécution de base des programmes Go
Je vais commencer par un exemple de programme Go qui affiche simplement “Hello World” à l’écran.
$ cat hello.go
package mainimport "fmt"
func main() {
fmt.Println("Hello World")
}
$
Avant de discuter d’options plus avancées, je vais vous expliquer comment compiler un exemple de programme Go. je me sers de build
suivi du nom du fichier source du programme Go, qui dans ce cas est hello.go
.
$ go build hello.go
Si tout fonctionne correctement, vous devriez voir un exécutable nommé hello
créé dans votre répertoire courant. Vous pouvez vérifier qu’il est au format exécutable binaire ELF (sur la plate-forme Linux) en utilisant la commande file. Vous pouvez également l’exécuter et voir qu’il affiche “Hello World”.
$ ls
hello hello.go
$
$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$
$ ./hello
Hello World
$
Go fournit un outil pratique run
option au cas où vous ne voudriez pas avoir un binaire résultant et que vous vouliez plutôt voir si le programme fonctionne correctement et imprime la sortie souhaitée. Gardez à l’esprit que même si vous ne voyez pas l’exécutable dans votre répertoire actuel, Go compile et produit toujours l’exécutable quelque part, l’exécute, puis le supprime du système. J’expliquerai dans une section ultérieure de cet article.
$ go run hello.go
Hello World
$
$ ls
hello.go
$
Sous la capuche
Les commandes ci-dessus ont fonctionné comme un jeu d’enfant pour exécuter mon programme avec un minimum d’effort. Cependant, si vous voulez savoir ce que Go fait sous le capot pour compiler ces programmes, Go fournit un -x
option qui imprime tout ce que Go fait pour produire l’exécutable.
Un coup d’œil rapide vous indique que Go crée un répertoire de travail temporaire dans /tmp
produit l’exécutable, puis le déplace vers le répertoire courant où se trouvait le programme Go source.
$ go build -x hello.goWORK=/tmp/go-build1944767317
mkdir -p $WORK/b001/<< snip >>
mkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK \
/b001/exe/a.out -importcfg $WORK/b001 \
/importcfg.link -buildmode=exe -buildid=K26hEYzgDkqJjx2Hf-wz/\
nDueg0kBjIygx25rYwbK/W-eJaGIOdPEWgwC6o546 \
/K26hEYzgDkqJjx2Hf-wz -extld=gcc /root/.cache/go-build /cc \
/cc72cb2f4fbb61229885fc434995964a7a4d6e10692a23cc0ada6707c5d3435b-d
/usr/lib/golang/pkg/tool/linux_amd64/buildid -w $WORK \
/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out hello
rm -r $WORK/b001/
Cela aide à résoudre les mystères lorsqu’un programme s’exécute mais qu’aucun exécutable résultant n’est créé dans le répertoire actuel. En utilisant -x
montre que le fichier exécutable a bien été créé dans un /tmp
répertoire de travail et a été exécuté. Cependant, contrairement au build
l’exécutable n’a pas été déplacé vers le répertoire courant, ce qui donne l’impression qu’aucun exécutable n’a été créé.
$ go run -x hello.gomkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK/b001 \
/exe/hello -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=hK3wnAP20DapUDeuvAAS/E_TzkbzwXz6tM5dEC8Mx \
/7HYBzuaDGVdaZwSMEWAa/hK3wnAP20DapUDeuvAAS -extld=gcc \
/root/.cache/go-build/75/ \
7531fcf5e48444eed677bfc5cda1276a52b73c62ebac3aa99da3c4094fa57dc3-d
$WORK/b001/exe/hello
Hello World
Simuler la compilation sans produire l’exécutable
Ssupposez que vous ne le faites pas voulez compiler le programme et produire un binaire réel, mais vous voulez voir toutes les étapes du processus. Vous pouvez le faire en utilisant le -n
build option, qui imprime les étapes qu’il exécuterait normalement sans créer réellement le binaire.
$ go build -n hello.go
Enregistrer les répertoires temporaires
Beaucoup de travail se fait dans le /tmp
répertoire de travail, qui est supprimé une fois l’exécutable créé et exécuté. Mais que se passe-t-il si vous voulez voir quels fichiers ont été créés lors du processus de compilation ? Go fournit un -work
option qui peut être utilisée lors de la compilation d’un programme. Le -work
L’option imprime le chemin du répertoire de travail en plus d’exécuter le programme, mais elle ne supprime pas le répertoire de travail par la suite, vous pouvez donc vous déplacer vers ce répertoire et examiner tous les fichiers créés pendant le processus de compilation.
$ go run -work hello.go
WORK=/tmp/go-build3209320645
Hello World
$
$ find /tmp/go-build3209320645
/tmp/go-build3209320645
/tmp/go-build3209320645/b001
/tmp/go-build3209320645/b001/importcfg.link
/tmp/go-build3209320645/b001/exe
/tmp/go-build3209320645/b001/exe/hello
$
$ /tmp/go-build3209320645/b001/exe/hello
Hello World
$
Options de compilation alternatives
Et si, au lieu d’utiliser la magie build/run de Go, vous vouliez compiler le programme à la main et vous retrouver avec un exécutable qui peut être exécuté directement par votre système d’exploitation (dans ce cas, Linux) ? Ce processus peut être divisé en deux parties : compiler et lier. Utilisez le tool
option pour voir comment cela fonctionne.
Tout d’abord, utilisez le tool compile
possibilité de produire le résultat ar
fichier d’archive, qui contient le .o
dossier intermédiaire. Ensuite, utilisez le tool link
option sur ce hello.o
fichier pour produire l’exécutable final, qui peut ensuite s’exécuter.
$ go tool compile hello.go
$
$ file hello.o
hello.o: current ar archive
$
$ ar t hello.o
__.PKGDEF
_go_.o
$
$ go tool link -o hello hello.o
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$
$ ./hello
Hello World
$
Pour jeter un coup d’œil plus loin dans le processus de lien de production de l’exécutable à partir du hello.o
fichier, vous pouvez utiliser le -v
option, qui recherche le runtime.a
fichier inclus dans chaque exécutable Go.
$ go tool link -v -o hello hello.o
HEADER = -H5 -T0x401000 -R0x1000
searching for runtime.a in /usr/lib/golang/pkg/linux_amd64/runtime.a
82052 symbols, 18774 reachable
1 package symbols, 1106 hashed symbols, 77185 non-package symbols, 3760 external symbols
81968 liveness data
$
Options de compilation croisée
Maintenant que j’ai expliqué la compilation d’un programme Go, je vais vous montrer comment Go vous permet de créer un exécutable ciblé sur différentes architectures matérielles et systèmes d’exploitation en fournissant deux variables d’environnement (GOOS et GOARCH) avant le véritable build
commande.
Pourquoi est-ce important ? Vous pouvez voir un exemple lorsqu’un exécutable produit pour l’architecture ARM (aarch64) ne s’exécute pas sur une architecture Intel (x86_64) et produit une erreur de format Exec.
Ces options rendent triviale la production de binaires multiplateformes.
$ GOOS=linux GOARCH=arm64 go build hello.go
$
$ file ./hello
./hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped
$$ ./hello
bash: ./hello: cannot execute binary file: Exec format error
$
$ uname -m
x86_64
$
Vous pouvez lire mon article de blog précédent sur mes expériences de compilation croisée à l’aide de Go pour en savoir plus.
Afficher les instructions d’assemblage sous-jacentes
Le code source n’est pas directement converti en un exécutable, bien qu’il génère un format d’assemblage intermédiaire qui est ensuite assemblé en un exécutable. Dans Go, cela est mappé sur un format d’assemblage intermédiaire plutôt que sur les instructions d’assemblage du matériel sous-jacent.
Pour afficher ce format d’assemblage intermédiaire, utilisez -gcflags
suivi par -S
donnée à la commande build. Cette commande affiche les instructions d’assemblage.
$ go build -gcflags="-S" hello.go
# command-line-arguments
"".main STEXT size=138 args=0x0 locals=0x58 funcid=0x0
0x0000 00000 (/test/hello.go:5) TEXT "".main(SB), ABIInternal, $88-0
0x0000 00000 (/test/hello.go:5) MOVQ (TLS), CX
0x0009 00009 (/test/hello.go:5) CMPQ SP, 16(CX)
0x000d 00013 (/test/hello.go:5) PCDATA $0, $-2
0x000d 00013 (/test/hello.go:5) JLS 128<< snip >>
$
Vous pouvez également utiliser le objdump -s
option, comme indiqué ci-dessous, pour voir les instructions d’assemblage d’un programme exécutable qui a déjà été compilé.
$ ls
hello hello.go
$
$
$ go tool objdump -s main.main hello
TEXT main.main(SB) /test/hello.go
hello.go:5 0x4975a0 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
hello.go:5 0x4975a9 483b6110 CMPQ 0x10(CX), SP
hello.go:5 0x4975ad 7671 JBE 0x497620
hello.go:5 0x4975af 4883ec58 SUBQ $0x58, SP
hello.go:6 0x4975d8 4889442448 MOVQ AX, 0x48(SP)<< snip >>
$
Supprimer les fichiers binaires pour réduire leur taille
Les binaires Go sont généralement volumineux. Par exemple, un simple programme Hello World produit un binaire de taille 1,9 Mo.
$ go build hello.go
$
$ du -sh hello
1.9M hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$
Pour réduire la taille du binaire résultant, vous pouvez supprimer les informations inutiles lors de l’exécution. En utilisant -ldflags
suivi par -s -w
flags rend le binaire résultant légèrement plus léger, à 1,3M.
$ go build -ldflags="-s -w" hello.go
$
$ du -sh hello
1.3M hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$
Conclusion
J’espère que cet article vous a présenté quelques options de construction Go pratiques qui peuvent vous aider à mieux comprendre le processus de compilation Go. Pour plus d’informations sur le processus de construction et d’autres options intéressantes disponibles, reportez-vous à la section d’aide :
$ go help build