Gamified input-output tables (no GUI).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

532 lines
17 KiB

const std = @import("std");
//fn thisDir(comptime suffix: []const u8) []const u8 {
// @setEvalBranchQuota(10000);
// return comptime blk: {
// const root_dir = std.fs.path.dirname(@src().file) orelse ".";
// break :blk root_dir ++ "/" ++ suffix;
// };
//}
pub fn module(b: *std.build.Builder) *std.build.Module {
return b.createModule(.{
.source_file = .{ .path = (comptime thisDir()) ++ "/src/main.zig" },
});
}
pub const luajit_src_path = thisDir() ++ "/third_party/luajit/src/";
const ljlib_c: []const []const u8 = &.{
"lib_base.c", "lib_math.c", "lib_bit.c", "lib_string.c",
"lib_table.c", "lib_io.c", "lib_os.c", "lib_package.c",
"lib_debug.c", "lib_jit.c", "lib_ffi.c", "lib_buffer.c",
};
// NOTE: as opposed to the Makefile, does not include ljlib_c
const ljcore_c: []const []const u8 = &.{
"lj_assert.c", "lj_gc.c", "lj_err.c", "lj_char.c",
"lj_bc.c", "lj_obj.c", "lj_buf.c", "lj_str.c",
"lj_tab.c", "lj_func.c", "lj_udata.c", "lj_meta.c",
"lj_debug.c", "lj_prng.c", "lj_state.c", "lj_dispatch.c",
"lj_vmevent.c", "lj_vmmath.c", "lj_strscan.c", "lj_strfmt.c",
"lj_strfmt_num.c", "lj_serialize.c", "lj_api.c", "lj_profile.c",
"lj_lex.c", "lj_parse.c", "lj_bcread.c", "lj_bcwrite.c",
"lj_load.c", "lj_ir.c", "lj_opt_mem.c", "lj_opt_fold.c",
"lj_opt_narrow.c", "lj_opt_dce.c", "lj_opt_loop.c", "lj_opt_split.c",
"lj_opt_sink.c", "lj_mcode.c", "lj_snap.c", "lj_record.c",
"lj_crecord.c", "lj_ffrecord.c", "lj_asm.c", "lj_trace.c",
"lj_gdbjit.c", "lj_ctype.c", "lj_cdata.c", "lj_cconv.c",
"lj_ccall.c", "lj_ccallback.c", "lj_carith.c", "lj_clib.c",
"lj_cparse.c", "lj_lib.c", "lj_alloc.c", "lib_aux.c",
"lib_init.c",
};
fn thisDir() []const u8 {
return std.fs.path.dirname(@src().file) orelse ".";
}
pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{
.name = "zig-luajit",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = mode,
});
addLuajit(lib) catch unreachable;
lib.addIncludePath(luajit_src_path);
lib.install();
const main_tests = b.addTest("src/main.zig");
main_tests.setTarget(target);
main_tests.setBuildMode(mode);
addLuajit(main_tests) catch unreachable;
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
buildExample(b, target, mode);
}
pub fn buildExample(
b: *std.build.Builder,
target: std.zig.CrossTarget,
mode: std.builtin.Mode,
) void {
const exe = b.addExecutable(.{
.name = "my-lua-proj",
.root_source_file = .{ .path = "example/main.zig" },
.target = target,
.optimize = mode,
});
// earlier in your build script; you would have
// const addLuajit = @import("path/to/this/build.zig").addLuajit;
addLuajit(exe) catch unreachable;
// run step
const run_exe = exe.run();
run_exe.cwd = "./example/";
const run_step = b.step("run-example", "Run luajit example");
run_step.dependOn(&run_exe.step);
// add some tests!
}
/// returns an owned slice
fn generateLjArchOutput(
zig_exe: []const u8,
allocator: std.mem.Allocator,
) ![]u8 {
var zig_cmd = std.ArrayList([]const u8).init(allocator);
defer zig_cmd.deinit();
try zig_cmd.append(zig_exe);
try zig_cmd.append("cc");
try zig_cmd.append("-E");
try zig_cmd.append(luajit_src_path ++ "lj_arch.h");
try zig_cmd.append("-dM");
var child_proc = std.ChildProcess.init(zig_cmd.items, allocator);
child_proc.stdin_behavior = .Close;
child_proc.stdout_behavior = .Pipe;
child_proc.stderr_behavior = .Inherit;
child_proc.spawn() catch |err| {
std.log.err(
"Error spawning {s}: {s}\n",
.{ zig_cmd.items[0], @errorName(err) },
);
return err;
};
return try child_proc.stdout.?.reader().readAllAlloc(
allocator,
2 * 1024 * 1024,
);
}
fn createBuildvmExeStep(
builder: *std.build.Builder,
host_cflags: []const []const u8,
) !*std.build.LibExeObjStep {
const buildvm_c: []const []const u8 = &.{
luajit_src_path ++ "/host/buildvm.c",
luajit_src_path ++ "/host/buildvm_asm.c",
luajit_src_path ++ "/host/buildvm_peobj.c",
luajit_src_path ++ "/host/buildvm_lib.c",
luajit_src_path ++ "/host/buildvm_fold.c",
};
var buildvm_exe = builder.addExecutable(.{
.name = "buildvm",
.target = std.zig.CrossTarget.fromTarget(builder.host.target),
.optimize = .ReleaseSmall,
});
buildvm_exe.addIncludePath(.{ .path = luajit_src_path });
buildvm_exe.addCSourceFiles(buildvm_c, host_cflags);
buildvm_exe.linkSystemLibrary("m");
buildvm_exe.linkLibC();
return buildvm_exe;
}
fn createBuildvmGenStep(
builder: *std.build.Builder,
buildvm_exe: *std.build.LibExeObjStep,
target_os_tag: std.Target.Os.Tag,
) !*std.build.Step {
var buildvm_gen_step = try builder.allocator.create(std.build.Step);
buildvm_gen_step.* = std.build.Step.init(.{
.id = .custom,
.name = "buildvm",
.owner = builder,
});
// those targets all build the same
const simple_targets: []const []const u8 = &.{
"bcdef", "ffdef", "libdef", "recdef",
};
inline for (simple_targets) |t| {
const target = "lj_" ++ t ++ ".h";
const buildvm_gen_simple = builder.addRunArtifact(buildvm_exe);
buildvm_gen_simple.cwd = luajit_src_path;
buildvm_gen_simple.addArgs(&.{
"-m", t, "-o", target,
});
buildvm_gen_simple.addArgs(ljlib_c);
buildvm_gen_step.dependOn(&buildvm_gen_simple.step);
}
// remains 3 target that require custom handling:
// * folddef
// * vmdef
// * a platform dependent one
{ // folddef
var buildvm_gen_folddef = builder.addRunArtifact(buildvm_exe);
buildvm_gen_folddef.cwd = luajit_src_path;
buildvm_gen_folddef.addArgs(&.{
"-m", "folddef", "-o", "lj_folddef.h", "lj_opt_fold.c",
});
buildvm_gen_step.dependOn(&buildvm_gen_folddef.step);
}
{ // vmdef
var buildvm_gen_vmdef = builder.addRunArtifact(buildvm_exe);
buildvm_gen_vmdef.cwd = luajit_src_path;
buildvm_gen_vmdef.addArgs(&.{
"-m", "vmdef", "-o", "jit/vmdef.lua",
});
buildvm_gen_vmdef.addArgs(ljlib_c);
buildvm_gen_step.dependOn(&buildvm_gen_vmdef.step);
}
{ // platform dependent
const mode = blk: {
switch (target_os_tag) {
.macos, .ios => break :blk "machasm",
.windows => break :blk "peobj",
else => break :blk "elfasm",
}
};
const target = blk: {
switch (target_os_tag) {
.windows => break :blk "lj_vm.o",
else => break :blk "lj_vm.S",
}
};
var buildvm_gen_ljvm = builder.addRunArtifact(buildvm_exe);
buildvm_gen_ljvm.cwd = luajit_src_path;
buildvm_gen_ljvm.addArgs(&.{
"-m", mode, "-o", target,
});
buildvm_gen_step.dependOn(&buildvm_gen_ljvm.step);
}
return buildvm_gen_step;
}
pub fn addLuajit(allocator: std.mem.Allocator, exe: *std.build.LibExeObjStep) !void {
const target_os_tag = exe.target.os_tag orelse
exe.step.owner.host.target.os.tag;
const target_arch = exe.target.cpu_arch orelse
exe.step.owner.host.target.cpu.arch;
var stdout = try generateLjArchOutput(
exe.step.owner.zig_exe,
allocator,
);
var minilua_flags = std.ArrayList([]const u8).init(allocator);
defer minilua_flags.deinit();
// disable UB-san as minilua relies way too much on them
// NOTE: we could replace minilua with other interpreter
try minilua_flags.append("-fno-sanitize=undefined");
// TARGET_ARCH from target cpu arch
// NOTE: TARGET_ARCH ends up containing many things but we can discriminate
// them in three categories:
// - target cpu arch
// - target os related (though there is only things for PS3 here, that we
// do not support)
// - features related to the target platform
switch (target_arch) {
.aarch64_be => {
try minilua_flags.append("-D__AARCH64EB__=1");
},
.powerpc => {
try minilua_flags.append("-DLJ_ARCH_ENDIAN=LUAJIT_BE");
},
.powerpcle => {
try minilua_flags.append("-DLJ_ARCH_ENDIAN=LUAJIT_LE");
},
.mipsel, .mips64el => {
try minilua_flags.append("-D__MIPSEL__=1");
},
else => {},
}
// TARGET_LJARCH
const luajit_arch_name = blk: {
switch (target_arch) {
.arm, .mips, .mips64, .mipsel, .mips64el => {
break :blk @tagName(target_arch);
},
// other arch have different names for luajit
.aarch64, .aarch64_be => {
break :blk "arm64";
},
.x86 => {
break :blk "x86";
},
.x86_64 => {
break :blk "x64";
},
.powerpc, .powerpcle => {
break :blk "ppc";
},
// every other arch are not supported
else => {
return error.UnsupportedCpuArchitecture;
},
}
};
var minilua_luajit_target = try std.fmt.allocPrint(
allocator,
"-DLUAJIT_TARGET=LUAJIT_ARCH_{s}",
.{luajit_arch_name},
);
try minilua_flags.append(minilua_luajit_target);
// CCOPT_$arch
switch (target_arch) {
.x86 => {
try minilua_flags.append("-march=i686");
try minilua_flags.append("-msse");
try minilua_flags.append("-msse2");
try minilua_flags.append("-mfpmath=sse");
},
// No specific flags for other platforms by default
else => {},
}
var dasm_aflags = std.ArrayList([]const u8).init(allocator);
defer dasm_aflags.deinit();
if (std.mem.indexOf(u8, stdout, "LJ_LE 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("ENDIAN_LE");
} else {
try dasm_aflags.append("-D");
try dasm_aflags.append("ENDIAN_BE");
}
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_BITS 64")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("P64");
}
if (std.mem.indexOf(u8, stdout, "LJ_HASJIT 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("JIT");
}
if (std.mem.indexOf(u8, stdout, "LJ_HASFFI 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("FFI");
}
if (std.mem.indexOf(u8, stdout, "LJ_DUALNUM 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("DUALNUM");
}
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_HASFPU 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("FPU");
try minilua_flags.append("-DLJ_ARCH_HASFPU=1");
} else {
try minilua_flags.append("-DLJ_ARCH_HASFPU=0");
}
if (std.mem.indexOf(u8, stdout, "LJ_ABI_SOFTFP 1")) |_| {
try minilua_flags.append("-DLJ_ABI_SOFTFP=1");
} else {
try dasm_aflags.append("-D");
try dasm_aflags.append("HFABI");
try minilua_flags.append("-DLJ_ABI_SOFTFP=0");
}
if (std.mem.indexOf(u8, stdout, "LJ_NO_UNWIND 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("NO_UNWIND");
try minilua_flags.append("-DLUAJIT_NO_UNWIND");
}
const version_define = "LJ_ARCH_VERSION ";
const version = blk: {
var vs: []const u8 = "";
if (std.mem.indexOf(u8, stdout, version_define)) |i| {
const version_start = i + version_define.len;
if (std.mem.indexOfPosLinear(
u8,
stdout,
version_start,
"\n",
)) |version_end| {
vs = stdout[version_start..version_end];
}
}
break :blk try std.fmt.allocPrint(allocator, "VER={s}", .{vs});
};
try dasm_aflags.append("-D");
try dasm_aflags.append(version);
if (target_os_tag == .windows) {
try dasm_aflags.append("-D");
try dasm_aflags.append("WIN");
}
var dasm_arch = luajit_arch_name;
if (target_arch == .aarch64) {
// need to rename aarch64 to arm64 to match the vm_<arch>.dasc filename
dasm_arch = "arm64";
}
if (target_arch == .x86_64) {
if (std.mem.indexOf(u8, stdout, "LJ_FR2 1")) |_| {} else {
dasm_arch = "x86";
}
} else if (target_arch == .arm) {
if (target_os_tag == .ios) {
try dasm_aflags.append("-D");
try dasm_aflags.append("IOS");
}
} else {
if (std.mem.indexOf(u8, stdout, "LJ_TARGET_MIPSR6 ")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("MIPSR6");
}
if (target_arch == .powerpc) {
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_SQRT 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("SQRT");
}
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_ROUND 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("ROUND");
}
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_PPC32ON64 1")) |_| {
try dasm_aflags.append("-D");
try dasm_aflags.append("GPR64");
}
// .ps3 not available (has .ps4 though)
//if (target_os_tag == .ps3) {
// try dasm_aflags.append("-D");
// try dasm_aflags.append("PPE");
// try dasm_aflags.append("-D");
// try dasm_aflags.append("TOC");
//}
}
}
try dasm_aflags.append("-o");
try dasm_aflags.append("host/buildvm_arch.h");
var dasm_dasc = try std.fmt.allocPrint(
allocator,
"vm_{s}.dasc",
.{dasm_arch},
);
try dasm_aflags.append(dasm_dasc);
// compile minilua for the host machine, will be needed to generate various
// files before the compilation of luajit
var minilua_exe = exe.step.owner.addExecutable(.{
.name = "minilua",
.target = std.zig.CrossTarget.fromTarget(exe.step.owner.host.target),
.optimize = .ReleaseSmall,
});
minilua_exe.addCSourceFile(.{
.file = .{ .path = luajit_src_path ++ "/host/minilua.c" },
.flags = minilua_flags.items,
});
minilua_exe.linkSystemLibrary("m");
minilua_exe.linkLibC();
const minilua_run = exe.step.owner.addRunArtifact(minilua_exe);
minilua_run.cwd = luajit_src_path;
minilua_run.addArg("../dynasm/dynasm.lua");
minilua_run.addArgs(dasm_aflags.items);
var buildvm_exe = try createBuildvmExeStep(
exe.step.owner,
minilua_flags.items,
);
buildvm_exe.step.dependOn(&minilua_run.step);
var buildvm_gen_step = try createBuildvmGenStep(
exe.step.owner,
buildvm_exe,
target_os_tag,
);
var luajit_sys_libs =
try std.ArrayList([]const u8).initCapacity(allocator, 4);
defer luajit_sys_libs.deinit();
var luajit_flags =
try std.ArrayList([]const u8).initCapacity(allocator, 4);
defer luajit_flags.deinit();
try luajit_flags.append("-fno-sanitize=undefined");
try luajit_flags.append("-O2");
try luajit_flags.append("-fomit-frame-pointer");
try luajit_sys_libs.append("m");
switch (target_os_tag) {
.freebsd => try luajit_sys_libs.append("dl"),
.linux => {
try luajit_sys_libs.append("dl");
try luajit_sys_libs.append("unwind");
try luajit_flags.append("-funwind-tables");
try luajit_flags.append("-DLUAJIT_UNWIND_EXTERNAL");
},
// NOTE: PS3 would require pthread but it's not supported by zig
else => {},
}
var luajit_src = try std.ArrayList([]const u8).initCapacity(
allocator,
ljlib_c.len + ljcore_c.len,
);
inline for (ljlib_c) |c| {
try luajit_src.append(luajit_src_path ++ c);
}
inline for (ljcore_c) |c| {
try luajit_src.append(luajit_src_path ++ c);
}
var luajit_lib = exe.step.owner.addStaticLibrary(.{
.name = "luajit",
.target = exe.target,
.optimize = exe.optimize,
});
luajit_lib.step.dependOn(buildvm_gen_step);
luajit_lib.linkLibC();
luajit_lib.addCSourceFiles(luajit_src.items, luajit_flags.items);
if (target_os_tag == .windows) {
luajit_lib.addObjectFile(.{ .path = luajit_src_path ++ "/lj_vm.o" });
} else {
luajit_lib.addAssemblyFile(.{ .path = luajit_src_path ++ "/lj_vm.S" });
}
exe.linkLibrary(luajit_lib);
for (luajit_sys_libs.items) |l| {
exe.linkSystemLibrary(l);
}
exe.addIncludePath(.{ .path = luajit_src_path });
exe.addModule("luajit", module(exe.step.owner));
}
pub fn includeLuajit(exe: *std.build.LibExeObjStep) void {
exe.addIncludePath(luajit_src_path);
}