Ein Beispiel für das Erstellen eines Makefile für Go-Anwendungen

In diesem Tutorial sehen wir uns an, wie ein Go-Entwickler ein Makefile verwenden kann, um seine eigenen Anwendungen zu entwickeln.

Bild

Was sind Makefiles?


Makefile ist ein unglaublich nützliches Automatisierungstool, mit dem Sie Anwendungen nicht nur unter Go, sondern auch in den meisten anderen Programmiersprachen ausführen und erstellen können.

Es ist häufig im Stammverzeichnis vieler Go-Apps auf Github und Gitlab zu sehen. Es wird häufig als Werkzeug zur Automatisierung von Aufgaben verwendet, die häufig von Entwicklern begleitet werden.

Wenn Sie zum Erstellen von Webdiensten Go verwenden, hilft das Makefile bei der Lösung der folgenden Aufgaben:

  • Automatisieren Sie den Aufruf einfacher Befehle wie Kompilieren, Starten, Stoppen, Beobachten usw.
  • Projektspezifische Umgebungsvariablen verwalten. Es sollte die .env-Datei enthalten.
  • Ein Entwicklungsmodus, der bei Änderungen automatisch kompiliert wird.
  • Ein Entwicklungsmodus, der Kompilierungsfehler anzeigt.
  • Definieren von GOPATH für ein bestimmtes Projekt, damit Abhängigkeiten im Lieferantenordner gespeichert werden können.
  • Durch die vereinfachte Dateiüberwachung wird beispielsweise watch run = „go test“ ausgeführt. / ... "

Hier ist eine typische Verzeichnisstruktur für ein Projekt:

.env Makefile main.go bin/ src/ vendor/ 

Wenn wir den Befehl make in diesem Verzeichnis aufrufen, erhalten wir die folgende Ausgabe:

 $ make Choose a command run in my-web-server: install Install missing dependencies. Runs `go get` internally. start Start in development mode. Auto-starts when code changes. stop Stop development mode. compile Compile the binary. watch Run given command when code changes. eg; make watch run="go test ./..." exec Run given command, wrapped with custom GOPATH. eg; make exec run="go test ./..." clean Clean build files. Runs `go clean` internally. 

Umgebungsvariablen


Das erste, was wir vom Makefile erwarten, ist, die Umgebungsvariablen einzuschließen, die wir für das Projekt definiert haben. Daher sieht die erste Zeile folgendermaßen aus:

 include .env 

Als nächstes definieren wir den Projektnamen, Ordner / Dateien, Pfade zu PID ...

 PROJECTNAME=$(shell basename "$(PWD)") # Go . GOBASE=$(shell pwd) GOPATH=$(GOBASE)/vendor:$(GOBASE):/home/azer/code/golang #       . GOBIN=$(GOBASE)/bin GOFILES=$(wildcard *.go) #     ,       . STDERR=/tmp/.$(PROJECTNAME)-stderr.txt # PID-    ,       PID=/tmp/.$(PROJECTNAME)-api-server.pid # Make     Linux.   silent. MAKEFLAGS += --silent 

Im Rest des Makefiles verwenden wir häufig die Variable GOPATH. Alle unsere Teams müssen mit dem GOPATH eines bestimmten Projekts verbunden sein, sonst funktionieren sie nicht. Dies sorgt für eine saubere Isolation unserer Projekte, erschwert aber gleichzeitig die Arbeit. Um die Aufgabe zu vereinfachen, können wir einen exec-Befehl hinzufügen, der jeden Befehl mit unserem GOPATH ausführt.

 # exec:     GOPATH. : make exec run = " go test ./...” exec: @GOPATH=$(GOPATH) GOBIN=$(GOBIN) $(run) 

Beachten Sie jedoch, dass Sie exec nur verwenden müssen, wenn Sie etwas tun möchten, das nicht in das Makefile geschrieben werden kann.

Entwicklungsmodus


Der Entwicklungsmodus sollte:

  • Löschen Sie den Build-Cache
  • Code kompilieren
  • Führen Sie den Dienst im Hintergrund aus
  • Wiederholen Sie diese Schritte, wenn sich der Code ändert.

Das klingt einfach. Die Schwierigkeit liegt jedoch in der Tatsache, dass wir gleichzeitig den Dienst und den Datei-Watcher ausführen. Bevor wir einen neuen Prozess starten, müssen wir sicherstellen, dass er korrekt gestoppt wird und dass wir nicht das übliche Befehlszeilenverhalten verletzen, wenn wir Strg-C oder Strg-D drücken.

 start: bash -c "trap 'make stop' EXIT; $(MAKE) compile start-server watch run='make compile start-server'" stop: stop-server 

Der oben beschriebene Code löst die folgenden Aufgaben:

  • Kompiliert und führt den Dienst im Hintergrund aus.
  • Der Hauptprozess läuft nicht im Hintergrund, daher können wir ihn mit Control-C unterbrechen.
  • Stoppt Hintergrundprozesse, wenn der Hauptprozess unterbrochen wird. Falle wird nur dafür benötigt.
  • Kompiliert den Server neu und startet ihn neu, wenn sich der Code ändert.

In den folgenden Abschnitten werde ich diese Befehle genauer erläutern.

Zusammenstellung


Der Befehl compile ruft nicht nur go compile im Hintergrund auf, sondern löscht die Fehlerausgabe und druckt eine vereinfachte Version.

So sieht die Befehlszeilenausgabe aus, als wir wichtige Änderungen vorgenommen haben:

Bild

 compile: @-touch $(STDERR) @-rm $(STDERR) @-$(MAKE) -s go-compile 2> $(STDERR) @cat $(STDERR) | sed -e '1s/.*/\nError:\n/' | sed 's/make\[.*/ /' | sed "/^/s/^/ /" 1>&2 

Server starten / stoppen


start-server startet eine im Hintergrund kompilierte Binärdatei und speichert deren PID in einer temporären Datei. stop-server liest die PID und beendet den Prozess bei Bedarf.

 start-server: @echo " > $(PROJECTNAME) is available at $(ADDR)" @-$(GOBIN)/$(PROJECTNAME) 2>&1 & echo $$! > $(PID) @cat $(PID) | sed "/^/s/^/ \> PID: /" stop-server: @-touch $(PID) @-kill `cat $(PID)` 2> /dev/null || true @-rm $(PID) restart-server: stop-server start-server 

Änderungsüberwachung


Wir benötigen eine Überwachungsdatei, um Änderungen zu verfolgen. Ich habe viele ausprobiert, konnte aber kein passendes finden, also habe ich mein eigenes Tool zur Dateiüberwachung geschrieben - yolo . Installieren Sie es mit dem folgenden Befehl:

 $ go get github.com/azer/yolo 

Nach der Installation können wir Änderungen im Projektverzeichnis beobachten, mit Ausnahme der Hersteller- und Bin-Ordner.

 ## watch:      ,  make watch run="echo 'hey'" watch: @yolo -i . -e vendor -e bin -c $(run) 

Wir haben jetzt einen Überwachungsbefehl, der Änderungen am Projektverzeichnis mit Ausnahme des Herstellerverzeichnisses rekursiv verfolgt. Wir können einfach jeden Befehl zum Ausführen übergeben.
Beispiel: Start ruft make-start-server auf, wenn sich der Code ändert:

 make watch run="make compile start-server" 

Wir können damit Tests durchführen oder die Rennbedingungen automatisch überprüfen. Umgebungsvariablen werden zur Laufzeit festgelegt, sodass Sie sich keine Gedanken über GOPATH machen müssen:

 make watch run="go test ./..." 

Ein schönes Feature von Yolo ist das Webinterface. Wenn Sie es aktivieren, können Sie die Ausgabe Ihres Befehls sofort in der Weboberfläche sehen. Alles was Sie tun müssen, ist die Option -a zu übergeben:

 yolo -i . -e vendor -e bin -c "go run foobar.go" -a localhost:9001 

Öffnen Sie localhost: 9001 in einem Browser und sehen Sie sofort das Ergebnis der Arbeit:

Bild

Abhängigkeitsinstallation


Wenn wir Änderungen am Code vornehmen, möchten wir, dass die fehlenden Abhängigkeiten vor dem Kompilieren geladen werden. Der Installationsbefehl erledigt die Aufgabe für uns:

 install: go-get 

Wir werden den Installationsaufruf automatisieren, wenn sich die Datei vor dem Kompilieren ändert, sodass die Abhängigkeiten automatisch installiert werden. Wenn Sie die Abhängigkeit manuell installieren möchten, können Sie Folgendes ausführen:

 make install get="github.com/foo/bar" 

Intern wird dieser Befehl konvertiert in:

 $ GOPATH=~/my-web-server GOBIN=~/my-web-server/bin go get github.com/foo/bar 

Wie funktioniert es Im nächsten Abschnitt fügen wir reguläre Go-Befehle hinzu, um Befehle höherer Ebene zu implementieren.

Gehe zu Befehlen


Da wir GOPATH im Projektverzeichnis installieren möchten, um das Abhängigkeitsmanagement zu vereinfachen, das im Go-Ökosystem noch nicht offiziell gelöst wurde, müssen wir alle Go-Befehle in ein Makefile einschließen.

 go-compile: go-clean go-get go-build go-build: @echo " > Building binary..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECTNAME) $(GOFILES) go-generate: @echo " > Generating dependency files..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate) go-get: @echo " > Checking if there is any missing dependencies..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get $(get) go-install: @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES) go-clean: @echo " > Cleaning build cache" @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean 

Hilfe


Schließlich benötigen wir den Befehl help, um eine Liste der verfügbaren Befehle anzuzeigen. Mit den Befehlen sed und column können wir automatisch schön formatierte Hilfeausgaben generieren:

 help: Makefile @echo " Choose a command run in "$(PROJECTNAME)":" @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 

Der folgende Befehl durchsucht das Makefile nach Zeilen, die mit ## beginnen, und zeigt sie an. Auf diese Weise können Sie einfach bestimmte Befehle kommentieren, und Kommentare werden mit dem Hilfebefehl angezeigt.

Wenn wir ein paar Kommentare hinzufügen:

 ## install: Install missing dependencies. Runs `go get` internally. install: go-get ## start: Start in development mode. Auto-starts when code changes. start: ## stop: Stop development mode. stop: stop-server 

Wir werden bekommen:

 $ make help Choose a command run in my-web-server: install Install missing dependencies. Runs `go get` internally. start Start in development mode. Auto-starts when code changes. stop Stop development mode. 

Endgültige Version


 include .env PROJECTNAME=$(shell basename "$(PWD)") # Go related variables. GOBASE=$(shell pwd) GOPATH="$(GOBASE)/vendor:$(GOBASE)" GOBIN=$(GOBASE)/bin GOFILES=$(wildcard *.go) # Redirect error output to a file, so we can show it in development mode. STDERR=/tmp/.$(PROJECTNAME)-stderr.txt # PID file will keep the process id of the server PID=/tmp/.$(PROJECTNAME).pid # Make is verbose in Linux. Make it silent. MAKEFLAGS += --silent ## install: Install missing dependencies. Runs `go get` internally. eg; make install get=github.com/foo/bar install: go-get ## start: Start in development mode. Auto-starts when code changes. start: bash -c "trap 'make stop' EXIT; $(MAKE) compile start-server watch run='make compile start-server'" ## stop: Stop development mode. stop: stop-server start-server: stop-server @echo " > $(PROJECTNAME) is available at $(ADDR)" @-$(GOBIN)/$(PROJECTNAME) 2>&1 & echo $$! > $(PID) @cat $(PID) | sed "/^/s/^/ \> PID: /" stop-server: @-touch $(PID) @-kill `cat $(PID)` 2> /dev/null || true @-rm $(PID) ## watch: Run given command when code changes. eg; make watch run="echo 'hey'" watch: @GOPATH=$(GOPATH) GOBIN=$(GOBIN) yolo -i . -e vendor -e bin -c "$(run)" restart-server: stop-server start-server ## compile: Compile the binary. compile: @-touch $(STDERR) @-rm $(STDERR) @-$(MAKE) -s go-compile 2> $(STDERR) @cat $(STDERR) | sed -e '1s/.*/\nError:\n/' | sed 's/make\[.*/ /' | sed "/^/s/^/ /" 1>&2 ## exec: Run given command, wrapped with custom GOPATH. eg; make exec run="go test ./..." exec: @GOPATH=$(GOPATH) GOBIN=$(GOBIN) $(run) ## clean: Clean build files. Runs `go clean` internally. clean: @(MAKEFILE) go-clean go-compile: go-clean go-get go-build go-build: @echo " > Building binary..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECTNAME) $(GOFILES) go-generate: @echo " > Generating dependency files..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate) go-get: @echo " > Checking if there is any missing dependencies..." @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get $(get) go-install: @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES) go-clean: @echo " > Cleaning build cache" @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean .PHONY: help all: help help: Makefile @echo @echo " Choose a command run in "$(PROJECTNAME)":" @echo @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' @echo 

Source: https://habr.com/ru/post/de461467/


All Articles