diff --git a/BUILD.md b/BUILD.md index 3cccad9eb7..7f99ff9254 100644 --- a/BUILD.md +++ b/BUILD.md @@ -288,6 +288,18 @@ cmake --build build - Using `ninja` is strongly recommended. 4. If treesitter parsers are not bundled, they need to be available in a `parser/` runtime directory (e.g. `/usr/share/nvim/runtime/parser/`). +### How to build without unibilium + +Unibilium is the only dependency which is licensed under LGPLv3 (there are no +GPLv3-only dependencies). This library is used for loading the terminfo database at +runtime, and can be disabled if the internal definitions for common terminals +are good enough. To avoid this dependency, build with support for loading +custom terminfo at runtime, use + +```sh +make CMAKE_EXTRA_FLAGS="-DENABLE_UNIBILIUM=0" BUNDLED_CMAKE_FLAG="-DUSE_BUNDLED_UNIBILIUM=0" +``` + ### How to build static binary (on Linux) 1. Use a linux distribution which uses musl C. We will use Alpine Linux but any distro with musl should work. (glibc does not support static linking) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5bfdb214c..f0a58a828f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ else() option(ENABLE_LTO "enable link time optimization" ON) endif() option(ENABLE_LIBINTL "enable libintl" ON) +option(ENABLE_UNIBILIUM "enable unibilium" ON) option(ENABLE_WASMTIME "enable wasmtime" OFF) message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") diff --git a/build.zig b/build.zig index 13f5d8eeb1..73406123fc 100644 --- a/build.zig +++ b/build.zig @@ -46,6 +46,8 @@ pub fn build(b: *std.Build) !void { // without cross_compiling we like to reuse libluv etc at the same optimize level const optimize_host = if (cross_compiling) .ReleaseSafe else optimize; + const use_unibilium = b.option(bool, "unibilium", "use unibilium") orelse true; + // puc lua 5.1 is not ReleaseSafe "safe" const optimize_lua = if (optimize == .Debug or optimize == .ReleaseSafe) .ReleaseSmall else optimize; @@ -92,7 +94,7 @@ pub fn build(b: *std.Build) !void { } else libluv; const utf8proc = b.dependency("utf8proc", .{ .target = target, .optimize = optimize }); - const unibilium = b.dependency("unibilium", .{ .target = target, .optimize = optimize }); + const unibilium = if (use_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 }); @@ -250,7 +252,7 @@ pub fn build(b: *std.Build) !void { libuv.getEmittedIncludeTree(), libluv.getEmittedIncludeTree(), utf8proc.artifact("utf8proc").getEmittedIncludeTree(), - unibilium.artifact("unibilium").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/"), }; @@ -279,7 +281,7 @@ pub fn build(b: *std.Build) !void { nvim_exe.linkLibrary(libluv); if (iconv) |dep| nvim_exe.linkLibrary(dep.artifact("iconv")); nvim_exe.linkLibrary(utf8proc.artifact("utf8proc")); - nvim_exe.linkLibrary(unibilium.artifact("unibilium")); + if (unibilium) |u| nvim_exe.linkLibrary(u.artifact("unibilium")); nvim_exe.linkLibrary(treesitter.artifact("tree-sitter")); if (is_windows) { nvim_exe.linkSystemLibrary("netapi32"); @@ -317,6 +319,7 @@ pub fn build(b: *std.Build) !void { if (is_windows) "-DMSWIN" else "", if (is_windows) "-DWIN32_LEAN_AND_MEAN" else "", if (is_windows) "-DUTF8PROC_STATIC" else "", + if (use_unibilium) "-DHAVE_UNIBILIUM" else "", }; nvim_exe.addCSourceFiles(.{ .files = src_paths, .flags = &flags }); diff --git a/build.zig.zon b/build.zig.zon index a73ab6d855..b69580650a 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -30,7 +30,7 @@ .hash = "libuv-1.51.0-htqqv6liAADxBLIBCZT-qUh_3nRRwtNYsOFQOUmrd_sx", }, .utf8proc = .{ .path = "./deps/utf8proc/" }, - .unibilium = .{ .path = "./deps/unibilium/" }, + .unibilium = .{ .path = "./deps/unibilium/", .lazy = true }, .libiconv = .{ .url = "git+https://github.com/allyourcodebase/libiconv#9def4c8a1743380e85bcedb80f2c15b455e236f3", .hash = "libiconv-1.18.0-p9sJwWnqAACzVYeWgXB5r5lOQ74XwTPlptixV0JPRO28", diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 975564460f..02fe076fd8 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -153,7 +153,12 @@ 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. +• Nvim can be built without Unibilium (terminfo implementation), in which case + the user's terminfo database won't be loaded and only internal definitions + for the most common terminals are used. > + make distclean && make CMAKE_EXTRA_FLAGS="-DENABLE_UNIBILIUM=0" BUNDLED_CMAKE_FLAG="-DUSE_BUNDLED_UNIBILIUM=0" +< DEFAULTS • 'diffopt' default value now includes "indent-heuristic" and "inline:char". diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 86fc4937be..2b486c40ca 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -473,8 +473,9 @@ TUI: - Note: If you want to detect when Nvim is running in a terminal, use `has('gui_running')` |has()| or see |nvim_list_uis()| for an example of how to inspect the UI channel. -- "builtin_x" means one of the |builtin-terms| was chosen, because the expected - terminfo file was not found on the system. +- Nvim might optionally be compiled with unibilium, in which case the terminfo + database will be used. Otherwise, or if the terminal was not found in + the database, a table of builtin terminal definitions will be used. - Nvim will use 256-colour capability on Linux virtual terminals. Vim uses only 8 colours plus bright foreground on Linux VTs. - Vim combines what is in its |builtin-terms| with what it reads from terminfo, diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3d5e3e188a..3fbed68bf2 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -32,14 +32,12 @@ find_package(Iconv REQUIRED) find_package(Libuv 1.28.0 REQUIRED) find_package(Lpeg REQUIRED) find_package(Treesitter 0.25.0 REQUIRED) -find_package(Unibilium 2.0 REQUIRED) find_package(UTF8proc REQUIRED) target_link_libraries(main_lib INTERFACE iconv lpeg treesitter - unibilium utf8proc) target_link_libraries(nlua0 PUBLIC lpeg) @@ -48,6 +46,12 @@ if(ENABLE_LIBINTL) target_link_libraries(main_lib INTERFACE libintl) endif() +if(ENABLE_UNIBILIUM) + find_package(Unibilium 2.0 REQUIRED) + target_compile_definitions(nvim_bin PRIVATE HAVE_UNIBILIUM) + target_link_libraries(main_lib INTERFACE unibilium) +endif() + if(ENABLE_WASMTIME) find_package(Wasmtime 29.0.1 EXACT REQUIRED) target_link_libraries(main_lib INTERFACE wasmtime) diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 89d999d857..a1d5712962 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -2,7 +2,10 @@ #include #include -#include + +#ifdef HAVE_UNIBILIUM +# include +#endif #include "klib/kvec.h" #include "nvim/api/private/defs.h" @@ -68,71 +71,60 @@ bool terminfo_is_bsd_console(const char *term) const TerminfoEntry *terminfo_from_builtin(const char *term, char **termname) { if (terminfo_is_term_family(term, "xterm")) { - *termname = "builtin_xterm"; + *termname = "xterm"; return &xterm_256colour_terminfo; } else if (terminfo_is_term_family(term, "screen")) { - *termname = "builtin_screen"; + *termname = "screen"; return &screen_256colour_terminfo; } else if (terminfo_is_term_family(term, "tmux")) { - *termname = "builtin_tmux"; + *termname = "tmux"; return &tmux_256colour_terminfo; } else if (terminfo_is_term_family(term, "rxvt")) { - *termname = "builtin_rxvt"; + *termname = "rxvt"; return &rxvt_256colour_terminfo; } else if (terminfo_is_term_family(term, "putty")) { - *termname = "builtin_putty"; + *termname = "putty"; return &putty_256colour_terminfo; } else if (terminfo_is_term_family(term, "linux")) { - *termname = "builtin_linux"; + *termname = "linux"; return &linux_16colour_terminfo; } else if (terminfo_is_term_family(term, "interix")) { - *termname = "builtin_interix"; + *termname = "interix"; return &interix_8colour_terminfo; } else if (terminfo_is_term_family(term, "iterm") || terminfo_is_term_family(term, "iterm2") || terminfo_is_term_family(term, "iTerm.app") || terminfo_is_term_family(term, "iTerm2.app")) { - *termname = "builtin_iterm"; + *termname = "iterm"; return &iterm_256colour_terminfo; } else if (terminfo_is_term_family(term, "st")) { - *termname = "builtin_st"; + *termname = "st"; return &st_256colour_terminfo; } else if (terminfo_is_term_family(term, "gnome") || terminfo_is_term_family(term, "vte")) { - *termname = "builtin_vte"; + *termname = "vte"; return &vte_256colour_terminfo; } else if (terminfo_is_term_family(term, "cygwin")) { - *termname = "builtin_cygwin"; + *termname = "cygwin"; return &cygwin_terminfo; } else if (terminfo_is_term_family(term, "win32con")) { - *termname = "builtin_win32con"; + *termname = "win32con"; return &win32con_terminfo; } else if (terminfo_is_term_family(term, "conemu")) { - *termname = "builtin_conemu"; + *termname = "conemu"; return &conemu_terminfo; } else if (terminfo_is_term_family(term, "vtpcon")) { - *termname = "builtin_vtpcon"; + *termname = "vtpcon"; return &vtpcon_terminfo; } else { - *termname = "builtin_ansi"; + *termname = "ansi"; return &ansi_terminfo; } } -static ssize_t unibi_find_ext_str(unibi_term *ut, const char *name) -{ - size_t max = unibi_count_ext_str(ut); - for (size_t i = 0; i < max; i++) { - const char *n = unibi_get_ext_str_name(ut, i); - if (n && 0 == strcmp(n, name)) { - return (ssize_t)i; - } - } - return -1; -} - -bool terminfo_from_unibilium(TerminfoEntry *ti, char *termname, Arena *arena) +bool terminfo_from_database(TerminfoEntry *ti, char *termname, Arena *arena) { +#ifdef HAVE_UNIBILIUM unibi_term *ut = unibi_from_term(termname); if (!ut) { return false; @@ -156,9 +148,9 @@ bool terminfo_from_unibilium(TerminfoEntry *ti, char *termname, Arena *arena) } static const enum unibi_string uni_ids[] = { -#define X(name) unibi_##name, +# define X(name) unibi_##name, XLIST_TERMINFO_BUILTIN -#undef X +# undef X }; for (size_t i = 0; i < ARRAY_SIZE(uni_ids); i++) { @@ -167,26 +159,31 @@ bool terminfo_from_unibilium(TerminfoEntry *ti, char *termname, Arena *arena) } static const char *uni_ext[] = { -#define X(informal_name, terminfo_name) #terminfo_name, +# define X(informal_name, terminfo_name) #terminfo_name, XLIST_TERMINFO_EXT -#undef X +# undef X }; + size_t max = unibi_count_ext_str(ut); for (size_t i = 0; i < ARRAY_SIZE(uni_ext); i++) { - ssize_t val = unibi_find_ext_str(ut, uni_ext[i]); - if (val >= 0) { - const char *data = unibi_get_ext_str(ut, (size_t)val); - ti->defs[kTermExtOffset + i] = data ? arena_strdup(arena, data) : NULL; + const char *name = uni_ext[i]; + for (size_t val = 0; val < max; val++) { + const char *n = unibi_get_ext_str_name(ut, val); + if (n && strequal(n, name)) { + const char *data = unibi_get_ext_str(ut, val); + ti->defs[kTermExtOffset + i] = data ? arena_strdup(arena, data) : NULL; + break; + } } } -#define X(name) { unibi_key_##name, unibi_string_begin_ }, -#define Y(name) { unibi_key_##name, unibi_key_s##name }, +# define X(name) { unibi_key_##name, unibi_string_begin_ }, +# define Y(name) { unibi_key_##name, unibi_key_s##name }, static const enum unibi_string uni_keys[][2] = { XYLIST_TERMINFO_KEYS }; -#undef X -#undef Y +# undef X +# undef Y for (size_t i = 0; i < ARRAY_SIZE(uni_keys); i++) { const char *val = unibi_get_str(ut, uni_keys[i][0]); @@ -200,9 +197,9 @@ bool terminfo_from_unibilium(TerminfoEntry *ti, char *termname, Arena *arena) } static const enum unibi_string uni_fkeys[] = { -#define X(name) unibi_key_##name, +# define X(name) unibi_key_##name, XLIST_TERMINFO_FKEYS -#undef X +# undef X }; for (size_t i = 0; i < ARRAY_SIZE(uni_fkeys); i++) { @@ -212,6 +209,9 @@ bool terminfo_from_unibilium(TerminfoEntry *ti, char *termname, Arena *arena) unibi_destroy(ut); return true; +#else + return false; +#endif } static const char *fmt(bool val) @@ -223,11 +223,16 @@ static const char *fmt(bool val) /// Serves a similar purpose as Vim `:set termcap` (removed in Nvim). /// /// @return allocated string -String terminfo_info_msg(const TerminfoEntry *ti, const char *termname) +String terminfo_info_msg(const TerminfoEntry *ti, const char *termname, bool from_db) { StringBuilder data = KV_INITIAL_VALUE; kv_printf(data, "&term: %s\n", termname); + if (from_db) { + kv_printf(data, "using terminfo database\n"); + } else { + kv_printf(data, "using builtin terminfo\n"); + } kv_printf(data, "\n"); kv_printf(data, "Boolean capabilities:\n"); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e90de6f062..0540734e1f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -86,6 +86,7 @@ struct TUIData { int row, col; int out_fd; int pending_resize_events; + bool terminfo_found_in_db; bool can_change_scroll_region; bool has_left_and_right_margin_mode; bool has_sync_mode; @@ -382,15 +383,15 @@ static void terminfo_start(TUIData *tui) #endif // Set up terminfo. - bool found_in_db = false; + tui->terminfo_found_in_db = false; if (term) { - if (terminfo_from_unibilium(&tui->ti, term, &tui->ti_arena)) { + if (terminfo_from_database(&tui->ti, term, &tui->ti_arena)) { tui->term = arena_strdup(&tui->ti_arena, term); - found_in_db = true; + tui->terminfo_found_in_db = true; } } - if (!found_in_db) { + if (!tui->terminfo_found_in_db) { const TerminfoEntry *new = terminfo_from_builtin(term, &tui->term); // we will patch it below, so make a copy memcpy(&tui->ti, new, sizeof tui->ti); @@ -1596,7 +1597,7 @@ static void show_verbose_terminfo(TUIData *tui) ADD_C(title, CSTR_AS_OBJ("Title")); ADD_C(chunks, ARRAY_OBJ(title)); MAXSIZE_TEMP_ARRAY(info, 1); - String str = terminfo_info_msg(&tui->ti, tui->term); + String str = terminfo_info_msg(&tui->ti, tui->term, tui->terminfo_found_in_db); ADD_C(info, STRING_OBJ(str)); ADD_C(chunks, ARRAY_OBJ(info)); MAXSIZE_TEMP_ARRAY(end_fold, 2); diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 43f0c64567..4de9ab16d8 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -2177,7 +2177,7 @@ describe('TUI', function() it('in nvim_list_uis(), sets nvim_set_client_info()', function() -- $TERM in :terminal. - local exp_term = is_os('bsd') and 'builtin_xterm' or 'xterm-256color' + local exp_term = is_os('bsd') and 'xterm' or 'xterm-256color' local ui_chan = 1 local expected = { { @@ -3447,14 +3447,14 @@ describe("TUI 'term' option", function() end it('gets builtin term if $TERM is invalid', function() - assert_term('foo', 'builtin_ansi') + assert_term('foo', 'ansi') end) it('gets system-provided term if $TERM is valid', function() if is_os('openbsd') then assert_term('xterm', 'xterm') elseif is_os('bsd') then -- BSD lacks terminfo, builtin is always used. - assert_term('xterm', 'builtin_xterm') + assert_term('xterm', 'xterm') elseif is_os('mac') then local status, _ = pcall(assert_term, 'xterm', 'xterm') if not status then @@ -3467,9 +3467,9 @@ describe("TUI 'term' option", function() it('builtin terms', function() -- These non-standard terminfos are always builtin. - assert_term('win32con', 'builtin_win32con') - assert_term('conemu', 'builtin_conemu') - assert_term('vtpcon', 'builtin_vtpcon') + assert_term('win32con', 'win32con') + assert_term('conemu', 'conemu') + assert_term('vtpcon', 'vtpcon') end) end)