Как написать Makefile для Go #
Инструкция о том, как написать удобный Makefile для сборки небольшого API-сервиса на Go.
Задача #
Особенности проекта:
- Он называется
my_project - Исполняемый файл называется
my-project - Проект написан на современном Go с использованием модулей
- Нам нужны цели для сборки, для запуска тестов и для очистки
Решение #
.PHONY: all test tidy build run clean
all: tidy test build
test:
go test ./...
tidy:
go mod tidy && \
go mod vendor
build:
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/my-project cmd/main.go
# Цель не запустится без явного указания
run:
go run .
# Цель не запустится без явного указания
clean:
go clean -testcache
rm -f bin/my-project
Объяснение #
Доступные цели:
| Цель сборки | Предназначение |
|---|---|
| make | эквивалент make tidy test build |
| test | запускает тесты на Go |
| build | собирает проект на Go в каталоге cmd/main.go |
| run | запускает проект на Go |
| clean | очищает кэш тестов и результат сборки (исполняемый файл) |
Другие особенности
- Цель
.PHONYобъявляет цели, которые не создают таких выходных файлов, которые бы отслеживались инструментом GNU make. Это отключит механизм пропуска целей сборки при наличии одноимённых файлов. - Цель
allидёт первой в этом файле — она будет выполнена при вызовеmakeбез аргументов - Цель
buildсоздаёт, а цельcleanочищает исполняемый файлbin/my-project
Добавляем другие цели #
Не забывайте новую цель добавить в
.PHONY
Сборка docker-образа #
Цель сборки docker-образа:
image:
docker build -t docker.local/my-project .
Описание Dockerfile см. в статье Как написать Dockerfile для Go
Генерация кода через ogen #
ogen — генератор кода на Go из OpenAPI схемы; он отличается высоким качеством, расширяемостью и генерирует идиоматичный код.
Предположим, что мы не хотим использовать go generate и полагаемся на запуск shell-скрипта.
Цель для запуска shell-скрипта bin/generate-code.sh:
generate:
bin/generate-code.sh
Допустим, OpenAPI-схема находится в файле api/my_project.yaml.
Скрипт bin/generate-code.sh имеет флаг исполнения +x и выглядит так:
#!/usr/bin/env bash
set -o errexit
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
BACKEND_ROOT=$(dirname "$SCRIPT_DIR")
pushd "$BACKEND_ROOT" >/dev/null
go run github.com/ogen-go/ogen/cmd/ogen@latest \
-package "api" \
-config "ogen.yaml" \
-target "internal/restapi/api" \
-clean \
"api/my_project.yaml"
popd >/dev/nullФайл конфигурации ogen.yaml выглядит, например, так:
# sets generator options.
generator:
# sets generator features.
features:
enable:
# Enables paths server generation
- "paths/server"
# Enables validation of client requests
- "client/request/validation"
# Enables paths client generation
- "paths/client"
# Enables validation of server responses
- "server/response/validation"
disable_all: true