Как кросс-компилировать WebView Go-приложение с CGO для Linux с macOS: Zig, Docker и немного магии

от автора

Пролог

Недавно я столкнулся с задачей собрать своё 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 в плане гибкости настройки компиляции.

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/894426/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *