mirror of
https://github.com/neovim/neovim.git
synced 2026-01-16 09:57:00 +00:00
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.
179 lines
5.1 KiB
Lua
179 lines
5.1 KiB
Lua
local grammar = require('gen.c_grammar').grammar
|
|
|
|
--- @param fname string
|
|
--- @return string?
|
|
local function read_file(fname)
|
|
local f = io.open(fname, 'r')
|
|
if not f then
|
|
return
|
|
end
|
|
local contents = f:read('*a')
|
|
f:close()
|
|
return contents
|
|
end
|
|
|
|
--- @param fname string
|
|
--- @param contents string[]
|
|
local function write_file(fname, contents)
|
|
local contents_s = table.concat(contents, '\n') .. '\n'
|
|
local fcontents = read_file(fname)
|
|
if fcontents == contents_s then
|
|
return
|
|
end
|
|
local f = assert(io.open(fname, 'w'))
|
|
f:write(contents_s)
|
|
f:close()
|
|
end
|
|
|
|
--- @param fname string
|
|
--- @param non_static_fname string
|
|
--- @return string? non_static
|
|
local function add_iwyu_non_static(fname, non_static_fname)
|
|
if fname:find('.*/src/nvim/.*%.c$') then
|
|
-- Add an IWYU pragma comment if the corresponding .h file exists.
|
|
local header_fname = fname:sub(1, -3) .. '.h'
|
|
local header_f = io.open(header_fname, 'r')
|
|
if header_f then
|
|
header_f:close()
|
|
return (header_fname:gsub('.*/src/nvim/', 'nvim/'))
|
|
end
|
|
elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
|
|
return 'nvim/api/private/dispatch.h'
|
|
elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
|
|
return 'nvim/ui.h'
|
|
elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
|
|
return 'nvim/ui_client.h'
|
|
elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
|
|
return 'nvim/api/ui.h'
|
|
end
|
|
end
|
|
|
|
--- @param d string
|
|
local function process_decl(d)
|
|
-- Comments are really handled by preprocessor, so the following is not
|
|
-- needed
|
|
d = d:gsub('/%*.-%*/', '')
|
|
d = d:gsub('//.-\n', '\n')
|
|
d = d:gsub('# .-\n', '')
|
|
d = d:gsub('\n', ' ')
|
|
d = d:gsub('%s+', ' ')
|
|
d = d:gsub(' ?%( ?', '(')
|
|
d = d:gsub(' ?, ?', ', ')
|
|
d = d:gsub(' ?(%*+) ?', ' %1')
|
|
d = d:gsub(' ?(FUNC_ATTR_)', ' %1')
|
|
d = d:gsub(' $', '')
|
|
d = d:gsub('^ ', '')
|
|
return d .. ';'
|
|
end
|
|
|
|
--- @param fname string
|
|
--- @param text string
|
|
--- @return string[] static
|
|
--- @return string[] non_static
|
|
--- @return boolean any_static
|
|
local function gen_declarations(text)
|
|
local non_static = {} --- @type string[]
|
|
local static = {} --- @type string[]
|
|
|
|
local any_static = false
|
|
for _, node in ipairs(grammar:match(text)) do
|
|
if node[1] == 'proto' then
|
|
local node_text = text:sub(node.pos, node.endpos - 1)
|
|
local declaration = process_decl(node_text)
|
|
|
|
if node.static then
|
|
if not any_static and declaration:find('FUNC_ATTR_') then
|
|
any_static = true
|
|
end
|
|
static[#static + 1] = declaration
|
|
else
|
|
non_static[#non_static + 1] = 'DLLEXPORT ' .. declaration
|
|
end
|
|
end
|
|
end
|
|
|
|
return static, non_static, any_static
|
|
end
|
|
|
|
local usage = [[
|
|
Usage:
|
|
|
|
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. Also generate an IWYU comment.
|
|
]]
|
|
|
|
local function main()
|
|
local fname = arg[1]
|
|
local static_fname = arg[2]
|
|
local non_static_fname = arg[3]
|
|
local static_basename = arg[4]
|
|
|
|
if fname == '--help' or #arg < 4 then
|
|
print(usage)
|
|
os.exit()
|
|
end
|
|
|
|
local text = assert(read_file(fname))
|
|
|
|
local static_decls, non_static_decls, any_static = gen_declarations(text)
|
|
|
|
local static = {} --- @type string[]
|
|
if fname:find('.*/src/nvim/.*%.h$') then
|
|
static[#static + 1] = ('// IWYU pragma: private, include "%s"'):format(
|
|
fname:gsub('.*/src/nvim/', 'nvim/')
|
|
)
|
|
end
|
|
vim.list_extend(static, {
|
|
'#define DEFINE_FUNC_ATTRIBUTES',
|
|
'#include "nvim/func_attr.h"',
|
|
'#undef DEFINE_FUNC_ATTRIBUTES',
|
|
})
|
|
vim.list_extend(static, static_decls)
|
|
vim.list_extend(static, {
|
|
'#define DEFINE_EMPTY_ATTRIBUTES',
|
|
'#include "nvim/func_attr.h" // IWYU pragma: export',
|
|
'',
|
|
})
|
|
|
|
write_file(static_fname, static)
|
|
|
|
if any_static then
|
|
local orig_text = assert(read_file(fname))
|
|
local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
|
|
local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
|
|
if not orig_text:find(pat) and not orig_text:find(pat_comment) then
|
|
error(('fail: missing include for %s in %s'):format(static_basename, fname))
|
|
end
|
|
end
|
|
|
|
if non_static_fname ~= 'SKIP' then
|
|
local non_static = {} --- @type string[]
|
|
local iwyu_non_static = add_iwyu_non_static(fname, non_static_fname)
|
|
if iwyu_non_static then
|
|
non_static[#non_static + 1] = ('// IWYU pragma: private, include "%s"'):format(
|
|
iwyu_non_static
|
|
)
|
|
end
|
|
vim.list_extend(non_static, {
|
|
'#define DEFINE_FUNC_ATTRIBUTES',
|
|
'#include "nvim/func_attr.h"',
|
|
'#undef DEFINE_FUNC_ATTRIBUTES',
|
|
'#ifndef DLLEXPORT',
|
|
'# ifdef MSWIN',
|
|
'# define DLLEXPORT __declspec(dllexport)',
|
|
'# else',
|
|
'# define DLLEXPORT',
|
|
'# endif',
|
|
'#endif',
|
|
})
|
|
vim.list_extend(non_static, non_static_decls)
|
|
non_static[#non_static + 1] = '#include "nvim/func_attr.h"'
|
|
write_file(non_static_fname, non_static)
|
|
end
|
|
end
|
|
|
|
return main()
|