Solo-C: a tiny coding agent in a single C file
main.c is the whole agent: a readline REPL that talks to any OpenAI-compatible /chat/completions endpoint and exposes exactly one tool, bash(command). Commands run through the user's shell with stdout and stderr merged, a hard timeout, and a process-group kill; the output and an exit marker go back to the model until it answers without calling a tool. make static-deps builds curl and c-ares from source into static-deps/, so `make static` can link a fully static binary on distros that get in the way: the distro libcurl.a drags in libpsl, brotli, zstd and nghttp2, whose static objects are GCC 14 LTO bytecode a newer linker cannot read, and a minimal system often ships no libcares.a at all. Two details worth keeping in mind when editing mkstaticdeps.sh. curl 8.22 takes --enable-ares=PREFIX rather than the older --with-cares, and it only warns about options it does not recognise, so the wrong spelling turns off async DNS in silence instead of failing. And link order is load-bearing: libcares.a has to come after libcurl.a or ares_* stays undefined.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -std=c99 -march=native $(shell pkg-config --cflags libcurl json-c readline)
|
||||
LDLIBS = $(shell pkg-config --libs libcurl json-c readline)
|
||||
|
||||
# Fully static binary.
|
||||
#
|
||||
# The distro libcurl.a cannot be linked statically on Alpine: it pulls in
|
||||
# libpsl, libbrotli, libzstd and libnghttp2, whose static archives hold LTO
|
||||
# bytecode from GCC 14 that the installed GCC 15 cannot read ("bytecode
|
||||
# stream ... generated with LTO version 14.0 instead of the expected 15.1"),
|
||||
# so psl_*/Brotli*/ZSTD* never resolve. -fno-lto does not help; the objects
|
||||
# are bytecode, not C.
|
||||
#
|
||||
# static-deps/libcurl.a is curl built without those four libraries, leaving
|
||||
# only LTO-free static deps. Regenerate with: make static-deps
|
||||
#
|
||||
# c-ares is built from source by the same script, for two reasons: a minimal
|
||||
# system often has no libcares.a at all (Void's c-ares-devel ships the shared
|
||||
# library only), and where an archive does exist it can carry the same LTO
|
||||
# bytecode as the distro libcurl.a. Building it also pins the version.
|
||||
#
|
||||
# Archive order matters: libcurl.a pulls in ares_* symbols, so libcares.a has to
|
||||
# come after it on the link line.
|
||||
CURL_STATIC ?= static-deps/libcurl.a
|
||||
CARES_STATIC ?= static-deps/libcares.a
|
||||
STATIC_LIBS = $(CURL_STATIC) $(CARES_STATIC) -lssl -lcrypto -lz -lidn2 -lunistring \
|
||||
-lreadline -lncursesw -ljson-c -lm -ldl -pthread
|
||||
|
||||
solo: main.c
|
||||
$(CC) $(CFLAGS) -o solo main.c $(LDLIBS)
|
||||
strip solo
|
||||
|
||||
static: main.c
|
||||
@for f in $(CURL_STATIC) $(CARES_STATIC); do \
|
||||
test -f $$f || { echo "missing $$f - run: make static-deps"; exit 1; }; \
|
||||
done
|
||||
$(CC) $(CFLAGS) -fno-lto -static -o solo-static main.c $(STATIC_LIBS)
|
||||
strip solo-static
|
||||
|
||||
static-deps:
|
||||
sh ./mkstaticdeps.sh
|
||||
|
||||
# Rebuild both archives from scratch, ignoring what is already there.
|
||||
static-deps-force:
|
||||
FORCE=1 sh ./mkstaticdeps.sh
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
INSTALL ?= install
|
||||
|
||||
install: solo
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 solo $(DESTDIR)$(BINDIR)/solo
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/solo $(DESTDIR)$(BINDIR)/solo-static
|
||||
|
||||
clean:
|
||||
rm -f solo solo-static
|
||||
|
||||
.PHONY: clean static static-deps static-deps-force install uninstall
|
||||
Reference in New Issue
Block a user