При развертывании приложений в производственной среде сборка двоичных файлов с информацией о версии и другими метаданными помогает улучшить процессы мониторинга, регистрации данных и отладки за счет добавления идентификационной информации для отслеживания версий сборок. Информация о версии часто содержит динамические данные, такие как время сборки, компьютер или пользователя, выполняющих сборку двоичного файла, идентификатор в системе контроля версий (VCS) и т. д. Поскольку эти значения постоянно меняются, внесение этих данных в исходный код и их изменение при каждой сборке становится непростой задачей, где могут возникнуть ошибки. Файлы исходного кода могут перемещаться, переменные и константы могут менять файлы во время разработки, и все это может нарушить процесс сборки.
В Go эту проблему можно решить с помощью опции -ldflags
команды go build
. Эта опция вставляет в двоичный файл во время сборки динамическую информацию, не требуя изменения исходного кода. В этой опции ld
означает программу linker, которая связывает разные фрагменты скомпилированного исходного кода в двоичном файле. Таким образом, название опции ldflags
означает флаги linker. Данная опция передает файл в инструмент linker цепи инструментов Go cmd/link
, позволяющий изменять значения импортированных пакетов во время сборки из командной строки.
В этом обучающем руководстве вы научитесь использовать -ldflags
для изменения значения переменных во время сборки и внесения в двоичный файл собственных динамических данных, используя для этого образец приложения, которое распечатывает на экране информацию о версии.
Для выполнения примера из этой статьи вам потребуется следующее:
Прежде чем вы сможете использовать ldflags
для внесения динамических данных, вам потребуется приложение, куда нужно вставлять эти данные. На этом шаге вы создадите такое приложение, которое на данном этапе будет только выводить статическую информацию о версии. Давайте создадим это приложение.
Создайте внутри каталога src
подкаталог с именем вашего приложения. В этом обучающем руководстве мы будем использовать имя приложения app
:
- mkdir app
Смените рабочий каталог на эту папку:
- cd app
Затем используйте предпочитаемый текстовый редактор для создания входной точки программы, main.go
:
- nano main.go
Добавьте следующий код, чтобы ваше приложение распечатывало информацию о версии:
package main
import (
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
}
Внутри функции main()
вы декларировали переменную Version
, затем распечатали строку Version:
с символом табуляции \t
, а затем декларированную переменную.
На данном этапе переменная Version
имеет заданное значение development
, и эта версия будет использоваться для приложения по умолчанию. Впоследствии вы сможете заменить это значение на официальный номер версии, соответствующий желаемому семантическому формату версий.
Сохраните и закройте файл. После этого выполните сборку и запустите приложение, чтобы подтвердить распечатку правильной версии:
- go build
- ./app
Результат будет выглядеть следующим образом:
- OutputVersion: development
Теперь у вас есть приложение, которое распечатывает информацию о версии по умолчанию, однако у вас еще нет способа передачи информации о текущей версии во время сборки. На следующем шаге вы используете -ldflags
и go build
для решения этой проблемы.
ldflags
с go build
Как указывалось выше, ldflags
означает флаги linker и используется для передачи флагов в linker через цепочку инструментов Go. При этом используется следующий синтаксис:
- go build -ldflags="-flag"
В этом примере мы передали флаг
в соответствующую команду go tool link
, которая запускается в рамках go build
. Эта команда заключает в двойные кавычки передаваемое в ldflags
содержимое, чтобы предотвратить разрыв символов или неправильную интерпретацию передаваемых символов командной строкой. Отсюда вы можете передать много разных флагов link
. В этом обучающем руководстве мы используем флаг -X
для записи информации в переменную во время связи, а затем указываем путь пакета к переменной и ее новое значение:
- go build -ldflags="-X 'package_path.variable_name=new_value'"
Внутри кавычек содержится опция -X
и пара ключ-значение, соответствующая изменяемой переменной и ее новому значению. Символ .
разделяет путь пакета и имя переменной, а одинарные кавычки используются для предотвращения разрыва символов в паре ключ-значение.
Чтобы заменить переменную Version
в нашем примере приложения, используйте синтаксис последнего блока команд для передачи нового значения и сборки нового двоичного файла:
- go build -ldflags="-X 'main.Version=v1.0.0'"
В этой команде main
— это путь пакета переменной Version
, поскольку данная переменная содержится в файле main.go
. Version
— это переменная, в которую выполняется запись, а v1.0.0
— это новое значение.
Для использования ldflags
необходимо, чтобы изменяемое значение существовало и относилось к переменной уровня пакета типа string
. Эта переменная может быть экспортированной или неэкспортированной. Значение не может быть const
и не может задаваться результатом вызова функции. К счастью, Version
соответствует всем этим требованиям. Она уже декларирована как переменная в файле main.go
и ее текущее значение (development
) и желаемое значение (v1.0.0
) являются строками.
После сборки нового двоичного файла app
запустите приложение:
- ./app
Результат будет выглядеть следующим образом:
- OutputVersion: v1.0.0
С помощью -ldflags
вы успешно изменили значение переменной Version
с development
на v1.0.0
.
Теперь вы изменили переменную string
внутри простого приложения во время сборки. С помощью ldflags
вы можете вставлять в двоичный файл сведения о версии, информацию о лицензирование и другие данные, используя только командную строку.
В этом примере измененная переменная находилась в программе main
, и благодаря этому было проще определить имя пути. Однако иногда путь к таким переменным бывает сложнее найти. На следующем шаге вы запишете значения в переменные в субпакетах, используя наилучший способ определения более сложных путей пакетов.
В последнем разделе вы внесли изменения в переменную Version
в пакете верхнего уровня приложения. Однако ситуация не всегда такая простая. Очень часто наиболее практичным способом будет поместить эти переменные в другой пакет, поскольку пакет main
не является импортируемым. Чтобы смоделировать это в образце приложения вы создадите новый субпакет app/build
, в котором будет храниться информация о времени сборки двоичного файла и имени пользователя, отправившего команду сборки.
Чтобы добавить новый субпакет, добавьте в проект новый каталог с именем build
:
- mkdir -p build
Затем создайте новый файл с именем build.go
для хранения новых переменных:
- nano build/build.go
Добавьте в текстовом редакторе новые переменные Time
и User
:
package build
var Time string
var User string
Переменная Time
хранит время сборки двоичного файла в форме строки. Переменная User
хранит имя пользователя, который выполнил сборку двоичного файла. Поскольку эти две переменные всегда имеют значения, их не нужно инициализировать с помощью значений по умолчанию, как вы это делали для переменной Version
.
Сохраните и закройте файл.
Затем откройте файл main.go
для добавления этих переменных в ваше приложение:
- nano main.go
Добавьте в файл main.go
следующие выделенные строки:
package main
import (
"app/build"
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
fmt.Println("build.Time:\t", build.Time)
fmt.Println("build.User:\t", build.User)
}
В этих строках вы вначале импортируете пакет app/build
, а затем выполняете печать build.Time
и build.User
точно так же, как и при печати переменной Version
.
Сохраните файл и закройте текстовый редактор.
Чтобы сделать эти переменные целью опции ldflags
, вы можете использовать путь импорта app/build
с символом .
в конце User или . Time
, поскольку вам уже известен путь импорта. Для моделирования более сложной ситуации, когда путь к переменной не очевиден, мы используем команду nm
в цепочке инструментов Go.
Команда go tool nm
выводит символы из заданного исполняемого файла, объектного файла или архива. В данном случае символ относится к объекту в коде, например, к определенной или импортированной переменной или функции. Генерирование таблицы символов с помощью команды nm
и использование grep
для поиска переменной позволяет быстро найти информацию о пути.
Примечание: команда nm
не поможет найти путь к переменной, если имя пакета содержит любые символы, кроме символов ASCII, или содержит символ "
или %
, поскольку это ограничение относится к самому инструменту.
Для использования этой команды выполните сборку двоичного файла для app
:
- go build
После сборки app
укажите на него инструменту nm
и выполните поиск в результатах:
- go tool nm ./app | grep app
Инструмент nm
выводит большое количество данных. В связи с этим в предыдущей команде использовался оператор |
для передачи результатов в команду grep
, которая выполняет поиск терминов с app
верхнего уровня в заголовке.
Результат должен выглядеть примерно так:
Output 55d2c0 D app/build.Time
55d2d0 D app/build.User
4069a0 T runtime.appendIntStr
462580 T strconv.appendEscapedRune
. . .
В данном случае первые две строки набора результатов содержат пути к двум переменным, которые вы ищете: app/build.Time
и app/build.User
.
Теперь вам известны пути, и вы можете снова выполнить сборку приложения с изменением переменных Version
, User
и Time
во время сборки. Для этого нужно передать несколько флагов -X
в -ldflags
:
- go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'"
Вы передали команду Bash id -u -n
для вывода текущего пользователя и команду date
для вывода текущей даты.
После сборки исполняемого файла запустите программу:
- ./app
В системе Unix эта команда выводит примерно следующие результаты:
OutputVersion: v1.0.0
build.Time: Fri Oct 4 19:49:19 UTC 2019
build.User: sammy
Теперь у вас имеется двоичный файл с информацией о версии и сборке, который будет полезен при решении возможных проблем в производственной среде.
В этом обучающем руководстве показано, как правильно использовать мощный инструмент ldflags
для вставки в двоичные файлы ценной информации во время сборки. Так вы можете контролировать флаги функций, информацию о среде, информацию о версиях и другие данные, не внося изменений в исходный код. Использование ldflags
в рабочем процессе сборки позволяет в полной мере получить преимущества самодостаточного формата распространения двоичных файлов Go.
Дополнительную информацию о языке программирования Go можно найти в серии статей и обучающих статей Программирование на Go. Если вам нужно больше решений для контроля версий, воспользуйтесь справочным руководством Использование Git.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!