mirror of
https://github.com/ion232/reticulum-zig.git
synced 2025-12-22 06:37:05 +00:00
build: add option to export core as static c lib
This commit is contained in:
30
build.zig
30
build.zig
@@ -5,6 +5,7 @@ const Optimize = std.builtin.OptimizeMode;
|
|||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
var builder = Builder.init(b);
|
var builder = Builder.init(b);
|
||||||
|
builder.c();
|
||||||
builder.examples();
|
builder.examples();
|
||||||
builder.tests();
|
builder.tests();
|
||||||
}
|
}
|
||||||
@@ -16,6 +17,7 @@ const Builder = struct {
|
|||||||
const TestSuite = enum { integration, simulation };
|
const TestSuite = enum { integration, simulation };
|
||||||
|
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
|
options: *std.Build.Step.Options,
|
||||||
target: Target,
|
target: Target,
|
||||||
optimize: Optimize,
|
optimize: Optimize,
|
||||||
|
|
||||||
@@ -37,11 +39,32 @@ const Builder = struct {
|
|||||||
|
|
||||||
return .{
|
return .{
|
||||||
.b = b,
|
.b = b,
|
||||||
|
.options = b.addOptions(),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn c(self: *Self) void {
|
||||||
|
const step = self.b.step("c", "Build core as a c library");
|
||||||
|
const static_lib = self.b.addLibrary(.{
|
||||||
|
.name = self.b.fmt("rtcore", .{}),
|
||||||
|
.root_module = self.module(.core),
|
||||||
|
.linkage = .static,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exports = @import("core/exports.zig");
|
||||||
|
const write_files = self.b.addWriteFiles();
|
||||||
|
const header_path = write_files.add("rt_core.h", exports.generateHeader());
|
||||||
|
|
||||||
|
const install_header_file = self.b.addInstallHeaderFile(header_path, "rt_core.h");
|
||||||
|
const install_static_lib = self.b.addInstallArtifact(static_lib, .{});
|
||||||
|
|
||||||
|
install_header_file.step.dependOn(&write_files.step);
|
||||||
|
step.dependOn(&install_static_lib.step);
|
||||||
|
step.dependOn(&install_header_file.step);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn examples(self: *Self) void {
|
pub fn examples(self: *Self) void {
|
||||||
const step = self.b.step("example", "Run an example");
|
const step = self.b.step("example", "Run an example");
|
||||||
|
|
||||||
@@ -89,6 +112,7 @@ const Builder = struct {
|
|||||||
.target = self.target,
|
.target = self.target,
|
||||||
.optimize = self.optimize,
|
.optimize = self.optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
step.dependOn(&self.b.addRunArtifact(compile).step);
|
step.dependOn(&self.b.addRunArtifact(compile).step);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +166,10 @@ const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addImport(self: *Self, compile: *std.Build.Step.Compile, comptime package: Package) void {
|
fn addImport(self: *Self, compile: *std.Build.Step.Compile, comptime package: Package) void {
|
||||||
compile.root_module.addImport(@tagName(package), self.b.modules.getPtr(@tagName(package)).?.*);
|
compile.root_module.addImport(@tagName(package), self.module(package));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn module(self: *Self, comptime package: Package) *std.Build.Module {
|
||||||
|
return self.b.modules.getPtr(@tagName(package)).?.*;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
// TODO: Add storage interface.
|
|
||||||
|
|
||||||
pub const Clock = struct {
|
pub const Clock = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
@@ -15,5 +13,57 @@ pub const Clock = struct {
|
|||||||
|
|
||||||
pub const Rng = std.Random;
|
pub const Rng = std.Random;
|
||||||
|
|
||||||
|
pub const SimpleClock = struct {
|
||||||
|
pub const Callback = *const fn () callconv(.c) u64;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
monotonicMicrosFn: Callback,
|
||||||
|
|
||||||
|
pub fn init(monotonicMicrosFn: Callback) Self {
|
||||||
|
return .{
|
||||||
|
.monotonicMicrosFn = monotonicMicrosFn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monotonicMicros(ptr: *anyopaque) u64 {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ptr));
|
||||||
|
return self.monotonicMicrosFn();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clock(self: *Self) Clock {
|
||||||
|
return .{
|
||||||
|
.ptr = self,
|
||||||
|
.monotonicMicrosFn = monotonicMicros,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SimpleRng = struct {
|
||||||
|
pub const Callback = *const fn (buf: [*]u8, length: usize) callconv(.c) void;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
rngFillFn: Callback,
|
||||||
|
|
||||||
|
pub fn init(rngFillFn: Callback) Self {
|
||||||
|
return .{
|
||||||
|
.rngFillFn = rngFillFn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rngFill(ptr: *anyopaque, buf: []u8) void {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ptr));
|
||||||
|
self.rngFillFn(buf.ptr, buf.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rng(self: *Self) Rng {
|
||||||
|
return .{
|
||||||
|
.ptr = self,
|
||||||
|
.fillFn = rngFill,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
clock: Clock,
|
clock: Clock,
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
|
|||||||
187
core/exports.zig
Normal file
187
core/exports.zig
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
//! This file is not part of the core module and therefore can contain non-freestanding code.
|
||||||
|
//! Be aware that this file will be imported into the build executable in order to generate the header file.
|
||||||
|
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const std = @import("std");
|
||||||
|
const lib = @import("lib.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Gpa = std.heap.GeneralPurposeAllocator(.{});
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
var gpa: ?Gpa = null;
|
||||||
|
var allocator: ?Allocator = null;
|
||||||
|
var clock: lib.System.SimpleClock = undefined;
|
||||||
|
var rng: lib.System.SimpleRng = undefined;
|
||||||
|
var system: lib.System = undefined;
|
||||||
|
|
||||||
|
// Exported structs and functions.
|
||||||
|
|
||||||
|
pub const Error = enum(c_int) {
|
||||||
|
none = 0,
|
||||||
|
missing_allocator = 1,
|
||||||
|
out_of_memory = 2,
|
||||||
|
leaked_memory = 3,
|
||||||
|
unknown = 255,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn libInit(
|
||||||
|
monotonicMicros: lib.System.SimpleClock.Callback,
|
||||||
|
rngFill: lib.System.SimpleRng.Callback,
|
||||||
|
) callconv(.c) Error {
|
||||||
|
if (allocator != null) return .missing_allocator;
|
||||||
|
|
||||||
|
gpa = Gpa{};
|
||||||
|
allocator = gpa.?.allocator();
|
||||||
|
clock = lib.System.SimpleClock.init(monotonicMicros);
|
||||||
|
rng = lib.System.SimpleRng.init(rngFill);
|
||||||
|
system = lib.System{
|
||||||
|
.clock = clock.clock(),
|
||||||
|
.rng = rng.rng(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return .none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn libDeinit() callconv(.c) Error {
|
||||||
|
if (gpa == null or allocator == null) return .none;
|
||||||
|
|
||||||
|
if (gpa) |*g| {
|
||||||
|
if (g.deinit() == .leak) {
|
||||||
|
return .leaked_memory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn makeNode(node_ptr: **anyopaque) callconv(.c) Error {
|
||||||
|
const ally = allocator orelse return .missing_allocator;
|
||||||
|
|
||||||
|
const node = ally.create(lib.Node) catch |e| return convertError(Allocator.Error, e);
|
||||||
|
errdefer ally.destroy(node);
|
||||||
|
|
||||||
|
node_ptr.* = node;
|
||||||
|
node.* = lib.Node.init(
|
||||||
|
ally,
|
||||||
|
&system,
|
||||||
|
null,
|
||||||
|
.{},
|
||||||
|
) catch |e| return convertError(lib.Node.Error, e);
|
||||||
|
|
||||||
|
return .none;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convertError(comptime E: type, e: E) Error {
|
||||||
|
if (E == lib.Node.Error) {
|
||||||
|
return switch (e) {
|
||||||
|
error.OutOfMemory => .out_of_memory,
|
||||||
|
else => .unknown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return .unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform exports.
|
||||||
|
// This is currently also adding these symbols to the build executable.
|
||||||
|
// As far as I can tell it won't cause any issues and can probably be changed later.
|
||||||
|
comptime {
|
||||||
|
for (@typeInfo(@This()).@"struct".decls) |declaration| {
|
||||||
|
const field = @field(@This(), declaration.name);
|
||||||
|
const info = @typeInfo(@TypeOf(field));
|
||||||
|
|
||||||
|
if (info != .@"fn") continue;
|
||||||
|
if (std.mem.eql(u8, declaration.name, "generateHeader")) continue;
|
||||||
|
|
||||||
|
const function: *const anyopaque = @ptrCast(&field);
|
||||||
|
const export_options = std.builtin.ExportOptions{
|
||||||
|
.name = deriveName(declaration.name),
|
||||||
|
.linkage = .strong,
|
||||||
|
};
|
||||||
|
|
||||||
|
@export(function, export_options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This needs to be public for use in the build c step.
|
||||||
|
pub fn generateHeader() []const u8 {
|
||||||
|
comptime var header: []const u8 =
|
||||||
|
\\#ifndef RT_CORE_H
|
||||||
|
\\#define RT_CORE_H
|
||||||
|
\\
|
||||||
|
\\#include <stddef.h>
|
||||||
|
\\#include <stdint.h>
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
|
||||||
|
inline for (@typeInfo(Self).@"struct".decls) |declaration| {
|
||||||
|
const field = @field(Self, declaration.name);
|
||||||
|
const info = @typeInfo(@TypeOf(field));
|
||||||
|
|
||||||
|
if (info != .@"fn") continue;
|
||||||
|
if (comptime std.mem.eql(u8, declaration.name, "generateHeader")) continue;
|
||||||
|
|
||||||
|
const function = info.@"fn";
|
||||||
|
const name = comptime deriveName(declaration.name);
|
||||||
|
const return_type = switch (function.return_type.?) {
|
||||||
|
Error => "int",
|
||||||
|
*anyopaque => "void*",
|
||||||
|
c_int => "int",
|
||||||
|
else => @typeName(function.return_type.?),
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime var forward_declaration: []const u8 = return_type ++ " " ++ name ++ "(";
|
||||||
|
|
||||||
|
inline for (function.params, 0..) |param, i| {
|
||||||
|
const param_type = switch (param.type.?) {
|
||||||
|
lib.System.SimpleClock.Callback => "int64_t (*monotonic_micros)(void)",
|
||||||
|
lib.System.SimpleRng.Callback => "void (*rng_fill)(uint8_t* buf, size_t length)",
|
||||||
|
*anyopaque => "void*",
|
||||||
|
**anyopaque => "void**",
|
||||||
|
c_int => "int",
|
||||||
|
else => @typeName(param.type.?),
|
||||||
|
};
|
||||||
|
|
||||||
|
forward_declaration = forward_declaration ++ param_type;
|
||||||
|
|
||||||
|
if (i != function.params.len - 1) {
|
||||||
|
forward_declaration = forward_declaration ++ ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_declaration = forward_declaration ++ ");\n";
|
||||||
|
|
||||||
|
header = header ++ forward_declaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = header ++
|
||||||
|
\\
|
||||||
|
\\#endif
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deriveName(comptime name: []const u8) []const u8 {
|
||||||
|
var c_name: []const u8 = "rt_";
|
||||||
|
|
||||||
|
inline for (name) |char| {
|
||||||
|
if (std.ascii.isUpper(char)) {
|
||||||
|
c_name = c_name ++ .{ '_', std.ascii.toLower(char) };
|
||||||
|
} else {
|
||||||
|
c_name = c_name ++ .{char};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const arch = builtin.target.cpu.arch;
|
||||||
|
|
||||||
|
if (arch == .wasm32 or arch == .wasm64) {
|
||||||
|
return name;
|
||||||
|
} else {
|
||||||
|
return c_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,9 @@ comptime {
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
// We import the file so that the exports actually run for this module.
|
||||||
|
_ = @import("exports.zig");
|
||||||
|
|
||||||
// Ensure unit tests are ran by referencing relevant files.
|
// Ensure unit tests are ran by referencing relevant files.
|
||||||
if (builtin.is_test) {
|
if (builtin.is_test) {
|
||||||
// Private files must be specifically enumerated.
|
// Private files must be specifically enumerated.
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ pub fn main() !void {
|
|||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
const ally = gpa.allocator();
|
const ally = gpa.allocator();
|
||||||
var clock = try io.os.Clock.init();
|
var clock = try io.system.Clock.init();
|
||||||
var rng = io.os.Rng.init();
|
|
||||||
var system = core.System{
|
var system = core.System{
|
||||||
.clock = clock.clock(),
|
.clock = clock.clock(),
|
||||||
.rng = rng.rng(),
|
.rng = std.crypto.random,
|
||||||
};
|
};
|
||||||
|
|
||||||
var node = try core.Node.init(ally, &system, null, .{});
|
var node = try core.Node.init(ally, &system, null, .{});
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub const driver = @import("driver.zig");
|
pub const driver = @import("driver.zig");
|
||||||
pub const os = @import("os.zig");
|
pub const system = @import("system.zig");
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
const core = @import("core");
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Clock = @import("os/Clock.zig");
|
|
||||||
pub const Rng = @import("os/Rng.zig");
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
const core = @import("core");
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const Timer = std.time.Timer;
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
timer: Timer,
|
|
||||||
|
|
||||||
pub fn init() Timer.Error!Self {
|
|
||||||
return .{
|
|
||||||
.timer = try Timer.start(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn monotonicMicros(ptr: *anyopaque) u64 {
|
|
||||||
const self: *Self = @ptrCast(@alignCast(ptr));
|
|
||||||
return self.timer.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clock(self: *Self) core.System.Clock {
|
|
||||||
return .{
|
|
||||||
.ptr = self,
|
|
||||||
.monotonicMicrosFn = monotonicMicros,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const core = @import("core");
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn init() Self {
|
|
||||||
return .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rng(self: *Self) core.System.Rng {
|
|
||||||
_ = self;
|
|
||||||
return std.crypto.random;
|
|
||||||
}
|
|
||||||
28
io/system.zig
Normal file
28
io/system.zig
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const core = @import("core");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Clock = struct {
|
||||||
|
const Timer = std.time.Timer;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
timer: Timer,
|
||||||
|
|
||||||
|
pub fn init() Timer.Error!Self {
|
||||||
|
return .{
|
||||||
|
.timer = try Timer.start(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monotonicMicros(ptr: *anyopaque) u64 {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ptr));
|
||||||
|
return self.timer.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clock(self: *Self) core.System.Clock {
|
||||||
|
return .{
|
||||||
|
.ptr = self,
|
||||||
|
.monotonicMicrosFn = monotonicMicros,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user