Skip to content

Add build option to provide an additional header file to the Lua compilation #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ pub fn build(b: *Build) void {
const library_name = b.option([]const u8, "library_name", "Library name for lua linking, default is `lua`") orelse "lua";
const shared = b.option(bool, "shared", "Build shared library instead of static") orelse false;
const luau_use_4_vector = b.option(bool, "luau_use_4_vector", "Build Luau to use 4-vectors instead of the default 3-vector.") orelse false;
const lua_user_h = b.option(Build.LazyPath, "lua_user_h", "Lazy path to user supplied c header file") orelse null;

if (lang == .luau and shared) {
std.debug.panic("Luau does not support compiling or loading shared modules", .{});
}

if (lua_user_h != null and (lang == .luajit or lang == .luau)) {
std.debug.panic("Only basic lua supports a user provided header file", .{});
}

// Zig module
const zlua = b.addModule("zlua", .{
.root_source_file = b.path("src/lib.zig"),
Expand All @@ -44,7 +49,7 @@ pub fn build(b: *Build) void {
const lib = switch (lang) {
.luajit => luajit_setup.configure(b, target, optimize, upstream, shared),
.luau => luau_setup.configure(b, target, optimize, upstream, luau_use_4_vector),
else => lua_setup.configure(b, target, optimize, upstream, lang, shared, library_name),
else => lua_setup.configure(b, target, optimize, upstream, lang, shared, library_name, lua_user_h),
};

// Expose the Lua artifact, and get an install step that header translation can refer to
Expand Down Expand Up @@ -103,6 +108,7 @@ pub fn build(b: *Build) void {
var common_examples = [_]struct { []const u8, []const u8 }{
.{ "interpreter", "examples/interpreter.zig" },
.{ "zig-function", "examples/zig-fn.zig" },
.{ "multithreaded", "examples/multithreaded.zig" },
};
const luau_examples = [_]struct { []const u8, []const u8 }{
.{ "luau-bytecode", "examples/luau-bytecode.zig" },
Expand Down
11 changes: 10 additions & 1 deletion build/lua.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub const Language = enum {
luau,
};

pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, lang: Language, shared: bool, library_name: []const u8) *Step.Compile {
pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, lang: Language, shared: bool, library_name: []const u8, lua_user_h: ?Build.LazyPath) *Step.Compile {
const version: std.SemanticVersion = switch (lang) {
.lua51 => .{ .major = 5, .minor = 1, .patch = 5 },
.lua52 => .{ .major = 5, .minor = 2, .patch = 4 },
Expand All @@ -38,6 +38,8 @@ pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.

lib.addIncludePath(upstream.path("src"));

const user_header = "user.h";

const flags = [_][]const u8{
// Standard version used in Lua Makefile
"-std=gnu99",
Expand All @@ -55,6 +57,8 @@ pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.

// Build as DLL for windows if shared
if (target.result.os.tag == .windows and shared) "-DLUA_BUILD_AS_DLL" else "",

if (lua_user_h) |_| b.fmt("-DLUA_USER_H=\"{s}\"", .{user_header}) else "",
};

const lua_source_files = switch (lang) {
Expand Down Expand Up @@ -87,6 +91,11 @@ pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.
lib.installHeader(upstream.path("src/lauxlib.h"), "lauxlib.h");
lib.installHeader(upstream.path("src/luaconf.h"), "luaconf.h");

if (lua_user_h) |user_h| {
lib.addIncludePath(user_h.dirname());
lib.installHeader(user_h, user_header);
}

return lib;
}

Expand Down
81 changes: 81 additions & 0 deletions examples/multithreaded.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Multi threaded lua program
//! The additional header must be passed to the build using `-Dlua_user_h=examples/user.h`
//! Checkout http://lua-users.org/wiki/ThreadsTutorial for more info

const std = @import("std");
const zlua = @import("zlua");

var mutex = std.Thread.Mutex{};

export fn lua_zlock(L: *zlua.LuaState) callconv(.C) void {
_ = L;
mutex.lock();
}

export fn lua_zunlock(L: *zlua.LuaState) callconv(.C) void {
_ = L;
mutex.unlock();
}

fn add_to_x(lua: *zlua.Lua, num: usize) void {
for (0..num) |_| {
// omit error handling for brevity
lua.loadString("x = x + 1\n") catch return;
lua.protectedCall(.{}) catch return;
}

const size = 256;
var buf = [_:0]u8{0} ** size;
_ = std.fmt.bufPrint(&buf, "print(\"{}: \", x)", .{std.Thread.getCurrentId()}) catch return;

// The printing from different threads does not always work nicely
// There seems to be a separate sterr lock on each argument to print
lua.loadString(&buf) catch return;
lua.protectedCall(.{}) catch return;
}

pub fn main() anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();

// Initialize The Lua vm and get a reference to the main thread
var lua = try zlua.Lua.init(allocator);
defer lua.deinit();

lua.openLibs();

// create a global variable accessible by all threads
// omit error handling for brevity
try lua.loadString("_G.x = 0\n");
try lua.protectedCall(.{});

const num = 1_000;
const n_jobs = 5;
var subs: [n_jobs]*zlua.Lua = undefined;

// create a thread pool to run all the functions
var pool: std.Thread.Pool = undefined;
try pool.init(.{ .allocator = allocator, .n_jobs = n_jobs });
defer pool.deinit();

var wg: std.Thread.WaitGroup = .{};

for (0..n_jobs) |i| {
subs[i] = lua.newThread();
pool.spawnWg(&wg, add_to_x, .{ subs[i], num });
}

// also do the thing from the main thread
add_to_x(lua, num);

wg.wait();

for (subs) |sub| {
try lua.closeThread(sub);
}

// print the final value
try lua.loadString("print(x)\n");
try lua.protectedCall(.{});
}
6 changes: 6 additions & 0 deletions examples/user.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#define lua_lock(L) lua_zlock(L)
#define lua_unlock(L) lua_zunlock(L)

void lua_zlock(lua_State* L);
void lua_zunlock(lua_State* L);

1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ There are currently three additional options that can be passed to `b.dependency
* `.lang`: Set the Lua language to build and embed. Defaults to `.lua54`. Possible values are `.lua51`, `.lua52`, `.lua53`, `.lua54`, and `luau`.
* `.shared`: Defaults to `false` for embedding in a Zig program. Set to `true` to dynamically link the Lua source code (useful for creating shared modules).
* `luau_use_4_vector`: defaults to false. Set to true to use 4-vectors instead of the default 3-vector in Luau.
* `lua_user_h`: defaults to null. Provide a path to an additional header file for the Lua compilation. Must be set to to `examples/user.h` in order to run the multithreaded example.

For example, here is a `b.dependency()` call that and links against a shared Lua 5.2 library:

Expand Down