Как кросс-компилировать WebView Go-приложение с CGO для Linux с macOS: Zig, Docker и немного магии
- четверг, 27 марта 2025 г. в 00:00:12
Недавно я столкнулся с задачей собрать своё Go‑приложение написаное вокруг библиотеки github.com/webview/webview_go, которая является обёрткой для C/C++ библиотеки webview, для Linux, из чего вытекает необходимость использования CGO. Казалось бы, что сложного? Укажи GOOS=linux, пропиши компилятор CC= и CXX=, выполни go build — и готово. Но нет, CGO и зависимости вроде GTK превратили это в настоящий квест. После нескольких часов борьбы с ошибками вроде Package webkit2gtk-4.0 was not found in the pkg-config search path., я наконец‑то разобрался, как это сделать без виртуальной машины, используя Zig и Docker (я знаю что на macOS докер крутится в виртуалке, тут он только для получения файлов для компиляции). В этой статье делюсь своим решением — надеюсь, оно сэкономит вам время.
Zig: Компилятор для C‑кода в CGO. Zig умеет кросс‑компиляцию и заменяет GCC/Clang для сборки под Linux.
Docker: Чтобы вытащить нужные Linux‑библиотеки без запуска полноценной VM при последующих сборках.
Go: Ну, тут всё понятно.
Так как мы пользуем CGO это значит, что нам нужны заголовочные файлы и библиотеки для целевой платформы (Linux x86_64), которых на macOS нет. Компилятор который может собирать C под любые платформы, я давно пользуюсь zig для этих дел, потому изобретать нового не стал. Вообще zig хорошо интегрируется во многие проекты, так например для Rust можно собирать через cargo zigbuild, но разговор тут не про это.
Установка Zig
Первым делом Zig — он будет нашим мостом для компиляции C‑кода под Linux.
brew install zigСоздание sysroot
В sysroot будут хранится библиотеки и заголовки для целевой платформы, ими zig будет пользоваться при сборке.
Создаём папку для sysroot:
mkdir -p linux-sysrootЗапускаем контейнер Ubuntu:
docker run -it ubuntu:latest bashВнутри контейнера обновляем пакеты и ставим зависимости:
apt update
apt install -y libgtk-3-dev libwebkit2gtk-4.0-devУ меня установилось около гигабайта файлов, к слову итоговый sysroot столько и занимал в будущем, я думаю можно удалить от туда много лишнего чтоб не расходовать место на диске.
Теперь перенесем нужные файлы на хост, для переноса архивирую в tar, так как при копировании из контейнера на хост ломались ссылки на файлы что в итоге не давало скомпилировать приложение.
tar -czf /tmp/sysroot.tar.gz /usr/lib/x86_64-linux-gnu /usr/include
tar -czf /tmp/wayland.tar.gz /usr/share/pkgconfig/wayland-protocols.pc /usr/share/wayland-protocolsВыходим из контейнера exit
И найдем id контейнера в котором мы работали:
docker ps -aКопируем файлы на хост и распаковываем:
docker cp <container_id>:/tmp/sysroot.tar.gz .
docker cp <container_id>:/tmp/wayland.tar.gz .
tar -xzf sysroot.tar.gz -C ./linux-sysroot
tar -xzf wayland.tar.gz -C ./linux-sysrootесли ваше приложение тянет другие C библиотеки, то проделайте аналогичное действие и с ними, но
/usr/lib/x86_64-linux-gnuи/usr/includeвполне должно хватить для многого
Настройка окружения
Чтобы pkg-config видел наши .pc‑файлы, добавляем пути:
export PKG_CONFIG_PATH="$PWD/linux-sysroot/usr/lib/x86_64-linux-gnu/pkgconfig:$PWD/linux-sysroot/usr/share/pkgconfig:$PKG_CONFIG_PATH"Проверяем:
pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0Если ваш вывод похож на мой, то sysroot был найден:
-I/usr/include/webkitgtk-4.0 -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include -I/usr/local/Cellar/xorgproto/2024.1/include -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/atk-1.0 -I/usr/include/fribidi -I/usr/include/harfbuzz -I/usr/include/pixman-1 -I/usr/include/uuid -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/gio-unix-2.0 -I/usr/include/libsoup-2.4 -pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/libxml2 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -L/usr/lib/x86_64-linux-gnu -lwebkit2gtk-4.0 -lgtk-3 -lgdk-3 -lpangocairo-1.0 -lcairo-gobject -lgdk_pixbuf-2.0 -latk-1.0 -lpango-1.0 -lharfbuzz -lcairo -lsoup-2.4 -lgio-2.0 -ljavascriptcoregtk-4.0 -lgobject-2.0 -lglib-2.0На этом этапе все готово для сборки, по непонятной причине пути из pkg-config не подтянулись в zig автоматически, потому вручную каждый из параметров прописал через параметр -isystem для zig cc и zig c++. Чтоб вам не заниматься этим привожу ниже получившийся список.
#!/bin/bash
SYSROOT="$PWD/linux-sysroot"
INCLUDE_PATHS=(
"$SYSROOT/usr/include"
"$SYSROOT/usr/include/webkitgtk-4.0"
"$SYSROOT/usr/include/gtk-3.0"
"$SYSROOT/usr/include/glib-2.0"
"$SYSROOT/usr/lib/x86_64-linux-gnu/glib-2.0/include"
"$SYSROOT/usr/include/pango-1.0"
"$SYSROOT/usr/include/harfbuzz"
"$SYSROOT/usr/include/cairo"
"$SYSROOT/usr/include/gdk-pixbuf-2.0"
"$SYSROOT/usr/include/at-spi2-atk/2.0"
"$SYSROOT/usr/include/at-spi-2.0"
"$SYSROOT/usr/include/dbus-1.0"
"$SYSROOT/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
"$SYSROOT/usr/include/atk-1.0"
"$SYSROOT/usr/include/fribidi"
"$SYSROOT/usr/include/pixman-1"
"$SYSROOT/usr/include/uuid"
"$SYSROOT/usr/include/freetype2"
"$SYSROOT/usr/include/libpng16"
"$SYSROOT/usr/include/gio-unix-2.0"
"$SYSROOT/usr/include/libsoup-2.4"
"$SYSROOT/usr/include/libmount"
"$SYSROOT/usr/include/blkid"
"$SYSROOT/usr/include/libxml2"
"$SYSROOT/usr/lib/x86_64-linux-gnu"
)
ISYSTEM_FLAGS=$(printf " -isystem %s" "${INCLUDE_PATHS[@]}")
CGO_ENABLED=1 \
GOOS=linux \
GOARCH=amd64 \
CC="zig cc -target x86_64-linux-gnu$ISYSTEM_FLAGS" \
CXX="zig c++ -target x86_64-linux-gnu$ISYSTEM_FLAGS" \
CGO_LDFLAGS="-L$SYSROOT/usr/lib/x86_64-linux-gnu" \
PKG_CONFIG_PATH="$SYSROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$SYSROOT/usr/share/pkgconfig" \
go build -o $RELEASE_DIR/WebViewApp-nix-amd64 -ldflags="-s -w" main.go
После выполнения собрался заветный файлик и каким‑то образом ещё и заработал. Контейнер который использовался ранее можно смело удалять. Теоретически аналогичным путём можно собрать и под arm и вообще что угодно.
Я в очередной раз поражаюсь возможностями zig и беспомощностью go в плане гибкости настройки компиляции.
Если у вас есть похожая задача, надеюсь, этот гайд поможет. Вопросы или предложения — пишите в комментариях!
зы. моя первая статья на данном ресурсе, так что надеюсь не нарушил никаких местных религий