Сборка исполняемых файлов Go для различных платформ в Ubuntu 16.04

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

Данное руководство научит пользоваться инструментами Go, получать пакеты из контроля версий и устанавливать исполняемые файлы автоматически и вручную. Также вы узнаете, как собирать исполняемые файлы для различных архитектур и автоматизировать процесс сборки (например, для Windows и macOS).

Требования

1: Установка программ Go из контроля версий

Прежде чем начать создавать исполняемые файлы из пакета Go, необходимо получить его исходный код. Инструмент go get может извлекать пакеты из систем управления версиями, таких как GitHub. Команда go get клонирует пакеты в подкаталоги $GOPATH/src/. Затем, если это возможно, она устанавливает пакет, создавая его исполняемый файл и помещая его в каталог $GOPATH/bin.

Если вы настроили Go, как описано в этом руководстве, каталог $GOPATH/bin включен в переменную окружения $PATH; это значит, что вы можете использовать установленные пакеты из любой точки системы.

Синтаксис команды:

go get package-import-path

где строка package-import-path – уникальный идентификатор пакета. Часто это местонахождение пакета в удаленном репозитории типа Github или подкаталог $GOPATH/src/ на локальной машине.

В команде go get часто используется флаг –u, который загружает зависимости (или обновляет их, если они уже есть на машине).

Для примера попробуйте установить Caddy, веб-сервер, написанный на Go.

Читайте также: Обслуживание сайта с помощью Caddy в Ubuntu 16.04

Согласно инструкциям Caddy в качестве пути можно использовать github.com/mholt/caddy/caddy. Установите Caddy с помощью go get.

go get -u github.com/mholt/caddy/caddy

На установку уйдет некоторое время. Отсутствие вывода указывает, что команда выполнена успешно.

Когда команда будет выполнена, вы найдете исходный код Caddy в $GOPATH/src/github.com/mholt/caddy. Кроме того, у Caddy есть исполняемый файл, который создается автоматически и хранится в каталоге $GOPATH/bin. Проверьте наличие этого файла.

which caddy
/home/8host/work/bin/caddy

Примечание: Команда go get устанавливает пакеты из стандартной ветки репозитория Git, обычно это ветка master. Ознакомьтесь с инструкциями по работе с пакетом в файле README.

Чтобы выбрать другую ветку, используйте команду git checkout.

Читайте также: Использование веток Git

2: Сборка исполняемого файла

Команда go get загрузила исходный код и установила исполняемый файл Caddy. Если вы хотите пересобрать исполняемый файл или собрать его из собственного кода, используйте команду go build.

Хотя веб-сервер Caddy уже установлен, попробуйте собрать его вручную, чтобы ознакомиться с этим процессом.

go build github.com/mholt/caddy/caddy

Как и ранее, отсутствие вывода означает, что команда была выполнена успешно. Исполняемый файл будет сгенерирован в текущем каталоге и унаследует его имя. В этом случае исполняемый файл будет называться caddy.

Если вы находитесь в каталоге пакета, вы можете опустить путь к пакету и просто запустить go build.

Чтобы указать другое имя или место для исполняемого файла, используйте флаг -o. Попробуйте создать исполняемый файл caddy-server и поместить его в каталог build в текущем рабочем каталоге:

go build -o build/caddy-server github.com/mholt/caddy/caddy

Команда создаст исполняемый файл и каталог ./build (если его не существует).

3: Установка исполняемых файлов

Процесс сборки создает исполняемый файл в текущем (или в другом) каталоге. Установка исполняемого файла – это процесс создания исполняемого файла и его сохранения в $GOPATH/bin. Команда go install работает так же, как go build, но размещает выходной файл в нужном месте.

Чтобы установить исполняемый файл, укажите в команде go install путь импортируемого пакета. Например:

go install github.com/mholt/caddy/caddy

Исполняемый файл наследует имя каталога, в котором хранится пакет. На этот раз исполняемый файл хранится в $GOPATH/bin. Если $GOPATH/bin указан в переменной окружения $PATH, исполняемый файл будет доступен из любой точки вашей операционной системы. Вы можете проверить его местоположение:

which caddy
/home/8host/work/bin/caddy

Вы ознакомились с базовыми командами Go. Теперь рассмотрим одну из самых популярных функций Go: создание исполняемых файлов для различных платформ.

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

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

Кросскомпиляция работает путем установки необходимых переменных среды, которые определяют целевую операционную систему и архитектуру. Переменная GOOS определяет целевую операционную систему, а GOARCH — целевую архитектуру. Чтобы создать исполняемый файл, нужно запустить такую команду:

env GOOS=target-OS GOARCH=target-architecture go build package-import-path

Команда env запускает программу в измененной среде. Это позволяет использовать переменные среды только для текущего выполнения команды. После выполнения команды переменные будут сброшены или переустановлены.

В этой таблице вы найдете возможные комбинации GOOS и GOARCH

GOOS – целевая ОС GOARCH – целевая платформа
android arm
darwin 386
darwin amd64
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux mips
linux mipsle
linux mips64
linux mips64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
windows amd64

Важно! Для кросскомпиляции исполняемых файлов для Android требуется Android NDK, а также некоторые дополнительные настройки, которые не входят в данное руководство.

Используя значения в таблице, можно построить Caddy для 64-битной системы Windows:

env GOOS=windows GOARCH=amd64 go build github.com/mholt/caddy/caddy

Исполняемый файл будет создан в текущем каталоге и унаследует имя пакета. Поскольку этот исполняемый файл предназначен для Windows, имя заканчивается суффиксом .exe.

Файл caddy.exe появится в текущем каталоге, это можно проверить с помощью команды ls.

ls caddy.exe
caddy.exe

Примечание: С помощью флага –о можно переименовать или переместить исполняемый файл. Переименовывая исполняемые файлы для Windows, не забудьте добавить суффикс .exe.

5: Сценарий для автоматической кросскомпиляции

Процесс создания исполняемых файлов для многих платформ может занять немало времени и сил, но его можно автоматизировать с помощью сценария.

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

Перейдите в домашний каталог и создайте файл go-executable-build.bash.

cd ~
nano go-executable-build.bash

Начните сценарий с последовательности шебанг (символов #!). Эта строка определяет интерпретатор,  спомощью которого будет запускаться исполняемый файл (в данном случае это будет bash).

#!/usr/bin/env bash

Используйте путь импортируемого пакета как аргумент командной строки. Для этого можно использовать переменную $n, где n — неотрицательное число. Переменная $0 содержит имя выполненного сценария, а $1 и выше будут содержать аргументы, предоставленные пользователем. Добавьте эту строку в сценарий, который возьмет первый аргумент из командной строки и сохранит его в переменной package:

...
package=$1

Затем нужно сделать так, чтоб пользователь предоставил это значение. Если значение не будет указано, сценарий прекратит работу и выведет сообщение с объяснением, как использовать сценарий:

...
if [[ -z "$package" ]]; then
echo "usage: $0 <package-name>"
exit 1
fi

Оператор if проверяет значение переменной $package. Если он не установлен, сценарий выведет сообщение с помощью echo, а затем прекратит работу с помощью exit. Exit принимает возвращаемое значение в качестве аргумента (0, если выполнение прошло успешно, и любое ненулевое значение, если во время исполнения произошла ошибка). Поскольку сценарий не был выполнен успешно, используйте 1.

Примечание: Если вы хотите, чтобы этот сценарий работал с предопределенным пакетом, измените переменную package и укажите путь к пакету:

...
package="github.com/user/hello"

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

package_split=(${package//\// })

Имя пакета должно быть последним элементом нового массива $package_split. В Bash можно использовать отрицательный индекс массива, чтобы получить доступ к данным с конца, а не с начала. Добавьте эту строку, чтобы извлечь имя пакета из массива и сохранить его в переменной package_name:

...
package_name=${package_split[-1]}

Теперь нужно решить, для каких платформ и архитектур необходимо создавать исполняемые файлы. В этом примере показано, как создать исполняемые файлы для 64-битной MacOS, 64-битной и 32-битной системы Windows. Поместите целевые платформы в массив согласно формату OS/Platform. Каждую пару можно разделить на переменные GOOS и GOARCH, используя тот же метод, который использовался для извлечения имени пакета из пути. Добавьте платформы в сценарий:

...
platforms=("windows/amd64" "windows/386" "darwin/amd64")

Затем нужно выполнить итерацию массива платформ и разделить каждую запись на значения для переменных среды GOOS и GOARCH;  их можно использовать для создания исполняемого файла. Для этого добавьте цикл for:

...
for platform in "${platforms[@]}"
do
...
done

Переменная platform  будет содержать запись из массива platforms в каждой итерации. Теперь нужно разделить значение platform на две переменные — GOOS и GOARCH. Добавьте следующие строки в цикл for:

for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
done

Затем сгенерируйте имя исполняемого файла, объединив имя пакета с ОС и архитектурой. Файлы для Windows должны содержать суффикс .exe. Добавьте этот код в цикл for:

for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
output_name=$package_name'-'$GOOS'-'$GOARCH
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
done

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

...
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package
done

Наконец, нужно обеспечить обработку ошибок при создании исполняемого файла. Например, Go может столкнуться с ошибкой, если попытается создать пакет, для которого нет исходного кода. Можно проверить код возврата команды go build для ненулевого значения. Переменная $? содержит код возврата выполнения предыдущей команды. Если go build возвращает 0, значит, возникла проблема и сценарий прекратит работу. Добавьте этот код в цикл for, после команды go build и перед done.

...
env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi

Теперь у вас есть сценарий, который будет собирать разные исполняемые файлы из пакетов Go. Вот полный код сценария:

#!/usr/bin/env bash
package=$1
if [[ -z "$package" ]]; then
echo "usage: $0 <package-name>"
exit 1
fi
package_split=(${package//\// })
package_name=${package_split[-1]}
platforms=("windows/amd64" "windows/386" "darwin/amd64")
for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
output_name=$package_name'-'$GOOS'-'$GOARCH
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi
done

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

Сделайте сценарий исполняемым:

chmod +x go-executable-build.bash

Протестируйте работу сценария. Соберите исполняемый файл для Caddy:

./go-executable-build.bash github.com/mholt/caddy/caddy

Если сценарий выполнен правильно, в текущем каталоге появятся исполняемые файлы. Отсутствие вывода означает, что сценарий выполнен успешно. Проверьте исполняемые файлы:

ls caddy*
caddy-darwin-amd64 caddy-windows-386.exe caddy-windows-amd64.exe

Чтобы изменить целевые платформы, измените значение переменной platforms.

Заключение

Теперь вы умеете пользоваться базовыми командами Go и выполнять кросскомпиляцию исполняемых файлов.

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

Чтобы убедиться, что приложение работает правильно, вы можете использовать Travis-CI и AppVeyor для тестирования в Windows.

Tags: , ,