mirror of
https://github.com/neovim/neovim.git
synced 2026-06-15 16:23:48 +00:00
build: add wasm/emscripten build support
- Add is_wasm detection for wasm32-emscripten target - Add -Demscripten-sysroot option and libc path setup - Default cross_compiling=true for wasm - Use lua5.1 instead of luajit for wasm (luajit unsupported) - Add emscripten include paths to all dependencies - Build nvim as static library for wasm target - Add emcc link step with Asyncify and SUPPORT_LONGJMP - Filter wasm_stubs.c from non-wasm builds Known limitation: setjmp/longjmp not yet fully functional due to Zig compiling dependencies outside emcc pipeline. build: fix wasm emscripten output path tracking
This commit is contained in:
113
build.zig
113
build.zig
@@ -39,6 +39,12 @@ pub fn lazyArtifact(d: *std.Build.Dependency, name: []const u8) ?*std.Build.Step
|
||||
return found;
|
||||
}
|
||||
|
||||
fn addEmscriptenIncludes(emscripten_include: ?std.Build.LazyPath, artifact: *std.Build.Step.Compile) void {
|
||||
if (emscripten_include) |inc| {
|
||||
artifact.root_module.addSystemIncludePath(inc);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
@@ -50,10 +56,30 @@ pub fn build(b: *std.Build) !void {
|
||||
const is_linux = (os_tag == .linux);
|
||||
const is_darwin = os_tag.isDarwin();
|
||||
const modern_unix = is_darwin or os_tag.isBSD() or is_linux;
|
||||
const is_wasm = t.cpu.arch == .wasm32;
|
||||
|
||||
const cross_compiling = b.option(bool, "cross", "cross compile") orelse is_wasm;
|
||||
const emscripten_sysroot = b.option([]const u8, "emscripten-sysroot", "path to emscripten sysroot");
|
||||
const emscripten_include = if (emscripten_sysroot) |s|
|
||||
std.Build.LazyPath{ .cwd_relative = b.pathJoin(&.{ s, "include" }) }
|
||||
else
|
||||
null;
|
||||
|
||||
const emscripten_libc_path: ?std.Build.LazyPath = if (is_wasm) blk: {
|
||||
if (emscripten_sysroot) |s| {
|
||||
const libc_txt = b.fmt(
|
||||
"include_dir={s}/include\nsys_include_dir={s}/include\ncrt_dir={s}/lib/wasm32-emscripten\nmsvc_lib_dir=\nkernel32_lib_dir=\ngcc_dir=\n",
|
||||
.{ s, s, s },
|
||||
);
|
||||
const wf = b.addWriteFiles();
|
||||
break :blk wf.add("emscripten-libc.txt", libc_txt);
|
||||
}
|
||||
break :blk null;
|
||||
} else null;
|
||||
|
||||
const cross_compiling = b.option(bool, "cross", "cross compile") orelse false;
|
||||
// TODO(bfredl): option to set nlua0 target explicitly when cross compiling?
|
||||
const target_host = if (cross_compiling) b.graph.host else target;
|
||||
|
||||
// without cross_compiling we like to reuse libluv etc at the same optimize level
|
||||
const optimize_host = if (cross_compiling) .ReleaseSafe else optimize;
|
||||
|
||||
@@ -62,7 +88,7 @@ pub fn build(b: *std.Build) !void {
|
||||
// puc lua 5.1 is not ReleaseSafe "safe"
|
||||
const optimize_lua = if (optimize == .Debug or optimize == .ReleaseSafe) .ReleaseSmall else optimize;
|
||||
|
||||
const use_luajit = b.option(bool, "luajit", "use luajit") orelse true;
|
||||
const use_luajit = b.option(bool, "luajit", "use luajit") orelse !is_wasm;
|
||||
const lualib_name = if (use_luajit) "luajit" else "lua5.1";
|
||||
const host_use_luajit = if (cross_compiling) false else use_luajit;
|
||||
const E = enum { luajit, lua51 };
|
||||
@@ -98,6 +124,8 @@ pub fn build(b: *std.Build) !void {
|
||||
// this is currently not necessary, as ziglua currently doesn't use lazy dependencies
|
||||
// to circumvent ziglua.artifact() failing in a bad way.
|
||||
lua = lazyArtifact(ziglua, "lua") orelse return;
|
||||
addEmscriptenIncludes(emscripten_include, lua.?);
|
||||
|
||||
if (cross_compiling) {
|
||||
_ = lazyArtifact(ziglua_host, "lua") orelse return;
|
||||
}
|
||||
@@ -108,8 +136,14 @@ pub fn build(b: *std.Build) !void {
|
||||
const optimize_uv = if (optimize == .Debug and target.result.os.tag == .windows) .ReleaseSafe else optimize;
|
||||
if (b.lazyDependency("libuv", .{ .target = target, .optimize = optimize_uv })) |dep| {
|
||||
libuv = dep.artifact("uv");
|
||||
|
||||
if (emscripten_libc_path) |lp| libuv.?.setLibCFile(lp);
|
||||
addEmscriptenIncludes(emscripten_include, libuv.?);
|
||||
|
||||
libluv = try build_lua.build_libluv(b, target, optimize, lua, libuv.?, use_luajit);
|
||||
|
||||
if (libluv) |luv| addEmscriptenIncludes(emscripten_include, luv);
|
||||
|
||||
libluv_host = if (cross_compiling) libluv_host: {
|
||||
const libuv_dep_host = b.lazyDependency("libuv", .{
|
||||
.target = target_host,
|
||||
@@ -139,6 +173,7 @@ pub fn build(b: *std.Build) !void {
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const unibilium = if (use_unibilium and !sys_opts.unibilium) b.lazyDependency("unibilium", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
@@ -151,6 +186,15 @@ pub fn build(b: *std.Build) !void {
|
||||
.optimize = optimize_ts,
|
||||
});
|
||||
|
||||
if (utf8proc) |dep|
|
||||
addEmscriptenIncludes(emscripten_include, dep.artifact("utf8proc"));
|
||||
|
||||
if (unibilium) |dep|
|
||||
addEmscriptenIncludes(emscripten_include, dep.artifact("unibilium"));
|
||||
|
||||
if (treesitter) |dep|
|
||||
addEmscriptenIncludes(emscripten_include, dep.artifact("tree-sitter"));
|
||||
|
||||
const nlua0 = try build_lua.build_nlua0(
|
||||
b,
|
||||
io,
|
||||
@@ -442,8 +486,16 @@ pub fn build(b: *std.Build) !void {
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
});
|
||||
const nvim_exe = b.addExecutable(.{ .name = "nvim", .root_module = nvim_mod });
|
||||
|
||||
const nvim_exe = if (is_wasm) b.addLibrary(.{
|
||||
.linkage = .static,
|
||||
.name = "nvim",
|
||||
.root_module = nvim_mod,
|
||||
}) else b.addExecutable(.{ .name = "nvim", .root_module = nvim_mod });
|
||||
nvim_exe.rdynamic = true; // -E
|
||||
if (emscripten_libc_path) |lp| nvim_exe.setLibCFile(lp);
|
||||
if (is_wasm) nvim_exe.entry = .disabled;
|
||||
if (is_wasm) nvim_exe.linker_allow_shlib_undefined = true;
|
||||
|
||||
if (sys_opts.lua) {
|
||||
nvim_exe.root_module.linkSystemLibrary(lualib_name, .{});
|
||||
@@ -496,11 +548,15 @@ pub fn build(b: *std.Build) !void {
|
||||
}
|
||||
}
|
||||
|
||||
const src_paths = try b.allocator.alloc([]u8, nvim_sources.items.len + unit_test_sources.items.len);
|
||||
for (nvim_sources.items, 0..) |s, i| {
|
||||
src_paths[i] = b.fmt("src/nvim/{s}", .{s.name});
|
||||
var filtered_sources = try std.ArrayList([]u8).initCapacity(b.allocator, nvim_sources.items.len);
|
||||
for (nvim_sources.items) |s| {
|
||||
// wasm_stubs.c defines libuv symbols & excludes it when real libuv is linked
|
||||
if (!is_wasm and std.mem.eql(u8, s.name, "wasm_stubs.c")) continue;
|
||||
try filtered_sources.append(b.allocator, b.fmt("src/nvim/{s}", .{s.name}));
|
||||
}
|
||||
@memcpy(src_paths[nvim_sources.items.len..], unit_test_sources.items);
|
||||
const src_paths = try b.allocator.alloc([]u8, filtered_sources.items.len + unit_test_sources.items.len);
|
||||
@memcpy(src_paths[0..filtered_sources.items.len], filtered_sources.items);
|
||||
@memcpy(src_paths[filtered_sources.items.len..], unit_test_sources.items);
|
||||
|
||||
const flags = [_][]const u8{
|
||||
"-std=gnu99",
|
||||
@@ -532,15 +588,46 @@ pub fn build(b: *std.Build) !void {
|
||||
}
|
||||
|
||||
const nvim_exe_step = b.step("nvim_bin", "only the binary (not a fully working install!)");
|
||||
const nvim_exe_install = b.addInstallArtifact(nvim_exe, .{});
|
||||
const nvim_exe_install = b.addInstallArtifact(nvim_exe, .{ .dest_dir = if (is_wasm) .{ .override = .{ .custom = "wasm" } } else .default });
|
||||
|
||||
nvim_exe_step.dependOn(&nvim_exe_install.step);
|
||||
if (is_wasm) {
|
||||
const s = emscripten_sysroot orelse @panic("-Demscripten-sysroot required for wasm target");
|
||||
const emcc = b.addSystemCommand(&.{"emcc"});
|
||||
emcc.step.dependOn(&nvim_exe.step);
|
||||
|
||||
emcc.addArg("-Wl,--whole-archive");
|
||||
emcc.addFileArg(nvim_exe.getEmittedBin());
|
||||
if (lua) |l| emcc.addFileArg(l.getEmittedBin());
|
||||
if (libuv) |uv| emcc.addFileArg(uv.getEmittedBin());
|
||||
if (libluv) |luv| emcc.addFileArg(luv.getEmittedBin());
|
||||
if (utf8proc) |dep| emcc.addFileArg(dep.artifact("utf8proc").getEmittedBin());
|
||||
if (unibilium) |dep| emcc.addFileArg(dep.artifact("unibilium").getEmittedBin());
|
||||
if (treesitter) |dep| emcc.addFileArg(dep.artifact("tree-sitter").getEmittedBin());
|
||||
emcc.addArg("-Wl,--no-whole-archive");
|
||||
|
||||
emcc.addArgs(&.{
|
||||
b.fmt("--sysroot={s}", .{s}),
|
||||
"-sALLOW_MEMORY_GROWTH=1",
|
||||
"-sEXPORT_ALL=1",
|
||||
"-sINITIAL_MEMORY=67108864",
|
||||
"-sFORCE_FILESYSTEM=1",
|
||||
"-sERROR_ON_UNDEFINED_SYMBOLS=0",
|
||||
"--no-entry",
|
||||
"-Wl,--export-all",
|
||||
"-Wl,--no-gc-sections",
|
||||
});
|
||||
emcc.addArg("-o");
|
||||
const nvim_js = emcc.addOutputFileArg("nvim.js");
|
||||
nvim_exe_step.dependOn(&b.addInstallFileWithDir(nvim_js, .bin, "nvim.js").step);
|
||||
nvim_exe_step.dependOn(&b.addInstallFileWithDir(nvim_js.dirname().path(b, "nvim.wasm"), .bin, "nvim.wasm").step);
|
||||
} else {
|
||||
nvim_exe_step.dependOn(&nvim_exe_install.step);
|
||||
}
|
||||
|
||||
const gen_runtime = try runtime.nvim_gen_runtime(b, nlua0, funcs_data);
|
||||
|
||||
const test_deps = b.step("test_deps", "test prerequisites");
|
||||
test_deps.dependOn(&nvim_exe_install.step);
|
||||
// running tests doesn't require copying the static runtime, only the generated stuff
|
||||
if (!is_wasm) test_deps.dependOn(&nvim_exe_install.step); // running tests doesn't require copying the static runtime, only the generated stuff
|
||||
const test_runtime_install = b.addInstallDirectory(.{
|
||||
.source_dir = gen_runtime.getDirectory(),
|
||||
.install_dir = .prefix,
|
||||
@@ -551,7 +638,7 @@ pub fn build(b: *std.Build) !void {
|
||||
const nvim_dev = b.step("nvim_dev", "build the editor for development");
|
||||
b.default_step = nvim_dev;
|
||||
|
||||
nvim_dev.dependOn(&nvim_exe_install.step);
|
||||
if (!is_wasm) nvim_dev.dependOn(&nvim_exe_install.step);
|
||||
nvim_dev.dependOn(&test_runtime_install.step);
|
||||
|
||||
// run from dev environment
|
||||
@@ -568,7 +655,7 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
// installation
|
||||
const install = b.getInstallStep();
|
||||
install.dependOn(&nvim_exe_install.step);
|
||||
if (!is_wasm) install.dependOn(&nvim_exe_install.step);
|
||||
b.installDirectory(.{
|
||||
.source_dir = b.path("runtime/"),
|
||||
.install_dir = .prefix,
|
||||
|
||||
Reference in New Issue
Block a user