Um exemplo de criação de aplicativos Makefile for Go

Neste tutorial, veremos como um desenvolvedor Go pode usar um Makefile para desenvolver seus próprios aplicativos.

imagem

O que são Makefiles?


Makefile é uma ferramenta de automação incrivelmente útil que você pode usar para executar e criar aplicativos não apenas no Go, mas também na maioria das outras linguagens de programação.

Muitas vezes, isso pode ser visto no diretório raiz de muitos aplicativos Go no Github e Gitlab. É amplamente utilizado como uma ferramenta para automatizar tarefas que geralmente acompanham os desenvolvedores.

Se você usar o Go para criar serviços da Web, o Makefile ajudará a resolver as seguintes tarefas:

  • Automatize a chamada de comandos simples, como: compilar, iniciar, parar, assistir, etc.
  • Gerenciar variáveis ​​de ambiente específicas do projeto. Deve incluir o arquivo .env.
  • Um modo de desenvolvimento que é compilado automaticamente após a alteração.
  • Um modo de desenvolvimento que mostra erros de compilação.
  • Definir GOPATH para um projeto específico, para que possamos armazenar dependências na pasta do fornecedor.
  • O monitoramento simplificado de arquivos, por exemplo, faz o watch run = “go test. / ... "

Aqui está uma estrutura de diretórios típica para um projeto:

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

Se chamarmos o comando make neste diretório, obteremos a seguinte saída:

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

Variáveis ​​de ambiente


A primeira coisa que queremos do Makefile é incluir as variáveis ​​de ambiente que definimos para o projeto. Portanto, a primeira linha ficará assim:

 include .env 

Em seguida, definimos o nome do projeto, Go folders / files, caminhos para 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 

No restante do Makefile, frequentemente usaremos a variável GOPATH. Todas as nossas equipes devem estar associadas ao GOPATH de um projeto específico, caso contrário elas não funcionarão. Isso fornece um isolamento limpo de nossos projetos, mas ao mesmo tempo complica o trabalho. Para simplificar a tarefa, podemos adicionar um comando exec que executará qualquer comando com nosso GOPATH.

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

No entanto, vale lembrar que você precisa usar o exec apenas se quiser fazer algo que não possa ser escrito no makefile.

Modo de desenvolvimento


O modo de desenvolvimento deve:

  • Limpar cache de compilação
  • Código de compilação
  • Execute o serviço em segundo plano
  • Repita essas etapas quando o código for alterado.

Isso parece fácil. No entanto, a dificuldade está no fato de executar simultaneamente o serviço e o observador de arquivos. Antes de iniciar um novo processo, devemos garantir que ele pare corretamente e que não violemos o comportamento usual da linha de comando ao pressionar Control-C ou Control-D.

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

O código descrito acima resolve as seguintes tarefas:

  • Compila e executa o serviço em segundo plano.
  • O processo principal não é executado em segundo plano, portanto, podemos interrompê-lo usando Control-C.
  • Pára os processos em segundo plano quando o processo principal é interrompido. armadilha é necessária apenas para isso.
  • Recompila e reinicia o servidor quando o código é alterado.

Nas seções a seguir, explicarei esses comandos com mais detalhes.

Compilação


O comando compile não chama apenas compilação go em segundo plano - limpa a saída de erro e imprime uma versão simplificada.

É assim que a saída da linha de comando se parece quando fizemos edições recentes:

imagem

 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 

Início / parada do servidor


O start-server inicia um binário compilado em segundo plano, salvando seu PID em um arquivo temporário. stop-server lê o PID e mata o processo, se necessário.

 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 

Monitoramento de mudanças


Precisamos de um arquivo inspetor para rastrear as alterações. Eu tentei muitos, mas não consegui encontrar um adequado, então escrevi minha própria ferramenta de monitoramento de arquivos - yolo . Instale-o usando o comando:

 $ go get github.com/azer/yolo 

Após a instalação, podemos observar alterações no diretório do projeto, excluindo as pastas fornecedor e bin.

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

Agora temos um comando watch que rastreia recursivamente as alterações no diretório do projeto, com exceção do diretório do fornecedor. Podemos apenas passar qualquer comando para executar.
Por exemplo, inicie as chamadas make-start-server quando o código for alterado:

 make watch run="make compile start-server" 

Podemos usá-lo para executar testes ou verificar as condições da corrida automaticamente. As variáveis ​​de ambiente serão definidas em tempo de execução, para que você não precise se preocupar com GOPATH:

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

Um recurso interessante do Yolo é sua interface da web. Se você ativá-lo, poderá ver imediatamente a saída do seu comando na interface da web. Tudo que você precisa fazer é passar a opção -a:

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

Abra o host local: 9001 em um navegador e veja imediatamente o resultado do trabalho:

imagem

Instalação de Dependências


Quando fazemos alterações no código, queremos que as dependências ausentes sejam carregadas antes da compilação. O comando de instalação fará o trabalho para nós:

 install: go-get 

Automatizaremos a chamada de instalação quando o arquivo for alterado antes da compilação, para que as dependências sejam instaladas automaticamente. Se você deseja instalar a dependência manualmente, você pode executar:

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

Internamente, este comando será convertido para:

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

Como isso funciona? Consulte a próxima seção, onde adicionamos comandos Go regulares para implementar comandos de nível superior.

Comandos Go


Como queremos instalar o GOPATH no diretório do projeto para simplificar o gerenciamento de dependências, que ainda não foi formalmente resolvido no ecossistema Go, precisamos agrupar todos os comandos Go em um 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 

Ajuda


Finalmente, precisamos do comando help para ver uma lista dos comandos disponíveis. Podemos gerar automaticamente uma saída de ajuda lindamente formatada usando os comandos sed e column:

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

O comando a seguir verifica o Makefile em busca de linhas começando com ## e as exibe. Dessa forma, você pode simplesmente comentar sobre comandos específicos, e os comentários serão exibidos com o comando help.

Se adicionarmos alguns comentários:

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

Nós teremos:

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

Versão 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/pt461467/


All Articles