From 6d9031390cfc06300d60f96f63ba2bd95b4c7b3b Mon Sep 17 00:00:00 2001 From: Chinmay Dalal Date: Sat, 20 Dec 2025 00:11:05 -0500 Subject: [PATCH] feat(build.zig): add option to use system dependencies Problem: build.zig always downloads dependencies and statically links them, which is frowned upon by distro packagers. Solution: Add option to use system libraries. --- build.zig | 257 ++++++++++++++++++++++++++--------- build.zig.zon | 17 ++- deps/unibilium/build.zig | 19 +-- deps/unibilium/build.zig.zon | 1 + deps/utf8proc/build.zig | 15 +- deps/utf8proc/build.zig.zon | 1 + src/build_lua.zig | 141 ++++++++++++++----- 7 files changed, 331 insertions(+), 120 deletions(-) diff --git a/build.zig b/build.zig index 61d76492d1..e999266804 100644 --- 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,68 @@ 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; + 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); - const lpeg = b.dependency("lpeg", .{}); + 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 lpeg = if (system_integration_options.lpeg) null else b.lazyDependency("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; - } - // 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 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 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 @@ -228,6 +255,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,7 +265,7 @@ 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 version_medium = if (opt_version_string) |version_string| version_string else v: { @@ -282,17 +312,45 @@ 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); @@ -309,24 +367,50 @@ pub fn build(b: *std.Build) !void { .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) { @@ -415,12 +499,12 @@ pub fn build(b: *std.Build) !void { 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(.{ @@ -434,28 +518,39 @@ pub fn build(b: *std.Build) !void { xxd_exe.linkLibC(); 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)); - 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)); - 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)); - 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)); - 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)); - - const unit_headers: ?[]const LazyPath = if (support_unittests) &(unittest_include_path ++ .{gen_headers.getDirectory()}) else null; + if (b.lazyDependency("treesitter_c", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "c", parser.path("."), false, target, optimize)); + } + if (b.lazyDependency("treesitter_markdown", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "markdown", parser.path("tree-sitter-markdown/"), true, target, optimize)); + test_deps.dependOn(add_ts_parser(b, "markdown_inline", parser.path("tree-sitter-markdown-inline/"), true, target, optimize)); + } + if (b.lazyDependency("treesitter_vim", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "vim", parser.path("."), true, target, optimize)); + } + if (b.lazyDependency("treesitter_vimdoc", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "vimdoc", parser.path("."), false, target, optimize)); + } + if (b.lazyDependency("treesitter_lua", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "lua", parser.path("."), true, target, optimize)); + } + if (b.lazyDependency("treesitter_query", .{ .target = target, .optimize = optimize })) |parser| { + test_deps.dependOn(add_ts_parser(b, "query", parser.path("."), false, target, optimize)); + } + 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, @@ -475,7 +570,13 @@ pub fn test_fixture( 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; } @@ -551,3 +652,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 index b4b93bd24d..8c3ede40d1 100644 --- 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", @@ -43,26 +48,32 @@ .treesitter_c = .{ .url = "git+https://github.com/tree-sitter/tree-sitter-c?ref=v0.24.1#7fa1be1b694b6e763686793d97da01f36a0e5c12", .hash = "N-V-__8AANxPSABzw3WBTSH_YkwaGAfrK6PBqAMqQedkDDim", + .lazy = true, }, .treesitter_markdown = .{ .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-markdown?ref=v0.5.1#2dfd57f547f06ca5631a80f601e129d73fc8e9f0", .hash = "N-V-__8AABcZUwBZelO8MiLRwuLD1Wk34qHHbXtS4UW3Khys", + .lazy = true, }, .treesitter_lua = .{ .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-lua?ref=v0.4.1#816840c592ab973500ae9750763c707b447e7fef", .hash = "N-V-__8AAHCmCAAf-5sa_C1N5Ts8B7V-vTKqUEMJZVnNkq_y", + .lazy = true, }, .treesitter_vim = .{ .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-vim?ref=v0.7.0#3dd4747082d1b717b8978211c06ef7b6cd16125b", .hash = "N-V-__8AAMArVAB4uo2wg2XRs8HBviQ4Pq366cC_iRolX4Vc", + .lazy = true, }, .treesitter_vimdoc = .{ .url = "git+https://github.com/neovim/tree-sitter-vimdoc?ref=v4.1.0#f061895a0eff1d5b90e4fb60d21d87be3267031a", .hash = "N-V-__8AAI7VCgBqRcQ-vIxB8DJJFhmLG42p6rfwCWIdypSJ", + .lazy = true, }, .treesitter_query = .{ .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-query?ref=v0.8.0#a225e21d81201be77da58de614e2b7851735677a", .hash = "N-V-__8AAMR5AwAzZ5_8S2p2COTEf5usBeeT4ORzh-lBGkWy", + .lazy = true, }, }, .paths = .{ diff --git a/deps/unibilium/build.zig b/deps/unibilium/build.zig index 358e7ccb4f..c7da82efaf 100644 --- 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 index 7f9c16d2a5..7fcf396b03 100644 --- 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 index 0bdd4fa408..f88209987d 100644 --- 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 index f5fffeba38..96cb149f45 100644 --- 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 index 5a2059fc00..3d9b634ab1 100644 --- 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; +}