Add TCC project (as a library)

This commit is contained in:
2026-01-15 22:33:42 +01:00
parent d03910f6f0
commit b0deb958d2
26 changed files with 32056 additions and 1 deletions

264
packages/tcc/src/root.zig Normal file
View File

@@ -0,0 +1,264 @@
pub const std = @import("std");
pub const State = opaque {
pub fn init() !*State {
return import.tcc_new() orelse error.OutOfMemory;
}
pub fn deinit(self: *State) void {
import.tcc_delete(self);
}
/// Set TCC's private include and library path. Equivalent to
/// `CONFIG_TCCDIR` define or `-B` option.
pub fn setLibPath(self: *State, path: [*:0]const u8) void {
import.tcc_set_lib_path(self, path);
}
pub fn setErrorCallback(self: *State, context: ?*anyopaque, callback: ?*const ErrorCallbackFn) void {
import.tcc_set_error_func(self, context, callback);
}
/// Set TCC options using command line arguments syntax. You can specify
/// multiple options.
pub fn setOptions(self: *State, options: [*:0]const u8) void {
import.tcc_set_options(self, options);
}
// --- PREPROCESSOR ---
/// Add include path. Equivalent to `-I` option.
pub fn addIncludePath(self: *State, path: [*:0]const u8) void {
_ = import.tcc_add_include_path(self, path);
}
/// Add system include path. Equivalent to `-isystem` option.
pub fn addSystemIncludePath(self: *State, path: [*:0]const u8) void {
_ = import.tcc_add_sysinclude_path(self, path);
}
/// Add a preprocessor define. Defaults to `1`.
pub fn addDefine(self: *State, symbol: [*:0]const u8, value: ?[*:0]const u8) void {
import.tcc_define_symbol(self, symbol, value);
}
/// Remove a preprocessor define, if exists.
pub fn removeDefine(self: *State, symbol: [*:0]const u8) void {
import.tcc_undefine_symbol(self, symbol);
}
// --- COMPILING ---
/// Add a file (C source file, assembly, object file, library, DLL or an ld
/// script).
pub fn addFile(self: *State, filename: ?[*:0]const u8) !void {
const result = import.tcc_add_file(self, filename);
if (result < 0) return error.FileError;
}
/// Compile a C source string.
pub fn compileString(self: *State, source: [*:0]const u8) !void {
const result = import.tcc_compile_string(self, source);
if (result < 0) return error.CompileError;
}
// --- LINKING COMMANDS ---
/// Set output type. Must be called before any compilation.
pub fn setOutputType(self: *State, output_type: OutputType) void {
_ = import.tcc_set_output_type(self, output_type);
}
/// Add library search path. Equivalent to `-L` option.
pub fn addLibraryPath(self: *State, path: [*:0]const u8) void {
_ = import.tcc_add_library_path(self, path);
}
/// Link library. Equivalent to `-l` option. The library name should be the
/// same as if provided to the command line option, i.e. with "lib" prefix
/// and the extension possibly omitted.
pub fn addLibrary(self: *State, library: ?[*:0]const u8) !void {
const result = import.tcc_add_library(self, library);
if (result < 0) return error.AddLibraryError;
}
/// Add a symbol to the compiled program (like an extern function or data).
pub fn addSymbol(self: *State, name: [*:0]const u8, value: ?*anyopaque) void {
_ = import.tcc_add_symbol(self, name, value);
}
/// Output an executable, library or object file. Must not call
/// `tcc_relocate` beforehand.
pub fn outputFile(self: *State, filename: [*:0]const u8) !void {
const result = import.tcc_output_file(self, filename);
if (result < 0) return error.OutputFileError;
}
/// Link and run `main` function and return its value. Must not call
/// `tcc_relocate` beforehand.
pub fn run(self: *State, args: [:null][*:0]u8) c_int {
return import.tcc_run(self, @intCast(args.len), args.ptr);
}
/// Do all relocations necessary before calling `State.getSymbol`. Use
/// internal memory management.
pub fn relocateAuto(self: *State) !void {
const result = import.tcc_relocate(self, RELOCATE_AUTO);
if (result < 0) return error.RelocateError;
}
/// Do all relocations necessary before calling `State.getSymbol`. Use
/// provided `allocator` for allocating the result.
///
/// If the function returns without an error, the user is responsible for
/// calling `allocator.free` on the returned memory to free it.
pub fn relocateAlloc(self: *State, allocator: std.mem.Allocator) ![]const u8 {
const size = import.tcc_relocate(self, null);
if (size < 0) return error.RelocateError;
const memory = try allocator.alignedAlloc(u8, .fromByteUnits(16), @intCast(size));
errdefer allocator.free(memory);
const result = import.tcc_relocate(self, memory.ptr);
if (result < 0) return error.RelocateError;
return memory;
}
/// Get a pointer to a symbol value. Returns `null` if not found.
pub fn getSymbol(self: *State, name: [*:0]const u8) ?*anyopaque {
return import.tcc_get_symbol(self, name);
}
};
pub const ErrorCallbackFn = fn (context: ?*anyopaque, message: ?[*:0]const u8) callconv(.c) void;
pub const OutputType = enum(c_int) {
/// Output will be run in memory (default).
memory = 1,
/// Executable file.
exe = 2,
/// Dynamic library.
dll = 3,
/// Object file.
obj = 4,
/// Only preprocess (used internally).
preprocess = 5,
};
/// Magic constant for `tcc_relocate`.
pub const RELOCATE_AUTO: ?*anyopaque = @ptrFromInt(1);
pub const import = struct {
/// Create a new TCC compilation context.
pub extern fn tcc_new() ?*State;
/// Free a TCC compilation context.
pub extern fn tcc_delete(state: *State) void;
/// Set CONFIG_TCCDIR at runtime.
pub extern fn tcc_set_lib_path(state: *State, path: [*:0]const u8) void;
/// Set error/warning display callback.
pub extern fn tcc_set_error_func(state: *State, context: ?*anyopaque, callback: ?*const ErrorCallbackFn) void;
/// Set options as from command line (multiple supported).
pub extern fn tcc_set_options(state: *State, options: [*:0]const u8) void;
// --- PREPROCESSOR ---
/// Add include path.
///
/// NOTE Always returns `0`.
pub extern fn tcc_add_include_path(state: *State, path: [*:0]const u8) c_int;
/// Add in system include path.
///
/// NOTE Always returns `0`.
pub extern fn tcc_add_sysinclude_path(state: *State, path: [*:0]const u8) c_int;
/// Define preprocessor symbol `symbol`. Can put optional value.
///
/// NOTE Defaults to `1`.
pub extern fn tcc_define_symbol(state: *State, symbol: [*:0]const u8, value: ?[*:0]const u8) void;
/// Undefine preprocess symbol `symbol`.
pub extern fn tcc_undefine_symbol(state: *State, symbol: [*:0]const u8) void;
// --- COMPILING ---
/// Add a file (C file, dll, object, library, ld script). Return -1 if error.
///
/// NOTE Returns only either `0` or `-1`.
pub extern fn tcc_add_file(state: *State, filename: [*:0]const u8) c_int;
/// Compile a string containing a C source. Return -1 if error.
///
/// NOTE Returns only either `0` or `-1`.
pub extern fn tcc_compile_string(state: *State, source: [*:0]const u8) c_int;
// --- LINKING COMMANDS ---
/// Set output type. MUST BE CALLED before any compilation.
///
/// NOTE Always returns `0`.
pub extern fn tcc_set_output_type(state: *State, output_type: OutputType) c_int;
/// Equivalent to -Lpath option.
///
/// NOTE Always returns `0`.
pub extern fn tcc_add_library_path(state: *State, path: [*:0]const u8) c_int;
/// The library name is the same as the argument of the `-l` option.
pub extern fn tcc_add_library(state: *State, library: [*:0]const u8) c_int;
/// Add a symbol to the compiled program.
///
/// NOTE Always returns `0`.
pub extern fn tcc_add_symbol(state: *State, name: [*:0]const u8, value: ?*anyopaque) c_int;
/// Output an executable, library or object file. DO NOT call tcc_relocate()
/// before.
pub extern fn tcc_output_file(state: *State, filename: [*:0]const u8) c_int;
/// Link and run main() function and return its value. DO NOT call
/// tcc_relocate() before.
pub extern fn tcc_run(state: *State, argc: c_int, argv: [*:null][*:0]u8) c_int;
/// Do all relocations (needed before using tcc_get_symbol()).
///
/// Possible values for `ptr`:
/// * `RELOCATE_AUTO`: Allocate and manage memory internally
/// * `null`: return required memory size for the step below
/// * memory address: copy code to memory passed by the caller
///
/// Returns -1 if error.
pub extern fn tcc_relocate(state: *State, ptr: ?*anyopaque) c_int;
/// Return symbol value or NULL if not found.
pub extern fn tcc_get_symbol(state: *State, name: [*:0]const u8) ?*anyopaque;
};
test {
const add = &struct {
pub fn add(a: c_int, b: c_int) callconv(.c) c_int {
return a + b;
}
}.add;
var tcc: *State = try .init();
tcc.setOutputType(.memory);
tcc.setOptions("-nostdlib");
try tcc.compileString(
\\int add(int a, int b);
\\int add_one(int a) { return add(a, 1); }
);
tcc.addSymbol("add", @constCast(add));
try tcc.relocateAuto();
const add_one: *const fn (a: c_int) callconv(.c) c_int = @ptrCast(tcc.getSymbol("add_one").?);
try std.testing.expectEqual(0, add_one(-1));
try std.testing.expectEqual(1, add_one(0));
try std.testing.expectEqual(2, add_one(1));
}