summaryrefslogtreecommitdiff
path: root/src/luarocks_runtime.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/luarocks_runtime.zig')
-rw-r--r--src/luarocks_runtime.zig40
1 files changed, 39 insertions, 1 deletions
diff --git a/src/luarocks_runtime.zig b/src/luarocks_runtime.zig
index e652096..7f01c19 100644
--- a/src/luarocks_runtime.zig
+++ b/src/luarocks_runtime.zig
@@ -38,6 +38,7 @@ const panto_home = @import("panto_home.zig");
const embedded_luarocks = @import("embedded_luarocks");
const embedded_lua_headers = @import("embedded_lua_headers");
const embedded_agent = @import("embedded_agent");
+const embedded_panto_so = @import("embedded_panto_so");
const lua_bridge = @import("lua_bridge.zig");
const c = lua_bridge.c;
@@ -137,6 +138,7 @@ pub fn bootstrap(
try panto_home.ensureDirsExist(layout, io);
try stageLuaHeaders(allocator, io, layout);
try stageAgentTree(allocator, io, layout);
+ try stagePantoModule(allocator, io, layout);
try stageDefaultConfig(allocator, io, layout);
const lua_wrapper_path = try writeLuaWrapper(allocator, io, layout, panto_executable_path);
defer allocator.free(lua_wrapper_path);
@@ -203,6 +205,33 @@ fn stageLuaHeaders(
}
// ---------------------------------------------------------------------------
+// Stage the embedded native `panto.so`
+// ---------------------------------------------------------------------------
+
+/// Write the binary-embedded `libpanto-lua` shared object to
+/// `<tree>/lib/lua/<short>/panto.so`, which is already on `package.cpath`
+/// (see `configurePackagePaths`). This guarantees the embedded VM's
+/// `require('panto')` resolves to the native agent/stream module on a
+/// cold, network-less machine — the module is *ours* and version-locked
+/// to this binary's Lua, so unlike luv it cannot be fetched from luarocks.
+///
+/// The `.so` leaves `lua_*` undefined and resolves them against the host
+/// binary at `dlopen` time, identical to how a luarocks-installed C rock
+/// (e.g. luv) works — `exe.rdynamic = true` keeps those symbols findable.
+///
+/// Uses the same `writeIfDifferent` discipline as `stageLuaHeaders` so an
+/// unchanged `.so` keeps its on-disk mtime across reruns.
+fn stagePantoModule(
+ allocator: Allocator,
+ io: Io,
+ layout: panto_home.Layout,
+) !void {
+ var dir = try Io.Dir.cwd().openDir(io, layout.lib_lua_dir, .{});
+ defer dir.close(io);
+ try writeIfDifferent(allocator, io, dir, "panto.so", embedded_panto_so.bytes);
+}
+
+// ---------------------------------------------------------------------------
// Stage the embedded `agent/` tree
// ---------------------------------------------------------------------------
@@ -311,11 +340,20 @@ fn writeIfDifferent(
name: []const u8,
contents: []const u8,
) !void {
- if (dir.readFileAlloc(io, name, allocator, .limited(1 << 22))) |existing| {
+ // Size the read limit to the new content (plus a small margin) so it
+ // never truncates the comparison — staged files range from tiny
+ // headers to a multi-megabyte `panto.so`. A larger on-disk file can't
+ // match `contents` anyway, so capping at `contents.len + 1` is enough
+ // to detect inequality without reading unbounded bytes.
+ const limit = contents.len + 1;
+ if (dir.readFileAlloc(io, name, allocator, .limited(limit))) |existing| {
defer allocator.free(existing);
if (std.mem.eql(u8, existing, contents)) return;
} else |err| switch (err) {
error.FileNotFound => {},
+ // A file larger than our limit definitionally differs from
+ // `contents`; fall through and rewrite.
+ error.StreamTooLong => {},
else => return err,
}
try dir.writeFile(io, .{ .sub_path = name, .data = contents });