Tutorial

Настройка бинарных файлов в Go с помощью тегов сборки

GoDevelopment

Введение

В Go тег сборки, или ограничение сборки, — это идентификатор, добавляемый в кусок кода, который определяет, когда файл должен быть включен в пакет в процессе сборки. Это позволяет создавать различные версии вашего приложения Go из одного исходного кода и переключаться между ними в быстрой и организованной манере. Многие разработчики используют теги сборки для упрощения рабочего процесса при сборке кросс-платформенных приложений, например, программ, которые требуют изменений кода для учета различий между разными операционными системами. Теги сборки также используются для тестирования интеграции, позволяя вам быстро переключаться между интегрированным кодом и кодом со службой mock или stub, а также для разделения уровней наборов функций внутри приложения.

Давайте рассмотрим проблему разделения наборов клиентских функций в качестве примера. При написании некоторых приложений вы можете захотеть контролировать, какие функции включать в бинарный файл, например, при работе с приложением, которое имеет бесплатный, профессиональный и корпоративный уровни. Если пользователь повышает уровень подписки в этих приложениях, все больше функций становятся доступными. Чтобы решить эту проблему, вы можете поддерживать отдельные проекты и пытаться держать их в синхронизированном состоянии через использование оператора import. Хотя этот подход может сработать, со временем он вызовет все больше затруднений и ошибок. Альтернативным способом может быть использование тегов сборки.

В этой статье вы будете использовать теги сборки в Go для генерации различных исполняемых бинарных файлов, которые предлагают бесплатные, профессиональные и корпоративные наборы функций примера приложения. Каждый из них будет иметь различный набор доступных функций, а по умолчанию будет доступна бесплатная версия.

Предварительные требования

Для выполнения примера из этой статьи вам потребуется следующее:

Сборка бесплатной версии

Давайте начнем со сборки бесплатной версии приложения, поскольку она будет использоваться по умолчанию при запуске go build без каких-либо тегов сборки. Позднее мы будем использовать теги сборки, чтобы избирательно добавлять другие части нашей программы.

В директории src создайте папку с именем вашего приложения. В этом обучающем руководстве мы будем использовать app:

  • mkdir app

Перейдите в эту папку:

  • cd app

Затем создайте новый текстовый файл в текстовом редакторе по вашему выбору с именем main.go:

  • nano main.go

Теперь мы определим бесплатную версию приложения. Добавьте следующее содержимое в main.go:

main.go
package main

import "fmt"

var features = []string{
  "Free Feature #1",
  "Free Feature #2",
}

func main() {
  for _, f := range features {
    fmt.Println(">", f)
  }
}

В этом файле мы создали программу, которая объявляет срез с именем features, который хранит две строки, представляющие собой функции бесплатной версии приложения. Функция main() в приложении использует цикл for для прохождения по срезу features и вывода всех доступных функций на экране.

Сохраните и закройте файл. Теперь, когда этот файл сохранен, нам больше не нужно будет редактировать его до конца статьи. Вместо этого мы будем использовать теги сборки, чтобы изменять функции бинарных файлов, которые мы будем собирать.

Выполните сборку и запустите программу:

  • go build
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2

Программа вывела на экран две наши бесплатные функции, которые входят в бесплатную версию нашего приложения.

Итак, сейчас у вас есть приложение с базовым набором функций. Далее вы узнаете, как добавить дополнительные функции в приложение во время сборки.

Добавление профессиональных функций с помощью команды go build

До этого мы избегали внесения изменений в main.go, симулируя стандартную рабочую среду, в которой код должен добавляться без изменений и возможного нарушения основного кода. Поскольку мы не можем изменять файл main.go, нам нужно будет использовать другой механизм для введения нового функционала в срез features с помощью тегов сборки.

Давайте создадим новый файл pro.go, который будет использовать функцию init() для добавления дополнительных функций в срез features:

  • nano pro.go

После открытия файла в редакторе добавьте следующие строки:

pro.go
package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

В этом коде мы использовали init() для запуска кода перед функцией main() нашего приложения, а затем append() для добавления профессиональных функций в срез features. Сохраните и закройте файл.

Скомпилируйте и запустите приложение с помощью команды go build:

  • go build

Поскольку в нашей текущей директории есть два файла (pro.go и main.go), go build будет создавать бинарный файл для обоих из них. Запустите этот бинарный файл:

  • ./app

В результате мы получим следующий набор функций:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

Теперь приложение включает как профессиональные, так и бесплатные функции. Однако это нежелательно, поскольку различия между версиями отсутствуют, а бесплатная версия включает функции, которые должны быть доступны только в профессиональной версии. Чтобы исправить это, вы можете добавить код для управления разными слоями приложения, либо вы можете использовать теги сборки, чтобы сообщить цепочке инструментов Go, какие файлы .go нужно использовать для сборки, а какие игнорировать. Давайте добавим теги сборки в следующем шаге.

Добавление тегов сборки

Теперь вы можете использовать теги сборки, чтобы отметить различия между бесплатной и профессиональной версиями.

Давайте начнем с изучения того, как выглядит тег сборки:

// +build tag_name

Добавив следующую строку кода в качестве первой строки вашего пакета и заменив tag_name на имя вашего тега сборки, вы пометите этот пакет в качестве кода, который может быть выборочно включен в конечный бинарный файл. Давайте посмотрим это в действии, добавив тег сборки в файл pro.go, чтобы указать команде go build игнорировать его, если тег не указан. Откройте файл в своем текстовом редакторе:

  • nano pro.go

А затем добавьте следующую выделенную строку:

pro.go
// +build pro

package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

В верхней части файла pro.go мы добавили //+build pro с чистой новой строкой. Эта последующая строка должна присутствовать обязательно, иначе Go будет интерпретировать это содержимое как комментарий. Объявления тега сборки должны находиться в самой верхней части файла .go. Ничего, даже комментарии, не может быть выше тегов сборки.

Объявление +build указывает команде go build, что это не комментарий, а тег сборки. Вторая часть — это тег pro. Добавив этот тег в верхней части файла pro.go, команда go build будет включать только файл pro.go только при наличии тега pro.

Скомпилируйте и снова запустите приложение:

  • go build
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2

Поскольку файл pro.go требует наличия тега pro, файл будет игнорироваться, а приложение будет компилироваться без этого файла.

При запуске команды go build мы можем использовать флаг -tags для условного включения кода в скомпилированный источник, добавив тег как аргумент. Давайте сделаем это для тега pro:

  • go build -tags pro

В результате вы получите следующий вывод:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

Теперь мы получим только дополнительные функции, когда будем выполнять сборку приложения, используя тег сборки pro.

Это нормально, если у вас есть только две версии, но при добавлении дополнительных тегов все усложняется. Чтобы добавить корпоративную версию вашего приложения в следующем шаге, мы будем использовать несколько тегов сборки, объединенных булевой логикой.

Булева логика для тегов сборки

Когда в пакете Go есть несколько тегов сборки, теги взаимодействуют друг с другом с помощью булевой логики. Чтобы продемонстрировать это, мы добавим корпоративный уровень вашего приложения, используя тег pro и тег enterprise.

Чтобы создать бинарный файл для корпоративной версии, нам потребуется включить функции по умолчанию, функции профессионального уровня и новый набор функций для корпоративного уровня. Сначала откройте редактор и создайте новый файл enterprise.go, который будет добавлять новые функции корпоративного уровня:

  • nano enterprise.go

Содержимое файла enterprise.go будет выглядеть почти так же, как и в pro.go, но будет содержать новые функции. Добавьте в файл следующие строки:

enterprise.go
package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Сохраните и закройте файл.

В настоящее время в файле enterprise.go нет тегов сборки, а как вы узнали при добавлении pro.go, это означает, что эти функции будут добавляться в бесплатную версию при исполнении go.build. Для pro.go вы добавили //+build pro и новую строку сверху файла, чтобы указать go build, что данный файл следует включать только при использовании -tags pro. В этой ситуации вам потребуется только один тег сборки для достижения цели. Однако при добавлении новых корпоративных функций у вас должны быть в наличии функции профессионального уровня.

Давайте добавим поддержку тега сборки pro в файл enterprise.go. Откройте этот файл в текстовом редакторе:

  • nano enterprise.go

Далее добавьте тег сборки перед основным объявлением пакета main и обязательно добавьте новую строку после тега сборки:

enterprise.go
// +build pro

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Сохраните и закройте файл.

Скомпилируйте и запустите приложение без каких-либо тегов:

  • go build
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2

Функции корпоративной версии больше не отображаются в бесплатной версии. Теперь мы добавим тег сборки pro и выполним сборку и запуск приложения еще раз:

  • go build -tags pro
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

Это все еще не совсем то, что нам нужно: корпоративные функции отображаются при попытке сборки профессиональной версии. Чтобы устранить эту проблему, нам потребуется другой тег сборки. В отличие от тега pro, нам нужно убедиться, что функции профессиональной и корпоративной версий доступны.

Система сборки Go позволяет решить эту ситуацию посредством использования базовой булевой логики в системе тегов сборки.

Давайте снова откроем файл enterprise.go​​​:

  • nano enterprise.go

Добавьте другой тег сборки, enterprise, в той же строке, что и тег pro:

enterprise.go
// +build pro enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Сохраните и закройте файл.

Теперь мы скомпилируем и запустим приложение с новым тегом сборки enterprise.

  • go build -tags enterprise
  • ./app

В результате вы получите следующий вывод:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2

Теперь мы потеряли функции профессиональной версии. Это объясняется тем, что при вводе нескольких тегов сборки в одной строке файла .go команда go build интерпретирует их, используя логику OR. После добавления строки //+build pro enterprise файл enterprise.go будет использоваться при сборке, только если любой из тегов сборки pro или enterprise будет присутствовать. Нам нужно корректно настроить теги сборки, чтобы требовать наличие обоих тегов и использовать логику AND.

Вместо того, чтобы помещать оба тега в одной строке, если мы разместим их в отдельных строках, команда go build будет толковать эти теги, используя логику AND.

Откройте файл enterprise.go еще раз и поместите теги сборки в разных строках.

enterprise.go
// +build pro
// +build enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

Теперь мы скомпилируем и запустим приложение с новым тегом сборки enterprise.

  • go build -tags enterprise
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2

И все равно это еще не все: поскольку оператор AND требует, чтобы оба элемента имели значение true, мы должны использовать оба тега сборки, как pro, так и enterprise.

Давайте попробуем еще раз:

  • go build -tags "enterprise pro"
  • ./app

Вывод должен выглядеть следующим образом:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

Теперь наше приложение можно собирать из одного дерева источника различным образом, разблокируя функции приложения соответствующим образом.

В данном примере мы использовали новый тег //+build для обозначения логики AND, но есть альтернативные способы использования булевой логики с помощью тегов сборки. В данной таблице приведены примеры других синтаксических форматов для тегов сборки наряду с их эквивалентом в булевой логике:

Синтаксис тега сборки Пример тега сборки Логический оператор
Разделенные пробелами элементы // +build pro enterprise pro OR enterprise
Разделенные запятой элементы // +build pro,enterprise pro AND enterprise
Элементы с восклицательным знаком // +build ! pro NOT pro

Заключение

В этом обучающем руководстве вы использовали теги сборки для контроля того, какая часть кода будет компилироваться в бинарном файле. Сначала вы объявили теги сборки и использовали их с помощью команды go build, а затем сочетали несколько тегов с помощью булевой логики. Затем вы создали программу, представляющую различные наборы функций для бесплатной, профессиональной и корпоративной версий, для демонстрации уровня контроля, который теги сборки могут предоставить для вашего проекта.

Если вы хотите узнать больше о тегах сборки, посмотрите документацию Golang по этой теме или продолжите изучение нашей серии статей о программировании на языке Go.

Creative Commons License