From 1f004970f00bd8ef7ad955b52857df136f82185b Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 11 Mar 2025 14:01:55 +0100 Subject: [PATCH] feat(build): build.zig MVP: build and run functionaltests on linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEW BUILD SYSTEM! This is a MVP implementation which supports building the "nvim" binary, including cross-compilation for some targets. As an example, you can build a aarch64-macos binary from an x86-64-linux-gnu host, or vice versa Add CI target for build.zig currently for functionaltests on linux x86_64 only Follow up items: - praxis for version and dependency bumping - windows 💀 - full integration of libintl and gettext (or a desicion not to) - update help and API metadata files - installation into a $PREFIX - more tests and linters --- .github/workflows/test.yml | 14 + CMakeLists.txt | 2 +- build.zig | 423 ++++++++++++++++++ build.zig.zon | 65 +++ cmake.config/versiondef.h.in | 2 +- cmake/RunTests.cmake | 2 +- deps/iconv_apple/build.zig | 90 ++++ deps/iconv_apple/build.zig.zon | 12 + deps/iconv_apple/include/os/variant_private.h | 1 + deps/libuv/build.zig | 125 ++++++ deps/libuv/build.zig.zon | 12 + deps/unibilium/build.zig | 27 ++ deps/unibilium/build.zig.zon | 12 + deps/utf8proc/build.zig | 24 + deps/utf8proc/build.zig.zon | 12 + runtime/CMakeLists.txt | 5 + runtime/doc/news.txt | 6 + runtime/embedded_data.zig | 3 + runtime/gen_runtime.zig | 39 ++ src/build_lua.zig | 138 ++++++ src/gen/gen_api_dispatch.lua | 13 +- src/gen/gen_eval.lua | 7 +- src/gen/gen_eval_files.lua | 2 +- src/gen/gen_events.lua | 3 +- src/gen/gen_ex_cmds.lua | 10 +- src/gen/gen_keycodes.lua | 3 +- src/gen/gen_options.lua | 4 +- src/gen/gen_steps.zig | 237 ++++++++++ src/gen/gen_vimvim.lua | 12 +- src/nlua0.zig | 120 +++++ src/nvim/CMakeLists.txt | 18 +- src/nvim/lua/converter.c | 2 +- src/nvim/macros_defs.h | 4 +- src/nvim/os/lang.c | 4 +- src/nvim/os/pty_proc_unix.c | 3 + src/versiondef.h.in | 16 + test/cmakeconfig/paths.lua.in | 1 + test/functional/api/vim_spec.lua | 14 +- .../swapfile_preserve_recover_spec.lua | 3 +- test/functional/lua/comment_spec.lua | 3 +- test/functional/lua/fs_spec.lua | 21 +- test/functional/lua/vim_spec.lua | 11 +- test/functional/plugin/lsp_spec.lua | 5 +- test/functional/testnvim.lua | 8 +- .../vimscript/api_functions_spec.lua | 2 +- test/lua_runner.lua | 6 +- test/run_tests.zig | 44 ++ test/testutil.lua | 4 + 48 files changed, 1530 insertions(+), 64 deletions(-) create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 deps/iconv_apple/build.zig create mode 100644 deps/iconv_apple/build.zig.zon create mode 100644 deps/iconv_apple/include/os/variant_private.h create mode 100644 deps/libuv/build.zig create mode 100644 deps/libuv/build.zig.zon create mode 100644 deps/unibilium/build.zig create mode 100644 deps/unibilium/build.zig.zon create mode 100644 deps/utf8proc/build.zig create mode 100644 deps/utf8proc/build.zig.zon create mode 100644 runtime/embedded_data.zig create mode 100644 runtime/gen_runtime.zig create mode 100644 src/build_lua.zig create mode 100644 src/gen/gen_steps.zig create mode 100644 src/nlua0.zig create mode 100644 src/versiondef.h.in create mode 100644 test/run_tests.zig diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fec6c15026..2fc0e818a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -201,6 +201,20 @@ jobs: name: Show logs run: cat $(find "$LOG_DIR" -type f) + zig-build: + runs-on: ubuntu-24.04 + timeout-minutes: 45 + name: build using zig build + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v1 + with: + version: 0.14.0 + - run: sudo apt-get install -y inotify-tools + - run: zig build test_nlua0 + - run: zig build nvim_bin && ./zig-out/bin/nvim --version + - run: zig build functionaltest + windows: uses: ./.github/workflows/test_windows.yml diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b2d792947..314d7215f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,7 @@ set(STYLUA_DIRS runtime scripts src test contrib) add_glob_target( TARGET lintlua-luacheck COMMAND $ - FLAGS -ll ${PROJECT_SOURCE_DIR}/test/lua_runner.lua ${CMAKE_BINARY_DIR}/usr luacheck -q + FLAGS -ll ${PROJECT_SOURCE_DIR}/test/lua_runner.lua ${CMAKE_BINARY_DIR}/usr/share/lua/5.1 luacheck -q GLOB_DIRS runtime scripts src test GLOB_PAT *.lua TOUCH_STRATEGY PER_DIR) diff --git a/build.zig b/build.zig new file mode 100644 index 0000000000..91f04fadf2 --- /dev/null +++ b/build.zig @@ -0,0 +1,423 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; +const build_lua = @import("src/build_lua.zig"); +const gen = @import("src/gen/gen_steps.zig"); +const runtime = @import("runtime/gen_runtime.zig"); +const tests = @import("test/run_tests.zig"); + +const version = struct { + const major = 0; + const minor = 12; + const patch = 0; + const prerelease = "-dev"; + + const api_level = 14; + const api_level_compat = 0; + const api_prerelease = true; +}; + +// 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; + for (d.builder.install_tls.step.dependencies.items) |dep_step| { + const inst = dep_step.cast(std.Build.Step.InstallArtifact) orelse continue; + if (std.mem.eql(u8, inst.artifact.name, name)) { + if (found != null) std.debug.panic("artifact name '{s}' is ambiguous", .{name}); + found = inst.artifact; + } + } + return found; +} + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + 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; + const optimize_host = .ReleaseSafe; + + const t = target.result; + const tag = t.os.tag; + + // 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 false; + const host_use_luajit = if (cross_compiling) false else use_luajit; + const E = enum { luajit, lua51 }; + + const ziglua = b.dependency("lua_wrapper", .{ + .target = target, + .optimize = optimize_lua, + .lang = if (use_luajit) E.luajit else E.lua51, + .shared = false, + }); + + const ziglua_host = if (cross_compiling) b.dependency("lua_wrapper", .{ + .target = target_host, + .optimize = optimize_lua, + .lang = if (host_use_luajit) E.luajit else E.lua51, + .shared = false, + }) else ziglua; + + const lpeg = b.dependency("lpeg", .{}); + + const iconv_apple = if (cross_compiling and tag.isDarwin()) b.lazyDependency("iconv_apple", .{ .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; + 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 utf8proc = b.dependency("utf8proc", .{ .target = target, .optimize = optimize }); + const unibilium = b.dependency("unibilium", .{ .target = target, .optimize = optimize }); + // TODO(bfredl): fix upstream bugs with UBSAN + const treesitter = b.dependency("treesitter", .{ .target = target, .optimize = .ReleaseFast }); + + const nlua0 = build_lua.build_nlua0(b, target_host, optimize_host, host_use_luajit, ziglua_host, lpeg); + + // 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 + const subdirs = [_][]const u8{ + "", // src/nvim itself + "os/", + "api/", + "api/private/", + "msgpack_rpc/", + "tui/", + "tui/termkey/", + "event/", + "eval/", + "lib/", + "lua/", + "viml/", + "viml/parser/", + "vterm/", + }; + + // source names _relative_ src/nvim/, not including other src/ subdircs + var nvim_sources = try std.ArrayList(gen.SourceItem).initCapacity(b.allocator, 100); + var nvim_headers = try std.ArrayList([]u8).initCapacity(b.allocator, 100); + + // both source headers and the {module}.h.generated.h files + var api_headers = try std.ArrayList(std.Build.LazyPath).initCapacity(b.allocator, 10); + + const is_windows = (target.result.os.tag == .windows); + // 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 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; + + const src_dir = b.build_root.handle; + for (subdirs) |s| { + var dir = try src_dir.openDir(b.fmt("src/nvim/{s}", .{s}), .{ .iterate = true }); + defer dir.close(); + var it = dir.iterateAssumeFirstIteration(); + const api_export = std.mem.eql(u8, s, "api/"); + const os_check = std.mem.eql(u8, s, "os/"); + entries: while (try it.next()) |entry| { + if (entry.name.len < 3) continue; + if (entry.name[0] < 'a' or entry.name[0] > 'z') continue; + if (os_check) { + for (exclude_list) |name| { + if (std.mem.eql(u8, name, entry.name)) { + continue :entries; + } + } + } + if (std.mem.eql(u8, ".c", entry.name[entry.name.len - 2 ..])) { + try nvim_sources.append(.{ .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.fmt("{s}{s}", .{ s, entry.name })); + if (api_export and !std.mem.eql(u8, "ui_events.in.h", entry.name)) { + try api_headers.append(b.path(b.fmt("src/nvim/{s}{s}", .{ s, entry.name }))); + } + } + } + } + + const gen_config = b.addWriteFiles(); + + const version_lua = gen_config.add("nvim_version.lua", lua_version_info(b)); + + 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) }); + } + + const versiondef_step = b.addConfigHeader(.{ .style = .{ .cmake = b.path("src/versiondef.h.in") } }, .{ + .NVIM_VERSION_MAJOR = version.major, + .NVIM_VERSION_MINOR = version.minor, + .NVIM_VERSION_PATCH = version.patch, + .NVIM_VERSION_PRERELEASE = version.prerelease, + .VERSION_STRING = "TODO", // TODO(bfredl): not sure what to put here. summary already in "config_str" + .CONFIG = config_str, + }); + _ = gen_config.addCopyFile(versiondef_step.getOutput(), "auto/versiondef.h"); // run_preprocessor() workaronnd + + const isLinux = tag == .linux; + const modernUnix = tag.isDarwin() or tag.isBSD() or isLinux; + + const ptrwidth = t.ptrBitWidth() / 8; + 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), + .SIZEOF_SIZE_T = ptrwidth, + .SIZEOF_VOID_PTR = ptrwidth, + + .PROJECT_NAME = "nvim", + + .HAVE__NSGETENVIRON = tag.isDarwin(), + .HAVE_FD_CLOEXEC = modernUnix, + .HAVE_FSEEKO = modernUnix, + .HAVE_LANGINFO_H = modernUnix, + .HAVE_NL_LANGINFO_CODESET = modernUnix, + .HAVE_NL_MSG_CAT_CNTR = isLinux, + .HAVE_PWD_FUNCS = modernUnix, + .HAVE_READLINK = modernUnix, + .HAVE_STRNLEN = modernUnix, + .HAVE_STRCASECMP = modernUnix, + .HAVE_STRINGS_H = modernUnix, + .HAVE_STRNCASECMP = modernUnix, + .HAVE_STRPTIME = modernUnix, + .HAVE_XATTR = isLinux, + .HAVE_SYS_SDT_H = false, + .HAVE_SYS_UTSNAME_H = modernUnix, + .HAVE_SYS_WAIT_H = false, // unused + .HAVE_TERMIOS_H = modernUnix, + .HAVE_WORKING_LIBINTL = isLinux, + .UNIX = modernUnix, + .CASE_INSENSITIVE_FILENAME = tag.isDarwin() or tag == .windows, + .HAVE_SYS_UIO_H = modernUnix, + .HAVE_READV = modernUnix, + .HAVE_DIRFD_AND_FLOCK = modernUnix, + .HAVE_FORKPTY = modernUnix and !tag.isDarwin(), // also on Darwin but we lack the headers :( + .HAVE_BE64TOH = modernUnix and !tag.isDarwin(), + .ORDER_BIG_ENDIAN = t.cpu.arch.endian() == .big, + .ENDIAN_INCLUDE_FILE = "endian.h", + .HAVE_EXECINFO_BACKTRACE = modernUnix, + .HAVE_BUILTIN_ADD_OVERFLOW = true, + .HAVE_WIMPLICIT_FALLTHROUGH_FLAG = true, + .HAVE_BITSCANFORWARD64 = null, + + .VTERM_TEST_FILE = "test/vterm_test_output", // TODO(bfredl): revisit when porting libvterm tests + }); + + _ = gen_config.addCopyFile(sysconfig_step.getOutput(), "auto/config.h"); // run_preprocessor() workaronnd + _ = gen_config.add("auto/pathdef.h", b.fmt( + \\char *default_vim_dir = "/usr/local/share/nvim"; + \\char *default_vimruntime_dir = ""; + \\char *default_lib_dir = "/usr/local/lib/nvim"; + , .{})); + + // TODO(bfredl): include git version when available + const medium = b.fmt("v{}.{}.{}{s}+zig", .{ version.major, version.minor, version.patch, version.prerelease }); + const versiondef_git = gen_config.add("auto/versiondef_git.h", b.fmt( + \\#define NVIM_VERSION_MEDIUM "{s}" + \\#define NVIM_VERSION_BUILD "???" + \\ + , .{medium})); + + // TODO(zig): using getEmittedIncludeTree() is ugly af. we want run_preprocessor() + // to use the std.build.Module include_path thing + const include_path = &.{ + b.path("src/"), + gen_config.getDirectory(), + lua.getEmittedIncludeTree(), + libuv.getEmittedIncludeTree(), + libluv.getEmittedIncludeTree(), + utf8proc.artifact("utf8proc").getEmittedIncludeTree(), + unibilium.artifact("unibilium").getEmittedIncludeTree(), + treesitter.artifact("tree-sitter").getEmittedIncludeTree(), + }; + + const gen_headers, const funcs_data = try gen.nvim_gen_sources(b, nlua0, &nvim_sources, &nvim_headers, &api_headers, include_path, target, versiondef_git, version_lua); + + const test_config_step = b.addWriteFiles(); + _ = test_config_step.add("test/cmakeconfig/paths.lua", try test_config(b, gen_headers.getDirectory())); + + 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/" }); + 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); + + const nvim_exe = b.addExecutable(.{ + .name = "nvim", + .target = target, + .optimize = optimize, + }); + + nvim_exe.linkLibrary(lua); + nvim_exe.linkLibrary(libuv); + nvim_exe.linkLibrary(libluv); + if (iconv_apple) |iconv| { + nvim_exe.linkLibrary(iconv.artifact("iconv")); + } + nvim_exe.linkLibrary(utf8proc.artifact("utf8proc")); + nvim_exe.linkLibrary(unibilium.artifact("unibilium")); + nvim_exe.linkLibrary(treesitter.artifact("tree-sitter")); + 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); + + const src_paths = try b.allocator.alloc([]u8, nvim_sources.items.len); + for (nvim_sources.items, 0..) |s, i| { + src_paths[i] = b.fmt("src/nvim/{s}", .{s.name}); + } + + const flags = [_][]const u8{ + "-std=gnu99", + "-DINCLUDE_GENERATED_DECLARATIONS", + "-DZIG_BUILD", + "-D_GNU_SOURCE", + if (use_luajit) "" else "-DNVIM_VENDOR_BIT", + }; + nvim_exe.addCSourceFiles(.{ .files = src_paths, .flags = &flags }); + + nvim_exe.addCSourceFiles(.{ .files = &.{ + "src/xdiff/xdiffi.c", + "src/xdiff/xemit.c", + "src/xdiff/xhistogram.c", + "src/xdiff/xpatience.c", + "src/xdiff/xprepare.c", + "src/xdiff/xutils.c", + "src/cjson/lua_cjson.c", + "src/cjson/fpconv.c", + "src/cjson/strbuf.c", + }, .flags = &flags }); + + const nvim_exe_step = b.step("nvim_bin", "only the binary (not a fully working install!)"); + const nvim_exe_install = b.addInstallArtifact(nvim_exe, .{}); + + nvim_exe_step.dependOn(&nvim_exe_install.step); + + const gen_runtime = try runtime.nvim_gen_runtime(b, nlua0, nvim_exe, funcs_data); + const runtime_install = b.addInstallDirectory(.{ .source_dir = gen_runtime.getDirectory(), .install_dir = .prefix, .install_subdir = "runtime/" }); + + const nvim = b.step("nvim", "build the editor"); + + nvim.dependOn(&nvim_exe_install.step); + nvim.dependOn(&runtime_install.step); + + const lua_dev_deps = b.dependency("lua_dev_deps", .{}); + + const test_deps = b.step("test_deps", "test prerequisites"); + test_deps.dependOn(&nvim_exe_install.step); + test_deps.dependOn(&runtime_install.step); + + test_deps.dependOn(test_fixture(b, "shell-test", null, target, optimize)); + test_deps.dependOn(test_fixture(b, "tty-test", libuv, target, optimize)); + test_deps.dependOn(test_fixture(b, "pwsh-test", null, target, optimize)); + test_deps.dependOn(test_fixture(b, "printargs-test", null, target, optimize)); + test_deps.dependOn(test_fixture(b, "printenv-test", null, target, optimize)); + test_deps.dependOn(test_fixture(b, "streams-test", libuv, target, optimize)); + + 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)); + + try tests.test_steps(b, nvim_exe, test_deps, lua_dev_deps.path("."), test_config_step.getDirectory()); +} + +pub fn test_fixture( + b: *std.Build, + name: []const u8, + libuv: ?*std.Build.Step.Compile, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) *std.Build.Step { + const fixture = b.addExecutable(.{ + .name = name, + .target = target, + .optimize = optimize, + }); + const source = if (std.mem.eql(u8, name, "pwsh-test")) "shell-test" else name; + fixture.addCSourceFile(.{ .file = b.path(b.fmt("./test/functional/fixtures/{s}.c", .{source})) }); + fixture.linkLibC(); + if (libuv) |uv| fixture.linkLibrary(uv); + return &b.addInstallArtifact(fixture, .{}).step; +} + +pub fn add_ts_parser( + b: *std.Build, + name: []const u8, + parser_dir: LazyPath, + scanner: bool, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) *std.Build.Step { + const parser = b.addLibrary(.{ + .name = name, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + .linkage = .dynamic, + }); + parser.addCSourceFile(.{ .file = parser_dir.path(b, "src/parser.c") }); + if (scanner) parser.addCSourceFile(.{ .file = parser_dir.path(b, "src/scanner.c") }); + 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; +} + +pub fn lua_version_info(b: *std.Build) []u8 { + const v = version; + return b.fmt( + \\return {{ + \\ {{"major", {}}}, + \\ {{"minor", {}}}, + \\ {{"patch", {}}}, + \\ {{"prerelease", {}}}, + \\ {{"api_level", {}}}, + \\ {{"api_compatible", {}}}, + \\ {{"api_prerelease", {}}}, + \\}} + , .{ v.major, v.minor, v.patch, v.prerelease.len > 0, v.api_level, v.api_level_compat, v.api_prerelease }); +} + +pub fn test_config(b: *std.Build, gen_dir: LazyPath) ![]u8 { + var buf: [std.fs.max_path_bytes]u8 = undefined; + const src_path = try b.build_root.handle.realpath(".", &buf); + + // we don't use test/cmakeconfig/paths.lua.in because it contains cmake specific logic + return b.fmt( + \\local M = {{}} + \\ + \\M.include_paths = {{}} + \\M.translations_enabled = "$ENABLE_TRANSLATIONS" == "ON" + \\M.is_asan = "$ENABLE_ASAN_UBSAN" == "ON" + \\M.is_zig_build = true + \\M.vterm_test_file = "test/vterm_test_output" + \\M.test_build_dir = "{[bin_dir]s}" -- bull + \\M.test_source_path = "{[src_path]s}" + \\M.test_lua_prg = "" + \\M.test_luajit_prg = "" + \\table.insert(M.include_paths, "{[gen_dir]}/include") + \\table.insert(M.include_paths, "{[gen_dir]}/src/nvim/auto") + \\ + \\return M + , .{ .bin_dir = b.install_path, .src_path = src_path, .gen_dir = gen_dir }); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000000..e167cd7d2b --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,65 @@ +.{ + .name = .neovim, + .fingerprint = 0x66eb090879307a38, + .version = "0.12.0", + .minimum_zig_version = "0.14.0", + + .dependencies = .{ + .lua_wrapper = .{ + .url = "git+https://github.com/natecraddock/ziglua#7bfb3c2b87220cdc89ef01cc99a200dad7a28e50", + .hash = "lua_wrapper-0.1.0-OyMC27fOBAAU3E2ueB-EWGSgsuCFQZL83pT0nQJ1ufOI", + }, + .lpeg = .{ + .url = "https://github.com/neovim/deps/raw/d495ee6f79e7962a53ad79670cb92488abe0b9b4/opt/lpeg-1.1.0.tar.gz", + .hash = "122084badadeb91106dd06b2055119a944c340563536caefd8e22d4064182f7cd6e6", + }, + .libluv = .{ + .url = "https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz", + .hash = "122050b45fc1b2d1e72d30d3e4f446735ab95bbb88658bc1de736e5dc71c3e3cd767", + }, + .lua_compat53 = .{ + .url = "https://github.com/lunarmodules/lua-compat-5.3/archive/v0.13.tar.gz", + .hash = "1220e75685f00c242fbf6c1c60e98dd1b24ba99e38277970a10636e44ed08cf2af8a", + }, + .treesitter = .{ + .url = "git+https://github.com/tree-sitter/tree-sitter?ref=v0.25.3#dcdd5bc372dae42a14deafedba826d91f02a1ab0", + .hash = "tree_sitter-0.26.0-Tw2sR3K4CwA_GbHqW5STjLdERuYa_LBew1nhD3WG-vSw", + }, + .libuv = .{ .path = "./deps/libuv" }, + .utf8proc = .{ .path = "./deps/utf8proc/" }, + .unibilium = .{ .path = "./deps/unibilium/" }, + .iconv_apple = .{ .path = "./deps/iconv_apple/", .lazy = true }, + .lua_dev_deps = .{ + .url = "https://github.com/neovim/deps/raw/06ef2b58b0876f8de1a3f5a710473dcd7afff251/opt/lua-dev-deps.tar.gz", + .hash = "N-V-__8AAGevEQCHAkCozca5AIdN9DFc3Luf3g3r2AcbyOrm", + }, + .treesitter_c = .{ + .url = "https://github.com/tree-sitter/tree-sitter-c/archive/v0.23.4.tar.gz", + .hash = "N-V-__8AAECfSADUxb_Nvy_jsHRlD1-CdzGh4dbf2KCtv7v0", + }, + .treesitter_markdown = .{ + .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-markdown?ref=v0.4.1#413285231ce8fa8b11e7074bbe265b48aa7277f9", + .hash = "N-V-__8AAMgXUwC4v9_Fveeo7cvY1R3gEK58Pc4vW3gFiEaL", + }, + .treesitter_lua = .{ + .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-lua?ref=v0.3.0#db16e76558122e834ee214c8dc755b4a3edc82a9", + .hash = "N-V-__8AALBVCABzNpJmVFujBmfpbSS_5V-I8aZS6LaTtWWi", + }, + .treesitter_vim = .{ + .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-vim?ref=v0.5.0#0f31cb98e5c0cb3707e097bf95a04c0c2aac573d", + .hash = "N-V-__8AAAFtUgDN41WOv5Ch59n-6WdlCnD97F3jwboANSLU", + }, + .treesitter_vimdoc = .{ + .url = "git+https://github.com/neovim/tree-sitter-vimdoc?ref=v3.0.1#2694c3d27e2ca98a0ccde72f33887394300d524e", + .hash = "N-V-__8AAMADCgDGXU8mbjxUeAaHS-U5LXULALYNNvUslu9N", + }, + .treesitter_query = .{ + .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-query?ref=v0.5.1#930202c2a80965a7a9ca018b5b2a08b25dfa7f12", + .hash = "N-V-__8AAPx5BACi1uejjgFb72DsH6Y0_Z2A90uugFMKAsnj", + }, + }, + .paths = .{ + // TODO(bfredl): explicitly list the subdirs which actually are used + "", + }, +} diff --git a/cmake.config/versiondef.h.in b/cmake.config/versiondef.h.in index 9771e0d148..ccb74b4151 100644 --- a/cmake.config/versiondef.h.in +++ b/cmake.config/versiondef.h.in @@ -12,6 +12,6 @@ #endif #define NVIM_VERSION_CFLAGS "${VERSION_STRING}" -#define NVIM_VERSION_BUILD_TYPE "$" +#define NVIM_VERSION_BUILD_TYPE "${CONFIG}" #endif // AUTO_VERSIONDEF_H diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index ae93f3e158..fc4c2c587a 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -68,7 +68,7 @@ endif() execute_process( # Note: because of "-ll" (low-level interpreter mode), some modules like # _editor.lua are not loaded. - COMMAND ${NVIM_PRG} -ll ${WORKING_DIR}/test/lua_runner.lua ${DEPS_INSTALL_DIR} busted -v -o test.busted.outputHandlers.nvim + COMMAND ${NVIM_PRG} -ll ${WORKING_DIR}/test/lua_runner.lua ${DEPS_INSTALL_DIR}/share/lua/5.1/ busted -v -o test.busted.outputHandlers.nvim --lazy --helper=${TEST_DIR}/${TEST_TYPE}/preload.lua --lpath=${BUILD_DIR}/?.lua --lpath=${WORKING_DIR}/src/?.lua diff --git a/deps/iconv_apple/build.zig b/deps/iconv_apple/build.zig new file mode 100644 index 0000000000..b527cc39f3 --- /dev/null +++ b/deps/iconv_apple/build.zig @@ -0,0 +1,90 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const upstream = b.dependency("libiconv", .{}); + const lib = b.addStaticLibrary(.{ + .name = "iconv", + .target = target, + .optimize = optimize, + }); + + lib.addIncludePath(b.path("include/")); + lib.addIncludePath(upstream.path("")); + lib.addIncludePath(upstream.path("citrus/")); + lib.addIncludePath(upstream.path("libcharset/")); + lib.addIncludePath(upstream.path("libiconv_modules/UTF8/")); + // zig any-macos-any headers already includes iconv, it just cannot link without a SDK + // lib.installHeader(upstream.path("iconv.h"), "iconv.h"); + + lib.linkLibC(); + + lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ + "citrus/bsd_iconv.c", + "citrus/citrus_bcs.c", + "citrus/citrus_bcs_strtol.c", + "citrus/citrus_bcs_strtoul.c", + "citrus/citrus_csmapper.c", + "citrus/citrus_db.c", + "citrus/citrus_db_factory.c", + "citrus/citrus_db_hash.c", + "citrus/citrus_esdb.c", + "citrus/citrus_hash.c", + "citrus/citrus_iconv.c", + "citrus/citrus_lookup.c", + "citrus/citrus_lookup_factory.c", + "citrus/citrus_mapper.c", + "citrus/citrus_memstream.c", + "citrus/citrus_mmap.c", + "citrus/citrus_module.c", + "citrus/citrus_none.c", + "citrus/citrus_pivot_factory.c", + "citrus/citrus_prop.c", + "citrus/citrus_stdenc.c", + "citrus/__iconv.c", + "citrus/iconv.c", + "citrus/iconv_canonicalize.c", + "citrus/iconv_close.c", + "citrus/iconv_compat.c", + "citrus/iconvctl.c", + "citrus/__iconv_free_list.c", + "citrus/__iconv_get_list.c", + "citrus/iconvlist.c", + "citrus/iconv_open.c", + "citrus/iconv_open_into.c", + "citrus/iconv_set_relocation_prefix.c", + "libcharset/libcharset.c", + "libiconv_modules/BIG5/citrus_big5.c", + "libiconv_modules/DECHanyu/citrus_dechanyu.c", + "libiconv_modules/DECKanji/citrus_deckanji.c", + "libiconv_modules/EUC/citrus_euc.c", + "libiconv_modules/EUCTW/citrus_euctw.c", + "libiconv_modules/GBK2K/citrus_gbk2k.c", + "libiconv_modules/HZ/citrus_hz.c", + "libiconv_modules/iconv_none/citrus_iconv_none.c", + "libiconv_modules/iconv_std/citrus_iconv_std.c", + "libiconv_modules/ISO2022/citrus_iso2022.c", + "libiconv_modules/JOHAB/citrus_johab.c", + "libiconv_modules/mapper_646/citrus_mapper_646.c", + "libiconv_modules/mapper_none/citrus_mapper_none.c", + "libiconv_modules/mapper_serial/citrus_mapper_serial.c", + "libiconv_modules/mapper_std/citrus_mapper_std.c", + "libiconv_modules/mapper_zone/citrus_mapper_zone.c", + "libiconv_modules/MSKanji/citrus_mskanji.c", + "libiconv_modules/UES/citrus_ues.c", + "libiconv_modules/UTF1632/citrus_utf1632.c", + "libiconv_modules/UTF7/citrus_utf7.c", + "libiconv_modules/UTF8/citrus_utf8.c", + "libiconv_modules/UTF8MAC/citrus_utf8mac.c", + "libiconv_modules/VIQR/citrus_viqr.c", + "libiconv_modules/ZW/citrus_zw.c", + }, .flags = &.{ + "-D_PATH_I18NMODULE=\"/usr/lib/i18n\"", + "-D_PATH_ESDB=\"/usr/share/i18n/esdb\"", + "-D_PATH_CSMAPPER=\"/usr/share/i18n/csmapper\"", + } }); + + b.installArtifact(lib); +} diff --git a/deps/iconv_apple/build.zig.zon b/deps/iconv_apple/build.zig.zon new file mode 100644 index 0000000000..19a3797387 --- /dev/null +++ b/deps/iconv_apple/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "libiconv", + .version = "107.0.0", + .paths = .{""}, + + .dependencies = .{ + .libiconv = .{ + .url = "git+https://github.com/apple-oss-distributions/libiconv?ref=libiconv-107#a3f3b2c76dbf8ba11881debc6bcb4e309958d252", + .hash = "12202adcca76e9822f506b5acd2f0a3e285c152e2f48683341fd719d9f1df3a1296b", + }, + }, +} diff --git a/deps/iconv_apple/include/os/variant_private.h b/deps/iconv_apple/include/os/variant_private.h new file mode 100644 index 0000000000..d3e074337d --- /dev/null +++ b/deps/iconv_apple/include/os/variant_private.h @@ -0,0 +1 @@ +#define os_variant_has_internal_content(sys) false diff --git a/deps/libuv/build.zig b/deps/libuv/build.zig new file mode 100644 index 0000000000..81014684d3 --- /dev/null +++ b/deps/libuv/build.zig @@ -0,0 +1,125 @@ +const std = @import("std"); + +// Based on mitchellh/zig-libuv, with changes. +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const upstream = b.dependency("libuv", .{}); + const lib = b.addStaticLibrary(.{ + .name = "uv", + .target = target, + .optimize = optimize, + }); + + lib.addIncludePath(upstream.path("include")); + lib.addIncludePath(upstream.path("src")); + + lib.installHeadersDirectory(upstream.path("include"), ".", .{}); + + if (target.result.os.tag == .windows) { + lib.linkSystemLibrary("psapi"); + lib.linkSystemLibrary("user32"); + lib.linkSystemLibrary("advapi32"); + lib.linkSystemLibrary("iphlpapi"); + lib.linkSystemLibrary("userenv"); + lib.linkSystemLibrary("ws2_32"); + } + if (target.result.os.tag == .linux) { + lib.linkSystemLibrary("pthread"); + } + lib.linkLibC(); + + if (target.result.os.tag != .windows) { + lib.root_module.addCMacro("FILE_OFFSET_BITS", "64"); + lib.root_module.addCMacro("_LARGEFILE_SOURCE", ""); + } + + if (target.result.os.tag == .linux) { + lib.root_module.addCMacro("_GNU_SOURCE", ""); + lib.root_module.addCMacro("_POSIX_C_SOURCE", "200112"); + } + + if (target.result.os.tag.isDarwin()) { + lib.root_module.addCMacro("_DARWIN_UNLIMITED_SELECT", "1"); + lib.root_module.addCMacro("_DARWIN_USE_64_BIT_INODE", "1"); + } + + const root = upstream.path(""); + + // C files common to all platforms + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/fs-poll.c", + "src/idna.c", + "src/inet.c", + "src/random.c", + "src/strscpy.c", + "src/strtok.c", + "src/threadpool.c", + "src/timer.c", + "src/uv-common.c", + "src/uv-data-getter-setters.c", + "src/version.c", + } }); + + if (target.result.os.tag != .windows) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/async.c", + "src/unix/core.c", + "src/unix/dl.c", + "src/unix/fs.c", + "src/unix/getaddrinfo.c", + "src/unix/getnameinfo.c", + "src/unix/loop-watcher.c", + "src/unix/loop.c", + "src/unix/pipe.c", + "src/unix/poll.c", + "src/unix/process.c", + "src/unix/random-devurandom.c", + "src/unix/signal.c", + "src/unix/stream.c", + "src/unix/tcp.c", + "src/unix/thread.c", + "src/unix/tty.c", + "src/unix/udp.c", + } }); + } + + if (target.result.os.tag == .linux or target.result.os.tag.isDarwin()) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/proctitle.c", + } }); + } + + if (target.result.os.tag == .linux) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/linux.c", + "src/unix/procfs-exepath.c", + "src/unix/random-getrandom.c", + "src/unix/random-sysctl-linux.c", + } }); + } + + if (target.result.os.tag.isBSD()) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/bsd-ifaddrs.c", + "src/unix/kqueue.c", + } }); + } + + if (target.result.os.tag.isDarwin() or target.result.os.tag == .openbsd) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/random-getentropy.c", + } }); + } + + if (target.result.os.tag.isDarwin()) { + lib.addCSourceFiles(.{ .root = root, .files = &.{ + "src/unix/darwin-proctitle.c", + "src/unix/darwin.c", + "src/unix/fsevents.c", + } }); + } + + b.installArtifact(lib); +} diff --git a/deps/libuv/build.zig.zon b/deps/libuv/build.zig.zon new file mode 100644 index 0000000000..06bd25a5db --- /dev/null +++ b/deps/libuv/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "libuv", + .version = "1.51.0", + .paths = .{""}, + + .dependencies = .{ + .libuv = .{ + .url = "git+https://github.com/libuv/libuv?ref=v1.51.0#76fb3b73da3f8ddaeeb87d23fda04b9bda219f5e", + .hash = "N-V-__8AAExNRADXPh6GLMmWlqC2EVkp6hzH9wPuzjh_eSkE", + }, + }, +} diff --git a/deps/unibilium/build.zig b/deps/unibilium/build.zig new file mode 100644 index 0000000000..d7f7ca91a7 --- /dev/null +++ b/deps/unibilium/build.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const upstream = b.dependency("unibilium", .{}); + const lib = b.addStaticLibrary(.{ + .name = "unibilium", + .target = target, + .optimize = optimize, + }); + + lib.addIncludePath(upstream.path("")); + + lib.installHeader(upstream.path("unibilium.h"), "unibilium.h"); + + lib.linkLibC(); + + 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 new file mode 100644 index 0000000000..7e7aec4c34 --- /dev/null +++ b/deps/unibilium/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "unibilium", + .version = "2.1.2", + .paths = .{""}, + + .dependencies = .{ + .unibilium = .{ + .url = "git+https://github.com/neovim/unibilium?ref=v2.1.2#bfcb0350129dd76893bc90399cf37c45812268a2", + .hash = "1220a082fc7bdf2a6d9c19576c49bb4e33923ba54622a7340af611e9deb46f851f9a", + }, + }, +} diff --git a/deps/utf8proc/build.zig b/deps/utf8proc/build.zig new file mode 100644 index 0000000000..ed2b4c3eda --- /dev/null +++ b/deps/utf8proc/build.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const upstream = b.dependency("utf8proc", .{}); + const lib = b.addStaticLibrary(.{ + .name = "utf8proc", + .target = target, + .optimize = optimize, + }); + + lib.addIncludePath(upstream.path("")); + lib.installHeader(upstream.path("utf8proc.h"), "utf8proc.h"); + + lib.linkLibC(); + + lib.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ + "utf8proc.c", + } }); + + b.installArtifact(lib); +} diff --git a/deps/utf8proc/build.zig.zon b/deps/utf8proc/build.zig.zon new file mode 100644 index 0000000000..69f2db7a91 --- /dev/null +++ b/deps/utf8proc/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "utf8proc", + .version = "2.10.0", + .paths = .{""}, + + .dependencies = .{ + .utf8proc = .{ + .url = "git+https://github.com/JuliaStrings/utf8proc?ref=v2.10.0#a1b99daa2a3393884220264c927a48ba1251a9c6", + .hash = "1220d80ce0bde71b8acae76b9686fa785aa0cdf165f0d6e93a853d57bfbed129a5e7", + }, + }, +} diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index d103b5f4d7..fdd846561e 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -12,12 +12,17 @@ get_directory_property(LUA_GEN_DEPS DIRECTORY ${PROJECT_SOURCE_DIR}/src/nvim DEF add_custom_command(OUTPUT ${GENERATED_SYN_VIM} COMMAND ${LUA_GEN} ${SYN_VIM_GENERATOR} ${GENERATED_SYN_VIM} ${FUNCS_DATA} + ${PROJECT_SOURCE_DIR}/src/nvim/options.lua + ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua + ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua + ${PROJECT_SOURCE_DIR}/src/nvim/vvars.lua DEPENDS ${LUA_GEN_DEPS} ${SYN_VIM_GENERATOR} ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua ${PROJECT_SOURCE_DIR}/src/nvim/options.lua + ${PROJECT_SOURCE_DIR}/src/nvim/vvars.lua ${PROJECT_SOURCE_DIR}/src/nvim/eval.c ${FUNCS_DATA} ) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index a69520b8e2..bc42829012 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -107,6 +107,12 @@ API `max_height` is reached, and returns the `end_row` and `end_vcol` for which `max_height` or the calculated height is reached. +BUILD + +• A Zig-based build system has been added as an alternative to CMake. It is + currently limited in functionality, and CMake remains the recommended option + for the time being. + DEFAULTS • 'statusline' default is exposed as a statusline expression (previously it diff --git a/runtime/embedded_data.zig b/runtime/embedded_data.zig new file mode 100644 index 0000000000..5872a20faf --- /dev/null +++ b/runtime/embedded_data.zig @@ -0,0 +1,3 @@ +pub const inspect_module = @embedFile("./lua/vim/inspect.lua"); +pub const shared_module = @embedFile("./lua/vim/shared.lua"); +pub const iter_module = @embedFile("./lua/vim/iter.lua"); diff --git a/runtime/gen_runtime.zig b/runtime/gen_runtime.zig new file mode 100644 index 0000000000..1bf7bfde38 --- /dev/null +++ b/runtime/gen_runtime.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; + +pub const SourceItem = struct { name: []u8, api_export: bool }; + +pub fn nvim_gen_runtime( + b: *std.Build, + nlua0: *std.Build.Step.Compile, + nvim_bin: *std.Build.Step.Compile, + funcs_data: LazyPath, +) !*std.Build.Step.WriteFile { + const gen_runtime = b.addWriteFiles(); + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_vimvim.lua")); + const file = gen_step.addOutputFileArg("generated.vim"); + _ = gen_runtime.addCopyFile(file, "syntax/vim/generated.vim"); + gen_step.addFileArg(funcs_data); + gen_step.addFileArg(b.path("src/nvim/options.lua")); + gen_step.addFileArg(b.path("src/nvim/auevents.lua")); + gen_step.addFileArg(b.path("src/nvim/ex_cmds.lua")); + gen_step.addFileArg(b.path("src/nvim/vvars.lua")); + } + + { + const install_doc_files = b.addInstallDirectory(.{ .source_dir = b.path("runtime/doc"), .install_dir = .prefix, .install_subdir = "runtime/doc" }); + const gen_step = b.addRunArtifact(nvim_bin); + gen_step.step.dependOn(&install_doc_files.step); + gen_step.addArgs(&.{ "-u", "NONE", "-i", "NONE", "-e", "--headless", "-c", "helptags ++t doc", "-c", "quit" }); + // TODO(bfredl): ugly on purpose. nvim should be able to generate "tags" at a specificed destination + const install_path: std.Build.LazyPath = .{ .cwd_relative = b.install_path }; + gen_step.setCwd(install_path.path(b, "runtime/")); + + gen_runtime.step.dependOn(&gen_step.step); + } + + return gen_runtime; +} diff --git a/src/build_lua.zig b/src/build_lua.zig new file mode 100644 index 0000000000..be3c709aca --- /dev/null +++ b/src/build_lua.zig @@ -0,0 +1,138 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; + +pub fn build_nlua0( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + use_luajit: bool, + ziglua: *std.Build.Dependency, + lpeg: *std.Build.Dependency, +) *std.Build.Step.Compile { + const options = b.addOptions(); + options.addOption(bool, "use_luajit", use_luajit); + + const nlua0_exe = b.addExecutable(.{ + .name = "nlua0", + .root_source_file = b.path("src/nlua0.zig"), + .target = target, + .optimize = optimize, + }); + const nlua0_mod = nlua0_exe.root_module; + + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/nlua0.zig"), + .target = target, + .optimize = optimize, + }); + + const embedded_data = b.addModule("embedded_data", .{ + .root_source_file = b.path("runtime/embedded_data.zig"), + }); + + for ([2]*std.Build.Module{ nlua0_mod, exe_unit_tests.root_module }) |mod| { + mod.addImport("ziglua", ziglua.module("lua_wrapper")); + mod.addImport("embedded_data", embedded_data); + // addImport already links by itself. but we need headers as well.. + mod.linkLibrary(ziglua.artifact("lua")); + + mod.addOptions("options", options); + + mod.addIncludePath(b.path("src")); + mod.addIncludePath(b.path("src/includes_fixmelater")); + add_lua_modules(mod, lpeg, use_luajit, true); + } + + // for debugging the nlua0 environment + // like this: `zig build nlua0 -- script.lua {args}` + const run_cmd = b.addRunArtifact(nlua0_exe); + if (b.args) |args| { + run_cmd.addArgs(args); + } + const run_step = b.step("nlua0", "Run nlua0 build tool"); + run_step.dependOn(&run_cmd.step); + + b.installArtifact(nlua0_exe); // DEBUG + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + const test_step = b.step("test_nlua0", "Run unit tests for nlua0"); + test_step.dependOn(&run_exe_unit_tests.step); + + return nlua0_exe; +} + +pub fn add_lua_modules(mod: *std.Build.Module, lpeg: *std.Build.Dependency, use_luajit: bool, is_nlua0: bool) void { + const flags = [_][]const u8{ + // Standard version used in Lua Makefile + "-std=gnu99", + if (is_nlua0) "-DNVIM_NLUA0" else "", + }; + + mod.addIncludePath(lpeg.path("")); + mod.addCSourceFiles(.{ + .files = &.{ + "src/mpack/lmpack.c", + "src/mpack/mpack_core.c", + "src/mpack/object.c", + "src/mpack/conv.c", + "src/mpack/rpc.c", + }, + .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 (!use_luajit) { + mod.addCSourceFiles(.{ + .files = &.{ + "src/bit.c", + }, + .flags = &flags, + }); + } +} + +pub fn build_libluv( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + lua: *std.Build.Step.Compile, + libuv: *std.Build.Step.Compile, +) !*std.Build.Step.Compile { + const upstream = b.dependency("libluv", .{}); + const compat53 = b.dependency("lua_compat53", .{}); + const lib = b.addStaticLibrary(.{ + .name = "luv", + .target = target, + .optimize = optimize, + }); + + lib.linkLibrary(lua); + 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", + } }); + + return lib; +} diff --git a/src/gen/gen_api_dispatch.lua b/src/gen/gen_api_dispatch.lua index b5433527bc..6480e66489 100644 --- a/src/gen/gen_api_dispatch.lua +++ b/src/gen/gen_api_dispatch.lua @@ -11,8 +11,6 @@ local mpack = vim.mpack local hashy = require 'gen.hashy' -local pre_args = 7 -assert(#arg >= pre_args) -- output h file with generated dispatch functions (dispatch_wrappers.generated.h) local dispatch_outputf = arg[1] -- output h file with packed metadata (api_metadata.generated.h) @@ -23,6 +21,11 @@ local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c local keysets_outputf = arg[5] -- keysets_defs.generated.h local ui_metadata_inputf = arg[6] -- ui events metadata local git_version_inputf = arg[7] -- git version header +local nvim_version_inputf = arg[8] -- nvim version +local dump_bin_array_inputf = arg[9] +local dispatch_deprecated_inputf = arg[10] +local pre_args = 10 +assert(#arg >= pre_args) local functions = {} @@ -152,7 +155,7 @@ end -- Export functions under older deprecated names. -- These will be removed eventually. -local deprecated_aliases = require('nvim.api.dispatch_deprecated') +local deprecated_aliases = loadfile(dispatch_deprecated_inputf)() for _, f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, 'nvim_') then @@ -244,7 +247,7 @@ for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do table.insert(ui_options, x) end -local version = require 'nvim_version' -- `build/nvim_version.lua` file. +local version = loadfile(nvim_version_inputf)() local git_version = io.open(git_version_inputf):read '*a' local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL @@ -302,7 +305,7 @@ for i, item in ipairs(types) do end local packed = table.concat(pieces) -local dump_bin_array = require('gen.dump_bin_array') +local dump_bin_array = loadfile(dump_bin_array_inputf)() dump_bin_array(api_metadata_output, 'packed_api_metadata', packed) api_metadata_output:close() diff --git a/src/gen/gen_eval.lua b/src/gen/gen_eval.lua index 2f2d09485b..66c2a7c015 100644 --- a/src/gen/gen_eval.lua +++ b/src/gen/gen_eval.lua @@ -1,10 +1,9 @@ local mpack = vim.mpack -local autodir = arg[1] +local funcsfname = arg[1] local metadata_file = arg[2] local funcs_file = arg[3] - -local funcsfname = autodir .. '/funcs.generated.h' +local eval_file = arg[4] --Will generate funcs.generated.h with definition of functions static const array. @@ -48,7 +47,7 @@ hashpipe:write([[ ]]) -local funcs = require('nvim.eval').funcs +local funcs = loadfile(eval_file)().funcs for _, func in pairs(funcs) do if func.float_func then func.func = 'float_op_wrapper' diff --git a/src/gen/gen_eval_files.lua b/src/gen/gen_eval_files.lua index 4bf648f934..cfa4d096a3 100755 --- a/src/gen/gen_eval_files.lua +++ b/src/gen/gen_eval_files.lua @@ -5,7 +5,7 @@ local util = require('gen.util') local fmt = string.format -local DEP_API_METADATA = 'build/funcs_metadata.mpack' +local DEP_API_METADATA = arg[1] local TEXT_WIDTH = 78 --- @class vim.api.metadata diff --git a/src/gen/gen_events.lua b/src/gen/gen_events.lua index c6d97405af..76072c59d4 100644 --- a/src/gen/gen_events.lua +++ b/src/gen/gen_events.lua @@ -1,8 +1,9 @@ local fileio_enum_file = arg[1] local names_file = arg[2] +local auevents_file = arg[3] local hashy = require('gen.hashy') -local auevents = require('nvim.auevents') +local auevents = loadfile(auevents_file)() local events = auevents.events local aliases = auevents.aliases diff --git a/src/gen/gen_ex_cmds.lua b/src/gen/gen_ex_cmds.lua index 6c03e8fc4d..3662cb149b 100644 --- a/src/gen/gen_ex_cmds.lua +++ b/src/gen/gen_ex_cmds.lua @@ -1,17 +1,15 @@ -local includedir = arg[1] -local autodir = arg[2] - -- Will generate files ex_cmds_enum.generated.h with cmdidx_T enum -- and ex_cmds_defs.generated.h with main Ex commands definitions. -local enumfname = includedir .. '/ex_cmds_enum.generated.h' -local defsfname = autodir .. '/ex_cmds_defs.generated.h' +local enumfname = arg[1] -- '/ex_cmds_enum.generated.h' +local defsfname = arg[2] -- '/ex_cmds_defs.generated.h' +local ex_cmds_name = arg[3] -- 'ex_cmds.lua' local enumfile = io.open(enumfname, 'w') local defsfile = io.open(defsfname, 'w') local bit = require 'bit' -local ex_cmds = require('nvim.ex_cmds') +local ex_cmds = loadfile(ex_cmds_name)() local defs = ex_cmds.cmds local flags = ex_cmds.flags diff --git a/src/gen/gen_keycodes.lua b/src/gen/gen_keycodes.lua index 584f0a3d36..98b714dffe 100644 --- a/src/gen/gen_keycodes.lua +++ b/src/gen/gen_keycodes.lua @@ -1,7 +1,8 @@ local names_file = arg[1] +local keycodes_file = arg[2] local hashy = require('gen.hashy') -local keycodes = require('nvim.keycodes') +local keycodes = loadfile(keycodes_file)() local keycode_names = keycodes.names diff --git a/src/gen/gen_options.lua b/src/gen/gen_options.lua index 1947297a0e..9c88f96ae2 100644 --- a/src/gen/gen_options.lua +++ b/src/gen/gen_options.lua @@ -1,5 +1,7 @@ +local options_input_file = arg[5] + --- @module 'nvim.options' -local options = require('nvim.options') +local options = loadfile(options_input_file)() local options_meta = options.options local cstr = options.cstr local valid_scopes = options.valid_scopes diff --git a/src/gen/gen_steps.zig b/src/gen/gen_steps.zig new file mode 100644 index 0000000000..563a4418df --- /dev/null +++ b/src/gen/gen_steps.zig @@ -0,0 +1,237 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; + +pub const SourceItem = struct { name: []u8, api_export: bool }; + +pub fn nvim_gen_sources( + b: *std.Build, + nlua0: *std.Build.Step.Compile, + nvim_sources: *std.ArrayList(SourceItem), + nvim_headers: *std.ArrayList([]u8), + api_headers: *std.ArrayList(LazyPath), + include_path: []const LazyPath, + target: std.Build.ResolvedTarget, + versiondef_git: LazyPath, + version_lua: LazyPath, +) !struct { *std.Build.Step.WriteFile, LazyPath } { + const gen_headers = b.addWriteFiles(); + + for (nvim_sources.items) |s| { + const api_export = if (s.api_export) api_headers else null; + const input_file = b.path(b.fmt("src/nvim/{s}", .{s.name})); + _ = try generate_header_for(b, s.name, input_file, api_export, nlua0, include_path, target, gen_headers, false); + } + + for (nvim_headers.items) |s| { + const input_file = b.path(b.fmt("src/nvim/{s}", .{s})); + _ = try generate_header_for(b, s, input_file, null, nlua0, include_path, target, gen_headers, true); + } + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_ex_cmds.lua")); + _ = gen_header(b, gen_step, "ex_cmds_enum.generated.h", gen_headers); + _ = gen_header(b, gen_step, "ex_cmds_defs.generated.h", gen_headers); + gen_step.addFileArg(b.path("src/nvim/ex_cmds.lua")); + } + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_options.lua")); + _ = gen_header(b, gen_step, "options.generated.h", gen_headers); + _ = gen_header(b, gen_step, "options_enum.generated.h", gen_headers); + _ = gen_header(b, gen_step, "options_map.generated.h", gen_headers); + _ = gen_header(b, gen_step, "option_vars.generated.h", gen_headers); + gen_step.addFileArg(b.path("src/nvim/options.lua")); + + const test_gen_step = b.step("wipopt", "debug one nlua0 (options)"); + test_gen_step.dependOn(&gen_step.step); + } + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_events.lua")); + _ = gen_header(b, gen_step, "auevents_enum.generated.h", gen_headers); + _ = gen_header(b, gen_step, "auevents_name_map.generated.h", gen_headers); + gen_step.addFileArg(b.path("src/nvim/auevents.lua")); + } + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_keycodes.lua")); + _ = gen_header(b, gen_step, "keycode_names.generated.h", gen_headers); + gen_step.addFileArg(b.path("src/nvim/keycodes.lua")); + } + + { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_char_blob.lua")); + // TODO(bfredl): LUAC_PRG is missing. tricky with cross-compiling.. + // gen_step.addArg("-c"); + _ = gen_header(b, gen_step, "lua/vim_module.generated.h", gen_headers); + // NB: vim._init_packages and vim.inspect must be be first and second ones + // respectively, otherwise --luamod-dev won't work properly. + const names = [_][]const u8{ + "_init_packages", + "inspect", + "_editor", + "filetype", + "fs", + "F", + "keymap", + "loader", + "_defaults", + "_options", + "shared", + }; + for (names) |n| { + gen_step.addFileArg(b.path(b.fmt("runtime/lua/vim/{s}.lua", .{n}))); + gen_step.addArg(b.fmt("vim.{s}", .{n})); + } + } + + const ui_metadata = ui_step: { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_api_ui_events.lua")); + gen_step.addFileArg(b.path("src/nvim/api/ui_events.in.h")); + _ = try gen_header_with_header(b, gen_step, "ui_events_call.generated.h", nlua0, include_path, target, gen_headers); + _ = try gen_header_with_header(b, gen_step, "ui_events_remote.generated.h", nlua0, include_path, target, gen_headers); + const ui_metadata = gen_step.addOutputFileArg("ui_metadata.mpack"); + _ = try gen_header_with_header(b, gen_step, "ui_events_client.generated.h", nlua0, include_path, target, gen_headers); + break :ui_step ui_metadata; + }; + + const funcs_metadata = api_step: { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_api_dispatch.lua")); + _ = try gen_header_with_header(b, gen_step, "api/private/dispatch_wrappers.generated.h", nlua0, include_path, target, gen_headers); + _ = gen_header(b, gen_step, "api/private/api_metadata.generated.h", gen_headers); + const funcs_metadata = gen_step.addOutputFileArg("funcs_metadata.mpack"); + _ = gen_header(b, gen_step, "lua_api_c_bindings.generated.h", gen_headers); + _ = gen_header(b, gen_step, "keysets_defs.generated.h", gen_headers); + gen_step.addFileArg(ui_metadata); + gen_step.addFileArg(versiondef_git); + gen_step.addFileArg(version_lua); + gen_step.addFileArg(b.path("src/gen/dump_bin_array.lua")); + gen_step.addFileArg(b.path("src/nvim/api/dispatch_deprecated.lua")); + // now follows all .h files with exported functions + for (api_headers.items) |h| { + gen_step.addFileArg(h); + } + + break :api_step funcs_metadata; + }; + + const funcs_data = eval_step: { + const gen_step = b.addRunArtifact(nlua0); + gen_step.addFileArg(b.path("src/gen/gen_eval.lua")); + _ = gen_header(b, gen_step, "funcs.generated.h", gen_headers); + gen_step.addFileArg(funcs_metadata); + const funcs_data = gen_step.addOutputFileArg("funcs_data.mpack"); + gen_step.addFileArg(b.path("src/nvim/eval.lua")); + break :eval_step funcs_data; + }; + + return .{ gen_headers, funcs_data }; +} + +fn gen_header( + b: *std.Build, + gen_step: *std.Build.Step.Run, + name: []const u8, + gen_headers: *std.Build.Step.WriteFile, +) std.Build.LazyPath { + _ = b; + const header = gen_step.addOutputFileArg(name); + _ = gen_headers.addCopyFile(header, name); + return header; +} + +fn gen_header_with_header( + b: *std.Build, + gen_step: *std.Build.Step.Run, + name: []const u8, + nlua0: *std.Build.Step.Compile, + include_path: []const LazyPath, + target: ?std.Build.ResolvedTarget, + gen_headers: *std.Build.Step.WriteFile, +) !std.Build.LazyPath { + if (name.len < 12 or !std.mem.eql(u8, ".generated.h", name[name.len - 12 ..])) return error.InvalidBaseName; + const h = gen_header(b, gen_step, name, gen_headers); + _ = try generate_header_for(b, b.fmt("{s}.h", .{name[0 .. name.len - 12]}), h, null, nlua0, include_path, target, gen_headers, false); + return h; +} + +pub const PreprocessorOptions = struct { + include_dirs: []const LazyPath = &.{}, + c_macros: []const []const u8 = &.{}, + target: ?std.Build.ResolvedTarget = null, +}; + +fn run_preprocessor( + b: *std.Build, + src: LazyPath, + output_name: []const u8, + options: PreprocessorOptions, +) !LazyPath { + const run_step = std.Build.Step.Run.create(b, b.fmt("preprocess to get {s}", .{output_name})); + run_step.addArgs(&.{ b.graph.zig_exe, "cc", "-E" }); + run_step.addFileArg(src); + run_step.addArg("-o"); + const output = run_step.addOutputFileArg(output_name); + // upstream issue: include path logic for addCSourceFiles and TranslateC is _very_ different + for (options.include_dirs) |include_dir| { + run_step.addArg("-I"); + run_step.addDirectoryArg(include_dir); + } + for (options.c_macros) |c_macro| { + run_step.addArg(b.fmt("-D{s}", .{c_macro})); + } + if (options.target) |t| { + if (!t.query.isNative()) { + run_step.addArgs(&.{ + "-target", try t.query.zigTriple(b.allocator), + }); + } + } + run_step.addArgs(&.{ "-MMD", "-MF" }); + _ = run_step.addDepFileOutputArg(b.fmt("{s}.d", .{output_name})); + return output; +} + +fn generate_header_for( + b: *std.Build, + name: []const u8, + input_file: LazyPath, + api_export: ?*std.ArrayList(LazyPath), + nlua0: *std.Build.Step.Compile, + include_path: []const LazyPath, + target: ?std.Build.ResolvedTarget, + gen_headers: *std.Build.Step.WriteFile, + nvim_header: bool, +) !*std.Build.Step.Run { + if (name.len < 2 or !(std.mem.eql(u8, ".c", name[name.len - 2 ..]) or std.mem.eql(u8, ".h", name[name.len - 2 ..]))) return error.InvalidBaseName; + const basename = name[0 .. name.len - 2]; + const i_file = try run_preprocessor(b, input_file, b.fmt("{s}.i", .{basename}), .{ + .include_dirs = include_path, + .c_macros = &.{ "_GNU_SOURCE", "ZIG_BUILD" }, + .target = target, + }); + const run_step = b.addRunArtifact(nlua0); + run_step.addFileArg(b.path("src/gen/gen_declarations.lua")); + run_step.addFileArg(input_file); + const gen_name = b.fmt("{s}.{s}.generated.h", .{ basename, if (nvim_header) "h.inline" else "c" }); + _ = gen_header(b, run_step, gen_name, gen_headers); + if (nvim_header) { + run_step.addArg("SKIP"); + } else { + const h_file = gen_header(b, run_step, b.fmt("{s}.h.generated.h", .{basename}), gen_headers); + if (api_export) |api_files| { + try api_files.append(h_file); + } + } + + run_step.addFileArg(i_file); + run_step.addArg(gen_name); + return run_step; +} diff --git a/src/gen/gen_vimvim.lua b/src/gen/gen_vimvim.lua index 4cd88f0188..f2d3e18e72 100644 --- a/src/gen/gen_vimvim.lua +++ b/src/gen/gen_vimvim.lua @@ -2,6 +2,10 @@ local mpack = vim.mpack local syntax_file = arg[1] local funcs_file = arg[2] +local options_file = arg[3] +local auevents_file = arg[4] +local ex_cmds_file = arg[5] +local vvars_file = arg[6] local lld = {} local syn_fd = assert(io.open(syntax_file, 'w')) @@ -15,10 +19,10 @@ local function w(s) end end -local options = require('nvim.options') -local auevents = require('nvim.auevents') -local ex_cmds = require('nvim.ex_cmds') -local vvars = require('nvim.vvars') +local options = loadfile(options_file)() +local auevents = loadfile(auevents_file)() +local ex_cmds = loadfile(ex_cmds_file)() +local vvars = loadfile(vvars_file)() local function cmd_kw(prev_cmd, cmd) if not prev_cmd then diff --git a/src/nlua0.zig b/src/nlua0.zig new file mode 100644 index 0000000000..042ff01609 --- /dev/null +++ b/src/nlua0.zig @@ -0,0 +1,120 @@ +//! "nlua" is an abbreviation for nvim flavored lua, i e lua with the +//! extended standard library functionality added by nvim such as json, mpack +//! and libuv and a range of vim.* utility functions. +//! +//! nlua0 is an interpreter for the "bootstrap" lua code we need to run before +//! nvim can be built, in order to run lua scripts which process and generate +//! more .c code, which still need these extensions. +const std = @import("std"); +const ziglua = @import("ziglua"); +const options = @import("options"); + +const embedded_data = @import("embedded_data"); + +// these are common dependencies used by many generators +const hashy = @embedFile("gen/hashy.lua"); +const c_grammar = @embedFile("gen/c_grammar.lua"); + +const Lua = ziglua.Lua; + +extern "c" fn luaopen_mpack(ptr: *anyopaque) c_int; +extern "c" fn luaopen_lpeg(ptr: *anyopaque) c_int; +extern "c" fn luaopen_bit(ptr: *anyopaque) c_int; + +fn init() !*Lua { + // Initialize the Lua vm + var lua = try Lua.init(std.heap.c_allocator); + lua.openLibs(); + + // this sets _G.vim by itself, so we don't need to + try lua.loadBuffer(embedded_data.shared_module, "shared.lua"); + lua.call(.{ .results = 1 }); + + try lua.loadBuffer(embedded_data.inspect_module, "inspect.lua"); + lua.call(.{ .results = 1 }); + lua.setField(-2, "inspect"); + + try lua.loadBuffer(embedded_data.iter_module, "iter.lua"); + lua.call(.{ .results = 1 }); + lua.setField(-2, "iter"); + + _ = try lua.getGlobal("package"); + _ = lua.getField(-1, "preload"); + try lua.loadBuffer(hashy, "hashy.lua"); // [package, preload, hashy] + lua.setField(-2, "gen.hashy"); + try lua.loadBuffer(c_grammar, "c_grammar.lua"); // [package, preload, c_grammar] + lua.setField(-2, "gen.c_grammar"); + lua.pop(2); + + const retval = luaopen_mpack(lua); + if (retval != 1) return error.LoadError; + _ = lua.getField(-1, "NIL"); // [vim, mpack, NIL] + lua.setField(-3, "NIL"); // vim.NIL = mpack.NIL (wow BOB wow) + lua.setField(-2, "mpack"); + + const retval2 = luaopen_lpeg(lua); + if (retval2 != 1) return error.LoadError; + lua.setField(-3, "lpeg"); + + lua.pop(2); + + if (!options.use_luajit) { + lua.pop(luaopen_bit(lua)); + } + return lua; +} + +pub fn main() !void { + const argv = std.os.argv; + + const lua = try init(); + defer lua.deinit(); + + if (argv.len < 2) { + std.debug.print("USAGE: nlua0 script.lua args...\n\n", .{}); + return; + } + lua.createTable(@intCast(argv.len - 2), 1); + for (0.., argv[1..]) |i, arg| { + _ = lua.pushString(std.mem.span(arg)); + lua.rawSetIndex(-2, @intCast(i)); + } + lua.setGlobal("arg"); + + _ = try lua.getGlobal("debug"); + _ = lua.getField(-1, "traceback"); + try lua.loadFile(std.mem.span(argv[1])); + lua.protectedCall(.{ .msg_handler = -2 }) catch |e| { + if (e == error.LuaRuntime) { + const msg = try lua.toString(-1); + std.debug.print("{s}\n", .{msg}); + } + return e; + }; +} + +fn do_ret1(lua: *Lua, str: [:0]const u8) !void { + try lua.loadString(str); + try lua.protectedCall(.{ .results = 1 }); +} + +test "simple test" { + const lua = try init(); + defer lua.deinit(); + + try do_ret1(lua, "return vim.isarray({2,3})"); + try std.testing.expectEqual(true, lua.toBoolean(-1)); + lua.pop(1); + + try do_ret1(lua, "return vim.isarray({a=2,b=3})"); + try std.testing.expectEqual(false, lua.toBoolean(-1)); + lua.pop(1); + + try do_ret1(lua, "return vim.inspect(vim.mpack.decode('\\146\\42\\69'))"); + try std.testing.expectEqualStrings("{ 42, 69 }", try lua.toString(-1)); + lua.pop(1); + + try do_ret1(lua, "return require'bit'.band(7,12)"); + try std.testing.expectEqualStrings("4", try lua.toString(-1)); + lua.pop(1); +} diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2a60eefe7c..f6112a3c2a 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -595,8 +595,11 @@ add_custom_command( ${LUA_API_C_BINDINGS} ${GENERATED_KEYSETS_DEFS} ${UI_METADATA} - ${NVIM_VERSION_GIT_H} + ${NVIM_VERSION_GIT_H} ${NVIM_VERSION_LUA} + ${GENERATOR_DIR}/dump_bin_array.lua + ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ${API_HEADERS} + DEPENDS ${LUA_GEN_DEPS} ${API_HEADERS} @@ -604,6 +607,7 @@ add_custom_command( ${API_DISPATCH_GENERATOR} ${GENERATOR_C_GRAMMAR} ${GENERATOR_HASHY} + ${GENERATOR_DIR}/dump_bin_array.lua ${UI_METADATA} ${NVIM_VERSION_LUA} ${NVIM_VERSION_GIT_H} @@ -666,27 +670,27 @@ add_custom_command( ) add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} - COMMAND ${LUA_GEN} ${EX_CMDS_GENERATOR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} + COMMAND ${LUA_GEN} ${EX_CMDS_GENERATOR} ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua DEPENDS ${LUA_GEN_DEPS} ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua ) add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} - COMMAND ${LUA_GEN} ${FUNCS_GENERATOR} ${GENERATED_DIR} ${FUNCS_METADATA} ${FUNCS_DATA} + COMMAND ${LUA_GEN} ${FUNCS_GENERATOR} ${GENERATED_FUNCS} ${FUNCS_METADATA} ${FUNCS_DATA} ${CMAKE_CURRENT_LIST_DIR}/eval.lua DEPENDS ${LUA_GEN_DEPS} ${FUNCS_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${FUNCS_METADATA} ) add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} - COMMAND ${LUA_GEN} ${EVENTS_GENERATOR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + COMMAND ${LUA_GEN} ${EVENTS_GENERATOR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) add_custom_command(OUTPUT ${GENERATED_KEYCODE_NAMES} - COMMAND ${LUA_GEN} ${KEYCODES_GENERATOR} ${GENERATED_KEYCODE_NAMES} + COMMAND ${LUA_GEN} ${KEYCODES_GENERATOR} ${GENERATED_KEYCODE_NAMES} ${CMAKE_CURRENT_LIST_DIR}/keycodes.lua DEPENDS ${LUA_GEN_DEPS} ${KEYCODES_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/keycodes.lua ) add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS} - COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS} + COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS} ${CMAKE_CURRENT_LIST_DIR}/options.lua DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) @@ -967,7 +971,7 @@ add_target(doc-vim ) add_target(doc-eval - COMMAND ${NVIM_LUA} ${PROJECT_SOURCE_DIR}/src/gen/gen_eval_files.lua + COMMAND ${NVIM_LUA} ${PROJECT_SOURCE_DIR}/src/gen/gen_eval_files.lua ${FUNCS_METADATA} DEPENDS nvim ${FUNCS_METADATA} diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 292588ee64..98d99c1c95 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -661,7 +661,7 @@ static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, void nlua_push_String(lua_State *lstate, const String s, int flags) FUNC_ATTR_NONNULL_ALL { - lua_pushlstring(lstate, s.data, s.size); + lua_pushlstring(lstate, s.size ? s.data : "", s.size); } /// Convert given Integer to Lua number diff --git a/src/nvim/macros_defs.h b/src/nvim/macros_defs.h index 67da29031c..84417659e9 100644 --- a/src/nvim/macros_defs.h +++ b/src/nvim/macros_defs.h @@ -1,6 +1,8 @@ #pragma once -#include "auto/config.h" +#ifndef NVIM_NLUA0 +# include "auto/config.h" +#endif // EXTERN is only defined in main.c. That's where global variables are // actually defined and initialized. diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 6f6eba805a..47eeef702a 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -1,4 +1,4 @@ -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(ZIG_BUILD) # define Boolean CFBoolean // Avoid conflict with API's Boolean # define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo # include @@ -340,7 +340,7 @@ char *get_locales(expand_T *xp, int idx) void lang_init(void) { -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(ZIG_BUILD) if (!os_env_exists("LANG", true)) { char buf[50] = { 0 }; diff --git a/src/nvim/os/pty_proc_unix.c b/src/nvim/os/pty_proc_unix.c index 10a464bbc3..98d6f7a2f9 100644 --- a/src/nvim/os/pty_proc_unix.c +++ b/src/nvim/os/pty_proc_unix.c @@ -13,6 +13,9 @@ // forkpty is not in POSIX, so headers are platform-specific #if defined(__FreeBSD__) || defined(__DragonFly__) # include +// TODO(bfredl): this is avaliable on darwin, but there is an issue with cross-compile headers +#elif defined(__APPLE__) && !defined(HAVE_FORKPTY) +int forkpty(int *, char *, const struct termios *, const struct winsize *); #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include #elif defined(__sun) diff --git a/src/versiondef.h.in b/src/versiondef.h.in new file mode 100644 index 0000000000..32d9165244 --- /dev/null +++ b/src/versiondef.h.in @@ -0,0 +1,16 @@ +#ifndef AUTO_VERSIONDEF_H +#define AUTO_VERSIONDEF_H + +#define NVIM_VERSION_MAJOR @NVIM_VERSION_MAJOR@ +#define NVIM_VERSION_MINOR @NVIM_VERSION_MINOR@ +#define NVIM_VERSION_PATCH @NVIM_VERSION_PATCH@ +#define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" + +#ifndef NVIM_VERSION_MEDIUM +# include "auto/versiondef_git.h" +#endif + +#define NVIM_VERSION_CFLAGS "${VERSION_STRING}" +#define NVIM_VERSION_BUILD_TYPE "${CONFIG}" + +#endif // AUTO_VERSIONDEF_H diff --git a/test/cmakeconfig/paths.lua.in b/test/cmakeconfig/paths.lua.in index b6961a18c8..56d3257bcf 100644 --- a/test/cmakeconfig/paths.lua.in +++ b/test/cmakeconfig/paths.lua.in @@ -7,6 +7,7 @@ end M.translations_enabled = "${ENABLE_TRANSLATIONS}" == "ON" M.is_asan = "${ENABLE_ASAN_UBSAN}" == "ON" +M.is_zig_build = false M.vterm_test_file = "${VTERM_TEST_FILE}" M.test_build_dir = "${CMAKE_BINARY_DIR}" M.test_source_path = "${CMAKE_SOURCE_DIR}" diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 03253f2b47..be776220fc 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -4854,7 +4854,7 @@ describe('API', function() -- #20681 eq('Invalid command: "win_getid"', pcall_err(api.nvim_cmd, { cmd = 'win_getid' }, {})) eq('Invalid command: "echo "hi""', pcall_err(api.nvim_cmd, { cmd = 'echo "hi"' }, {})) - eq('Invalid command: "win_getid"', pcall_err(exec_lua, [[return vim.cmd.win_getid{}]])) + matches('Invalid command: "win_getid"$', pcall_err(exec_lua, [[return vim.cmd.win_getid{}]])) -- Lua call allows empty {} for dict item. eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]])) @@ -4862,16 +4862,16 @@ describe('API', function() eq('', api.nvim_cmd({ cmd = 'set', args = {}, magic = {} }, {})) -- Lua call does not allow non-empty list-like {} for dict item. - eq( - "Invalid 'magic': Expected Dict-like Lua table", + matches( + "Invalid 'magic': Expected Dict%-like Lua table$", pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]]) ) - eq( - "Invalid key: 'bogus'", + matches( + "Invalid key: 'bogus'$", pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]]) ) - eq( - "Invalid key: 'bogus'", + matches( + "Invalid key: 'bogus'$", pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, mods = { bogus = true } }]]) ) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 8eec02524e..8af16001ba 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -169,7 +169,8 @@ pcall(vim.cmd.edit, 'Xtest_swapredraw.lua') exec(init) command('edit! ' .. testfile) command('preserve') - local nvim2 = n.new_session(true, { args = { '--clean', '--embed' }, merge = false }) + local args2 = { '--clean', '--embed', '--cmd', n.runtime_set } + local nvim2 = n.new_session(true, { args = args2, merge = false }) set_session(nvim2) local screen2 = Screen.new(100, 40) screen2:add_extra_attr_ids({ diff --git a/test/functional/lua/comment_spec.lua b/test/functional/lua/comment_spec.lua index 9ae9ce84d0..3284276b32 100644 --- a/test/functional/lua/comment_spec.lua +++ b/test/functional/lua/comment_spec.lua @@ -47,7 +47,8 @@ local setup_treesitter = function() end before_each(function() - clear({ args_rm = { '--cmd' }, args = { '--clean' } }) + -- avoid options, but we still need TS parsers + clear({ args_rm = { '--cmd' }, args = { '--clean', '--cmd', n.runtime_set } }) end) describe('commenting', function() diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 33af25f629..dc1a1a0a5b 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -245,8 +245,8 @@ describe('vim.fs', function() describe('find()', function() it('works', function() eq( - { test_build_dir .. '/build' }, - vim.fs.find('build', { path = nvim_dir, upward = true, type = 'directory' }) + { test_build_dir .. '/bin' }, + vim.fs.find('bin', { path = nvim_dir, upward = true, type = 'directory' }) ) eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' })) @@ -255,7 +255,7 @@ describe('vim.fs', function() end) it('follows symlinks', function() - local build_dir = test_source_path .. '/build' ---@type string + local build_dir = test_build_dir ---@type string local symlink = test_source_path .. '/build_link' ---@type string vim.uv.fs_symlink(build_dir, symlink, { junction = true, dir = true }) @@ -263,8 +263,11 @@ describe('vim.fs', function() vim.uv.fs_unlink(symlink) end) + local cases = { nvim_prog, symlink .. '/bin/' .. nvim_prog_basename } + table.sort(cases) + eq( - { nvim_prog, symlink .. '/bin/' .. nvim_prog_basename }, + cases, vim.fs.find(nvim_prog_basename, { path = test_source_path, type = 'file', @@ -273,6 +276,9 @@ describe('vim.fs', function() }) ) + if t.is_zig_build() then + return pending('broken with build.zig') + end eq( { nvim_prog }, vim.fs.find(nvim_prog_basename, { @@ -285,6 +291,9 @@ describe('vim.fs', function() end) it('follow=true handles symlink loop', function() + if t.is_zig_build() then + return pending('broken/slow with build.zig') + end local cwd = test_source_path ---@type string local symlink = test_source_path .. '/loop_link' ---@type string vim.uv.fs_symlink(cwd, symlink, { junction = true, dir = true }) @@ -304,9 +313,9 @@ describe('vim.fs', function() it('accepts predicate as names', function() local opts = { path = nvim_dir, upward = true, type = 'directory' } eq( - { test_build_dir .. '/build' }, + { test_build_dir .. '/bin' }, vim.fs.find(function(x) - return x == 'build' + return x == 'bin' end, opts) ) eq( diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index dbddd76c99..c499f8bf25 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2100,9 +2100,9 @@ describe('lua stdlib', function() eq(false, fn.luaeval "vim.v['false']") eq(NIL, fn.luaeval 'vim.v.null') matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.v[0].progpath')) - eq('Key is read-only: count', pcall_err(exec_lua, [[vim.v.count = 42]])) - eq('Dict is locked', pcall_err(exec_lua, [[vim.v.nosuchvar = 42]])) - eq('Key is fixed: errmsg', pcall_err(exec_lua, [[vim.v.errmsg = nil]])) + matches('Key is read%-only: count$', pcall_err(exec_lua, [[vim.v.count = 42]])) + matches('Dict is locked$', pcall_err(exec_lua, [[vim.v.nosuchvar = 42]])) + matches('Key is fixed: errmsg$', pcall_err(exec_lua, [[vim.v.errmsg = nil]])) exec_lua([[vim.v.errmsg = 'set by Lua']]) eq('set by Lua', eval('v:errmsg')) exec_lua([[vim.v.errmsg = 42]]) @@ -2111,7 +2111,10 @@ describe('lua stdlib', function() eq({ 'one', 'two' }, eval('v:oldfiles')) exec_lua([[vim.v.oldfiles = {}]]) eq({}, eval('v:oldfiles')) - eq('Setting v:oldfiles to value with wrong type', pcall_err(exec_lua, [[vim.v.oldfiles = 'a']])) + matches( + 'Setting v:oldfiles to value with wrong type$', + pcall_err(exec_lua, [[vim.v.oldfiles = 'a']]) + ) eq({}, eval('v:oldfiles')) feed('i foo foo foo0/foo') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index ef88230105..f1f1dc7f43 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -491,7 +491,7 @@ describe('LSP', function() vim._with({ buf = _G.BUFFER }, function() keymap = vim.fn.maparg('K', 'n', false, false) end) - return keymap:match('') ~= nil + return keymap:match('') ~= nil end) ) end, @@ -499,6 +499,9 @@ describe('LSP', function() end) it('should overwrite options set by ftplugins', function() + if t.is_zig_build() then + return pending('TODO: broken with zig build') + end local client --- @type vim.lsp.Client local BUFFER_1 --- @type integer local BUFFER_2 --- @type integer diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua index ad7587b431..cf23682c8e 100644 --- a/test/functional/testnvim.lua +++ b/test/functional/testnvim.lua @@ -17,7 +17,9 @@ local sleep = uv.sleep --- Functions executing in the current nvim session/process being tested. local M = {} -local runtime_set = 'set runtimepath^=./build/lib/nvim/' +local lib_path = t.is_zig_build() and './zig-out/lib' or './build/lib/nvim/' +M.runtime_set = 'set runtimepath^=' .. lib_path + M.nvim_prog = (os.getenv('NVIM_PRG') or t.paths.test_build_dir .. '/bin/nvim') -- Default settings for the test session. M.nvim_set = ( @@ -34,7 +36,7 @@ M.nvim_argv = { 'NONE', -- XXX: find treesitter parsers. '--cmd', - runtime_set, + M.runtime_set, '--cmd', M.nvim_set, -- Remove default user commands and mappings. @@ -425,7 +427,7 @@ local function remove_args(args, args_rm) last = '' elseif vim.tbl_contains(args_rm, arg) then last = arg - elseif arg == runtime_set and vim.tbl_contains(args_rm, 'runtimepath') then + elseif arg == M.runtime_set and vim.tbl_contains(args_rm, 'runtimepath') then table.remove(new_args) -- Remove the preceding "--cmd". last = '' else diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index 5db8c24120..4532f9e8ff 100644 --- a/test/functional/vimscript/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -192,7 +192,7 @@ describe('eval-API', function() local screen = Screen.new(40, 8) command('set ft=vim') - command('set rtp^=build/runtime/') + n.add_builddir_to_rtp() command('syntax on') insert([[ call bufnr('%') diff --git a/test/lua_runner.lua b/test/lua_runner.lua index 3bee035e78..66ca5fb571 100644 --- a/test/lua_runner.lua +++ b/test/lua_runner.lua @@ -2,10 +2,10 @@ local platform = vim.uv.os_uname() local deps_install_dir = table.remove(_G.arg, 1) local subcommand = table.remove(_G.arg, 1) local suffix = (platform and platform.sysname:lower():find 'windows') and '.dll' or '.so' -package.path = (deps_install_dir .. '/share/lua/5.1/?.lua;') - .. (deps_install_dir .. '/share/lua/5.1/?/init.lua;') +package.path = (deps_install_dir .. '/?.lua;') + .. (deps_install_dir .. '/?/init.lua;') .. package.path -package.cpath = deps_install_dir .. '/lib/lua/5.1/?' .. suffix .. ';' .. package.cpath +package.cpath = deps_install_dir .. '/?' .. suffix .. ';' .. package.cpath local uv = vim.uv diff --git a/test/run_tests.zig b/test/run_tests.zig new file mode 100644 index 0000000000..534337b378 --- /dev/null +++ b/test/run_tests.zig @@ -0,0 +1,44 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; + +pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: *std.Build.Step, lua_deps: LazyPath, config_dir: LazyPath) !void { + const test_step = b.addRunArtifact(nvim_bin); + test_step.addArg("-ll"); + test_step.addFileArg(b.path("./test/lua_runner.lua")); + test_step.addDirectoryArg(lua_deps); + test_step.addArgs(&.{ "busted", "-v", "-o", "test.busted.outputHandlers.nvim", "--lazy" }); + // TODO(bfredl): a bit funky with paths, should work even if we run "zig build" in a nested dir + test_step.addArg("./test/functional/preload.lua"); // TEST_TYPE!! + test_step.addArg("--lpath=./src/?.lua"); + test_step.addArg("--lpath=./runtime/lua/?.lua"); + test_step.addArg("--lpath=./?.lua"); + test_step.addPrefixedFileArg("--lpath=", config_dir.path(b, "?.lua")); // FULING: not a real file but works anyway? + // TODO(bfredl): look into $BUSTED_ARGS user hook, TEST_TAG, TEST_FILTER + if (b.args) |args| { + test_step.addArgs(args); // accept TEST_FILE as a positional argument + } else { + test_step.addArg("./test/functional/"); + } + + test_step.step.dependOn(depend_on); + + const env = test_step.getEnvMap(); + try env.put("VIMRUNTIME", "runtime"); + try env.put("NVIM_RPLUGIN_MANIFEST", "Xtest_xdg/Xtest_rplugin_manifest"); + try env.put("XDG_CONFIG_HOME", "Xtest_xdg/config"); + try env.put("XDG_DATA_HOME", "Xtest_xdg/share"); + try env.put("XDG_STATE_HOME", "Xtest_xdg/state"); + try env.put("TMPDIR", b.fmt("{s}/Xtest_tmpdir", .{b.install_path})); + try env.put("NVIM_LOG_FILE", b.fmt("{s}/Xtest_nvimlog", .{b.install_path})); + + env.remove("NVIM"); + env.remove("XDG_DATA_DIRS"); + + const empty_dir = b.addWriteFiles(); + _ = empty_dir.add(".touch", ""); + const tmpdir_create = b.addInstallDirectory(.{ .source_dir = empty_dir.getDirectory(), .install_dir = .prefix, .install_subdir = "Xtest_tmpdir/" }); + test_step.step.dependOn(&tmpdir_create.step); + + const functionaltest_step = b.step("functionaltest", "run functionaltests"); + functionaltest_step.dependOn(&test_step.step); +} diff --git a/test/testutil.lua b/test/testutil.lua index b4055e2549..da2a255d44 100644 --- a/test/testutil.lua +++ b/test/testutil.lua @@ -422,6 +422,10 @@ function M.is_asan() return M.paths.is_asan end +function M.is_zig_build() + return M.paths.is_zig_build +end + local tmpname_id = 0 local tmpdir = os.getenv('TMPDIR') or os.getenv('TEMP') local tmpdir_is_local = not not (tmpdir and tmpdir:find('Xtest'))