refactor(build): don't use the preprocessor when generating headers

Using the preprocessor before generating prototypes provides some
"niceties" but the places that rely on these are pretty few.
Vastly simplifying the BUILD SYSTEM is a better trade-off.

Unbalancing { } blocks due to the preprocessor is cringe anyway (think
of the tree-sitter trees!), write these in a different way.

Add some workarounds for plattform specific features.

INCLUDE_GENERATED_DECLARATIONS flag is now technically redundant,
but will be cleaned up in a follow-up PR as it is a big mess.
This commit is contained in:
bfredl
2025-08-01 12:26:52 +02:00
parent b4c092a092
commit 79c8159f41
24 changed files with 74 additions and 143 deletions

View File

@@ -232,8 +232,8 @@ pub fn build(b: *std.Build) !void {
\\
, .{medium}));
// TODO(zig): using getEmittedIncludeTree() is ugly af. we want run_preprocessor()
// to use the std.build.Module include_path thing
// TODO(zig): using getEmittedIncludeTree() is ugly af. we want unittests
// to reuse the std.build.Module include_path thing
const include_path = [_]LazyPath{
b.path("src/"),
gen_config.getDirectory(),
@@ -245,7 +245,7 @@ pub fn build(b: *std.Build) !void {
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 gen_headers, const funcs_data = try gen.nvim_gen_sources(b, nlua0, &nvim_sources, &nvim_headers, &api_headers, versiondef_git, version_lua);
const test_config_step = b.addWriteFiles();
_ = test_config_step.add("test/cmakeconfig/paths.lua", try test_config(b));

View File

@@ -160,6 +160,8 @@ local typed_container = P({
)
+ Cg(opt(P('*')) * P('Dict')) * paren(C(id))
+ Cg(opt(P('*')) * P('DictAs')) * paren(C(id))
+ Cg(opt(P('*')) * P('Set')) * paren(C(id))
+ Cg(opt(P('*')) * P('PMap')) * paren(C(id))
),
-- Remove captures here (with / 0 ) as api_types will recursively run parse the type.
TY = Cg(V('S') / 0 + V('ID')),

View File

@@ -71,17 +71,13 @@ end
--- @return string[] static
--- @return string[] non_static
--- @return boolean any_static
local function gen_declarations(fname, text)
local function gen_declarations(text)
local non_static = {} --- @type string[]
local static = {} --- @type string[]
local neededfile = fname:match('[^/]+$')
local curfile = nil
local any_static = false
for _, node in ipairs(grammar:match(text)) do
if node[1] == 'preproc' then
curfile = node.content:match('^%a* %d+ "[^"]-/?([^"/]+)"') or curfile
elseif node[1] == 'proto' and curfile == neededfile then
if node[1] == 'proto' then
local node_text = text:sub(node.pos, node.endpos - 1)
local declaration = process_decl(node_text)
@@ -102,31 +98,27 @@ end
local usage = [[
Usage:
gen_declarations.lua definitions.c static.h non-static.h definitions.i
gen_declarations.lua definitions.c static.h non-static.h
Generates declarations for a C file definitions.c, putting declarations for
static functions into static.h and declarations for non-static functions into
non-static.h. File `definitions.i' should contain an already preprocessed
version of definitions.c and it is the only one which is actually parsed,
definitions.c is needed only to determine functions from which file out of all
functions found in definitions.i are needed and to generate an IWYU comment.
non-static.h. Also generate an IWYU comment.
]]
local function main()
local fname = arg[1]
local static_fname = arg[2]
local non_static_fname = arg[3]
local preproc_fname = arg[4]
local static_basename = arg[5]
local static_basename = arg[4]
if fname == '--help' or #arg < 5 then
if fname == '--help' or #arg < 4 then
print(usage)
os.exit()
end
local text = assert(read_file(preproc_fname))
local text = assert(read_file(fname))
local static_decls, non_static_decls, any_static = gen_declarations(fname, text)
local static_decls, non_static_decls, any_static = gen_declarations(text)
local static = {} --- @type string[]
if fname:find('.*/src/nvim/.*%.h$') then

View File

@@ -9,8 +9,6 @@ pub fn nvim_gen_sources(
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 } {
@@ -19,12 +17,12 @@ pub fn nvim_gen_sources(
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);
_ = try generate_header_for(b, s.name, input_file, api_export, nlua0, 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);
_ = try generate_header_for(b, s, input_file, null, nlua0, gen_headers, true);
}
{
@@ -95,17 +93,17 @@ pub fn nvim_gen_sources(
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);
_ = try gen_header_with_header(b, gen_step, "ui_events_call.generated.h", nlua0, gen_headers);
_ = try gen_header_with_header(b, gen_step, "ui_events_remote.generated.h", nlua0, 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);
_ = try gen_header_with_header(b, gen_step, "ui_events_client.generated.h", nlua0, 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);
_ = try gen_header_with_header(b, gen_step, "api/private/dispatch_wrappers.generated.h", nlua0, 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);
@@ -153,13 +151,11 @@ fn gen_header_with_header(
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);
_ = try generate_header_for(b, b.fmt("{s}.h", .{name[0 .. name.len - 12]}), h, null, nlua0, gen_headers, false);
return h;
}
@@ -169,55 +165,17 @@ pub const PreprocessorOptions = struct {
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);
@@ -232,7 +190,6 @@ fn generate_header_for(
}
}
run_step.addFileArg(i_file);
run_step.addArg(gen_name);
return run_step;
}

View File

@@ -84,6 +84,7 @@ if(NOT MSVC)
-Wmissing-noreturn
-Wmissing-format-attribute
-Wmissing-prototypes
-Wno-unused-function # because #35129
-fsigned-char)
# For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems
@@ -465,37 +466,19 @@ endif()
# Header generation
#-------------------------------------------------------------------------------
get_target_property(prop main_lib INTERFACE_COMPILE_DEFINITIONS)
if(NOT "${prop}" STREQUAL "prop-NOTFOUND")
foreach(gen_cdef ${prop})
if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS")
list(APPEND gen_cflags "-D${gen_cdef}")
endif()
endforeach()
endif()
get_directory_property(targets BUILDSYSTEM_TARGETS)
foreach(target ${targets})
get_target_property(prop ${target} INTERFACE_INCLUDE_DIRECTORIES)
if(NOT "${prop}" STREQUAL "prop-NOTFOUND")
message(STATUS "${target} props '${prop}'")
foreach(gen_include ${prop})
list(APPEND gen_cflags "-I${gen_include}")
list(APPEND TEST_INCLUDE_DIRS "${gen_include}")
endforeach()
endif()
endforeach()
list(REMOVE_DUPLICATES gen_cflags)
list(REMOVE_DUPLICATES TEST_INCLUDE_DIRS)
if(APPLE AND CMAKE_OSX_SYSROOT)
list(APPEND gen_cflags "-isysroot" "${CMAKE_OSX_SYSROOT}")
endif()
if(MSVC)
list(APPEND gen_cflags -wd4003)
endif()
set(NVIM_VERSION_GIT_H ${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef_git.h)
add_custom_target(update_version_stamp
COMMAND ${CMAKE_COMMAND}
@@ -558,13 +541,6 @@ foreach(sfile ${NVIM_SOURCES}
set(gf_h_h "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h")
set(gf_h_h_out "${gf_h_h}")
endif()
set(gf_i "${GENERATED_DIR}/${f}.i")
if(MSVC)
set(PREPROC_OUTPUT /P /Fi${gf_i} /nologo)
else()
set(PREPROC_OUTPUT -w -E -o ${gf_i})
endif()
set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}" "${GENERATOR_C_GRAMMAR}")
if("${f}" STREQUAL "version.c")
@@ -573,8 +549,7 @@ foreach(sfile ${NVIM_SOURCES}
endif()
add_custom_command(
OUTPUT "${gf_c_h}" ${gf_h_h_out}
COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags}
COMMAND ${LUA_GEN} "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" "${gf_basename}"
COMMAND ${LUA_GEN} "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_basename}"
DEPENDS ${depends})
list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}")
if (NOT ${sfile} IN_LIST NVIM_HEADERS)

View File

@@ -1,5 +1,7 @@
#pragma once
#include <stdbool.h>
#define ARABIC_CHAR(ch) (((ch) & 0xFF00) == 0x0600)
#ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@@ -31,7 +31,7 @@ static const uint8_t char_to_index[256] = {
};
#ifndef HAVE_BE64TOH
static inline uint64_t htobe64(uint64_t host_64bits)
static inline uint64_t vim_htobe64(uint64_t host_64bits)
{
# ifdef ORDER_BIG_ENDIAN
return host_64bits;
@@ -45,7 +45,7 @@ static inline uint64_t htobe64(uint64_t host_64bits)
# endif
}
static inline uint32_t htobe32(uint32_t host_32bits)
static inline uint32_t vim_htobe32(uint32_t host_32bits)
{
# ifdef ORDER_BIG_ENDIAN
return host_32bits;
@@ -58,6 +58,8 @@ static inline uint32_t htobe32(uint32_t host_32bits)
return ret;
# endif
}
# define htobe64 vim_htobe64
# define htobe32 vim_htobe32
#endif
/// Encode a string using Base64.

View File

@@ -729,18 +729,11 @@ void nlua_push_Array(lua_State *lstate, const Array array, int flags)
}
}
#define GENERATE_INDEX_FUNCTION(type) \
void nlua_push_##type(lua_State *lstate, const type item, int flags) \
FUNC_ATTR_NONNULL_ALL \
{ \
lua_pushnumber(lstate, (lua_Number)(item)); \
}
GENERATE_INDEX_FUNCTION(Buffer)
GENERATE_INDEX_FUNCTION(Window)
GENERATE_INDEX_FUNCTION(Tabpage)
#undef GENERATE_INDEX_FUNCTION
void nlua_push_handle(lua_State *lstate, const handle_T item, int flags)
FUNC_ATTR_NONNULL_ALL
{
lua_pushnumber(lstate, (lua_Number)(item));
}
/// Convert given Object to Lua value
///

View File

@@ -9,6 +9,10 @@
#define nlua_pop_Window nlua_pop_handle
#define nlua_pop_Tabpage nlua_pop_handle
#define nlua_push_Buffer nlua_push_handle
#define nlua_push_Window nlua_push_handle
#define nlua_push_Tabpage nlua_push_handle
/// Flags for nlua_push_*() functions.
enum {
kNluaPushSpecial = 0x01, ///< Use lua-special-tbl when necessary

View File

@@ -1,6 +1,7 @@
#pragma once
#include <lua.h> // IWYU pragma: keep
#include <stdbool.h>
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/stdlib.h.generated.h"

View File

@@ -47,6 +47,11 @@ typedef struct {
bool had_stdin_file; // explicit - as a file to edit
} mparm_T;
#if defined(MSWIN) && !defined(ENABLE_ASAN_UBSAN)
# define __asan_default_options vim__asan_default_options
# define __ubsan_default_options vim__ubsan_default_options
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "main.h.generated.h"
#endif

View File

@@ -717,11 +717,7 @@ static void fname2fnum(xfmark_T *fm)
// First expand "~/" in the file name to the home directory.
// Don't expand the whole name, it may contain other '~' chars.
#ifdef BACKSLASH_IN_FILENAME
if (fm->fname[0] == '~' && (fm->fname[1] == '/' || fm->fname[1] == '\\')) {
#else
if (fm->fname[0] == '~' && (fm->fname[1] == '/')) {
#endif
if (fm->fname[0] == '~' && vim_ispathsep_nocolon(fm->fname[1])) {
size_t len = expand_env("~/", NameBuff, MAXPATHL);
xstrlcpy(NameBuff + len, fm->fname + 2, MAXPATHL - len);
} else {

View File

@@ -1174,7 +1174,7 @@ bool utf_printable(int c)
// Return true if "c" is in "table".
static bool intable(const struct interval *table, size_t n_items, int c)
FUNC_ATTR_PURE
FUNC_ATTR_CONST
{
assert(n_items > 0);
// first quick check for Latin1 etc. characters
@@ -1202,11 +1202,11 @@ static bool intable(const struct interval *table, size_t n_items, int c)
// Return true for characters that can be displayed in a normal way.
// Only for characters of 0x100 and above!
bool utf_printable(int c)
FUNC_ATTR_PURE
FUNC_ATTR_CONST
{
// Sorted list of non-overlapping intervals.
// 0xd800-0xdfff is reserved for UTF-16, actually illegal.
static struct interval nonprint[] = {
static const struct interval nonprint[] = {
{ 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e },
{ 0x2060, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb },
{ 0xfffe, 0xffff }

View File

@@ -33,6 +33,10 @@
#include "nvim/ui.h"
#include "nvim/ui_client.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/channel.c.generated.h"
#endif
#ifdef NVIM_LOG_DEBUG
# define REQ "[request] "
# define RES "[response] "
@@ -66,10 +70,6 @@ static void log_notify(char *dir, uint64_t channel_id, const char *name)
# define log_notify(...)
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/channel.c.generated.h"
#endif
void rpc_init(void)
{
ch_before_blocking_events = multiqueue_new_child(main_loop.events);

View File

@@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stddef.h> // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@@ -1,5 +1,7 @@
#pragma once
#include <stdbool.h>
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/dl.h.generated.h"
#endif

View File

@@ -245,14 +245,12 @@ bool os_can_exe(const char *name, char **abspath, bool use_path)
{
if (!use_path || gettail_dir(name) != name) {
#ifdef MSWIN
if (is_executable_ext(name, abspath)) {
return is_executable_ext(name, abspath);
#else
// Must have path separator, cannot execute files in the current directory.
if ((use_path || gettail_dir(name) != name)
&& is_executable(name, abspath)) {
return ((use_path || gettail_dir(name) != name)
&& is_executable(name, abspath));
#endif
return true;
}
return false;
}
@@ -339,6 +337,8 @@ static bool is_executable_ext(const char *name, char **abspath)
}
return false;
}
#else
# define is_executable_ext is_executable
#endif
/// Checks if a file is in `$PATH` and is executable.
@@ -384,11 +384,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
xmemcpyz(buf, p, (size_t)(e - p));
(void)append_path(buf, name, bufsize);
#ifdef MSWIN
if (is_executable_ext(buf, abspath)) {
#else
if (is_executable(buf, abspath)) {
#endif
rv = true;
goto end;
}

View File

@@ -48,7 +48,7 @@
#endif
#ifdef MSWIN
static bool os_proc_tree_kill_rec(HANDLE proc, int sig)
static bool os_proc_tree_kill_rec(void *proc, int sig)
{
if (proc == NULL) {
return false;

View File

@@ -2,9 +2,7 @@
#include <stddef.h> // IWYU pragma: keep
#ifdef MSWIN
# include "nvim/api/private/defs.h" // IWYU pragma: keep
#endif
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/proc.h.generated.h"

View File

@@ -56,8 +56,8 @@ int forkpty(int *, char *, const struct termios *, const struct winsize *);
// inclusion of the header even though it gets include out of order.
# include <sys/stropts.h>
static int openpty(int *amaster, int *aslave, char *name, struct termios *termp,
struct winsize *winp)
static int vim_openpty(int *amaster, int *aslave, char *name, struct termios *termp,
struct winsize *winp)
{
int slave = -1;
int master = open("/dev/ptmx", O_RDWR);
@@ -117,7 +117,7 @@ error:
return -1;
}
static int login_tty(int fd)
static int vim_login_tty(int fd)
{
setsid();
if (ioctl(fd, TIOCSCTTY, NULL) == -1) {
@@ -134,10 +134,10 @@ static int login_tty(int fd)
return 0;
}
static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp)
pid_t vim_forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp)
{
int master, slave;
if (openpty(&master, &slave, name, termp, winp) == -1) {
if (vim_openpty(&master, &slave, name, termp, winp) == -1) {
return -1;
}
@@ -149,7 +149,7 @@ static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct win
return -1;
case 0:
close(master);
login_tty(slave);
vim_login_tty(slave);
return 0;
default:
close(slave);
@@ -157,7 +157,7 @@ static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct win
return pid;
}
}
# define forkpty vim_forkpty
#endif
/// @returns zero on success, or negative error code

View File

@@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stddef.h> // IWYU pragma: keep
#include <time.h> // IWYU pragma: keep

View File

@@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stddef.h> // IWYU pragma: keep
#include <stdint.h>

View File

@@ -3003,7 +3003,7 @@ static void shada_free_shada_entry(ShadaEntry *const entry)
}
#ifndef HAVE_BE64TOH
static inline uint64_t be64toh(uint64_t big_endian_64_bits)
static inline uint64_t vim_be64toh(uint64_t big_endian_64_bits)
{
# ifdef ORDER_BIG_ENDIAN
return big_endian_64_bits;
@@ -3019,6 +3019,7 @@ static inline uint64_t be64toh(uint64_t big_endian_64_bits)
return ret;
# endif
}
# define be64toh vim_be64toh
#endif
/// Read given number of bytes into given buffer, display error if needed

View File

@@ -1,5 +1,7 @@
#pragma once
#include <stdbool.h>
#include "nvim/garray_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS