Skip to content
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.zig-cache/
zig-out/
zig-pkg/
*.o
30 changes: 5 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,14 @@ A [vfox](https://github.com/version-fox/vfox) / [mise](https://mise.jdx.dev) plu

- **Dynamic version fetching**: Automatically fetches available versions from lua.org
- **Always up-to-date**: No static version list to maintain
- **Compiles from source**: Uses official Lua source releases
- **Compiles from source** via Zig: Cross-platform build with a single compiler
- **LuaRocks included**: Automatically installs LuaRocks for Lua 5.x versions
- **Cross-platform**: Works on Linux and macOS
- **Cross-platform**: Works on Linux, macOS, and Windows

## Requirements

- A C compiler (gcc or clang)
- make
- curl

### macOS

```bash
xcode-select --install
```

### Debian/Ubuntu

```bash
sudo apt-get install build-essential libreadline-dev
```

### RHEL/CentOS

```bash
sudo yum groupinstall "Development Tools"
sudo yum install readline-devel
```
- [Zig](https://ziglang.org/download/) 0.16.0
- make (not needed on Windows)

## Installation

Expand Down Expand Up @@ -81,7 +61,7 @@ This plugin:

1. Fetches the list of available versions from [lua.org/ftp](https://www.lua.org/ftp/)
2. Downloads the source tarball for the requested version
3. Compiles Lua from source using `make`
3. Copies `build.zig` and a readline shim into the source tree, then compiles with `zig build`
4. Installs LuaRocks (for Lua 5.x versions)

## License
Expand Down
113 changes: 113 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const std = @import("std");

fn addExe(b: *std.Build, name: []const u8, src: []const u8, flags: []const []const u8, lib: *std.Build.Step.Compile, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
mod.addIncludePath(b.path("src"));
mod.addCSourceFile(.{ .file = b.path(src), .flags = flags });
mod.linkLibrary(lib);

if (target.result.os.tag == .linux) {
mod.linkSystemLibrary("dl", .{});
}

const exe = b.addExecutable(.{ .name = name, .root_module = mod });
return exe;
}

pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const readline = b.option(bool, "readline", "Link readline for REPL") orelse false;

const src_dir = b.build_root.handle.openDir(b.graph.io, "src", .{ .iterate = true }) catch
@panic("src/ directory not found — run zig build from extracted Lua source root");
defer src_dir.close(b.graph.io);

var lib_srcs = std.ArrayListUnmanaged([]const u8).empty;
defer lib_srcs.deinit(b.allocator);

var iter = src_dir.iterate();
while (iter.next(b.graph.io) catch @panic("failed to read src/")) |entry| {
if (entry.kind != .file) continue;
if (!std.mem.endsWith(u8, entry.name, ".c")) continue;
if (std.mem.eql(u8, entry.name, "lua.c") or std.mem.eql(u8, entry.name, "luac.c")) continue;
try lib_srcs.append(b.allocator, b.dupe(entry.name));
}

const src_root = b.path("src");
const os_tag = target.result.os.tag;

var flags = std.ArrayListUnmanaged([]const u8).empty;
defer flags.deinit(b.allocator);

try flags.append(b.allocator, "-std=gnu99");

if (os_tag == .linux or os_tag == .windows) {
try flags.append(b.allocator, "-fvisibility=hidden");
}

switch (os_tag) {
.linux => try flags.append(b.allocator, "-DLUA_USE_LINUX"),
.macos => try flags.append(b.allocator, "-DLUA_USE_MACOSX"),
.windows => {},
else => try flags.append(b.allocator, "-DLUA_USE_POSIX"),
}

if (readline) {
try flags.append(b.allocator, "-DLUA_USE_READLINE");
}

if (optimize == .Debug) {
try flags.append(b.allocator, "-DLUA_USE_APICHECK");
try flags.append(b.allocator, "-fno-sanitize=undefined");
}

const lib_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
lib_module.addIncludePath(src_root);
lib_module.addCSourceFiles(.{
.root = src_root,
.files = lib_srcs.items,
.flags = flags.items,
.language = .c,
});

const lib = b.addLibrary(.{
.name = "lua",
.linkage = .static,
.root_module = lib_module,
});

const lua_exe = addExe(b, "lua", "src/lua.c", flags.items, lib, target, optimize);
const luac_exe = addExe(b, "luac", "src/luac.c", flags.items, lib, target, optimize);

if (readline) {
const linenoize = b.dependency("linenoize", .{
.target = target,
.optimize = optimize,
});
lua_exe.root_module.addIncludePath(linenoize.path("include"));
lua_exe.root_module.addIncludePath(b.path("."));
lua_exe.root_module.addCSourceFile(.{ .file = b.path("readline_shim.c") });
lua_exe.root_module.linkLibrary(linenoize.artifact("linenoise"));
}

b.installArtifact(lib);
b.installArtifact(lua_exe);
b.installArtifact(luac_exe);

const include_install = b.addInstallDirectory(.{
.source_dir = src_root,
.install_dir = .{ .custom = "include" },
.install_subdir = "",
.include_extensions = &.{ "lua.h", "luaconf.h", "lualib.h", "lauxlib.h" },
});
b.getInstallStep().dependOn(&include_install.step);
}
12 changes: 12 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.{
.name = .vfox_lua,
.version = "0.16.0",
.dependencies = .{
.linenoize = .{
.url = "git+https://github.com/hazre/linenoize.git#ddfa9a28c607b77359b2b28b84495699759ae3b5",
.hash = "linenoize-0.1.1-J7HK8IT0AABVYIWXsdseNxD1rOuQmikD0tmd1BYlTnFv",
},
},
.paths = .{""},
.fingerprint = 0x6d25d9ed3c490967,
}
5 changes: 5 additions & 0 deletions hk.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ local linters = new Mapping<String, Step> {
check = "stylua --check {{files}}"
fix = "stylua {{files}}"
}
["zig-fmt"] {
glob = "*.zig"
check = "zig fmt --check {{files}}"
fix = "zig fmt {{files}}"
}
["actionlint"] {
glob = ".github/workflows/*.{yml,yaml}"
check = "actionlint {{files}}"
Expand Down
36 changes: 14 additions & 22 deletions hooks/env_keys.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,23 @@
--- @param ctx table Context provided by vfox
--- @return table Environment configuration
function PLUGIN:EnvKeys(ctx)
local sdkInfo = ctx.sdkInfo["lua"]
local version = sdkInfo.version
local installDir = sdkInfo.path
local version = require("version")

-- Extract major.minor version for Lua paths
local shortVersion = string.match(version, "^(%d+%.%d+)")
local sdkInfo = ctx.sdkInfo["lua"]
local installDir = sdkInfo.path:gsub("\\", "/")
local v = version.parse(sdkInfo.version)
local shortVersion = v and (v.major .. "." .. v.minor)

local envs = {
{
key = "PATH",
value = installDir .. "/bin",
},
{ key = "PATH", value = installDir .. "/bin" },
{ key = "PATH", value = installDir .. "/luarocks" },
{ key = "PATH", value = installDir .. "/luarocks/bin" },
}

-- Add LuaRocks bin to PATH if it exists
local luarocksBin = installDir .. "/luarocks/bin"
local f = io.open(luarocksBin, "r")
if f ~= nil then
f:close()
table.insert(envs, {
key = "PATH",
value = luarocksBin,
})
end

-- Set LUA_INIT for package paths (similar to asdf-lua)
if shortVersion then
local soext = (RUNTIME.osType == "windows") and "dll" or "so"

local packagePath = string.format(
"package.path = package.path .. ';%s/share/lua/%s/?.lua;%s/share/lua/%s/?/init.lua;%s/luarocks/share/lua/%s/?.lua;%s/luarocks/share/lua/%s/?/init.lua'",
installDir,
Expand All @@ -41,11 +31,13 @@ function PLUGIN:EnvKeys(ctx)
shortVersion
)
local packageCpath = string.format(
"package.cpath = package.cpath .. ';%s/lib/lua/%s/?.so;%s/luarocks/lib/lua/%s/?.so'",
"package.cpath = package.cpath .. ';%s/lib/lua/%s/?.%s;%s/luarocks/lib/lua/%s/?.%s'",
installDir,
shortVersion,
soext,
installDir,
shortVersion
shortVersion,
soext
)

table.insert(envs, {
Expand Down
Loading