
Я иногда пишу консольные утилиты на Go под Linux. Недавно я освоил кросс-компиляцию, и теперь они прекрасно работают на Android и Windows (и Linux само собой). В статье собран практический опыт кросс‑компиляции, подготовки релизной версии и развёртывания бинарника, плюс несколько подводных камней.
1. Консольные программы ещё живы
Они до сих пор в деле, и вряд ли это когда-то изменится:
сервисы, демоны;
утилиты для пайплайнов, cron, CI/CD;
криптоноды и майнеры.
Их легко:
оборачивать в shell‑/bat‑скрипты;
запускать в Docker;
дёргать из системных сервисов (
cron,systemd,launchdи т.д.).
1.1. ARM‑процессоры и реальная мощность смартфонов
Современный ARM‑телефон по мощности одного ядра примерно соответствует одному логическому ядру десктопного компа. Меня восхищает архитектура ARM:
очень много регистров (в отличие от x86 и amd64);
ну и низкое энергопотребление.
Доступ к памяти DDR долгий, а к регистрам процессора практически мгновенный. Каждый, кто хоть раз программировал на ассемблере знает как сильно просаживается скорость программы с каждый обращением к памяти. На практике компиляторы редко выжимают из ARM > 30% от теоретического максимума (ИМХО) по причине их исторической заточки под архитектуры с меньшим числом регистров.
Кстати, возможно, высокая производительность эпловских процов M (arm64) — заслуга не только железа, но и компиляторов.
Но даже этих 20–30% от максимума хватает, чтобы консольный Go‑бинарник бодро крутился на телефоне и грел карман (в смысле денег, охлаждение всё-таки желательно). ARM-телефон, подключённый к розетке в режиме молотилки, потребляет всего около 7 Ватт (после того как зарядился).
Фактически, почти любой телефон можно превратить в сервер-малютку и запустить на нём что-то интересное. Интересный факт — Go компилирует программы статически, не нужны никакие зависимости и другие пакеты. Чтобы установить серверное Go-приложение, достаточно закачать всего 1 файл.
2. Возможности Go по кросс‑компиляции
Go из коробки умеет кросс-компилировать без танцев бубном. Компилятор самодостаточен, ему не нужны gcc/llvm:
задаём платформу через переменную среды
GOOS(linux,windows,darwin,androidи др.);задаём архитектуру через переменную
GOARCH(amd64,arm64,armи т.п.).
Простейший пример: собираем под Windows, находясь в Linux:
GOOS=windows GOARCH=amd64 go build -o app.exe console.go
Под Android arm64 (тот же исходник):
GOOS=android GOARCH=arm64 go build -o app-android console.go
2.1. Платформо‑специфичный код в Go
Go поддерживает платформо-специфичный код двумя основными способами:
build tags:
//go:build android
package main
суффиксы файлов:
net_windows.gonet_unix.gonet_android.go
Компилятор сам возьмёт нужный файл под нужную платформу, главное — корректно назвать.
2.2. Альтернативные компиляторы — Tinygo, gccgo, gollvm
Tinygo — совершенно прекрасная вещь, если вам нужно запустить Go на чайнике (микроконтроллере) или сделать компактный файл в WebAssembly (до 600КБ). Он генерит компактные бинарники. Сделан на бэкенде LLVM.
Обычный Go генерит слоноподобные бинарники для вебассемблера (от 7 МБ), которые, как правило, вообще не запускаются.
Хоть это особо не афишируется разработчиками (в справке поддерживаемых платформ нет, они боятся гнева Гугл) он умеет кросс-компилировать под amd64.
Но Tinygo поддерживает не все функции Go, например, есть проблемы с reflect и парсингом JSON (json.Unmarshal). Я получал ошибку исполнения panic: reflect: unimplemented: AssignableTo with interface.
Также он не поддерживает Go-ассемблер.
Формально есть ещё:
gccgogollvm
Пример компиляции gccgo с оптимизацией под текущую архитектуру:
# Компиляция через прямой вызов gccgo
go build -compiler=gccgo -tags=release -gccgoflags '-march=native -O3' -o app app.go./cmd/console
На текущий момент:
по уровню поддерживаемых фич они примерно на уровне Go 1.18 (Go-ассемблер поддерживается);
производительность — примерно в 9 раз медленнее обычного
go;интеграция с экосистемой Go хуже.
Практического смысла в их использовании нет — штатного компилятора Go более чем достаточно. Задумывались с целью автоматического получения супероптимизаций (векторные регистры, bmi2) при компиляции от развитых бэкэндов gcc и llvm.
3. Кросс‑компиляция «голым» Go
3.1. Базовые переменные среды
Минимальный набор:
GOOS— целевая ОС:linux,windows,darwin,android…GOARCH— архитектура:amd64,arm,arm64…
Примеры:
# Linux amd64
GOOS=linux GOARCH=amd64 go build -o app-linux-amd64 console.go
# Android arm (старые смартфоны)
GOOS=android GOARCH=arm go build -o app-android-arm console.go
# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o app-darwin-arm64 console.go
3.2. Проверить архитектуру скомпилированного бинарника
file myapp
# myapp: Mach-O 64-bit executable arm64
3.3. Полезные флаги компилятора
-a— принудительно перекомпилировать без использования кеша (у меня выполняется 7 секунд):
GOOS=linux GOARCH=amd64 go build -a -o app console.go
Полезно при смене экспериментальных флагов, обновлении библиотек и т.п.
Переменная окружения
GOEXPERIMENT=greenteagc— экспериментальный сборщик мусора (green tea GC):
GOEXPERIMENT=greenteagc GOOS=linux GOARCH=amd64 go build -o program console.go
По ощущениям новый сборщик мусора:
в среднем даёт ~6% ускорения;
иногда — до 30% в сценариях с активной аллокацией/GC.
Хоть у него экспериментальный статус, но в Гугле его активно используют и для своих утилит можно смело пробовать. У меня он работает отлично.
3.4. Уменьшаем размер и выкидываем личную информацию
3.4.1. -ldflags="-s -w"
GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w" \
-o app-small \
console.go
-s— выкинуть символы отладчика;-w— выкинуть DWARF‑информацию.
Результат — меньший бинарник, сложнее реверс-инжиниринг (хотя это и не обфускация).
3.4.2. -trimpath
go build -trimpath -o app-trim console.go
Удаляет локальные пути к исходникам из бинарника:
меньше «личных» путей вроде
/home/VasyaPupkin/project/...;полезно, если распространяете бинарник.
Я заметного изменения размера/производительности не увидел, но в теории из бинарника убираются пути к вашим папкам разработки, из которых можно что-то понять личное о вас. Лично я эффекта от этой опции не увидел.
Как проверить:
strings ваш_бинарник | grep "Фамилия"
3.4. Пара слов о garble
garble — инструмент для обфускации Go‑бинарников:
переименовывает символы;
режет отладочную информацию;
может усложнять реверс.
На практике:
идея хорошая;
но на момент написания мне не удалось заставить его оптимизировать/обфусцировать бинарник, собранный Go 1.25.3 —
garbleпросто отказывался работать с исполняемыми файлами, собранными этой версией Go.
Если хотите использовать обфускацию от garble, то придётся подбирать совместимую версию Go и не использовать экспериментальные флаги при компиляции.
4. Удобный инструмент для кросс‑компиляции: gox
gox — маленькая утилита, которая:
параллельно собирает бинарники под разные
GOOS/GOARCH;создаёт удобную структуру каталогов вида
os_arch/имя.
Устанавливаем:
go install github.com/mitchellh/gox@latest
Она компилирует многопоточно — очень быстро под все нужные вам платформы.
4.1. Список платформ
Некоторые типичные значения для -osarch:
gox -osarch "linux/amd64 linux/arm linux/arm64 windows/amd64 darwin/arm64" ...
4.2. Полезные ключи gox
Используем связку:
-osarch— список таргетов;-output— шаблон имени ({{.OS}},{{.Arch}});-ldflags— передаются как есть вgo build.
Пример:
GOEXPERIMENT=greenteagc gox \
-osarch "linux/amd64 linux/arm linux/arm64 windows/amd64 darwin/arm64" \
-ldflags="-s -w -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-output "cmd/console/builds/{{.OS}}_{{.Arch}}/app" \
console.go
4.3. Пример скрипта сборки с gox
Скрипт не самый маленький, поэтому в спойлере.
Скрипт gox для кросс-компиляции
#!/bin/bash
# Build script using gox for cross-compilation
# Creates: Linux amd64, Android 32-bit, Android 64-bit, Windows amd64
set -e # Exit on error
# Check if gox is installed
if ! command -v gox &> /dev/null; then
echo "gox is not installed. Installing..."
go install github.com/mitchellh/gox@latest
if [ $? -ne 0 ]; then
echo "Error: Failed to install gox"
exit 1
fi
echo "gox installed successfully"
fi
# Get version from config package
# Extract version from Go source file
VERSION=$(grep -E '^\s*Version\s*=\s*"[^"]+"' ../../config/app.go | sed 's/.*Version\s*=\s*"\([^"]*\)".*/\1/')
if [ -z "$VERSION" ]; then
echo "Error: Could not extract version from config/app.go"
exit 1
fi
echo "Building version: $VERSION"
# "0.1.0" -> "0_1_0"
VERSION_NUM=$(echo "$VERSION" | sed 's/\./_/g')
# Base directory for builds (in console directory)
BUILD_DIR="./builds/${VERSION}"
mkdir -p "$BUILD_DIR"
# Change to project root for building
cd ../..
# Output directory for gox (using gox structure: os_arch/filename)
OUTPUT_DIR="cmd/console/${BUILD_DIR}"
echo "Building with gox..."
# Build with greenteagc experiment and ldflags
GOEXPERIMENT=greenteagc gox \
-osarch "linux/amd64 linux/arm linux/arm64 windows/amd64 darwin/arm64" \
-ldflags="-s -w -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-output "$OUTPUT_DIR/{{.OS}}_{{.Arch}}/bm${VERSION_NUM}" \
./cmd/console
# Return to console directory
cd cmd/console
# Create zip archive
cd builds
zip bm${VERSION_NUM}.zip -r ${VERSION}
cd ..
echo ""
echo "Build complete! All executables are in: $BUILD_DIR"
echo "Version: $VERSION"
echo "Version number: $VERSION_NUM"
5. Особенности компиляции под Windows (и Windows 7 в частности)
Начиная с Go 1.21 официальная сборка Go перестала поддерживать Windows 7 / Windows Server 2008 R2. Для консольных утилит это до сих пор боль — много старых машин живут на этих ОС.
Практическое решение — использовать форк go-legacy-win7:
Это Go‑toolchain с:
поддержкой Windows 7/2008 R2;
откатами некоторых изменений рантайма/системных вызовов под старые WinAPI;
классическим поведением
go getвне модулей;при этом в форк бэкпортируются фичи и фиксы из соответствующих версий официального Go.
Создатели сборки не рекомендуют на одной машине иметь 2 разные версии Go. Так как они могут начать портить друг другу совместные файлы (список модулей, кеш компиляции). Они рекомендуют использовать их версию, так как в неё уже портирован уроверь Go 1.25.4.
Я отказался от компиляции под Windows 7. Но если бы мне это было нужно, то настроил бы в контейнере.
6. Особенности Android
6.1. Основные архитектуры
Реально интересны две:
arm64— ≈95% современных устройств (ARMv8‑A,arm64-v8a);arm— старые 32‑битные девайсы (ARMv7‑A,arm-v7a), которые мало кому нужны, но безумно дёшевы (у большинства есть старые ненужные телефоны).
Соответственно, собираем, как минимум, под:
GOOS=android GOARCH=arm64 ...
GOOS=android GOARCH=arm ...
6.2. Termux — отличный эмулятор терминала под Android
Я запускаю Go‑бинарники на Android через Termux. Фактически, это целый Linux в вашем телефоне со своей системой пакетов. Но в Goole Play версия старая. Ставить нужно с GitHub. Ставить можно последнюю бету-версию — работает очень стабильно по моему опыту.
Android может «прибивать» процесс Termux, если вы на автономном питании, поэтому стоит добавить его в исключения по энергосбережению.
Termux живёт в своей песочнице /data/data/com.termux/... — чтобы получить доступ из неё к «обычной» памяти (/storage/emulated/0/...), нужно выполнить termux-setup-storage.
6.3. Сеть, IPv6 и сертификаты
Если ваша Go-программа использует сеть, то 100%, что она не заработает в Termux с первого раза. Go на Android использует:
свою реализацию ДНС (через IP6), а не системный;
не имеет некоторых корневых сертификатов, например, те, которые нужны для Let’s Encrypt.
На практике это проявляется как:
невозможность разрешения ДНС-имён;
невозможность работы с защищёнными сайтами, подписанными Let's Encrypt.
Рабочий обходной путь:
использовать пакет
prootи командуtermux-chroot, чтобы подмонтировать нормальное окружение;явно указать
SSL_CERT_FILEиSSL_CERT_DIRна корректные сертификаты.
6.4. Инструкция по запуску консольных программ под Android/Termux (подробно)
Инструкция для «фанатов», которые хотят руками довести окружение до ума и запускать консольный Go‑бинарник
1. Установка Termux
-
Отключить Play Защиту (если мешает ставить apk не из Play):
Play Маркет → Профиль (иконка вверху справа) → Play Защита → отключить проверки (можно временно).
Скачать Termux не из Play Market, а с релизов GitHub:
https://github.com/termux/termux-app/releases-
Выбрать apk по архитектуре:
arm64-v8a.apk— для современных 64‑битных телефонов (99% случаев);v7a.apk— для старых 32‑битных;если сомневаетесь —
universal.apk.
Установить apk как обычное приложение.
Запустить Termux и по желанию обновить пакеты:
pkg update -y && pkg upgrade -y
2. Первичная инициализация Termux
Однократно выполняем:
termux-setup-storage # права на доступ к памяти устройства
pkg install termux-api # по желанию, для доступа к API Android
termux-wifi-connectioninfo # запросит разрешения, можно прервать Ctrl-C, если завис
Даём все запрошенные разрешения.
6.4.3. proot / chroot и сертификаты
Установить
proot:
pkg install proot
Запустить «chroot‑подобную» среду:
termux-chroot
Настроить переменные окружения для сертификатов (пути могут отличаться, пример):
export SSL_CERT_FILE=/etc/tls/cert.pem
export SSL_CERT_DIR=/etc/tls
Дальше Go‑приложения будут использовать эти сертификаты для TLS.
4. Копируем бинарник на телефон
Для удобства можно использовать Total Commander (из Play Market):
Скачиваем с сервера/Telegram архив с бинарниками, например
app011.zip.Открываем архив в Total Commander.
На второй панели (свайп влево/вправо) создаём папку, например
prgв «Память устройства».-
Копируем соответствующий бинарник в папку
prg:linux_arm64/android64— для новых смартфонов;linux_arm— для старых.
Итог: в памяти устройства есть что‑то вроде:
/storage/emulated/0/prg/app011
5. Настраиваем запуск в Termux
В Termux:
cp /storage/emulated/0/prg/app011 app011
chmod +x app011
./app011
Если Android любит выгружать Termux:
добавляем приложение Termux в исключения по батарее (на Android 11+ это встречается часто).
6. Автоматизация
Идеально это всё завернуть в скрипт:
установить дополнение Termux:Widget (из F-Droid или Google Play);
положить shell‑скрипт в
~/.shortcutsили~/.shortcuts/tasks(в зависимости от версии);вызывать всё одной кнопкой на виджете.
Пример содержимого скрипта (идея, не окончательный вариант):
#!/data/data/com.termux/files/usr/bin/bash
termux-chroot
export SSL_CERT_FILE=/etc/tls/cert.pem
export SSL_CERT_DIR=/etc/tls
cd /data/data/com.termux/files/home
./app011
После первой настройки дальнейший запуск сводится к одному нажатию или клик по «стрелке вверх» в Termux, чтобы повторить последнюю команду.
7. Перезагрузка телефона
После перезагрузки:
переменные окружения Termux теряются;
termux-chrootнужно снова запускать вручную;export SSL_CERT_FILE=...иexport SSL_CERT_DIR=...снова прописывать (или переложить в~/.bashrc/ скрипт‑виджет).
6.5. Автоматическая настройка окружения в Go‑коде
Часть рутины можно переложить на саму программу: если она видит, что оно на ARM и переменные сертификатов не заданы, то выставляет их сама.
Пример:
package app
import (
"os"
"runtime"
)
func (app *App) setupEnvironment() {
// Проверяем архитектуру
arch := runtime.GOARCH
if arch != "arm64" && arch != "arm" {
return // Не ARM архитектура, ничего не делаем
}
// Проверяем, установлена ли переменная SSL_CERT_FILE
if os.Getenv("SSL_CERT_FILE") == "" {
// Устанавливаем переменные окружения
_ = os.Setenv("SSL_CERT_FILE", "/etc/tls/cert.pem")
_ = os.Setenv("SSL_CERT_DIR", "/etc/tls")
}
}
Так программа сама подстраивает окружение на Android/ARM, если пользователь забыл экспортировать переменные в Termux.
7. Заключение и основные моменты
Итак, консольные утилиты на Go живут отлично на Linux, Windows, macOS и, с оговорками, на Android. А встроенная кросс‑компиляция Go плюс gox делает мультиплатформенную сборку тривиальной.
Компиляцию под Windows 7/2008 R2 нам поможет сделать форк go-legacy-win7.
На Android же для запуска консольных программ главное — дать права Termux, выставить переменные среды для SSL и запустить termux-chroot.
Да пребудет с вами сила go build!
© 2025 ООО «МТ ФИНАНС»
Комментарии (6)

AnatolyEmelin
21.11.2025 13:44Спасибо.

somethinside
21.11.2025 13:44Классный туториал: по сути готовый чек-лист «как превратить старый смартфон в мини-сервер на Go» плюс бонусом — gox, greenteagc и нюансы с Windows 7 и Termux.
Octagon77
К перечисленному можно, а может и нужно, много чего добавить, особенно в присутствии таких тузов как nvim, emacs, nnn, yazi, ncdu2, cmus, rtorrent, browsh.
И ещё, как выяснится ниже, использовать для превращения смартфонов, от себя добавлю - и старых компов, в весьма полезные вещи.
Запускать интересное - да, превратить - скорее нет. Какие разрешения и исключения ни делай, всё равно и приложения, и особенно подпроцессы внутри приложений, периодически прибиваются никчемными мобильными операционками.
А что на Андроид не живёт в своей песочнице? И где слово "пока" - Termux завис на третьей бете на пол года, а тот Андроид на котором он перестанет запускаться - не за горами. Отзывы об альтернативе, Debian в виртуалке Андроид 16, премерзкие. Так что праздник терминала на мобилке может и покончаться.
Права - да, конечно. Всё остальное мне лет за шесть так и не понадобилось.
Оно конечно, но как-то недообосновано, есть альтернативы. Для Go - Python, Rust если не думать про Эппл, JavaScript, Lua и даже Scheme с которой не так уж и плохо на мобилках. Для build - run, компиляция прямо на Termux - тоже вариант.
Всё ждал когда Автор чего-то предложит, не дождался, даже Телеграм канала не дождался. Поэтому предлагаю за него github.com/rivo/tview.
inetstar Автор
Спасибо за коммент!
Кстати, насчёт tview, сколько килобайт добавляетт ваш проект к исполнимому файлу?
Octagon77
На смартфоне 4.2 мегабайта без, 3.0 с -s -w.
inetstar Автор
Если у меня бинарник на 8МБ, то с этой библиотекой станет 12.2? Этот так работает?