En este tutorial, veremos cómo un desarrollador de Go puede usar un Makefile para desarrollar sus propias aplicaciones.
¿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)")
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.
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:

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

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/^
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:
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)")