为Go应用程序创建Makefile的示例

在本教程中,我们将研究Go开发人员如何使用Makefile开发自己的应用程序。

图片

什么是Makefile?


Makefile是非常有用的自动化工具,不仅可以在Go上运行,而且可以在大多数其他编程语言中运行和构建应用程序。

通常可以在Github和Gitlab上许多Go应用程序的根目录中看到它。 它广泛用作开发人员经常执行的自动化任务的工具。

如果使用Go创建Web服务,则Makefile将帮助解决以下任务:

  • 自动调用简单命令,例如:编译,启动,停止,监视等。
  • 管理特定于项目的环境变量。 它应包含.env文件。
  • 一种开发模式,可根据更改自动编译。
  • 显示编译错误的开发模式。
  • 为特定项目定义GOPATH,以便我们可以将依赖项存储在vendor文件夹中。
  • 简化的文件监视,例如,使watch run =“ go test。 / ...”

这是项目的典型目录结构:

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

如果在此目录中调用make命令,则会得到以下输出:

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

环境变量


我们需要从Makefile中获得的第一件事是包括我们为项目定义的环境变量。 因此,第一行将如下所示:

 include .env 

接下来,我们定义项目名称,转到文件夹/文件,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 

在Makefile的其余部分,我们将经常使用GOPATH变量。 我们所有的团队都必须与特定项目的GOPATH相关联,否则它们将无法工作。 这样可以完全隔离我们的项目,但同时会使工作复杂化。 为了简化任务,我们可以添加一个exec命令,该命令将使用GOPATH执行任何命令。

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

但是,值得记住的是,仅当您要执行makefile中无法编写的操作时才需要使用exec。

开发模式


开发模式应:

  • 清除构建缓存
  • 编译代码
  • 在后台运行服务
  • 代码更改时,请重复这些步骤。

听起来很简单。 但是,困难在于我们同时运行服务和文件监视程序。 在开始新进程之前,我们必须确保其正确停止,并且在按Control-C或Control-D时不违反命令行的通常行为。

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

上面描述的代码解决了以下任务:

  • 在后台编译并运行服务。
  • 主进程不在后台运行,因此我们可以使用Control-C中断它。
  • 当主进程被中断时,停止后台进程。 仅为此需要陷阱。
  • 代码更改后,重新编译并重新启动服务器。

在以下各节中,我将更详细地说明这些命令。

合编


compile命令不仅会在后台调用go compile,还会清除错误输出并输出简化版本。

这是我们进行中断编辑时的命令行输出:

图片

 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 

服务器启动/停止


启动服务器启动在后台编译的二进制文件,并将其PID保存到临时文件中。 停止服务器读取PID,并在必要时终止进程。

 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 

变更监控


我们需要一个观察程序文件来跟踪更改。 我尝试了很多,但是找不到合适的工具,所以我编写了自己的文件监视工具yolo 。 使用以下命令安装它:

 $ go get github.com/azer/yolo 

安装后,我们可以观察到项目目录中的更改,供应商和bin文件夹除外。

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

现在,我们有了一个watch命令,该命令以递归方式跟踪对项目目录的更改,但供应商目录除外。 我们可以传递任何命令来运行。
例如,当代码更改时,开始调用make-start-server:

 make watch run="make compile start-server" 

我们可以使用它来运行测试或自动检查比赛条件。 环境变量将在运行时设置,因此您不必担心GOPATH:

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

Yolo的一个不错的功能是它的Web界面。 如果启用它,则可以立即在Web界面中看到命令的输出。 您需要做的就是传递-a选项:

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

在浏览器中打开localhost:9001,立即看到工作结果:

图片

依赖安装


当我们更改代码时,我们希望在编译之前加载缺少的依赖项。 install命令将为我们完成这项工作:

 install: go-get 

当文件在编译前更改时,我们将自动执行install调用,因此将自动安装依赖项。 如果要手动安装依赖项,可以运行:

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

在内部,此命令将转换为:

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

如何运作? 请参阅下一节,我们在其中添加常规Go命令以实现更高级别的命令。

去命令


由于我们想在项目目录中安装GOPATH来简化依赖关系管理(这在Go生态系统中尚未正式决定),因此我们需要将所有Go命令包装在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 

帮忙


最后,我们需要help命令来查看可用命令列表。 我们可以使用sed和column命令自动生成格式精美的帮助输出:

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

以下命令扫描Makefile中以##开头的行并显示它们。 这样,您只需在特定命令上添加注释,注释就会与help命令一起显示。

如果我们添加一些评论:

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

我们会得到:

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

最终版本


 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/zh-CN461467/


All Articles