Repository navigation
Releases: AxDSan/kiln
Release list
Kiln 2.2.0
Kiln 2.2.0 — forms have themes, Classic included; every guide page is Kiln 2.
A program that built under 2.1 still builds and still behaves.
Themes
A form names the palette it is drawn from, and every control follows it:
public partial form MainWindow
{
Theme = "Classic"; // Light, Dark, HighContrast, Classic, System
Button save { Text = "Save"; }
}
- Five built in.
Lightis what 2.1 drew.Darkswaps the surfaces and the text.HighContrastis black on white with heavier outlines.Classicis Windows 98 — a grey face, a two-pixel bevel that inverts while a button is held, square corners, navy selection.Systemfollows the desktop's own light/dark setting. - Switching while it runs.
Ui.SetTheme("Dark")repaints every control without recreating one: a value being typed, a selected row, a scroll position and a wired handler all survive.Ui.Theme()answers what is in force. - A theme of your own.
themes/<name>.kthemesets the tokens it cares about over a named base, and is carried inside the built binary, so a program still ships as one file:
{ "base": "Dark", "name": "Ocean", "ground": "#0b2942", "accent": "#3fc1ff", "radius": 10 }KILN_UI_THEME=Dark ./appoverrides whatever the form says.- Studio's canvas previews it, project themes included, so the designer shows what the program will draw.
Fixed
- A kit's constants reached Kiln 2 with their spelling changed —
MB_OKarrived asMbOk,MEMORY_BASIC_INFORMATIONasMEMORYBASICINFORMATION. A name written the way C writes one now keeps it, in a kit's declarations and inkiln migrate. A program written against the old spelling needs the API's own names instead.
Documentation
- Every guide page is Kiln 2: components, networking, databases, data files, memory, build targets, kits, interop and the
winkit. Their samples are compiled bycheck-docs. - The
winkit's six examples and the loader-hook example now exist in Kiln 2, beside their 1.x originals. CONTRIBUTING.mdandSECURITY.mdat the root, and CI that runs the test suite, the generated reference,check-docsandcheck-releaseon every push.- Newly listed limitations: an event handler's parameters are not checked against the event, and a method C calls back is
cdecl, so a 32-bit Windows callback is not yet expressible.
Checksums are beside each archive.
Kiln 2.1.1
Kiln 2.1.1 — programs over several files, kits and written type arguments in Kiln 2; configurable shortcuts in Studio.
This is the first published build since 2.0.0, so it carries 2.1.0 as well. A program that built under 2.0.0 still builds and still behaves.
Fixed
- A record field of type
T?was given half the room it needs, so writing one overwrote whatever came after it. Present in 2.0.0. - Nested
?.built aT??, andOrderByon aboolputtruefirst. - A Windows cross-build failed to link with a mingw older than binutils 2.42 (
relocation truncated to fit … .debug_line_str). Debug information for a Windows target is now DWARF 4. - The debugger could not stop in, or read, a program built by a compiler that defaults to position-independent executables, such as Ubuntu's clang. A debug build is now always linked
-no-pie;--releaseis unchanged.
Kiln 2
- A program can span several files: a
usingthat names a file beside the program brings it in, and the debugger stops in the file a function is written in. - A kit's
.kdecldeclarations reach Kiln 2 —using Kiln.Win;brings thewinkit's records, constants and functions.CallConv.Systemis stdcall on 32-bit Windows. - Type arguments can be written where there is nothing to infer them from:
Make<List<int>>(). - A
T?stored where aTis wanted — a local, an assignment, a return, a field — is its value or a stop, as an argument already was. - A handler wired at run time takes the grid's row, as a lambda or a method, and
-=unwires a method again. kiln migratekeeps a 1.x unit a unit, a c-record'sbyte[N]inline, and an empty dictionary's value type.
Studio
- File has New Project, Open Project, Open File and Close Project.
- Every shortcut is a setting, under Settings → Keyboard shortcuts, recorded by pressing the keys.
- Kiln 2 code is coloured by the compiler's own lexer, through the language server's semantic tokens.
- The toolbar no longer repeats Save, Undo and Redo, and the welcome wordmark no longer covers the tagline.
Checksums are beside each archive.
Kiln 2.0.0
Kiln 2.0 — Kiln 2 is the language: C#-shaped, still one native binary.
Kiln 2.0 is the release where Kiln 2 is Kiln. A program is C#-shaped source —
namespaces, classes and records with methods, generics, lambdas, T?,
Result<T> and no exceptions — compiled to one native binary with no runtime,
no metadata in the binary and no interpreter inside. A 1.x program still builds,
with a deprecation note; kiln migrate converts one.
This release invalidates 1.x source and the C ABI. KILN_ABI_VERSION is 5:
event handlers carry an environment pointer, so a support library must be
rebuilt against it. The versioning promise starts here — 2.x will not break a
Kiln 2 program that compiles today.
Language
- Namespaces,
static class/class/record/enum/interface, methods and
constructors, visibility,const,var/let. - Generics, methods and types: monomorphised,
whereconstraints enforced. - Lambdas and capturing closures,
Func<>/Action<>,Where/Select/Any/
First/OrderBy/SortoverList<T>. Result<T>with?and??,T?with null-check narrowing,x!andx?..switchexpressions,defer,$"…{x}…"interpolation.[Table]/[Column]/[Auto]compile-time SQL,[Packed]binary layouts,
[Dll]and c-function-pointer interop,[CLayout]records.
The RAD half
formblocks andpartial form, components and properties,Click += …
wired to a method group or a lambda written at the wiring site.- Studio opens, edits, saves and wires Kiln 2 forms through
kiln inspectand
kiln edit— the CLI is the only reader and the only writer.
Targets
- Console and GUI programs,
sharedlibandstaticlibwith a C header, on
Linux x86-64, plus Windows x64/x86 cross builds.
Tooling
kiln fmt(fixed point, comments carried) andkiln migrate(1.x → K2).- Language server: diagnostics, outline, formatting, hover, completion.
- DWARF debug info: a K2 binary is steppable, and records, classes, lists,
dictionaries, sets,Result<T>,stringandT?all read as the shapes
they are.
Runtime
- The collector reclaims memory while the program runs, including values held
only in a module variable and environments held by handlers wired at run
time.
Bundles: kiln-2.0.0-linux-x86_64.tar.gz and
kiln-2.0.0-windows-x86_64.zip, with .sha256 checksums beside them.
Kiln 1.3.1
The writing, corrected
No code changed in 1.3.1: no command, no kit, no fix, and no ABI movement. What
changed is prose — the handbook's data-files page is titled for what it does,
several source comments and a bundled example say the same thing in general
terms, and two command descriptions in the generated reference were reworded at
their source and regenerated.
If you are on 1.3.0, take this release for the corrected handbook inside the
bundle. Your programs build identically: 16 kits, 397 commands and 21
components are unchanged, and KILN_ABI_VERSION stays at 4.
Both platforms, as always
kiln-1.3.1-linux-x86_64.tar.gz and kiln-1.3.1-windows-x86_64.zip, each with
its .sha256. Unpack and run — no installer, nothing to configure.
Kiln 1.3.0
XML: reading the tables data ships in
Kiln could read JSON and INI. It could not read XML, which is the format a great
many data tables ship in — including tables written for a Chinese market in 2008:
a GB2312 declaration over bytes that are really GBK, Chinese in the comment
blocks, CRLF line endings, and one self-closing element per row.
use xml reads those files as bytes and hands bytes back. Nothing is
decoded, so a declaration that lies about its own encoding is a non-event
rather than a parse error, and an element name in Chinese comes back as that
name. Decoding stays a deliberate, separate step.
- A 134KB table of 535 elements and 7,164 attributes: every element name, every
IDand all 10,362 attributes are byte-identical to Python's ElementTree
reading the same file throughiconv. - Nothing is decoded, nothing is copied while walking, and nothing recurses —
open elements are a heap stack, so a deeply nested layout cannot overflow the
machine stack. - An empty name means any element, which is how a table is walked when every
row is named differently. - A missing attribute answers
""and leaves the error slot clear — most
attributes are optional — withxml_has_attras the predicate that separates
absent from present-and-empty. - A malformed document is refused with the line:
line 41: expected </Swords>, found </Weapons>.
use encoding: the codepages data files use
encoding_decode and encoding_encode convert between UTF-8 text and the
codepages those files are written in. No conversion table ships: GBK alone is
23,940 mappings, and a hand-copied table is how a decoder ends up 99% right and
silently wrong on a name. It asks the platform instead — iconv on POSIX, the
Win32 codepage API on Windows — through a thin shim, with Latin-1 and both UTF-16
byte orders written out because each is a byte loop.
gbk, gb2312, cp936, gb18030, big5, shift-jis, latin1, utf-16le,
utf-16be and utf-8 all resolve, and encoding_known says whether this build
can do one before a program depends on it.
- Decoding a 134KB GBK file is byte-identical to
iconv, and re-encoding
returns the original 134,212 bytes. - Two decoders, deliberately.
encoding_decoderefuses input that is not of
the encoding and names the byte that failed;encoding_decode_lossyreplaces
it with U+FFFD and carries on, which is what .NET'sStreamReaderdoes by
default. A program replacing one needs the second. encoding_encodeis strict in both directions: text that is not UTF-8 is
refused rather than written out as a quietly wrong name.
db: statements that do not hold the loop
Every db_* call was synchronous, so a query inside an event handler held every
other event until it answered. On loopback that is a millisecond and invisible,
which is exactly how a timing problem survives every local test.
db_exec_async, db_exec_async_n, db_query_async and db_query_async_n run a
statement on a worker thread and answer a request id. db_req_ready is the poll;
db_req_rows, db_req_columns, db_req_text, db_req_is_null and
db_req_error are the reading side; db_req_free releases the answer.
- A statement that took 780ms: the event loop turned 39 times while it
ran. A synchronous call gets one turn, because the statement runs inside it. - The connection belongs to the worker until the answer is claimed.
db_exec,
db_query, the transactions anddb_closerefuse it by name — two threads on
one connection is a crash in the MySQL client and corruption in SQLite, not a
race to be survived. - The worker never calls into the runtime: no
kn_malloc, no error slot, no
handle table. Every handle a program sees is still built by the program, on its
own thread, when it claims the answer. - The worker runs the same code the synchronous path runs, so the tested surface
did not move.
Getting a release out
RELEASING.md and tools/check-release.sh: a release touches more surfaces than
the compiler — the generated reference, the landing page's counts, the README's
counts and unpack lines, the book's table of contents, the limitations page — and
the four that can be checked mechanically now are, in one command.
Compatibility
Nothing a 1.2.0 program can see changes. New kits and new commands, no language
change, and no structure in the ABI moved or changed value — KILN_ABI_VERSION
stays at 4 and a library built against it keeps loading.
Kiln 1.2.0
A 32-bit Windows target
Kiln could cross-build for Windows x64; it can now cross-build for 32-bit Windows
too, with --os windows --arch x86.
On that target a c-record's pointer-sized fields are four bytes rather than
eight, and a system declaration is stdcall, which is what a Win32 call is
there. The win kit's declarations follow the pointer width, so the same
declarations serve both targets instead of one set per architecture.
- Console programs and libraries cross-build for
--os windows --arch x86with
the matching mingw toolchain installed, and are tested under wine. - A 32-bit windowed program is not built yet. The vendored UI stack (RmlUi,
SDL2, FreeType) is x86-64 only, and--arch x86with--target guiis refused
by name rather than attempted. - Nothing a 1.1.0 program can see changes: the same source builds the same
x86-64 binary it did, byte for byte.
Minor, not major, by the promise in the README: a new target, and no language or
ABI change — KILN_ABI_VERSION stays at 4.
Published after the fact: this version was tagged on 2026-09-12 and the release
was cut later, from the tag itself.
Kiln 1.1.0
Memory comes back while a program runs
Every allocation used to live until the process exited. A loop building one
short text per iteration held every text it had ever built: two million of them
came to 220MB, and a server ticking at 20Hz reached that in minutes. There was
no way to write a long-running program in Kiln that did not eventually die.
There is now a collector — conservative mark-and-sweep, tracing the machine
stack, the registers and your module variables. Nothing in the language
changed: you still never free anything, two names for one record are still the
same fields, and no annotation appeared anywhere.
- 2,000,000 short texts: 220MB → 8MB, and 0.72s → 0.58s. Freed memory
is reused memory, and reused memory is in cache. - Ten times the iterations: the same 8MB.
- A 20,000-player world with a busy tick loop: 1.76GB → 18.7MB.
Two new commands, memory_in_use() and collect_garbage(), for programs that
know when they are idle. KILN_GC_TRACE=1 reports every collection,
KILN_GC_MIN_HEAP moves the trigger, and KILN_GC=0 restores the old
behaviour. See Memory.
The honest part: collection stops the program, and the pause grows with the
heap — a program holding tens of megabytes pauses for tens of milliseconds.
Fine for a server, visible in a game at 60Hz. Incremental collection is not
written yet.
use db — SQLite and MySQL, and no way to concatenate
A new bundled library. Four things a login path needs — a SELECT, an INSERT, an
UPDATE, a DELETE — with one rule: every value is a bound parameter. There is
no db_exec(h, sql) without a parameter list, so the shortest call is the safe
one and a library whose easy path teaches injection cannot exist here.
let h: int = db_open("sqlite:accounts.db")
call db_exec(h, "insert into people values (?, ?)", ["Ada", "36"])
let rows: int = db_query(h, "select name, age from people where name = ?", ["Ada"])
while db_next(rows)
call print_text("{db_text(rows, 1)} is {db_int(rows, 2)}")
end
sqlite: opens a file or :memory:; mysql://user:pass@host/db opens MySQL or
MariaDB. The _n forms bind SQL NULL, which text[] cannot say. Without the
client libraries installed the library still builds and every command answers
its sentinel with a message naming what to install.
Binary protocols over TCP
tcpserver and tcpclient gained receive_bytes and send_bytes. Delivery
used to go through a C string, so a frame truncated at its first NUL — which
made every binary protocol unwritable. A byte-set carries every byte.
Alongside it, bytes and ptr now bridge both ways: bytes_from_ptr,
bytes_copy_to_ptr and bytes_concat, so a buffer from a C library becomes a
byte-set and back without a copy you have to reason about.
kiln install
kiln install copies the toolchain to /usr/local (or --user, or
--prefix), symlinks kiln and kiln-studio onto PATH, and leaves a tree
that still finds its own runtime. --dry-run writes nothing and says what it
would do; replacing an existing install needs --force.
Kate, KWrite and KDevelop
A KSyntaxHighlighting definition ships in editors/kate/, and
kiln install --editors puts it where Kate looks. It also gives a .kiln
document a highlighting mode named "Kiln", which is what Kate's LSP client keys
its server entry on — without it the language server never starts.
Also
- 357 commands, up from 337.
- The website stopped shipping OpenEPL: the landing page's screenshots were
still the pre-rename Studio, and the handbook's favicon was still the old
logo.tools/shots.shnow keeps all three copies in step. - A rebuilt landing page — dark, and it shows the source and the window it
builds side by side instead of describing the pairing. - Debug information is emitted as
llvm.dbg.declarebelow LLVM 19, so the
toolchain builds on Ubuntu 24.04's clang 18.
Kiln 1.0.2
A windowed program was 25MB. It is now 6.2MB.
None of it was your program. The prebuilt accesskit-c archives ship with full
Rust debug information — 43MB for linux-x86_64 alone — and it was linked into
every windowed program built with Kiln. A default build of the GUI template
came out at 25MB, of which 19MB was .debug_str and .debug_info naming
crates in /home/runner/.cargo/registry. The program's own code was under 4MB.
A debug build keeps your program's debug information, which is what stepping
needs and is a few kilobytes. The archives are now stripped once when they are
fetched, so the toolchain has nothing extra to link.
- GUI template, default build: 25MB → 6.2MB
--release: 4.5MB, unchanged — it was already dropping it at the link- The Linux bundle itself: 16MB → 9.4MB
If you are on 1.0.1, this is the release to take: the fix is in the archives
the bundle ships, so upgrading the toolchain is what shrinks your builds.
One language guide, not three pages saying the same thing
"A tour of the language", "Language guide" and "Shorthands and sugar" were
three doors into one room, and the reader had to guess which. There is now one
Language guide, and it is the introduction: the whole language in the order
you meet it — a module, the values it holds, the ways it chooses, repeats and
calls, then what happens when something fails.
The shorthands are no longer a chapter of their own. Compound assignment sits
under let and var; interpolation, blocks and raw text under writing text;
for each, ranges and repeat N times immediately below the counting for
they are written as; otherwise, check, T? and defer next to the error
slot they exist for. Each still says what it is rewritten into — a point better
made beside the longhand than a page away from it.
New whole-program examples for what a newcomer looks up first: an
if/else if/else chain, conditions built from and, or and not, and
one program showing while, a counting for, a for with a step, and
break and continue together. Every listing on the page is a whole module
and the build compiles all of them.
Linux — tar xzf kiln-1.0.2-linux-x86_64.tar.gz && cd kiln-1.0.2-linux-x86_64 && bin/kiln-studio
Windows — unzip and run bin\kiln-studio.exe
Needs clang and ar on the path to build programs. Any clang from 15 up.
Kiln 1.0.1
1.0.0 was tagged and never published, and it does not build on Ubuntu 24.04.
This is the first release under the new name.
Debug information is on unless you ask for --release, and it was written in
the record spelling — #dbg_declare — which LLVM only learned to parse in 19.
Ubuntu 24.04 ships clang 18, so every default build on the most common Linux
baseline failed with expected instruction opcode.
There is no single spelling that works everywhere: records arrived in LLVM 19
and the llvm.dbg.* intrinsics were deleted in 21. Kiln now asks clang --version once per build and writes whichever form that clang can read.
Verified against Ubuntu 24.04's clang 18.1.3 and against clang 21.1.8; both
produce identical DWARF.
Studio
A menu bar you can get out of, and move across. Clicking an open title
closed nothing, and with a menu open, moving along the bar did nothing — every
menu needed its own click. Both now behave the way every other menu bar does.
Escape closes one too.
An outline in the handbook, and code you can select. The rail lists the
open page's own headings under its entry, so a long page is navigable without
scrolling it to find out what is in it. And every code block has a select
button beside copy that swaps its highlighted lines for the same code as
selectable text — RmlUi can only select inside a text control, so this is how
you take half a sample rather than all of it.
One program at a time, and one Stop that means it. Run while an app was
running stopped it and started another — after a full rebuild, so the first
sign was the output vanishing. Debug while an app was running started a second
process against the same console. Both are refused now, and Stop ends whichever
of the two is going rather than only the app: a debugging session whose process
Studio killed used to stay open, leaving the transport on the toolbar and the
editor stopped on a line no debugger was on.
The GUI template shipped the old icon. kiln new gui-app still pointed at
the OpenEPL mark, so the first window anyone built under the new name came up
wearing the old one.
Also
Rename collateral the suite caught: the language tour built its greeting from
"Open" + "EPL", and gdi32's brush styles had been swept from BS_ to KN_ —
those are Windows' names, not ours, and the layout check against <windows.h>
said so.
Linux — tar xzf kiln-1.0.1-linux-x86_64.tar.gz && cd kiln-1.0.1-linux-x86_64 && bin/kiln-studio
Windows — unzip and run bin\kiln-studio.exe
Needs clang and ar on the path to build programs. Any clang from 15 up.
OpenEPL 0.11.1
Studio's editor numbers its lines, and the gutter beside them says what is
happening on each.
Three columns: a red dot for a breakpoint the debugger bound, a hollow
one for a breakpoint it could not place, and a blue arrow for the line
about to run — with that line tinted and its number lit. The breakpoint and
the execution cue have their own columns rather than sharing one, because a
program stops where you put a breakpoint and the two facts are true of the
same line exactly when both matter.
Clicking anywhere in a row toggles its breakpoint, as before.
Line numbers are right-aligned against the code, so the digits line up
whatever their count and the eye reads down one edge.