commit 6082b7f850b592a9d2e3a55b00b22dc862ad1858
parent 405c6c9bb03fbb270284937f95fb9cf14f4c40dd
Author: bfredl <bjorn.linse@gmail.com>
Date: Thu, 15 Jan 2026 11:07:40 +0100
Merge pull request #37040 from p00f/push-nwlkmnvmmlrt
feat(build.zig): add option to use system dependencies
Diffstat:
9 files changed, 499 insertions(+), 132 deletions(-)
diff --git a/.editorconfig b/.editorconfig
@@ -7,6 +7,9 @@ tab_width = 8
end_of_line = lf
insert_final_newline = true
+[*.zig]
+indent_size = 4
+
[*.{c,h,in,lua}]
max_line_length = 100
diff --git a/BUILD.md b/BUILD.md
@@ -542,3 +542,19 @@ make CMAKE_BUILD_TYPE=Release MACOSX_DEPLOYMENT_TARGET=10.13 DEPS_CMAKE_FLAGS="-
Note that the C++ compiler is explicitly set so that it can be found when the deployment target is set.
+## Building with zig
+### Prerequisites
+ - zig 0.15.2
+### Instructions
+ - Build the editor: `zig build`, run it with `./zig-out/bin/nvim`
+ - Complete installation with runtime: `zig build install --prefix ~/.local`
+ - Tests:
+ + `zig build functionaltest` to run all functionaltests
+ + `zig build functionaltest -- test/functional/autocmd/bufenter_spec.lua` to run the tests in one file
+ + `zig build unittest` to run all unittests
+#### Using system dependencies
+ See "Available System Integrations" in `zig build -h` to see available system integrations. Enabling an integration, e.g. `zig build -fsys=utf8proc` will use the system's installation of utf8proc.
+
+`zig build --system deps_dir` will enable all integrations and turn off dependency fetching. This requires you to pre-download the dependencies which don't have a system integration into `deps_dir` (at the time of writing these are ziglua, [`lua_dev_deps`](https://github.com/neovim/deps/blob/master/opt/lua-dev-deps.tar.gz), and the built-in tree-sitter parsers). You have to create subdirectories whose names are the respective package's hash under `deps_dir` and unpack the dependencies inside that directory - ziglua should go under `deps_dir/zlua-0.1.0-hGRpC1dCBQDf-IqqUifYvyr8B9-4FlYXqY8cl7HIetrC` and so on. Hashes should be taken from `build.zig.zon`.
+
+See the `prepare` function of [this `PKGBUILD`](https://git.sr.ht/~chinmay/nvim_build/tree/26364a4cf9b4819f52a3e785fa5a43285fb9cea2/item/PKGBUILD#L90) for an example.
diff --git a/build.zig b/build.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const LazyPath = std.Build.LazyPath;
+const Compile = std.Build.Step.Compile;
const build_lua = @import("src/build_lua.zig");
const gen = @import("src/gen/gen_steps.zig");
const runtime = @import("runtime/gen_runtime.zig");
@@ -16,6 +17,15 @@ const version = struct {
const api_prerelease = true;
};
+pub const SystemIntegrationOptions = packed struct {
+ lpeg: bool,
+ lua: bool,
+ tree_sitter: bool,
+ unibilium: bool,
+ utf8proc: bool,
+ uv: bool,
+};
+
// TODO(bfredl): this is for an upstream issue
pub fn lazyArtifact(d: *std.Build.Dependency, name: []const u8) ?*std.Build.Step.Compile {
var found: ?*std.Build.Step.Compile = null;
@@ -54,51 +64,99 @@ pub fn build(b: *std.Build) !void {
const arch = t.cpu.arch;
const default_luajit = (is_linux and arch == .x86_64) or (is_darwin and arch == .aarch64);
const use_luajit = b.option(bool, "luajit", "use luajit") orelse default_luajit;
+ 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 };
+ const system_integration_options = SystemIntegrationOptions{
+ .lpeg = b.systemIntegrationOption("lpeg", .{}),
+ .lua = b.systemIntegrationOption("lua", .{}),
+ .tree_sitter = b.systemIntegrationOption("tree-sitter", .{}),
+ .unibilium = b.systemIntegrationOption("unibilium", .{}),
+ .utf8proc = b.systemIntegrationOption("utf8proc", .{}),
+ .uv = b.systemIntegrationOption("uv", .{}),
+ };
+
const ziglua = b.dependency("zlua", .{
.target = target,
.optimize = optimize_lua,
.lang = if (use_luajit) E.luajit else E.lua51,
.shared = false,
+ .system_lua = system_integration_options.lua,
});
-
const ziglua_host = if (cross_compiling) b.dependency("zlua", .{
.target = target_host,
.optimize = .ReleaseSmall,
.lang = if (host_use_luajit) E.luajit else E.lua51,
+ .system_lua = system_integration_options.lua,
.shared = false,
}) else ziglua;
-
- const lpeg = b.dependency("lpeg", .{});
-
- const iconv = if (is_windows or is_darwin) b.lazyDependency("libiconv", .{ .target = target, .optimize = optimize }) else null;
-
- // this is currently not necessary, as ziglua currently doesn't use lazy dependencies
- // to circumvent ziglua.artifact() failing in a bad way.
- const lua = lazyArtifact(ziglua, "lua") orelse return;
- if (cross_compiling) {
- _ = lazyArtifact(ziglua_host, "lua") orelse return;
+ var lua: ?*Compile = null;
+ var libuv: ?*Compile = null;
+ var libluv: ?*Compile = null;
+ var libluv_host: ?*Compile = null;
+ if (!system_integration_options.lua) {
+ // 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;
+ if (cross_compiling) {
+ _ = lazyArtifact(ziglua_host, "lua") orelse return;
+ }
+ }
+ if (!system_integration_options.uv) {
+ if (b.lazyDependency("libuv", .{ .target = target, .optimize = optimize })) |dep| {
+ libuv = dep.artifact("uv");
+ libluv = try build_lua.build_libluv(b, target, optimize, lua, libuv.?, use_luajit);
+
+ libluv_host = if (cross_compiling) libluv_host: {
+ const libuv_dep_host = b.lazyDependency("libuv", .{
+ .target = target_host,
+ .optimize = optimize_host,
+ });
+ const libuv_host = libuv_dep_host.?.artifact("uv");
+ break :libluv_host try build_lua.build_libluv(
+ b,
+ target_host,
+ optimize_host,
+ ziglua_host.artifact("lua"),
+ libuv_host,
+ host_use_luajit,
+ );
+ } else libluv;
+ }
}
- // const lua = ziglua.artifact("lua");
- const libuv_dep = b.dependency("libuv", .{ .target = target, .optimize = optimize });
- const libuv = libuv_dep.artifact("uv");
- const libluv = try build_lua.build_libluv(b, target, optimize, lua, libuv);
+ const lpeg = if (system_integration_options.lpeg) null else b.lazyDependency("lpeg", .{});
- const libluv_host = if (cross_compiling) libluv_host: {
- const libuv_dep_host = b.dependency("libuv", .{ .target = target_host, .optimize = optimize_host });
- const libuv_host = libuv_dep_host.artifact("uv");
- break :libluv_host try build_lua.build_libluv(b, target_host, optimize_host, ziglua_host.artifact("lua"), libuv_host);
- } else libluv;
+ const iconv = if (is_windows or is_darwin) b.lazyDependency("libiconv", .{
+ .target = target,
+ .optimize = optimize,
+ }) else null;
- const utf8proc = b.dependency("utf8proc", .{ .target = target, .optimize = optimize });
- const unibilium = if (use_unibilium) b.lazyDependency("unibilium", .{ .target = target, .optimize = optimize }) else null;
+ const utf8proc = if (system_integration_options.utf8proc) null else b.lazyDependency("utf8proc", .{
+ .target = target,
+ .optimize = optimize,
+ });
+ const unibilium = if (use_unibilium and !system_integration_options.unibilium) b.lazyDependency("unibilium", .{
+ .target = target,
+ .optimize = optimize,
+ }) else null;
// TODO(bfredl): fix upstream bugs with UBSAN
- const treesitter = b.dependency("treesitter", .{ .target = target, .optimize = .ReleaseFast });
+ const treesitter = if (system_integration_options.tree_sitter) null else b.lazyDependency("treesitter", .{
+ .target = target,
+ .optimize = .ReleaseFast,
+ });
- const nlua0 = build_lua.build_nlua0(b, target_host, optimize_host, host_use_luajit, ziglua_host, lpeg, libluv_host);
+ const nlua0 = try build_lua.build_nlua0(
+ b,
+ target_host,
+ optimize_host,
+ host_use_luajit,
+ ziglua_host,
+ lpeg,
+ libluv_host,
+ system_integration_options,
+ );
// usual caveat emptor: might need to force a rebuild if the only change is
// addition of new .c files, as those are not seen by any hash
@@ -127,7 +185,14 @@ pub fn build(b: *std.Build) !void {
var api_headers = try std.ArrayList(std.Build.LazyPath).initCapacity(b.allocator, 10);
// TODO(bfredl): these should just become subdirs..
- const windows_only = [_][]const u8{ "pty_proc_win.c", "pty_proc_win.h", "pty_conpty_win.c", "pty_conpty_win.h", "os_win_console.c", "win_defs.h" };
+ const windows_only = [_][]const u8{
+ "pty_proc_win.c",
+ "pty_proc_win.h",
+ "pty_conpty_win.c",
+ "pty_conpty_win.h",
+ "os_win_console.c",
+ "win_defs.h",
+ };
const unix_only = [_][]const u8{ "unix_defs.h", "pty_proc_unix.c", "pty_proc_unix.h" };
const exclude_list = if (is_windows) &unix_only else &windows_only;
@@ -149,7 +214,10 @@ pub fn build(b: *std.Build) !void {
}
}
if (std.mem.eql(u8, ".c", entry.name[entry.name.len - 2 ..])) {
- try nvim_sources.append(b.allocator, .{ .name = b.fmt("{s}{s}", .{ s, entry.name }), .api_export = api_export });
+ try nvim_sources.append(b.allocator, .{
+ .name = b.fmt("{s}{s}", .{ s, entry.name }),
+ .api_export = api_export,
+ });
}
if (std.mem.eql(u8, ".h", entry.name[entry.name.len - 2 ..])) {
try nvim_headers.append(b.allocator, b.fmt("{s}{s}", .{ s, entry.name }));
@@ -168,10 +236,16 @@ pub fn build(b: *std.Build) !void {
var config_str = b.fmt("zig build -Doptimize={s}", .{@tagName(optimize)});
if (cross_compiling) {
- config_str = b.fmt("{s} -Dcross -Dtarget={s} (host: {s})", .{ config_str, try t.linuxTriple(b.allocator), try b.graph.host.result.linuxTriple(b.allocator) });
+ config_str = b.fmt("{s} -Dcross -Dtarget={s} (host: {s})", .{
+ config_str,
+ try t.linuxTriple(b.allocator),
+ try b.graph.host.result.linuxTriple(b.allocator),
+ });
}
- const versiondef_step = b.addConfigHeader(.{ .style = .{ .cmake = b.path("cmake.config/versiondef.h.in") } }, .{
+ const versiondef_step = b.addConfigHeader(.{
+ .style = .{ .cmake = b.path("cmake.config/versiondef.h.in") },
+ }, .{
.NVIM_VERSION_MAJOR = version.major,
.NVIM_VERSION_MINOR = version.minor,
.NVIM_VERSION_PATCH = version.patch,
@@ -183,7 +257,9 @@ pub fn build(b: *std.Build) !void {
_ = gen_config.addCopyFile(versiondef_step.getOutput(), "auto/versiondef.h"); // run_preprocessor() workaronnd
const ptrwidth = t.ptrBitWidth() / 8;
- const sysconfig_step = b.addConfigHeader(.{ .style = .{ .cmake = b.path("cmake.config/config.h.in") } }, .{
+ const sysconfig_step = b.addConfigHeader(.{
+ .style = .{ .cmake = b.path("cmake.config/config.h.in") },
+ }, .{
.SIZEOF_INT = t.cTypeByteSize(.int),
.SIZEOF_INTMAX_T = t.cTypeByteSize(.longlong), // TODO
.SIZEOF_LONG = t.cTypeByteSize(.long),
@@ -228,6 +304,9 @@ pub fn build(b: *std.Build) !void {
.VTERM_TEST_FILE = "test/vterm_test_output", // TODO(bfredl): revisit when porting libvterm tests
});
+ const system_install_path = b.option([]const u8, "install-path", "Install path (for packagers)");
+ const install_path = system_install_path orelse b.install_path;
+ const lib_dir = if (system_install_path) |path| b.fmt("{s}/lib", .{path}) else b.lib_dir;
_ = gen_config.addCopyFile(sysconfig_step.getOutput(), "auto/config.h"); // run_preprocessor() workaronnd
_ = gen_config.add("auto/pathdef.h", b.fmt(
@@ -235,12 +314,20 @@ pub fn build(b: *std.Build) !void {
\\char *default_vimruntime_dir = "";
\\char *default_lib_dir = "{s}/nvim";
// b.lib_dir is typically b.install_path + "/lib" but may be overridden
- , .{ try replace_backslashes(b, b.install_path), try replace_backslashes(b, b.lib_dir) }));
+ , .{ try replace_backslashes(b, install_path), try replace_backslashes(b, lib_dir) }));
- const opt_version_string = b.option([]const u8, "version-string", "Override Neovim version string. Default is to find out with git.");
+ const opt_version_string = b.option(
+ []const u8,
+ "version-string",
+ "Override Neovim version string. Default is to find out with git.",
+ );
const version_medium = if (opt_version_string) |version_string| version_string else v: {
var code: u8 = undefined;
- const version_string = b.fmt("v{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
+ const version_string = b.fmt("v{d}.{d}.{d}", .{
+ version.major,
+ version.minor,
+ version.patch,
+ });
const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{
"git",
"-C", b.build_root.path orelse ".", // affects the --git-dir argument
@@ -282,51 +369,123 @@ pub fn build(b: *std.Build) !void {
// TODO(zig): using getEmittedIncludeTree() is ugly af. we want unittests
// to reuse the std.build.Module include_path thing
- const unittest_include_path = [_]LazyPath{
- b.path("src/"),
- gen_config.getDirectory(),
- lua.getEmittedIncludeTree(),
- libuv.getEmittedIncludeTree(),
- libluv.getEmittedIncludeTree(),
- utf8proc.artifact("utf8proc").getEmittedIncludeTree(),
- if (unibilium) |u| u.artifact("unibilium").getEmittedIncludeTree() else b.path("UNUSED_PATH/"), // :p
- treesitter.artifact("tree-sitter").getEmittedIncludeTree(),
- if (iconv) |dep| dep.artifact("iconv").getEmittedIncludeTree() else b.path("UNUSED_PATH/"),
- };
+ var unittest_include_path: std.ArrayList(LazyPath) = try .initCapacity(b.allocator, 2);
+ try unittest_include_path.append(b.allocator, b.path("src/"));
+ try unittest_include_path.append(b.allocator, gen_config.getDirectory());
+ if (system_integration_options.lua) {
+ try appendSystemIncludePath(b, &unittest_include_path, lualib_name);
+ } else if (lua) |compile| {
+ try unittest_include_path.append(b.allocator, compile.getEmittedIncludeTree());
+ }
+ if (system_integration_options.uv) {
+ try appendSystemIncludePath(b, &unittest_include_path, "libuv");
+ try appendSystemIncludePath(b, &unittest_include_path, "libluv");
+ } else {
+ if (libuv) |compile| try unittest_include_path.append(b.allocator, compile.getEmittedIncludeTree());
+ if (libluv) |compile| try unittest_include_path.append(b.allocator, compile.getEmittedIncludeTree());
+ }
+ if (system_integration_options.utf8proc) {
+ try appendSystemIncludePath(b, &unittest_include_path, "libutf8proc");
+ } else if (utf8proc) |dep| {
+ try unittest_include_path.append(b.allocator, dep.artifact("utf8proc").getEmittedIncludeTree());
+ }
+ if (use_unibilium) {
+ if (system_integration_options.unibilium) {
+ try appendSystemIncludePath(b, &unittest_include_path, "unibilium");
+ } else if (unibilium) |dep| {
+ try unittest_include_path.append(b.allocator, dep.artifact("unibilium").getEmittedIncludeTree());
+ }
+ }
+ if (system_integration_options.tree_sitter) {
+ try appendSystemIncludePath(b, &unittest_include_path, "tree-sitter");
+ } else if (treesitter) |dep| {
+ try unittest_include_path.append(b.allocator, dep.artifact("tree-sitter").getEmittedIncludeTree());
+ }
+ if (iconv) |dep| {
+ try unittest_include_path.append(b.allocator, dep.artifact("iconv").getEmittedIncludeTree());
+ }
- const gen_headers, const funcs_data = try gen.nvim_gen_sources(b, nlua0, &nvim_sources, &nvim_headers, &api_headers, versiondef_git, version_lua);
+ const gen_headers, const funcs_data = try gen.nvim_gen_sources(
+ b,
+ nlua0,
+ &nvim_sources,
+ &nvim_headers,
+ &api_headers,
+ versiondef_git,
+ version_lua,
+ );
const test_config_step = b.addWriteFiles();
_ = test_config_step.add("test/cmakeconfig/paths.lua", try test_config(b));
const test_gen_step = b.step("gen_headers", "debug: output generated headers");
- const config_install = b.addInstallDirectory(.{ .source_dir = gen_config.getDirectory(), .install_dir = .prefix, .install_subdir = "config/" });
+ const config_install = b.addInstallDirectory(.{
+ .source_dir = gen_config.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "config/",
+ });
test_gen_step.dependOn(&config_install.step);
- test_gen_step.dependOn(&b.addInstallDirectory(.{ .source_dir = gen_headers.getDirectory(), .install_dir = .prefix, .install_subdir = "headers/" }).step);
+ test_gen_step.dependOn(&b.addInstallDirectory(.{
+ .source_dir = gen_headers.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "headers/",
+ }).step);
const nvim_exe = b.addExecutable(.{
.name = "nvim",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
+ .link_libc = true,
}),
});
nvim_exe.rdynamic = true; // -E
- nvim_exe.linkLibrary(lua);
- nvim_exe.linkLibrary(libuv);
- nvim_exe.linkLibrary(libluv);
+ if (system_integration_options.lua) {
+ nvim_exe.root_module.linkSystemLibrary(lualib_name, .{});
+ } else if (lua) |compile| {
+ nvim_exe.root_module.linkLibrary(compile);
+ }
+ if (system_integration_options.uv) {
+ nvim_exe.root_module.linkSystemLibrary("libuv", .{});
+ nvim_exe.root_module.linkSystemLibrary("libluv", .{});
+ } else {
+ if (libuv) |compile| nvim_exe.root_module.linkLibrary(compile);
+ if (libluv) |compile| nvim_exe.root_module.linkLibrary(compile);
+ }
if (iconv) |dep| nvim_exe.linkLibrary(dep.artifact("iconv"));
- nvim_exe.linkLibrary(utf8proc.artifact("utf8proc"));
- if (unibilium) |u| nvim_exe.linkLibrary(u.artifact("unibilium"));
- nvim_exe.linkLibrary(treesitter.artifact("tree-sitter"));
+ if (system_integration_options.utf8proc) {
+ nvim_exe.root_module.linkSystemLibrary("utf8proc", .{});
+ } else if (utf8proc) |dep| {
+ nvim_exe.root_module.linkLibrary(dep.artifact("utf8proc"));
+ }
+ if (use_unibilium) {
+ if (system_integration_options.unibilium) {
+ nvim_exe.root_module.linkSystemLibrary("unibilium", .{});
+ } else if (unibilium) |dep| {
+ nvim_exe.root_module.linkLibrary(dep.artifact("unibilium"));
+ }
+ }
+ if (system_integration_options.tree_sitter) {
+ nvim_exe.root_module.linkSystemLibrary("tree-sitter", .{});
+ } else if (treesitter) |dep| {
+ nvim_exe.root_module.linkLibrary(dep.artifact("tree-sitter"));
+ }
if (is_windows) {
nvim_exe.linkSystemLibrary("netapi32");
}
nvim_exe.addIncludePath(b.path("src"));
nvim_exe.addIncludePath(gen_config.getDirectory());
nvim_exe.addIncludePath(gen_headers.getDirectory());
- build_lua.add_lua_modules(nvim_exe.root_module, lpeg, use_luajit, false);
+ try build_lua.add_lua_modules(
+ b,
+ t,
+ nvim_exe.root_module,
+ lpeg,
+ use_luajit,
+ false,
+ system_integration_options,
+ );
var unit_test_sources = try std.ArrayList([]u8).initCapacity(b.allocator, 10);
if (support_unittests) {
@@ -388,7 +547,11 @@ pub fn build(b: *std.Build) !void {
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
- const test_runtime_install = b.addInstallDirectory(.{ .source_dir = gen_runtime.getDirectory(), .install_dir = .prefix, .install_subdir = "runtime/" });
+ const test_runtime_install = b.addInstallDirectory(.{
+ .source_dir = gen_runtime.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "runtime/",
+ });
test_deps.dependOn(&test_runtime_install.step);
const nvim_dev = b.step("nvim_dev", "build the editor for development");
@@ -412,15 +575,41 @@ pub fn build(b: *std.Build) !void {
// installation
const install = b.getInstallStep();
install.dependOn(&nvim_exe_install.step);
- b.installDirectory(.{ .source_dir = b.path("runtime/"), .install_dir = .prefix, .install_subdir = "share/nvim/runtime/" });
- b.installDirectory(.{ .source_dir = gen_runtime.getDirectory(), .install_dir = .prefix, .install_subdir = "share/nvim/runtime/" });
+ b.installDirectory(.{
+ .source_dir = b.path("runtime/"),
+ .install_dir = .prefix,
+ .install_subdir = "share/nvim/runtime/",
+ });
+ b.installDirectory(.{
+ .source_dir = gen_runtime.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/nvim/runtime/",
+ });
- test_deps.dependOn(test_fixture(b, "shell-test", null, target, optimize, &flags));
- test_deps.dependOn(test_fixture(b, "tty-test", libuv, target, optimize, &flags));
- test_deps.dependOn(test_fixture(b, "pwsh-test", null, target, optimize, &flags));
- test_deps.dependOn(test_fixture(b, "printargs-test", null, target, optimize, &flags));
- test_deps.dependOn(test_fixture(b, "printenv-test", null, target, optimize, &flags));
- test_deps.dependOn(test_fixture(b, "streams-test", libuv, target, optimize, &flags));
+ test_deps.dependOn(test_fixture(b, "shell-test", false, false, null, target, optimize, &flags));
+ test_deps.dependOn(test_fixture(
+ b,
+ "tty-test",
+ true,
+ system_integration_options.uv,
+ libuv,
+ target,
+ optimize,
+ &flags,
+ ));
+ test_deps.dependOn(test_fixture(b, "pwsh-test", false, false, null, target, optimize, &flags));
+ test_deps.dependOn(test_fixture(b, "printargs-test", false, false, null, target, optimize, &flags));
+ test_deps.dependOn(test_fixture(b, "printenv-test", false, false, null, target, optimize, &flags));
+ test_deps.dependOn(test_fixture(
+ b,
+ "streams-test",
+ true,
+ system_integration_options.uv,
+ libuv,
+ target,
+ optimize,
+ &flags,
+ ));
// xxd - hex dump utility (vendored from Vim)
const xxd_exe = b.addExecutable(.{
@@ -435,27 +624,51 @@ pub fn build(b: *std.Build) !void {
test_deps.dependOn(&b.addInstallArtifact(xxd_exe, .{}).step);
const parser_c = b.dependency("treesitter_c", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize));
+ test_deps.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize, .install));
+
const parser_markdown = b.dependency("treesitter_markdown", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize));
- test_deps.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize));
+ test_deps.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize, .install));
+ test_deps.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize, .install));
+
const parser_vim = b.dependency("treesitter_vim", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize));
+ test_deps.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize, .install));
+
const parser_vimdoc = b.dependency("treesitter_vimdoc", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize));
+ test_deps.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize, .install));
+
const parser_lua = b.dependency("treesitter_lua", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize));
- const parser_query = b.dependency("treesitter_query", .{ .target = target, .optimize = optimize });
- test_deps.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize));
+ test_deps.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize, .install));
- const unit_headers: ?[]const LazyPath = if (support_unittests) &(unittest_include_path ++ .{gen_headers.getDirectory()}) else null;
+ const parser_query = b.dependency("treesitter_query", .{ .target = target, .optimize = optimize });
+ test_deps.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize, .test_));
+ install.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize, .install));
- try tests.test_steps(b, nvim_exe, test_deps, lua_dev_deps.path("."), test_config_step.getDirectory(), unit_headers);
+ var unit_headers: ?[]const LazyPath = null;
+ if (support_unittests) {
+ try unittest_include_path.append(b.allocator, gen_headers.getDirectory());
+ unit_headers = unittest_include_path.items;
+ }
+ try tests.test_steps(
+ b,
+ nvim_exe,
+ test_deps,
+ lua_dev_deps.path("."),
+ test_config_step.getDirectory(),
+ unit_headers,
+ );
}
pub fn test_fixture(
b: *std.Build,
name: []const u8,
+ use_libuv: bool,
+ use_system_libuv: bool,
libuv: ?*std.Build.Step.Compile,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
@@ -473,9 +686,18 @@ pub fn test_fixture(
fixture.mingw_unicode_entry_point = true; // uses UNICODE on WINDOWS :scream:
}
- fixture.addCSourceFile(.{ .file = b.path(b.fmt("./test/functional/fixtures/{s}.c", .{source})), .flags = flags });
+ fixture.addCSourceFile(.{
+ .file = b.path(b.fmt("./test/functional/fixtures/{s}.c", .{source})),
+ .flags = flags,
+ });
fixture.linkLibC();
- if (libuv) |uv| fixture.linkLibrary(uv);
+ if (use_libuv) {
+ if (use_system_libuv) {
+ fixture.root_module.linkSystemLibrary("libuv", .{});
+ } else if (libuv) |uv| {
+ fixture.linkLibrary(uv);
+ }
+ }
return &b.addInstallArtifact(fixture, .{}).step;
}
@@ -486,6 +708,7 @@ pub fn add_ts_parser(
scanner: bool,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
+ path: enum { test_, install },
) *std.Build.Step {
const parser = b.addLibrary(.{
.name = name,
@@ -500,8 +723,21 @@ pub fn add_ts_parser(
parser.addIncludePath(parser_dir.path(b, "src"));
parser.linkLibC();
- const parser_install = b.addInstallArtifact(parser, .{ .dest_sub_path = b.fmt("parser/{s}.so", .{name}) });
- return &parser_install.step;
+ switch (path) {
+ .install => {
+ const parser_install = b.addInstallArtifact(parser, .{
+ .dest_dir = .{ .override = .{ .custom = "share/nvim/runtime/parser" } },
+ .dest_sub_path = b.fmt("{s}.so", .{name}),
+ });
+ return &parser_install.step;
+ },
+ .test_ => {
+ const parser_install = b.addInstallArtifact(parser, .{
+ .dest_sub_path = b.fmt("parser/{s}.so", .{name}),
+ });
+ return &parser_install.step;
+ },
+ }
}
pub fn lua_version_info(b: *std.Build) []u8 {
@@ -516,7 +752,15 @@ pub fn lua_version_info(b: *std.Build) []u8 {
\\ {{"api_compatible", {}}},
\\ {{"api_prerelease", {}}},
\\}}
- , .{ v.major, v.minor, v.patch, v.prerelease.len > 0, v.api_level, v.api_level_compat, v.api_prerelease });
+ , .{
+ v.major,
+ v.minor,
+ v.patch,
+ v.prerelease.len > 0,
+ v.api_level,
+ v.api_level_compat,
+ v.api_prerelease,
+ });
}
/// Replace all backslashes in `input` with with forward slashes when the target is Windows.
@@ -551,3 +795,29 @@ pub fn test_config(b: *std.Build) ![]u8 {
\\return M
, .{ .bin_dir = try replace_backslashes(b, b.install_path), .src_path = try replace_backslashes(b, src_path) });
}
+
+fn appendSystemIncludePath(
+ b: *std.Build,
+ path: *std.ArrayList(LazyPath),
+ system_name: []const u8,
+) !void {
+ var code: u8 = 0;
+ const stdout = try b.runAllowFail(
+ &[_][]const u8{ "pkg-config", system_name, "--cflags-only-I", "--keep-system-cflags" },
+ &code,
+ .Ignore,
+ );
+ if (code != 0) return std.Build.PkgConfigError.PkgConfigFailed;
+ var arg_it = std.mem.tokenizeAny(u8, stdout, " \r\n\t");
+ while (arg_it.next()) |arg| {
+ if (std.mem.eql(u8, arg, "-I")) {
+ // -I /foo/bar
+ const dir = arg_it.next() orelse return std.Build.PkgConfigError.PkgConfigInvalidOutput;
+ try path.append(b.allocator, .{ .cwd_relative = dir });
+ } else if (std.mem.startsWith(u8, arg, "-I")) {
+ // -I/foo/bar
+ const dir = arg[("-I".len)..];
+ try path.append(b.allocator, .{ .cwd_relative = dir });
+ }
+ }
+}
diff --git a/build.zig.zon b/build.zig.zon
@@ -6,30 +6,35 @@
.dependencies = .{
.zlua = .{
- .url = "git+https://github.com/natecraddock/ziglua#a4d08d97795c312e63a0f09d456f7c6d280610b4",
- .hash = "zlua-0.1.0-hGRpC5c9BQAfU5bkkFfLV9B4a7Prw8N7JPIFAZBbRCkq",
+ .url = "git+https://github.com/natecraddock/ziglua#dca1800ea46f5a19fc9abf88b2f8c1617f86ac23",
+ .hash = "zlua-0.1.0-hGRpC1dCBQDf-IqqUifYvyr8B9-4FlYXqY8cl7HIetrC",
},
.lpeg = .{
.url = "https://github.com/neovim/deps/raw/d495ee6f79e7962a53ad79670cb92488abe0b9b4/opt/lpeg-1.1.0.tar.gz",
.hash = "N-V-__8AAMnaAwCEutreuREG3QayBVEZqUTDQFY1Nsrv2OIt",
+ .lazy = true,
},
.luv = .{
.url = "git+https://github.com/luvit/luv?ref=1.51.0-1#4c9fbc6cf6f3338bb0e0426710cf885ee557b540",
.hash = "N-V-__8AAMlNDwCY07jUoMiq3iORXdZy0uFWKiHsy8MaDBJA",
+ .lazy = true,
},
.lua_compat53 = .{
.url = "https://github.com/lunarmodules/lua-compat-5.3/archive/v0.13.tar.gz",
.hash = "N-V-__8AADi-AwDnVoXwDCQvv2wcYOmN0bJLqZ44J3lwoQY2",
+ .lazy = true,
},
.treesitter = .{
.url = "git+https://github.com/tree-sitter/tree-sitter?ref=v0.26.3#cd4b6e2ef996d4baca12caadb78dffc8b55bc869",
.hash = "tree_sitter-0.26.3-Tw2sRwKWCwB6DronNIXBd7Aq5TY4WlnmS4d8PB_M7TIU",
+ .lazy = true,
},
.libuv = .{
.url = "git+https://github.com/allyourcodebase/libuv#a2dfd385bd2a00d6d290fda85a40a55a9d6cffc5",
.hash = "libuv-1.51.0-htqqv6liAADxBLIBCZT-qUh_3nRRwtNYsOFQOUmrd_sx",
+ .lazy = true,
},
- .utf8proc = .{ .path = "./deps/utf8proc/" },
+ .utf8proc = .{ .path = "./deps/utf8proc/", .lazy = true },
.unibilium = .{ .path = "./deps/unibilium/", .lazy = true },
.libiconv = .{
.url = "git+https://github.com/allyourcodebase/libiconv#9def4c8a1743380e85bcedb80f2c15b455e236f3",
diff --git a/deps/unibilium/build.zig b/deps/unibilium/build.zig
@@ -4,7 +4,6 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
- const upstream = b.dependency("unibilium", .{});
const lib = b.addLibrary(.{
.name = "unibilium",
.linkage = .static,
@@ -14,17 +13,19 @@ pub fn build(b: *std.Build) !void {
}),
});
- lib.addIncludePath(upstream.path(""));
+ if (b.lazyDependency("unibilium", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
- lib.installHeader(upstream.path("unibilium.h"), "unibilium.h");
+ lib.installHeader(upstream.path("unibilium.h"), "unibilium.h");
- lib.linkLibC();
+ lib.linkLibC();
- lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{
- "unibilium.c",
- "uninames.c",
- "uniutil.c",
- }, .flags = &.{"-DTERMINFO_DIRS=\"/etc/terminfo:/usr/share/terminfo\""} });
+ lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{
+ "unibilium.c",
+ "uninames.c",
+ "uniutil.c",
+ }, .flags = &.{"-DTERMINFO_DIRS=\"/etc/terminfo:/usr/share/terminfo\""} });
+ }
b.installArtifact(lib);
}
diff --git a/deps/unibilium/build.zig.zon b/deps/unibilium/build.zig.zon
@@ -7,6 +7,7 @@
.unibilium = .{
.url = "git+https://github.com/neovim/unibilium?ref=v2.1.2#bfcb0350129dd76893bc90399cf37c45812268a2",
.hash = "N-V-__8AADO1CgCggvx73yptnBlXbEm7TjOSO6VGIqc0CvYR",
+ .lazy = true,
},
},
}
diff --git a/deps/utf8proc/build.zig b/deps/utf8proc/build.zig
@@ -4,7 +4,6 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
- const upstream = b.dependency("utf8proc", .{});
const lib = b.addLibrary(.{
.name = "utf8proc",
.linkage = .static,
@@ -14,14 +13,16 @@ pub fn build(b: *std.Build) !void {
}),
});
- lib.addIncludePath(upstream.path(""));
- lib.installHeader(upstream.path("utf8proc.h"), "utf8proc.h");
+ if (b.lazyDependency("utf8proc", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ lib.installHeader(upstream.path("utf8proc.h"), "utf8proc.h");
- lib.linkLibC();
+ lib.linkLibC();
- lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{
- "utf8proc.c",
- }, .flags = &.{"-DUTF8PROC_STATIC"} });
+ lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{
+ "utf8proc.c",
+ }, .flags = &.{"-DUTF8PROC_STATIC"} });
+ }
b.installArtifact(lib);
}
diff --git a/deps/utf8proc/build.zig.zon b/deps/utf8proc/build.zig.zon
@@ -7,6 +7,7 @@
.utf8proc = .{
.url = "git+https://github.com/juliastrings/utf8proc?ref=v2.11.3#e5e799221b45bbb90f5fdc5c69b6b8dfbf017e78",
.hash = "N-V-__8AACywKABFCj0r_Y-jIWsk9ahy10zlk78hjn6S-39g",
+ .lazy = true,
},
},
}
diff --git a/src/build_lua.zig b/src/build_lua.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const build = @import("../build.zig");
const LazyPath = std.Build.LazyPath;
pub fn build_nlua0(
@@ -7,9 +8,10 @@ pub fn build_nlua0(
optimize: std.builtin.OptimizeMode,
use_luajit: bool,
ziglua: *std.Build.Dependency,
- lpeg: *std.Build.Dependency,
- libluv: *std.Build.Step.Compile,
-) *std.Build.Step.Compile {
+ lpeg: ?*std.Build.Dependency,
+ libluv: ?*std.Build.Step.Compile,
+ system_integration_options: build.SystemIntegrationOptions,
+) !*std.Build.Step.Compile {
const options = b.addOptions();
options.addOption(bool, "use_luajit", use_luajit);
@@ -19,6 +21,7 @@ pub fn build_nlua0(
.root_source_file = b.path("src/nlua0.zig"),
.target = target,
.optimize = optimize,
+ .link_libc = true,
}),
});
const nlua0_mod = nlua0_exe.root_module;
@@ -28,6 +31,7 @@ pub fn build_nlua0(
.root_source_file = b.path("src/nlua0.zig"),
.target = target,
.optimize = optimize,
+ .link_libc = true,
}),
});
@@ -39,14 +43,23 @@ pub fn build_nlua0(
mod.addImport("ziglua", ziglua.module("zlua"));
mod.addImport("embedded_data", embedded_data);
// addImport already links by itself. but we need headers as well..
- mod.linkLibrary(ziglua.artifact("lua"));
- mod.linkLibrary(libluv);
+ if (system_integration_options.lua) {
+ const system_lua_lib = if (use_luajit) "luajit" else "lua5.1";
+ mod.linkSystemLibrary(system_lua_lib, .{});
+ } else {
+ mod.linkLibrary(ziglua.artifact("lua"));
+ }
+ if (libluv) |luv| {
+ mod.linkLibrary(luv);
+ } else {
+ mod.linkSystemLibrary("luv", .{});
+ }
mod.addOptions("options", options);
mod.addIncludePath(b.path("src"));
mod.addIncludePath(b.path("src/includes_fixmelater"));
- add_lua_modules(mod, lpeg, use_luajit, true);
+ try add_lua_modules(b, target.result, mod, lpeg, use_luajit, true, system_integration_options);
}
// for debugging the nlua0 environment
@@ -68,14 +81,24 @@ pub fn build_nlua0(
return nlua0_exe;
}
-pub fn add_lua_modules(mod: *std.Build.Module, lpeg: *std.Build.Dependency, use_luajit: bool, is_nlua0: bool) void {
+pub fn add_lua_modules(
+ b: *std.Build,
+ target: std.Target,
+ mod: *std.Build.Module,
+ lpeg_dep: ?*std.Build.Dependency,
+ use_luajit: bool,
+ is_nlua0: bool,
+ system_integration_options: build.SystemIntegrationOptions,
+) !void {
const flags = [_][]const u8{
// Standard version used in Lua Makefile
"-std=gnu99",
if (is_nlua0) "-DNVIM_NLUA0" else "",
};
- mod.addIncludePath(lpeg.path(""));
+ if (lpeg_dep) |lpeg| {
+ mod.addIncludePath(lpeg.path(""));
+ }
mod.addCSourceFiles(.{
.files = &.{
"src/mpack/lmpack.c",
@@ -86,18 +109,25 @@ pub fn add_lua_modules(mod: *std.Build.Module, lpeg: *std.Build.Dependency, use_
},
.flags = &flags,
});
- mod.addCSourceFiles(.{
- .root = .{ .dependency = .{ .dependency = lpeg, .sub_path = "" } },
- .files = &.{
- "lpcap.c",
- "lpcode.c",
- "lpcset.c",
- "lpprint.c",
- "lptree.c",
- "lpvm.c",
- },
- .flags = &flags,
- });
+ if (system_integration_options.lpeg) {
+ if (try findLpeg(b, target)) |lpeg_lib| {
+ mod.addLibraryPath(.{ .cwd_relative = std.fs.path.dirname(lpeg_lib).? });
+ mod.addObjectFile(.{ .cwd_relative = lpeg_lib });
+ }
+ } else if (lpeg_dep) |lpeg| {
+ mod.addCSourceFiles(.{
+ .root = .{ .dependency = .{ .dependency = lpeg, .sub_path = "" } },
+ .files = &.{
+ "lpcap.c",
+ "lpcode.c",
+ "lpcset.c",
+ "lpprint.c",
+ "lptree.c",
+ "lpvm.c",
+ },
+ .flags = &flags,
+ });
+ }
if (!use_luajit) {
mod.addCSourceFiles(.{
@@ -113,11 +143,12 @@ pub fn build_libluv(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
- lua: *std.Build.Step.Compile,
+ lua: ?*std.Build.Step.Compile,
libuv: *std.Build.Step.Compile,
+ use_luajit: bool,
) !*std.Build.Step.Compile {
- const upstream = b.dependency("luv", .{});
- const compat53 = b.dependency("lua_compat53", .{});
+ const upstream = b.lazyDependency("luv", .{});
+ const compat53 = b.lazyDependency("lua_compat53", .{});
const lib = b.addLibrary(.{
.name = "luv",
.linkage = .static,
@@ -127,21 +158,59 @@ pub fn build_libluv(
}),
});
- lib.linkLibrary(lua);
+ if (lua) |lua_lib| {
+ lib.root_module.linkLibrary(lua_lib);
+ } else {
+ const system_lua_lib = if (use_luajit) "luajit" else "lua5.1";
+ lib.root_module.linkSystemLibrary(system_lua_lib, .{});
+ }
lib.linkLibrary(libuv);
- lib.addIncludePath(upstream.path("src"));
- lib.addIncludePath(compat53.path("c-api"));
-
- lib.installHeader(upstream.path("src/luv.h"), "luv/luv.h");
-
- lib.addCSourceFiles(.{ .root = upstream.path("src/"), .files = &.{
- "luv.c",
- } });
-
- lib.addCSourceFiles(.{ .root = compat53.path("c-api"), .files = &.{
- "compat-5.3.c",
- } });
+ if (upstream) |dep| {
+ lib.addIncludePath(dep.path("src"));
+ lib.installHeader(dep.path("src/luv.h"), "luv/luv.h");
+ lib.addCSourceFiles(.{ .root = dep.path("src/"), .files = &.{
+ "luv.c",
+ } });
+ }
+ if (compat53) |dep| {
+ lib.addIncludePath(dep.path("c-api"));
+ lib.addCSourceFiles(.{ .root = dep.path("c-api"), .files = &.{
+ "compat-5.3.c",
+ } });
+ }
return lib;
}
+
+fn findLpeg(b: *std.Build, target: std.Target) !?[]const u8 {
+ const filenames = [_][]const u8{
+ "lpeg_a",
+ "lpeg",
+ "liblpeg_a",
+ "lpeg.so",
+ b.fmt("lpeg{s}", .{target.dynamicLibSuffix()}),
+ };
+ var code: u8 = 0;
+ const dirs_stdout = std.mem.trimEnd(u8, try b.runAllowFail(&[_][]const u8{
+ "pkg-config",
+ "--variable=pc_system_libdirs",
+ "--keep-system-cflags",
+ "pkg-config",
+ }, &code, .Ignore), "\r\n");
+ var paths: std.ArrayList([]const u8) = try .initCapacity(b.allocator, 0);
+ var path_it = std.mem.tokenizeAny(u8, dirs_stdout, " ,");
+ while (path_it.next()) |dir| {
+ try paths.append(b.allocator, dir);
+ try paths.append(b.allocator, b.fmt("{s}/lua/5.1", .{dir}));
+ }
+ for (paths.items) |path| {
+ var dir = std.fs.openDirAbsolute(path, .{}) catch continue;
+ defer dir.close();
+ for (filenames) |filename| {
+ dir.access(filename, .{}) catch continue;
+ return b.fmt("{s}/{s}", .{ path, filename });
+ }
+ }
+ return null;
+}