Always Learning

How to run racket binaries on Guix system

How to run compiled binaries on Guix system.

This is only for the Gnu Guix linux distro

Guix is a read-only system which uses soft links in a /gnu/store to link binaries and libraries to a guix-profile. Later I will write up docs explaining guix.

I wanted to learn how to interact with the racket ffi (foreign function interface) so I created a simple counter example.

#lang racket/gui


(require ffi/unsafe)

;; Dynamically fetch user home profile directory path to avoid hardcoded hashes
(define guix-home-lib (string-append (getenv "HOME") "/.guix-home/profile/lib/"))

================================================================

;; create state 
(define counter-state 0)

(define (increment-counter!)
  (set! counter-state (+ counter-state 1)))

;; create flutter like widgets.
(define (StatefulCounterView parent-scaffold)
  (define text-view
    (new message% [parent parent-scaffold]
                  [label "Decentralized Lisp Count: 0"]
                  [auto-resize #t]))
  
  (new button% [parent parent-scaffold]
               [label "Increment State"]
               [callback (lambda (button event)
                           (increment-counter!)
                           (send text-view set-label 
                                 (format "Decentralized Lisp Count: ~a" counter-state)))]))

;; Application launcher 

(define (main)
  (define app-window (new frame% [label "Aardvark Scheme Engine"]
                                 [width 400]
                                 [height 150]))
  (StatefulCounterView app-window)
  (send app-window show #t))

Compile the destkop-app.rkt

raco exe --guix desktop-app -o desktop-app.rkt

That should produce a 64bit elf image

file ./desktop-app 
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /gnu/store/m31vlvwm79m89fk3xk0z4h7snk61y510-glibc-2.41/lib/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped

Now lets run our gui example using guix shell

guix shell gtk+ glib libxrender libx11 fontconfig cairo pango   --preserve=^LD_LIBRARY_PATH   -- bash -c "export LD_LIBRARY_PATH=\$GUIX_ENVIRONMENT/lib; ./desktop-app

We have a gux application using racket as a standalone binary image.png

Running compiled racket app without having to specify the long guix shell commands ?

Have to create a bash script to build using guix shell and then launch the executable.

Create a bash script to compile and build a standalone racket gui app

#!/usr/bin/env bash
SOURCE_FILE="$1"
OUTPUT_BINARY="$2"

if [ -z "$SOURCE_FILE" ] || [ -z "$OUTPUT_BINARY" ]; then
    echo "Usage: racket-build.sh <source.rkt> <output_name>"
    exit 1
fi

# CRITICAL SAFETY FIX: Remove any stale old files to prevent 'Permission Denied' blocks
rm -f "$OUTPUT_BINARY"
rm -f "$OUTPUT_BINARY.raw"

echo "📦 Compiling Racket GUI Source..."
raco exe --gui -o "$OUTPUT_BINARY.raw" "$SOURCE_FILE"

echo "🎨 Fetching Guix Graphics Store Paths..."
GUIX_LIBS=$(guix build gtk+ glib mesa libxrender libjpeg libx11 fontconfig cairo pango zlib lz4 | tr '\n' ':' | sed 's|:$||' | sed 's|:|/lib:|g' | sed 's|$|/lib|')

echo "🚀 Injecting RPATH & Generating Clean Launcher..."
cat << EOF > "$OUTPUT_BINARY"
#!/usr/bin/env bash
export LD_LIBRARY_PATH="$GUIX_LIBS:\$LD_LIBRARY_PATH"
exec "\$(dirname "\$0")/$OUTPUT_BINARY.raw" "\$@"
EOF

# Grant execution permissions to both layers cleanly
chmod +x "$OUTPUT_BINARY"
chmod +x "$OUTPUT_BINARY.raw"

echo "✅ Success! Run your app directly with: ./$OUTPUT_BINARY