Un ejemplo de creación de aplicaciones Makefile for Go

En este tutorial, veremos cómo un desarrollador de Go puede usar un Makefile para desarrollar sus propias aplicaciones.

imagen

¿Qué son los Makefiles?


Makefile es una herramienta de automatización increíblemente útil que puede usar para ejecutar y crear aplicaciones no solo en Go, sino también en la mayoría de los otros lenguajes de programación.

A menudo se puede ver en el directorio raíz de muchas aplicaciones de Go en Github y Gitlab. Es ampliamente utilizado como una herramienta para automatizar tareas que a menudo acompañan a los desarrolladores.

Si usa Ir para crear servicios web, el Makefile lo ayudará a resolver las siguientes tareas:

  • Automatice la invocación de comandos simples, como: compilar, iniciar, detener, mirar, etc.
  • Gestionar variables de entorno específicas del proyecto. Debe incluir el archivo .env.
  • Un modo de desarrollo que se compila automáticamente al cambiar.
  • Un modo de desarrollo que muestra errores de compilación.
  • Definir GOPATH para un proyecto específico para que podamos almacenar dependencias en la carpeta del proveedor.
  • La supervisión de archivos simplificada, por ejemplo, hace que watch run = "go test. / ... "

Aquí hay una estructura de directorio típica para un proyecto:

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

Si llamamos al comando make en este directorio, obtenemos el siguiente resultado:

 $ 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. 

Variables de entorno


Lo primero que queremos del Makefile es incluir las variables de entorno que definimos para el proyecto. Por lo tanto, la primera línea se verá así:

 include .env 

A continuación, definimos el nombre del proyecto, Ir a carpetas / archivos, rutas a 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 

En el resto del Makefile, a menudo usaremos la variable GOPATH. Todos nuestros equipos deben estar asociados con el GOPATH de un proyecto específico, de lo contrario no funcionarán. Esto proporciona un aislamiento limpio de nuestros proyectos, pero al mismo tiempo complica el trabajo. Para simplificar la tarea, podemos agregar un comando exec que ejecutará cualquier comando con nuestro GOPATH.

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

Sin embargo, vale la pena recordar que necesita usar exec solo si desea hacer algo que no se puede escribir en el archivo MAKE.

Modo de desarrollo


El modo de desarrollo debería:

  • Borrar caché de compilación
  • Compilar código
  • Ejecute el servicio en segundo plano
  • Repita estos pasos cuando cambie el código.

Eso suena facil. Sin embargo, la dificultad radica en el hecho de que simultáneamente ejecutamos tanto el servicio como el observador de archivos. Antes de comenzar un nuevo proceso, debemos asegurarnos de que se detiene correctamente y que no violamos el comportamiento habitual de la línea de comando al presionar Control-C o Control-D.

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

El código descrito anteriormente resuelve las siguientes tareas:

  • Compila y ejecuta el servicio en segundo plano.
  • El proceso principal no se ejecuta en segundo plano, por lo que podemos interrumpirlo usando Control-C.
  • Detiene los procesos en segundo plano cuando se interrumpe el proceso principal. Se necesita trampa solo para esto.
  • Vuelve a compilar y reinicia el servidor cuando cambia el código.

En las siguientes secciones, explicaré estos comandos con más detalle.

Compilación


El comando de compilación no solo llama a ir a compilar en segundo plano, sino que borra la salida de error e imprime una versión simplificada.

Así es como se ve la salida de la línea de comandos cuando realizamos ediciones de última hora:

imagen

 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 

Servidor iniciar / detener


start-server inicia un binario compilado en segundo plano, guardando su PID en un archivo temporal. stop-server lee el PID y mata el proceso si es necesario.

 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 

Monitoreo de cambios


Necesitamos un archivo de monitor para rastrear los cambios. Intenté muchos, pero no pude encontrar uno adecuado, así que escribí mi propia herramienta de monitoreo de archivos: yolo . Instálelo usando el comando:

 $ go get github.com/azer/yolo 

Después de la instalación, podemos observar cambios en el directorio del proyecto, excluyendo el proveedor y las carpetas bin.

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

Ahora tenemos un comando watch que rastrea de forma recursiva los cambios en el directorio del proyecto, con la excepción del directorio de proveedores. Simplemente podemos pasar cualquier comando para ejecutar.
Por ejemplo, inicie llamadas a make-start-server cuando cambie el código:

 make watch run="make compile start-server" 

Podemos usarlo para ejecutar pruebas o verificar las condiciones de carrera automáticamente. Las variables de entorno se establecerán en tiempo de ejecución, por lo que no tiene que preocuparse por GOPATH:

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

Una buena característica de Yolo es su interfaz web. Si lo habilita, puede ver inmediatamente la salida de su comando en la interfaz web. Todo lo que necesitas hacer es pasar la opción -a:

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

Abra localhost: 9001 en un navegador e inmediatamente vea el resultado del trabajo:

imagen

Instalación de dependencia


Cuando hacemos cambios en el código, nos gustaría que las dependencias faltantes se carguen antes de la compilación. El comando de instalación hará el trabajo por nosotros:

 install: go-get 

Automatizaremos la llamada de instalación cuando el archivo cambie antes de la compilación, por lo que las dependencias se instalarán automáticamente. Si desea instalar la dependencia manualmente, puede ejecutar:

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

Internamente, este comando se convertirá a:

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

Como funciona Consulte la siguiente sección donde agregamos comandos Go regulares para implementar comandos de nivel superior.

Ir comandos


Dado que queremos instalar GOPATH en el directorio del proyecto para simplificar la gestión de dependencias, que aún no se ha decidido oficialmente en el ecosistema Go, necesitamos envolver todos los comandos Go en el Makefile.

 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 

Ayuda


Finalmente, necesitamos el comando de ayuda para ver una lista de comandos disponibles. Podemos generar automáticamente resultados de ayuda bellamente formateados utilizando los comandos sed y column:

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

El siguiente comando escanea el Makefile en busca de líneas que comienzan con ## y las muestra. De esa manera, simplemente puede comentar comandos específicos, y los comentarios se mostrarán con el comando de ayuda.

Si agregamos algunos comentarios:

 ## 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 

Obtendremos:

 $ 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. 

Versión final


 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/461467/


All Articles