Создание приложений Go для разных операционных систем и архитектур

При разработке программного обеспечения важно учитыватьoperating system и базовый процессорarchitecture, для которого вы хотите скомпилировать свой двоичный файл. Поскольку запуск двоичного файла на другой платформе ОС / архитектуры зачастую медленен или невозможен, общепринятой практикой является создание окончательного двоичного кода для множества различных платформ, чтобы максимально увеличить аудиторию вашей программы. Однако это может быть сложно, если платформа, которую вы используете для разработки, отличается от платформы, на которой вы хотите развернуть свою программу. В прошлом, например, разработка программы для Windows и ее развертывание на компьютере с Linux или macOS требовали настройки компьютеров сборки для каждой из сред, для которых вы хотели получить двоичные файлы. Вам также необходимо синхронизировать инструментальные средства, в дополнение к другим соображениям, которые могут повысить стоимость и затруднить совместное тестирование и распространение.

Go решает эту проблему, встраивая поддержку нескольких платформ непосредственно в инструментgo build, а также в остальную часть цепочки инструментов Go. Используяenvironment variables иbuild tags, вы можете контролировать, для какой ОС и архитектуры будет создан ваш окончательный двоичный файл, в дополнение к созданию рабочего процесса, который может быстро переключать включение зависимого от платформы кода без изменения вашей кодовой базы. .

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

Предпосылки

Чтобы следовать примеру в этой статье, вам понадобится:

Возможные платформы дляGOOS иGOARCH

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

Инструментарий Go содержит команду, которая может распечатать список возможных платформ, на которых может работать Go. Этот список может меняться с каждым новым выпуском Go, поэтому обсуждаемые здесь комбинации могут не совпадать в другой версии Go. На момент написания этого руководства текущий выпуск Go -1.13.

Чтобы найти этот список возможных платформ, выполните следующее:

go tool dist list

Вы получите вывод, подобный следующему:

Outputaix/ppc64        freebsd/amd64   linux/mipsle   openbsd/386
android/386      freebsd/arm     linux/ppc64    openbsd/amd64
android/amd64    illumos/amd64   linux/ppc64le  openbsd/arm
android/arm      js/wasm         linux/s390x    openbsd/arm64
android/arm64    linux/386       nacl/386       plan9/386
darwin/386       linux/amd64     nacl/amd64p32  plan9/amd64
darwin/amd64     linux/arm       nacl/arm       plan9/arm
darwin/arm       linux/arm64     netbsd/386     solaris/amd64
darwin/arm64     linux/mips      netbsd/amd64   windows/386
dragonfly/amd64  linux/mips64    netbsd/arm     windows/amd64
freebsd/386      linux/mips64le  netbsd/arm64   windows/arm

Этот вывод представляет собой набор пар ключ-значение, разделенных символом/. Первая часть комбинации перед/ - это операционная система. В Go эти операционные системы являются возможными значениями переменной средыGOOS, произносится как «гусь», что означаетGo Operating System. Вторая часть после/ - это архитектура. Как и раньше, это все возможные значения переменной окружения:GOARCH. Это произносится как «гор-ч» и означаетGo Architecture.

Давайте разберем одну из этих комбинаций, чтобы понять, что она означает и как работает, на примереlinux/386. Пара "ключ-значение" начинается сGOOS, которым в этом примере будетlinux, ссылаясь наLinux OS. GOARCH здесь будет386, что означаетIntel 80386 microprocessor.

Есть много платформ, доступных с командойgo build, но в большинстве случаев вы в конечном итоге будете использоватьlinux,windows илиdarwin в качестве значения дляGOOSс. Они охватывают три большие платформы ОС:Linux,Windows иmacOS, который основан наDarwin operating system и поэтому называетсяdarwin. Однако Go также может охватывать менее популярные платформы, такие какnacl, что представляет собойGoogle’s Native Client.

Когда вы запускаете такую ​​команду, какgo build, Go используетGOOS иGOARCH текущей платформы, чтобы определить, как построить двоичный файл. Чтобы узнать, какая комбинация у вашей платформы, вы можете использовать командуgo env и передатьGOOS иGOARCH в качестве аргументов:

go env GOOS GOARCH

При тестировании этого примера мы выполнили эту команду в macOS на машине сAMD64 architecture, поэтому мы получим следующий вывод:

Outputdarwin
amd64

Здесь вывод команды говорит нам, что в нашей системе естьGOOS=darwin иGOARCH=amd64.

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

Напишите платформо-зависимую программу сfilepath.Join()

Прежде чем приступить к созданию бинарных файлов для других платформ, давайте создадим пример программы. Хорошим примером для этой цели является функцияJoin из пакетаpath/filepath стандартной библиотеки Go. Эта функция принимает несколько строк и возвращает одну строку, которая объединена с правильным разделителем пути к файлу.

Это хороший пример программы, потому что работа программы зависит от того, на какой ОС она работает. В Windows разделителем пути является обратная косая черта\, а в системах на основе Unix используется косая черта/.

Давайте начнем с создания приложения, использующегоfilepath.Join(), а позже вы напишете свою собственную реализацию функцииJoin(), которая настраивает код для специфичных для платформы двоичных файлов.

Сначала создайте папку в каталогеsrc с именем вашего приложения:

mkdir app

Переместитесь в этот каталог:

cd app

Затем создайте новый файл в выбранном текстовом редакторе с именемmain.go. Для этого урока мы будем использовать Nano:

nano main.go

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

src/app/main.go

package main

import (
  "fmt"
  "path/filepath"
)

func main() {
  s := filepath.Join("a", "b", "c")
  fmt.Println(s)
}

Функцияmain() в этом файле используетfilepath.Join() для объединения трехstrings вместе с правильным, зависящим от платформы разделителем пути.

Сохраните и выйдите из файла, затем запустите программу:

go run main.go

При запуске этой программы вы получите различный вывод в зависимости от используемой вами платформы. В Windows вы увидите строки, разделенные\:

Outputa\b\c

В системах Unix, таких как macOS и Linux, вы получите следующее:

Outputa/b/c

Это показывает, что из-за разных протоколов файловой системы, используемых в этих операционных системах, программе придется создавать разный код для разных платформ. Но поскольку он уже использует другой разделитель файлов в зависимости от ОС, мы знаем, чтоfilepath.Join() уже учитывает разницу в платформе. Это связано с тем, что цепочка инструментов Go автоматически определяетGOOS иGOARCH вашего компьютера и использует эту информацию для использования фрагмента кода с правильнымbuild tags и разделителем файлов.

Давайте посмотрим, откуда функцияfilepath.Join() берет свой разделитель. Запустите следующую команду, чтобы проверить соответствующий фрагмент из стандартной библиотеки Go:

less /usr/local/go/src/os/path_unix.go

Это отобразит содержимоеpath_unix.go. Найдите следующую часть файла:

/usr/local/go/os/path_unix.go

. . .
// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris

package os

const (
  PathSeparator     = '/' // OS-specific path separator
  PathListSeparator = ':' // OS-specific path list separator
)
. . .

В этом разделе определяютсяPathSeparator для всех разновидностей Unix-подобных систем, которые поддерживает Go. Обратите внимание на все теги сборки вверху, каждая из которых является одной из возможных платформ UnixGOOS, связанных с Unix. КогдаGOOS соответствует этим условиям, ваша программа выдаст разделитель путей к файлам в стиле Unix.

Нажмитеq, чтобы вернуться в командную строку.

Затем откройте файл, который определяет поведениеfilepath.Join() при использовании в Windows:

less /usr/local/go/src/os/path_windows.go

Вы увидите следующее:

/usr/local/go/os/path_unix.go

. . .
package os

const (
        PathSeparator     = '\\' // OS-specific path separator
        PathListSeparator = ';'  // OS-specific path list separator
)
. . .

Хотя значениеPathSeparator здесь\, код будет отображать одиночную обратную косую черту (\), необходимую для путей к файлам Windows, поскольку первая обратная косая черта нужна только как escape-символ.

Обратите внимание, что, в отличие от файла Unix, в верхней части нет тегов сборки. Это связано с тем, чтоGOOS иGOARCH также могут быть переданы вgo build путем добавления символа подчеркивания (_) и значения переменной среды в качестве суффикса к имени файла, что мы и будем перейдите в разделUsing GOOS and GOARCH File Name Suffixes. Здесь часть_windowspath_windows.go заставляет файл действовать так, как если бы он имел тег сборки// +build windows в верхней части файла. По этой причине, когда ваша программа запускается в Windows, она будет использовать константыPathSeparator иPathListSeparator из фрагмента кодаpath_windows.go.

Чтобы вернуться в командную строку, выйдите изless, нажавq.

На этом шаге вы создали программу, которая показала, как Go автоматически конвертируетGOOS иGOARCH в теги сборки. Имея это в виду, теперь вы можете обновить свою программу и написать собственную реализациюfilepath.Join(), используя теги сборки для ручной установки правильногоPathSeparator для платформ Windows и Unix.

Реализация платформо-зависимой функции

Теперь, когда вы знаете, как стандартная библиотека Go реализует код для конкретной платформы, вы можете использовать теги сборки, чтобы сделать это в своей собственной программеapp. Для этого вы напишете свою собственную реализациюfilepath.Join().

Откройте файлmain.go:

nano main.go

Замените содержимоеmain.go следующим, используя свою собственную функцию с именемJoin():

src/app/main.go

package main

import (
  "fmt"
  "strings"
)

func Join(parts ...string) string {
  return strings.Join(parts, PathSeparator)
}

func main() {
  s := Join("a", "b", "c")
  fmt.Println(s)
}

ФункцияJoin берет числоparts и объединяет их вместе, используя методstrings.Join() изstrings package, чтобы объединитьparts вместе с помощьюPathSeparatorс.

Вы еще не определилиPathSeparator, поэтому сделайте это сейчас в другом файле. Сохраните и выйдите изmain.go, откройте ваш любимый редактор и создайте новый файл с именемpath.go:

nano path.go

ОпределитеPathSeparator и установите его равным разделителю пути к файлу Unix,/:

src/app/path.go

package main

const PathSeparator = "/"

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

go build
./app

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

Outputa/b/c

Это успешно выполняется, чтобы получить путь к файлу в стиле Unix. Но это еще не то, что нам нужно: результат всегда равенa/b/c, независимо от того, на какой платформе он работает. Чтобы добавить функциональность для создания путей к файлам в стиле Windows, вам нужно будет добавить версиюPathSeparator для Windows и указать командеgo build, какую версию использовать. В следующем разделе вы будете использоватьbuild tags для этого.

Использование тегов сборкиGOOS илиGOARCH

Чтобы учесть платформу Windows, вы создадите альтернативный файл дляpath.go и воспользуетесь тегами сборки, чтобы убедиться, что фрагменты кода выполняются только тогда, когдаGOOS иGOARCH являются подходящей платформой.

Но сначала добавьте тег сборки кpath.go, чтобы он велел собирать для всего, кроме Windows. Откройте файл:

nano path.go

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

src/app/path.go

// +build !windows

package main

const PathSeparator = "/"

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

Сохраните и выйдите из файла.

Теперь, если вы запустите эту программу в Windows, вы получите следующую ошибку:

Output./main.go:9:29: undefined: PathSeparator

В этом случае Go не сможет включитьpath.go для определения переменнойPathSeparator.

Теперь, когда вы убедились, чтоpath.go не будет запускаться, когдаGOOS - это Windows, добавьте новый файлwindows.go:

nano windows.go

Вwindows.go определите WindowsPathSeparator, а также тег сборки, чтобы командаgo build знала, что это реализация Windows:

src/app/windows.go

// +build windows

package main

const PathSeparator = "\\"

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

Хотя двоичные файлы теперь будут правильно собираться для их платформ, необходимо внести дополнительные изменения, чтобы скомпилировать для платформы, к которой у вас нет доступа. Для этого на следующем шаге вы измените свои локальные переменные средыGOOS иGOARCH.

Использование ваших локальных переменных средыGOOS иGOARCH

Ранее вы запускали командуgo env GOOS GOARCH, чтобы узнать, над какой ОС и архитектурой вы работали. Когда вы запускали командуgo env, она искала две переменные средыGOOS иGOARCH; если они будут найдены, будут использоваться их значения, но если они не найдены, Go установит их с информацией для текущей платформы. Это означает, что вы можете изменитьGOOS илиGOARCH, чтобы они не использовались по умолчанию для вашей локальной ОС и архитектуры.

Командаgo build действует аналогично командеgo env. Вы можете установить переменные средыGOOS илиGOARCH для сборки для другой платформы с помощьюgo build.

Если вы не используете систему Windows, создайте двоичный файлwindows дляapp, установив для переменной средыGOOS значениеwindows при выполнении командыgo build:

GOOS=windows go build

Теперь перечислите файлы в вашем текущем каталоге:

ls

Вывод списка каталога показывает, что теперь в каталоге проекта есть исполняемый файл Windowsapp.exe:

Outputapp  app.exe  main.go  path.go  windows.go

Используя командуfile, вы можете получить дополнительную информацию об этом файле, подтвердив его сборку:

file app.exe

Ты получишь:

Outputapp.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

Вы также можете установить одну или обе переменные среды во время сборки. Запустите следующее:

GOOS=linux GOARCH=ppc64 go build

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

file app

Вы получите вывод, подобный следующему:

app: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, not stripped

Установив локальные переменные средыGOOS иGOARCH, теперь вы можете создавать двоичные файлы для любой из совместимых с Go платформ без сложной конфигурации или настройки. Далее вы будете использовать соглашения об именах файлов, чтобы ваши файлы были аккуратно организованы и автоматически собирались для конкретных платформ без тегов сборки.

Использование суффиксов имени файлаGOOS иGOARCH

Как вы видели ранее, стандартная библиотека Go интенсивно использует теги сборки для упрощения кода путем разделения различных реализаций платформы на разные файлы. Когда вы открывали файлos/path_unix.go, там был тег сборки, в котором перечислены все возможные комбинации, которые считаются Unix-подобными платформами. Однако файлos/path_windows.go не содержал тегов сборки, потому что суффикса в имени файла было достаточно, чтобы сообщить Go, для какой платформы предназначен файл.

Давайте посмотрим на синтаксис этой функции. При именовании файла.go вы можете добавитьGOOS иGOARCH в качестве суффиксов к имени файла в указанном порядке, разделяя значения символами подчеркивания (_). Если у вас есть файл Go с именемfilename.go, вы можете указать ОС и архитектуру, изменив имя файла наfilename_GOOS_GOARCH.go. Например, если вы хотите скомпилировать его для Windows с 64-разрядной версиейARM architecture, вы должны указать имя файлаfilename_windows_arm64.go. Это соглашение об именах помогает поддерживать аккуратно организованный код.

Обновите вашу программу, чтобы использовать суффиксы имен файлов вместо тегов сборки. Сначала переименуйте файлpath.go иwindows.go, чтобы использовать соглашение, используемое в пакетеos:

mv path.go path_unix.go
mv windows.go path_windows.go

Изменив два имени файла, вы можете удалить тег сборки, который вы добавили вpath_windows.go:

nano path_windows.go

Удалите// +build windows, чтобы ваш файл выглядел так:

path_windows.go

package main

const PathSeparator = "\\"

Сохраните и выйдите из файла.

Посколькуunix не является допустимымGOOS, суффикс_unix.go не имеет значения для компилятора Go. Тем не менее, он передает предполагаемую цель файла. Как и файлos/path_unix.go, ваш файлpath_unix.go по-прежнему должен использовать теги сборки, поэтому оставьте этот файл без изменений.

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

Заключение

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

Для дальнейшего изученияgo build ознакомьтесь с нашим руководством поCustomizing Go Binaries with Build Tags. Если вы хотите узнать больше о языке программирования Go в целом, ознакомьтесь сHow To Code in Go series целиком.