Skip to content

Hello! 👋 My name is Nathan Weir. This is a fun personal project for using AI to build a bespoke, domain-specific programming language. It is not a serious, professional project. This site and the language itself are largely generated via Claude Code. If you find yourself programming with Weir, have fun - but use at your own risk!

Native Libraries

Weir packages can include native C source files that are automatically compiled and linked. This is how Weir integrates with system libraries like OpenGL, GLFW, and audio frameworks.

In weir.pkg, use the native section to declare C sources and link libraries:

(package
(name "weir-gl")
(version "0.1.0")
(sources "lib.weir")
(native
(sources "gl_helper.c")
(link "glfw" "GL" "m")))
FieldDescription
sourcesC source files to compile (relative to weir.pkg)
linkSystem libraries to link (-l flags)

The build process depends on the execution mode:

  1. All native C sources from all packages are collected
  2. Compiled into a shared library via cc -shared -fPIC -o tmp/libweir_native.so
  3. The shared library is loaded via dlopen before JIT execution
  4. Symbols are resolved via dlsym when extern functions are called
Terminal window
cd demos/tetris && weir run
# Internally: cc -shared -fPIC -o tmp/libweir_native.so gl_helper.c -lglfw -lGL -lm
# Then: dlopen("tmp/libweir_native.so")
# Then: JIT compile and run
  1. Native C sources are passed as extra arguments to the C compiler
  2. Link libraries are passed as -l flags
  3. Everything is linked into a single standalone binary
Terminal window
cd demos/tetris && weir build -o ../../tmp/tetris
# Internally: cc -o tetris program.o gl_helper.c -lglfw -lGL -lm

Same as JIT mode — the native library is compiled and loaded once at startup.

When an application depends on a library that has native code, the native sources and link flags are collected from all packages in the dependency tree:

demos/tetris/weir.pkg
└── deps: weir-gl
└── native: gl_helper.c, link: glfw GL m

All native sources and link flags bubble up to the top-level build.

Before the package system, native code required manual commands:

Terminal window
# Manual AOT:
weir build demos/tetris.weir -o tmp/tetris --cc-arg gl_helper.c -l glfw -l GL -l m
# Manual JIT:
cc -shared -fPIC -o tmp/libgl.so gl_helper.c -lglfw -lGL -lm
weir run demos/tetris.weir --load tmp/libgl.so

The package system makes this declarative — just weir build or weir run.

For JIT mode without a package system, you can manually pre-load shared libraries:

Terminal window
weir run game.weir --load /usr/lib/libglfw.so --load ./mylib.so

The --load flag calls dlopen with RTLD_GLOBAL, making all symbols available for dlsym resolution.

For AOT builds without a package system:

Terminal window
weir build game.weir --cc-arg helper.c --cc-arg audio.c -l glfw -l GL -l openal

--cc-arg passes raw arguments to the C compiler. -l passes library link flags.

The weir-gl library package provides OpenGL/GLFW bindings:

weir-gl/
weir.pkg # Package manifest
lib.weir # Weir extern "C" declarations
gl_helper.c # C wrapper functions

gl_helper.c contains C functions that wrap verbose OpenGL/GLFW APIs into simpler interfaces:

// gl_helper.c (simplified)
#include <GLFW/glfw3.h>
static GLFWwindow* window;
int64_t gl_init(int64_t w, int64_t h, const char* title) {
glfwInit();
window = glfwCreateWindow(w, h, title, NULL, NULL);
glfwMakeContextCurrent(window);
return window != NULL;
}

lib.weir declares these functions for Weir:

(extern "C"
(defn gl_init ((w : i64) (h : i64) (title : String)) : i64)
(defn gl_should_close () : i64)
(defn gl_begin_frame () : Unit)
(defn gl_draw_rect ((x : f64) (y : f64) (w : f64) (h : f64)
(r : f64) (g : f64) (b : f64) (a : f64)) : Unit)
(defn gl_end_frame () : Unit)
(defn gl_cleanup () : Unit))

Applications then import from the package:

(import weir-gl (gl_init gl_draw_rect gl_cleanup ...))