Add linux-tui/linux-qt static build targets using jerboa21/jerboa
ober
985d4ef8c2fbc442fff8edd188a7adb89297ed65
new file mode 100644 --- /dev/null +++ b/Dockerfile.qt @@ -0,0 +1,292 @@ +# Dockerfile.qt — Build jemacs-qt static binary using jerboa21/jerboa + Qt6 +# +# Multi-stage build: +# Stage 1 (Alpine): Build Qt6 static, QScintilla, tree-sitter, libvterm (musl) +# Stage 1.5 (Alpine+shim): Compile libqt_shim.a with musl-compatible g++ +# Stage 2 (jerboa21/jerboa): Copy Qt artifacts + Jerboa deps, build jemacs-qt +# +# Usage: +# docker build -f Dockerfile.qt -t jemacs-qt-builder . +# id=$(docker create jemacs-qt-builder) +# docker cp $id:/out/jemacs-qt ./jemacs-qt && docker rm $id +# chmod +x jemacs-qt + +# ── Stage 1: Build Qt6 + QScintilla + tree-sitter + libvterm on Alpine ─────── +FROM alpine:3.21 AS qt-alpine + +ARG QT6_VERSION=6.8.3 +ARG QSCI_VERSION=2.14.1 +ARG TS_VERSION=0.24.7 + +# ── Phase 1: Alpine build deps ────────────────────────────────────────── +RUN apk add --no-cache \ + cmake samurai perl python3 linux-headers patchelf \ + libxcb-dev xcb-util-dev xcb-util-image-dev \ + xcb-util-keysyms-dev xcb-util-renderutil-dev \ + xcb-util-wm-dev xcb-util-cursor-dev \ + libx11-dev libxkbcommon-dev \ + fontconfig-dev freetype-dev harfbuzz-dev \ + libpng-dev zlib-dev \ + pcre2-dev \ + at-spi2-core-dev libdrm-dev \ + zlib-static libxcb-static \ + fontconfig-static freetype-static harfbuzz-static \ + libpng-static bzip2-static expat-static brotli-static \ + libx11-static graphite2-static libxkbcommon-static \ + ncurses-dev ncurses-static \ + util-linux-dev util-linux-static \ + libvterm-dev libvterm-static \ + gcc g++ binutils make git curl wget + +# Build static libXau (no Alpine -static package available) +RUN apk add --no-cache libxau-dev && \ + cd /tmp && \ + wget -q https://xorg.freedesktop.org/releases/individual/lib/libXau-1.0.12.tar.xz && \ + tar xf libXau-1.0.12.tar.xz && \ + cd libXau-1.0.12 && \ + ./configure --prefix=/usr --enable-static --disable-shared && \ + make -j$(nproc) && make install && \ + cd / && rm -rf /tmp/libXau-1.0.12* + +# Build static libxcb-util (no Alpine -static package; needed by xcb-image) +RUN cd /tmp && \ + wget -q https://xcb.freedesktop.org/dist/xcb-util-0.4.1.tar.xz && \ + tar xf xcb-util-0.4.1.tar.xz && \ + cd xcb-util-0.4.1 && \ + ./configure --prefix=/usr --enable-static --disable-shared && \ + make -j$(nproc) && make install && \ + cd / && rm -rf /tmp/xcb-util-0.4.1* + +# ── Phase 2: Build Qt6 qtbase static ──────────────────────────────────── +RUN wget -q https://download.qt.io/official_releases/qt/6.8/${QT6_VERSION}/submodules/qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \ + tar xf qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \ + rm qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \ + cmake -S qtbase-everywhere-src-${QT6_VERSION} -B qt6-build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/opt/qt6-static \ + -DBUILD_SHARED_LIBS=OFF \ + -DQT_BUILD_EXAMPLES=OFF \ + -DQT_BUILD_TESTS=OFF \ + -DQT_BUILD_BENCHMARKS=OFF \ + -DFEATURE_xcb=ON \ + -DFEATURE_sql=OFF \ + -DFEATURE_network=OFF \ + -DFEATURE_testlib=OFF \ + -DFEATURE_printsupport=ON \ + -DFEATURE_dbus=OFF \ + -DFEATURE_opengl=OFF \ + -DFEATURE_vulkan=OFF \ + -DFEATURE_glib=OFF \ + -DFEATURE_icu=OFF && \ + cmake --build qt6-build --parallel && \ + cmake --install qt6-build && \ + rm -rf qtbase-everywhere-src-${QT6_VERSION} qt6-build + +# ── Phase 3: Build QScintilla static ──────────────────────────────────── +RUN wget -q https://www.riverbankcomputing.com/static/Downloads/QScintilla/${QSCI_VERSION}/QScintilla_src-${QSCI_VERSION}.tar.gz && \ + tar xf QScintilla_src-${QSCI_VERSION}.tar.gz && \ + rm QScintilla_src-${QSCI_VERSION}.tar.gz && \ + cd QScintilla_src-${QSCI_VERSION}/src && \ + /opt/qt6-static/bin/qmake CONFIG+=staticlib && \ + make -j$(nproc) && \ + make install && \ + cd / && rm -rf QScintilla_src-${QSCI_VERSION} + +# ── Phase 4: Generate pkg-config files with full transitive deps ───────── +RUN mkdir -p /opt/qt6-static/lib/pkgconfig && \ + prl_libs() { \ + grep '^QMAKE_PRL_LIBS ' "$1" | \ + sed 's/^QMAKE_PRL_LIBS *= *//' | \ + tr ' ' '\n' | grep '^-l' | \ + grep -v '^-lQt6' | tr '\n' ' '; \ + } && \ + CORE_PRIVATE=$(prl_libs /opt/qt6-static/lib/libQt6Core.prl) && \ + GUI_PRL=$(prl_libs /opt/qt6-static/lib/libQt6Gui.prl) && \ + XCB_PRL=$(prl_libs /opt/qt6-static/plugins/platforms/libqxcb.prl) && \ + TRANSITIVE="-lgraphite2 -lbz2 -lbrotlidec -lbrotlicommon -lexpat -lXau -lXdmcp" && \ + GUI_PRIVATE="$GUI_PRL $TRANSITIVE" && \ + XCB_TRANSITIVE="-lxcb-util -lxcb -lXau -lXdmcp" && \ + XCB_PRIVATE="$XCB_PRL $TRANSITIVE $XCB_TRANSITIVE" && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + '' \ + 'Name: Qt6Core' "Description: Qt6 Core" "Version: ${QT6_VERSION}" \ + 'Cflags: -I${includedir} -I${includedir}/QtCore' \ + "Libs: -L\${libdir} -lQt6Core" \ + "Libs.private: $CORE_PRIVATE" \ + > /opt/qt6-static/lib/pkgconfig/Qt6Core.pc && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + '' \ + 'Name: Qt6Gui' "Description: Qt6 Gui" "Version: ${QT6_VERSION}" \ + 'Requires: Qt6Core' \ + 'Cflags: -I${includedir} -I${includedir}/QtGui' \ + "Libs: -L\${libdir} -lQt6Gui" \ + "Libs.private: $GUI_PRIVATE" \ + > /opt/qt6-static/lib/pkgconfig/Qt6Gui.pc && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + '' \ + 'Name: Qt6Widgets' "Description: Qt6 Widgets" "Version: ${QT6_VERSION}" \ + 'Requires: Qt6Gui' \ + 'Cflags: -I${includedir} -I${includedir}/QtWidgets -I${includedir}/QtGui -I${includedir}/QtCore' \ + "Libs: -L\${libdir} -lQt6Widgets" \ + > /opt/qt6-static/lib/pkgconfig/Qt6Widgets.pc && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + '' \ + 'Name: Qt6PrintSupport' "Description: Qt6 PrintSupport" "Version: ${QT6_VERSION}" \ + 'Requires: Qt6Widgets' \ + 'Cflags: -I${includedir} -I${includedir}/QtPrintSupport' \ + "Libs: -L\${libdir} -lQt6PrintSupport" \ + > /opt/qt6-static/lib/pkgconfig/Qt6PrintSupport.pc && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + 'plugindir=${prefix}/plugins' \ + '' \ + 'Name: Qt6XcbPlugin' "Description: Qt6 XCB platform plugin" "Version: ${QT6_VERSION}" \ + 'Requires: Qt6Gui' \ + "Libs: -L\${libdir} -L\${plugindir}/platforms -lqxcb -lQt6XcbQpa" \ + "Libs.private: $XCB_PRIVATE" \ + > /opt/qt6-static/lib/pkgconfig/Qt6XcbPlugin.pc && \ + printf '%s\n' \ + 'prefix=/opt/qt6-static' \ + 'includedir=${prefix}/include' \ + 'libdir=${prefix}/lib' \ + '' \ + 'Name: QScintilla' "Description: QScintilla for Qt6" "Version: ${QSCI_VERSION}" \ + 'Requires: Qt6Widgets Qt6PrintSupport' \ + 'Cflags: -I${includedir} -I${includedir}/Qsci' \ + "Libs: -L\${libdir} -lqscintilla2_qt6" \ + > /opt/qt6-static/lib/pkgconfig/QScintilla.pc && \ + echo "Generated .pc files:" && ls /opt/qt6-static/lib/pkgconfig/ + +# ── Phase 5: Build tree-sitter + grammars (static) ────────────────────── +RUN git clone --depth 1 --branch v${TS_VERSION} \ + https://github.com/tree-sitter/tree-sitter /tmp/tree-sitter && \ + cd /tmp/tree-sitter && \ + cc -c -O2 -Ilib/include lib/src/lib.c -o lib/src/lib.o && \ + mkdir -p /opt/tree-sitter-lib && \ + ar rcs /opt/tree-sitter-lib/libtree-sitter.a lib/src/lib.o && \ + mkdir -p /opt/tree-sitter-include/tree_sitter && \ + cp lib/include/tree_sitter/api.h /opt/tree-sitter-include/tree_sitter/ && \ + rm -rf /tmp/tree-sitter + +RUN mkdir -p /opt/tree-sitter-grammars && \ + build_grammar() { \ + name=$1; repo=$2; tag=$3; subdir=${4:-.}; \ + git clone --depth 1 --branch "$tag" \ + "https://github.com/$repo" /tmp/ts-$name && \ + cd /tmp/ts-$name/$subdir && \ + SRC_DIR=src && \ + gcc -c -O2 -I/opt/tree-sitter-include -I$SRC_DIR \ + $SRC_DIR/parser.c -o parser.o && \ + if [ -f $SRC_DIR/scanner.c ]; then \ + gcc -c -O2 -I/opt/tree-sitter-include -I$SRC_DIR \ + $SRC_DIR/scanner.c -o scanner.o && \ + ar rcs /opt/tree-sitter-grammars/libtree-sitter-$name.a parser.o scanner.o; \ + else \ + ar rcs /opt/tree-sitter-grammars/libtree-sitter-$name.a parser.o; \ + fi && \ + cd / && rm -rf /tmp/ts-$name; \ + } && \ + build_grammar c tree-sitter/tree-sitter-c v0.23.5 && \ + build_grammar cpp tree-sitter/tree-sitter-cpp v0.23.4 && \ + build_grammar python tree-sitter/tree-sitter-python v0.23.6 && \ + build_grammar javascript tree-sitter/tree-sitter-javascript v0.23.1 && \ + build_grammar rust tree-sitter/tree-sitter-rust v0.23.3 && \ + build_grammar go tree-sitter/tree-sitter-go v0.23.4 && \ + build_grammar bash tree-sitter/tree-sitter-bash v0.23.3 && \ + build_grammar json tree-sitter/tree-sitter-json v0.24.8 && \ + build_grammar ruby tree-sitter/tree-sitter-ruby v0.23.1 && \ + build_grammar java tree-sitter/tree-sitter-java v0.23.5 && \ + build_grammar css tree-sitter/tree-sitter-css v0.23.2 && \ + build_grammar html tree-sitter/tree-sitter-html v0.23.2 && \ + build_grammar lua tree-sitter-grammars/tree-sitter-lua v0.3.0 && \ + build_grammar scheme 6cdh/tree-sitter-scheme main && \ + echo "Grammars built:" && ls /opt/tree-sitter-grammars/ + +# ── Stage 1.5: Compile libqt_shim.a with musl-compatible Alpine g++ ────────── +# Separate stage so Qt6 cache is preserved when qt_shim.cpp changes +FROM qt-alpine AS qt-shim-alpine + +COPY vendor/qt_shim.cpp vendor/qt_shim.h /tmp/qt-shim-src/ + +RUN mkdir -p /tmp/qt-shim-out && \ + QT_CFLAGS="-I/opt/qt6-static/include \ + -I/opt/qt6-static/include/QtCore \ + -I/opt/qt6-static/include/QtGui \ + -I/opt/qt6-static/include/QtWidgets" && \ + QSCI_FLAGS="-DQT_SCINTILLA_AVAILABLE \ + -I/opt/qt6-static/include \ + -I/opt/qt6-static/include/Qsci" && \ + g++ -c -fPIC -std=c++17 -DJEMACS_CHEZ_SMP \ + $QT_CFLAGS $QSCI_FLAGS \ + /tmp/qt-shim-src/qt_shim.cpp \ + -o /tmp/qt-shim-out/qt_shim_static.o && \ + ar rcs /tmp/qt-shim-out/libqt_shim.a \ + /tmp/qt-shim-out/qt_shim_static.o && \ + printf '#include <QtPlugin>\nQ_IMPORT_PLUGIN(QXcbIntegrationPlugin)\n' \ + > /tmp/qt-shim-out/qt_static_plugins.cpp && \ + g++ -c -fPIC -std=c++17 $QT_CFLAGS \ + /tmp/qt-shim-out/qt_static_plugins.cpp \ + -o /tmp/qt-shim-out/qt_static_plugins.o && \ + echo "libqt_shim.a built successfully" && \ + ls -lh /tmp/qt-shim-out/ + +# ── Stage 2: jerboa21/jerboa + Qt artifacts → build jemacs-qt ──────────────── +FROM jerboa21/jerboa AS builder + +# Install Ubuntu build deps needed for jemacs-qt compilation +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + libncurses-dev \ + uuid-dev \ + libssl-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# Copy Qt6 static libs and tools from Alpine stage +COPY --from=qt-alpine /opt/qt6-static /opt/qt6-static + +# Copy tree-sitter from Alpine stage +COPY --from=qt-alpine /opt/tree-sitter-lib /opt/tree-sitter-lib +COPY --from=qt-alpine /opt/tree-sitter-include /opt/tree-sitter-include +COPY --from=qt-alpine /opt/tree-sitter-grammars /opt/tree-sitter-grammars + +# Copy Alpine-compiled libvterm.a (Ubuntu doesn't ship a static version) +COPY --from=qt-alpine /usr/lib/libvterm.a /usr/local/lib/ +COPY --from=qt-alpine /usr/include/vterm.h /usr/local/include/ + +# Copy pre-compiled Qt shim (Alpine-musl g++, so ABI-compatible with musl final link) +COPY --from=qt-shim-alpine /tmp/qt-shim-out/ /opt/qt-shim/ + +# Create /opt/chez symlink → /build/chez-musl (musl Chez Scheme) +RUN ln -s /build/chez-musl /opt/chez + +# Copy source and run the in-container build +COPY . /build/mine/jerboa-emacs +WORKDIR /build/mine/jerboa-emacs + +RUN make linux-qt-local + +# Verify +RUN ./jemacs-qt --version && \ + file jemacs-qt && \ + echo "--- Binary info ---" && \ + ls -lh jemacs-qt + +# ── Final stage: extract binary ─────────────────────────────────────────────── +FROM ubuntu:24.04 +COPY --from=builder /build/mine/jerboa-emacs/jemacs-qt /out/jemacs-qt +CMD ["cat", "/out/jemacs-qt"] new file mode 100644 --- /dev/null +++ b/Dockerfile.tui @@ -0,0 +1,29 @@ +# Dockerfile.tui — Build jemacs (TUI) static binary using jerboa21/jerboa +# +# Produces a fully static jemacs binary with zero runtime dependencies. +# +# Usage: +# docker build -f Dockerfile.tui -t jemacs-tui-builder . +# id=$(docker create jemacs-tui-builder) +# docker cp $id:/out/jemacs ./jemacs && docker rm $id +# chmod +x jemacs + +FROM jerboa21/jerboa AS builder + +# ── Copy jerboa-emacs source ──────────────────────────────────────────────── +COPY . /build/mine/jerboa-emacs + +# ── Build jemacs-musl ──────────────────────────────────────────────────────── +WORKDIR /build/mine/jerboa-emacs +RUN make linux-tui-local + +# ── Verify ─────────────────────────────────────────────────────────────────── +RUN ./jemacs --version && \ + file jemacs && \ + echo "--- Binary info ---" && \ + ls -lh jemacs + +# ── Output ─────────────────────────────────────────────────────────────────── +FROM ubuntu:24.04 +COPY --from=builder /build/mine/jerboa-emacs/jemacs /out/jemacs +CMD ["cat", "/out/jemacs"] --- a/Makefile +++ b/Makefile @@ -55,6 +55,8 @@ endif test-pty test-emacs test-functional test-term-hang \ docker-deps static-qt static-tui clean-docker check-root \ build-jemacs-qt-static build-jemacs-tui-static binary macos \ + linux-tui linux-tui-local \ + linux-qt linux-qt-local \ stress-run stress-run-static stress-test stress-burn stress-burn-static \ test-behavioral @@ -684,7 +686,7 @@ build-jemacs-tui-static: check-root echo "Building Scintilla+termbox+Lexilla static archives from source..." && \ make -C /deps/sci-vendor/scintilla/termbox/termbox_next && \ make -C /deps/sci-vendor/scintilla/termbox && \ - make -C /deps/sci-vendor/lexilla/src liblexilla.a && \ + make -C /deps/sci-vendor/lexilla/src && \ JEMACS_STATIC=1 \ CHEZ_DIR=$(CHEZ_MUSL_DIR) \ JERBOA_DIR=/deps/jerboa/lib \ @@ -733,6 +735,242 @@ linux-static-tui-docker: cd /src && make build-jemacs-tui-static'" # ============================================================================= +# Static TUI via jerboa21/jerboa Docker image (like jerboa-gitsafe) +# ============================================================================= + +JERBOA_IMAGE ?= jerboa21/jerboa +MUSL_CHEZ_DIR = $(shell ls -d /build/chez-musl/lib/csv*/ta6le 2>/dev/null | head -1) +MUSL_SCHEME = /usr/local/bin/scheme + +# Docker build: produces ./jemacs static binary +linux-tui: + @echo "=== Building jemacs static TUI binary in Docker ===" + docker build --platform linux/amd64 -f Dockerfile.tui -t jemacs-tui-builder . + @id=$$(docker create --platform linux/amd64 jemacs-tui-builder) && \ + docker cp $$id:/out/jemacs ./jemacs && \ + docker rm $$id >/dev/null + @chmod +x jemacs + @echo "" + @ls -lh jemacs + @file jemacs + +# In-container build target (called inside jerboa21/jerboa) +# All deps are pre-installed in the image at /build/mine/ and /build/sci-vendor/ +linux-tui-local: + @echo "=== Building jemacs static TUI (in-container) ===" + rm -f src/.jerbuild-hashes + find lib -name '*.so' -o -name '*.wpo' | xargs rm -f 2>/dev/null; true + find src/jerboa-emacs -name '*.ss' | sed 's|src/|lib/|; s|\.ss$$|.sls|' | xargs rm -f 2>/dev/null; true + find /build/mine/jerboa/lib -name '*.so' -delete 2>/dev/null; true + find /build/mine/jerboa/lib -name '*.wpo' -delete 2>/dev/null; true + find /build/mine/chez-scintilla -name '*.so' -delete 2>/dev/null; true + find /build/mine/chez-pcre2 -name '*.so' -delete 2>/dev/null; true + find /build/mine/jerboa-shell -name '*.wpo' -delete 2>/dev/null; true + find vendor/gherkin-runtime -name '*.so' -delete 2>/dev/null; true + find vendor/gherkin-runtime -name '*.wpo' -delete 2>/dev/null; true + find vendor/jerboa-shell -name '*.so' -delete 2>/dev/null; true + find vendor/jerboa-shell -name '*.wpo' -delete 2>/dev/null; true + @echo "Recompiling jerboa core + prelude..." + echo '(import (chezscheme)) (compile-imported-libraries #t) (import (jerboa core)) (import (jerboa prelude))' \ + | $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib -q + rm -f /build/mine/jerboa/lib/jerboa/*.wpo + $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib --script /build/mine/jerboa/jerbuild.ss src/ lib/ + cp vendor/chez-pcre2-ffi-static.ss /build/mine/chez-pcre2/chez-pcre2/ffi.ss + $(MUSL_SCHEME) --libdirs /build/mine/chez-pcre2 \ + --compile-imported-libraries --script vendor/chez-pcre2-compile-libs.ss + rm -f /build/mine/chez-pcre2/chez-pcre2/*.wpo + cp vendor/chez-scintilla-ffi-static.sls /build/mine/chez-scintilla/src/chez-scintilla/ffi.sls + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/chez-scintilla/src \ + --compile-imported-libraries --script vendor/chez-scintilla-compile-libs.ss + rm -f /build/mine/chez-scintilla/src/chez-scintilla/*.wpo + cp vendor/jerboa-net-tcp-static.sls /build/mine/jerboa/lib/std/net/tcp.sls + cp vendor/jerboa-net-tcp-raw-static.sls /build/mine/jerboa/lib/std/net/tcp-raw.sls + cp vendor/jerboa-net-uri.sls /build/mine/jerboa/lib/std/net/uri.sls + cp vendor/jerboa-net-tls-rustls-static.sls /build/mine/jerboa/lib/std/net/tls-rustls.sls + rm -f /build/mine/jerboa/lib/std/net/*.wpo /build/mine/jerboa/lib/std/net/*.so + cp vendor/jerboa-crypto-native-static.sls /build/mine/jerboa/lib/std/crypto/native.sls + rm -f /build/mine/jerboa/lib/std/crypto/*.wpo /build/mine/jerboa/lib/std/crypto/*.so + mkdir -p /build/mine/jerboa/lib/std/security + cp vendor/jerboa-security-capsicum-static.sls /build/mine/jerboa/lib/std/security/capsicum.sls + rm -f /build/mine/jerboa/lib/std/security/*.wpo /build/mine/jerboa/lib/std/security/*.so + cp vendor/jerboa-os-landlock-static.sls /build/mine/jerboa/lib/std/os/landlock.sls + rm -f /build/mine/jerboa/lib/std/os/landlock.wpo /build/mine/jerboa/lib/std/os/landlock.so + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-tcp.ss + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-tcp-raw.ss + $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-uri.ss + rm -f /build/mine/jerboa/lib/std/net/*.wpo + cp vendor/jerboa-repl-static.sls /build/mine/jerboa/lib/std/repl.sls + rm -f /build/mine/jerboa/lib/std/repl.wpo /build/mine/jerboa/lib/std/repl.so + cd /build/mine/jerboa/lib && $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script $(CURDIR)/vendor/jerboa-compile-repl.ss + rm -f /build/mine/jerboa/lib/std/repl.wpo + rm -f lib/jerboa/*.wpo lib/jerboa/*.so + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs lib \ + --compile-imported-libraries --script vendor/jerboa-compile-repl-socket.ss + rm -f lib/jerboa/*.wpo + find /build/mine/jerboa-shell -name '*.wpo' -delete 2>/dev/null; true + JEMACS_STATIC=1 \ + CHEZ_DIR=$(MUSL_CHEZ_DIR) \ + JERBOA_DIR=/build/mine/jerboa/lib \ + JSH_DIR=vendor/jerboa-shell/src \ + GHERKIN_DIR=vendor/gherkin-runtime \ + CHEZ_PCRE2_DIR=/build/mine/chez-pcre2 \ + CHEZ_SCINTILLA_DIR=/build/mine/chez-scintilla/src \ + SCI_VENDOR_DIR=/build/sci-vendor \ + $(MUSL_SCHEME) \ + --libdirs lib:/build/mine/jerboa/lib:vendor/jerboa-shell/src:vendor/gherkin-runtime:/build/mine/chez-pcre2:/build/mine/chez-scintilla/src \ + --script build-binary.ss + @echo "" + @echo "=== jemacs static TUI binary built ===" + @ls -lh jemacs + @file jemacs + +# ============================================================================= +# Static Qt binary via jerboa21/jerboa Docker image +# ============================================================================= + +# Docker build: produces ./jemacs-qt static binary +linux-qt: + @echo "=== Building jemacs-qt static binary in Docker ===" + docker build --platform linux/amd64 -f Dockerfile.qt -t jemacs-qt-builder . + @id=$$(docker create --platform linux/amd64 jemacs-qt-builder) && \ + docker cp $$id:/out/jemacs-qt ./jemacs-qt && \ + docker rm $$id >/dev/null + @chmod +x jemacs-qt + @echo "" + @ls -lh jemacs-qt + @file jemacs-qt + +# In-container build target (called inside Dockerfile.qt → jerboa21/jerboa stage) +# Qt6 static libs are at /opt/qt6-static (copied from Alpine stage). +# Tree-sitter is at /opt/tree-sitter-* (copied from Alpine stage). +# Pre-compiled libqt_shim.a is at /opt/qt-shim/ (Alpine-musl g++, ABI-compatible). +# /opt/chez → /build/chez-musl symlink provides musl Chez Scheme. +linux-qt-local: + @echo "=== Building jemacs-qt static (in-container) ===" + rm -f src/.jerbuild-hashes + find lib -name '*.so' -o -name '*.wpo' | xargs rm -f 2>/dev/null; true + find src/jerboa-emacs -name '*.ss' | sed 's|src/|lib/|; s|\.ss$$|.sls|' | xargs rm -f 2>/dev/null; true + find /build/mine/jerboa/lib -name '*.so' -delete 2>/dev/null; true + find /build/mine/jerboa/lib -name '*.wpo' -delete 2>/dev/null; true + find /build/mine/chez-scintilla -name '*.so' -delete 2>/dev/null; true + find /build/mine/chez-pcre2 -name '*.so' -delete 2>/dev/null; true + find /build/mine/jerboa-shell -name '*.wpo' -delete 2>/dev/null; true + find vendor/gherkin-runtime -name '*.so' -delete 2>/dev/null; true + find vendor/gherkin-runtime -name '*.wpo' -delete 2>/dev/null; true + find vendor/jerboa-shell -name '*.so' -delete 2>/dev/null; true + find vendor/jerboa-shell -name '*.wpo' -delete 2>/dev/null; true + find vendor/chez-ssl -name '*.so' -delete 2>/dev/null; true + find vendor/chez-ssl -name '*.wpo' -delete 2>/dev/null; true + find vendor/chez-https -name '*.so' -delete 2>/dev/null; true + find vendor/chez-https -name '*.wpo' -delete 2>/dev/null; true + find vendor/jerboa-aws -name '*.so' -delete 2>/dev/null; true + find vendor/jerboa-aws -name '*.wpo' -delete 2>/dev/null; true + @echo "Recompiling jerboa core + prelude..." + echo '(import (chezscheme)) (compile-imported-libraries #t) (import (jerboa core)) (import (jerboa prelude))' \ + | $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib -q + rm -f /build/mine/jerboa/lib/jerboa/*.wpo + $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib --script /build/mine/jerboa/jerbuild.ss src/ lib/ + # Set up writable chez-qt copy from vendor (compile Scheme library) + mkdir -p /tmp/jemacs-build/chez-qt + cp -a vendor/chez-qt/. /tmp/jemacs-build/chez-qt/ + find /tmp/jemacs-build/chez-qt -name '*.so' -delete 2>/dev/null; true + find /tmp/jemacs-build/chez-qt -name '*.wpo' -delete 2>/dev/null; true + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /tmp/jemacs-build/chez-qt \ + --compile-imported-libraries --script /tmp/jemacs-build/chez-qt/compile-libs.ss + rm -f /tmp/jemacs-build/chez-qt/chez-qt/*.wpo + cp vendor/chez-pcre2-ffi-static.ss /build/mine/chez-pcre2/chez-pcre2/ffi.ss + $(MUSL_SCHEME) --libdirs /build/mine/chez-pcre2 \ + --compile-imported-libraries --script vendor/chez-pcre2-compile-libs.ss + rm -f /build/mine/chez-pcre2/chez-pcre2/*.wpo + cp vendor/chez-scintilla-ffi-static.sls /build/mine/chez-scintilla/src/chez-scintilla/ffi.sls + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/chez-scintilla/src \ + --compile-imported-libraries --script vendor/chez-scintilla-compile-libs.ss + rm -f /build/mine/chez-scintilla/src/chez-scintilla/*.wpo + cp vendor/jerboa-net-tcp-static.sls /build/mine/jerboa/lib/std/net/tcp.sls + cp vendor/jerboa-net-tcp-raw-static.sls /build/mine/jerboa/lib/std/net/tcp-raw.sls + cp vendor/jerboa-net-uri.sls /build/mine/jerboa/lib/std/net/uri.sls + cp vendor/jerboa-net-tls-rustls-static.sls /build/mine/jerboa/lib/std/net/tls-rustls.sls + rm -f /build/mine/jerboa/lib/std/net/*.wpo /build/mine/jerboa/lib/std/net/*.so + cp vendor/jerboa-crypto-native-static.sls /build/mine/jerboa/lib/std/crypto/native.sls + rm -f /build/mine/jerboa/lib/std/crypto/*.wpo /build/mine/jerboa/lib/std/crypto/*.so + mkdir -p /build/mine/jerboa/lib/std/security + cp vendor/jerboa-security-capsicum-static.sls /build/mine/jerboa/lib/std/security/capsicum.sls + rm -f /build/mine/jerboa/lib/std/security/*.wpo /build/mine/jerboa/lib/std/security/*.so + cp vendor/jerboa-os-landlock-static.sls /build/mine/jerboa/lib/std/os/landlock.sls + rm -f /build/mine/jerboa/lib/std/os/landlock.wpo /build/mine/jerboa/lib/std/os/landlock.so + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-tcp.ss + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-tcp-raw.ss + $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script vendor/jerboa-compile-uri.ss + rm -f /build/mine/jerboa/lib/std/net/*.wpo + cp vendor/jerboa-repl-static.sls /build/mine/jerboa/lib/std/repl.sls + rm -f /build/mine/jerboa/lib/std/repl.wpo /build/mine/jerboa/lib/std/repl.so + cd /build/mine/jerboa/lib && $(MUSL_SCHEME) --libdirs /build/mine/jerboa/lib \ + --compile-imported-libraries --script $(CURDIR)/vendor/jerboa-compile-repl.ss + rm -f /build/mine/jerboa/lib/std/repl.wpo + rm -f lib/jerboa/*.wpo lib/jerboa/*.so + JEMACS_STATIC=1 $(MUSL_SCHEME) --libdirs lib \ + --compile-imported-libraries --script vendor/jerboa-compile-repl-socket.ss + rm -f lib/jerboa/*.wpo + # Compile SSL/HTTPS/AWS + cp vendor/chez-ssl-static.sls vendor/chez-ssl/src/chez-ssl.sls + cp vendor/jerboa-aws-crypto-native.sls vendor/jerboa-aws/jerboa-aws/crypto.sls + find vendor/chez-ssl -name '*.so' -delete; find vendor/chez-ssl -name '*.wpo' -delete + find vendor/chez-https -name '*.so' -delete; find vendor/chez-https -name '*.wpo' -delete + find vendor/jerboa-aws -name '*.so' -delete; find vendor/jerboa-aws -name '*.wpo' -delete + JEMACS_STATIC=1 $(MUSL_SCHEME) \ + --libdirs vendor/chez-ssl/src:/build/mine/jerboa/lib \ + --compile-imported-libraries -q --script vendor/chez-ssl-compile-libs.ss + find vendor/chez-ssl -name '*.wpo' -delete + JEMACS_STATIC=1 $(MUSL_SCHEME) \ + --libdirs vendor/chez-https/src:vendor/chez-ssl/src \ + --compile-imported-libraries -q --script vendor/chez-https-compile-libs.ss + find vendor/chez-https -name '*.wpo' -delete + JEMACS_STATIC=1 $(MUSL_SCHEME) \ + --libdirs vendor/jerboa-aws:vendor/chez-https/src:vendor/chez-ssl/src:/build/mine/jerboa/lib \ + --compile-imported-libraries -q --script vendor/jerboa-aws-compile-libs.ss + find vendor/jerboa-aws -name '*.wpo' -delete + # Build tree-sitter C shim objects + mkdir -p /tmp/jemacs-build + gcc -c -O2 -I/opt/tree-sitter-include -o /tmp/jemacs-build/treesitter_shim.o \ + support/treesitter_shim.c -Wall + gcc -c -O2 -o /tmp/jemacs-build/treesitter_queries.o \ + support/treesitter_queries.c -Wall + # Run the main Qt build script + JEMACS_STATIC=1 \ + CHEZ_DIR=$(MUSL_CHEZ_DIR) \ + JERBOA_DIR=/build/mine/jerboa/lib \ + JSH_DIR=vendor/jerboa-shell/src \ + GHERKIN_DIR=vendor/gherkin-runtime \ + CHEZ_PCRE2_DIR=/build/mine/chez-pcre2 \ + CHEZ_SCINTILLA_DIR=/build/mine/chez-scintilla/src \ + CHEZ_QT_DIR=/tmp/jemacs-build/chez-qt \ + CHEZ_QT_SHIM_DIR=/opt/qt-shim \ + JSH_COREUTILS_LIB=vendor/libjsh_coreutils_stub.a \ + JAWS_DIR=vendor/jerboa-aws \ + CHEZ_SSL_DIR=vendor/chez-ssl \ + CHEZ_HTTPS_DIR=vendor/chez-https/src \ + TREE_SITTER_INCLUDE=/opt/tree-sitter-include \ + TREE_SITTER_LIB=/opt/tree-sitter-lib \ + TREE_SITTER_GRAMMARS=/opt/tree-sitter-grammars \ + TREE_SITTER_SHIM_OBJ=/tmp/jemacs-build/treesitter_shim.o \ + TREE_SITTER_QUERIES_OBJ=/tmp/jemacs-build/treesitter_queries.o \ + PKG_CONFIG_PATH=/opt/qt6-static/lib/pkgconfig \ + $(MUSL_SCHEME) \ + --libdirs lib:/build/mine/jerboa/lib:vendor/jerboa-shell/src:vendor/gherkin-runtime:/build/mine/chez-pcre2:/build/mine/chez-scintilla/src:/tmp/jemacs-build/chez-qt:vendor/jerboa-aws:vendor/chez-ssl/src:vendor/chez-https/src \ + --script build-binary-qt.ss + @echo "" + @echo "=== jemacs-qt static binary built ===" + @ls -lh jemacs-qt + @file jemacs-qt + +# ============================================================================= # Stress testing targets # ============================================================================= --- a/build-binary.ss +++ b/build-binary.ss @@ -439,7 +439,7 @@ (include-dir chez-dir) (gen-cmd (format - "{ { cat ~a; find ~a ~a/jerboa ~a/std/os ~a/std/net ~a/std/crypto ~a/std/security ~a/chez-scintilla lib/jerboa-emacs lib/jerboa vendor -name '*.sls' -o -name '*.ss' | xargs cat 2>/dev/null; } | \ + "{ { cat ~a; find ~a ~a/jerboa ~a/std/os ~a/std/net ~a/std/crypto ~a/std/security ~a/chez-scintilla lib/jerboa-emacs lib/jerboa vendor -path '*/qt/*' -prune -o -name '*.sls' -print -o -name '*.ss' -print | xargs cat 2>/dev/null; } | \ sed 's/;;.*//' | grep -oE '(foreign-procedure|foreign-entry\\?) \"[^\"]*\"' | sed 's/.* \"//;s/\"//'; \ { cat ~a; } | sed 's/;;.*//' | grep -o 'define-optional-ffi [^ ]* \"[^\"]*\"' | sed 's/.*define-optional-ffi [^ ]* \"//;s/\"//'; \ find ~a -name '*.sls' -o -name '*.ss' | \ @@ -451,7 +451,9 @@ grep -v '^jerboa_' | grep -v '^SSL_' | grep -v '^TLS_' | grep -v '^EVP_' | \ grep -v '^CRYPTO_' | grep -v '^PKCS5_' | grep -v '^RAND_' | \ grep -v '^QRcode_' | grep -v '^embed_encrypt$' | grep -v '^embed_random_bytes$' | \ grep -v '^kqueue$' | grep -v '^kevent$' | grep -v '^sandbox_' | \ -grep -v '^__error$' > /tmp/tui_ffi_syms.txt && \ +grep -v '^__error$' | grep -v '^ts_shim_' | grep -v '^ts_parser_' | grep -v '^ts_tree_' | grep -v '^ts_node_' | \ +grep -v '^ts_query_' | grep -v '^ts_language_' | grep -v '^tree_sitter_' | \ +grep -v '^qt_' | grep -v '^QRcode_' | grep -v '^chez_tcp_' | grep -v '^chez_ssl_' | grep -v '^chez_qt_' | grep -v '^chez_conn_' > /tmp/tui_ffi_syms.txt && \ awk '\ BEGIN{ print \"/* Auto-generated — do not edit */\"; \ print \"#include \\\"scheme.h\\\"\"; print \"\"; } \ @@ -581,12 +583,12 @@ tui_static_symbols.o \ ~a \ -L~a -lkernel -llz4 -lz \ -Wl,--whole-archive ~a ~a ~a -Wl,--no-whole-archive \ -~a ~a \ +~a ~a ~a \ -lm -ldl -lpthread -luuid -lstdc++ 2>&1" jsh-coreutils-lib chez-dir sci-a lex-a tbx-a - pcre2-libs ncurses-libs))) + pcre2-libs ncurses-libs vterm-libs))) (printf " ~a~n" cmd) (unless (= 0 (system cmd)) (display "Error: Static link failed\n") --- a/jemacs-main.c +++ b/jemacs-main.c @@ -85,9 +85,10 @@ int main(int argc, char *argv[]) { Sbuild_heap(NULL, NULL); #ifdef JEMACS_STATIC_BUILD - /* Static builds: register FFI symbols compiled into the binary. - * In musl static builds dlopen(NULL) is a stub, so foreign-procedure - * can't find symbols by name at runtime. We pre-register them here. */ + /* Static builds: tell Scheme not to call load-shared-object, and + * register FFI symbols compiled into the binary so foreign-procedure + * can find them (dlopen(NULL) is a stub in musl static builds). */ + setenv("JEMACS_STATIC", "1", 1); extern void register_static_foreign_symbols(void); register_static_foreign_symbols(); #endif new file mode 100644 --- /dev/null +++ b/jemacs-ssh-agent-stub.c @@ -0,0 +1,3 @@ +/* Stub — jemacs doesn't use ssh-agent */ +void chez_ssh_agent_stop(void) {} +int chez_ssh_agent_is_running(void) { return 0; } new file mode 100644 --- /dev/null +++ b/jemacs-tls-stubs.c @@ -0,0 +1,17 @@ +/* Stub — jemacs TUI doesn't use rustls TLS */ +#include <stddef.h> +#include <stdint.h> +uint64_t jerboa_tls_server_new(const char *c, const char *k) { return 0; } +uint64_t jerboa_tls_server_new_mtls(const char *c, const char *k, const char *ca) { return 0; } +void jerboa_tls_server_free(uint64_t s) {} +uint64_t jerboa_tls_accept(uint64_t s, int fd) { return 0; } +uint64_t jerboa_tls_connect(const char *h, int p) { return 0; } +uint64_t jerboa_tls_connect_pinned(const char *h, int p, const uint8_t *fp, size_t fplen) { return 0; } +uint64_t jerboa_tls_connect_mtls(const char *h, int p, const char *c, const char *k, const char *ca) { return 0; } +int jerboa_tls_read(uint64_t s, uint8_t *buf, uint64_t len) { return -1; } +int jerboa_tls_write(uint64_t s, const uint8_t *buf, uint64_t len) { return -1; } +int jerboa_tls_flush(uint64_t s) { return -1; } +void jerboa_tls_close(uint64_t s) {} +int jerboa_tls_set_nonblock(uint64_t s, int nb) { return -1; } +int jerboa_tls_get_fd(uint64_t s) { return -1; } +size_t jerboa_last_error(uint8_t *buf, size_t len) { return 0; } --- a/lib/jerboa-emacs/async.sls +++ b/lib/jerboa-emacs/async.sls @@ -552,8 +552,8 @@ (let ([v (hashtable-ref cache key - '#{miss dtdkom0leeg8kp0f535kz6s40-1})]) - (if (eq? v '#{miss dtdkom0leeg8kp0f535kz6s40-2}) + '#{miss jdswg0innsj0c7bc23vlvqgfo-1})]) + (if (eq? v '#{miss jdswg0innsj0c7bc23vlvqgfo-2}) (if (null? default) #f (car default)) v))) (def (weak-cache-set! cache key value) --- a/lib/jerboa-emacs/qt/window.sls +++ b/lib/jerboa-emacs/qt/window.sls @@ -399,36 +399,6 @@ (+ i 1) (cons 500 acc)))))))) (qt-edit-window-editor new-win))] - [(split-leaf? (qt-frame-root fr)) - (qt-splitter-set-orientation! - root-spl - orientation) - (let* ([new-win (qt-make-new-window! - root-spl - new-buf)] - [new-leaf (make-split-leaf new-win)] - [new-node (make-split-node - orientation - root-spl - (list cur-leaf new-leaf))]) - (qt-frame-root-set! fr new-node) - (qt-frame-windows-set! - fr - (split-tree-flatten (qt-frame-root fr))) - (let ([new-idx (list-index - (lambda (w) (eq? w new-win)) - (qt-frame-windows fr))]) - (qt-frame-current-idx-set! - fr - (or new-idx 0))) - (qt-window-process-events!) - (with-catch - (lambda (_e) (void)) - (lambda () - (qt-splitter-set-sizes! - root-spl - (list 500 500)))) - (qt-edit-window-editor new-win))] [else (let* ([parent-spl (if parent (split-node-splitter new file mode 100644 --- /dev/null +++ b/vendor/jerboa-net-tls-rustls-static.sls @@ -0,0 +1,57 @@ +#!chezscheme +;;; (std net tls-rustls) — static stub for jemacs TUI build +;;; +;;; The TUI binary does not use TLS. This stub exports the correct interface +;;; but raises an error if any function is actually called at runtime. +;;; It avoids foreign-procedure so the static binary does not need the +;;; Rust rustls library (libjerboa_native). + +(library (std net tls-rustls) + (export + rustls-server-ctx-new + rustls-server-ctx-new-mtls + rustls-server-ctx-free + rustls-accept + rustls-connect + rustls-connect-pinned + rustls-connect-mtls + rustls-read + rustls-write + rustls-flush + rustls-close + rustls-set-nonblock + rustls-get-fd) + + (import (chezscheme)) + + (define (tls-not-available who . args) + (error who "TLS not available in static jemacs build")) + + (define (rustls-server-ctx-new cert-path key-path) + (tls-not-available 'rustls-server-ctx-new)) + (define (rustls-server-ctx-new-mtls cert-path key-path client-ca-path) + (tls-not-available 'rustls-server-ctx-new-mtls)) + (define (rustls-server-ctx-free handle) + (tls-not-available 'rustls-server-ctx-free)) + (define (rustls-accept server-ctx tcp-fd) + (tls-not-available 'rustls-accept)) + (define (rustls-connect host port) + (tls-not-available 'rustls-connect)) + (define (rustls-connect-pinned host port pin-sha256) + (tls-not-available 'rustls-connect-pinned)) + (define (rustls-connect-mtls host port cert-path key-path ca-cert-path) + (tls-not-available 'rustls-connect-mtls)) + (define (rustls-read handle buf max-len) + (tls-not-available 'rustls-read)) + (define (rustls-write handle buf len) + (tls-not-available 'rustls-write)) + (define (rustls-flush handle) + (tls-not-available 'rustls-flush)) + (define (rustls-close handle) + (tls-not-available 'rustls-close)) + (define (rustls-set-nonblock handle nonblock?) + (tls-not-available 'rustls-set-nonblock)) + (define (rustls-get-fd handle) + (tls-not-available 'rustls-get-fd)) + + ) ;; end library