Add automatic generation of headers

- The 'stripdecls.py' script replaces declarations in all headers by includes to
  generated headers.
  `ag '#\s*if(?!ndef NEOVIM_).*((?!#\s*endif).*\n)*#ifdef INCLUDE_GENERATED'`
  was used for this.
- Add and integrate gendeclarations.lua into the build system to generate the
  required includes.
- Add -Wno-unused-function
- Made a bunch of old-style definitions ANSI

This adds a requirement: all type and structure definitions must be present
before INCLUDE_GENERATED_DECLARATIONS-protected include.

Warning: mch_expandpath (path.h.generated.h) was moved manually. So far it is
the only exception.
This commit is contained in:
ZyX
2014-05-10 17:24:13 +04:00
committed by Thiago de Arruda
parent 880957ad4e
commit 70929f7e16
154 changed files with 2190 additions and 6184 deletions

232
scripts/gendeclarations.lua Executable file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/lua
local fname = arg[1]
local static_fname = arg[2]
local non_static_fname = arg[3]
local cpp = arg[4]
cpp = cpp:gsub(' %-DINCLUDE_GENERATED_DECLARATIONS ', ' ')
local lpeg = require('lpeg')
local fold = function (func, ...)
local result = nil
for i, v in ipairs({...}) do
if result == nil then
result = v
else
result = func(result, v)
end
end
return result
end
local folder = function (func)
return function (...)
return fold(func, ...)
end
end
local lit = lpeg.P
local set = function(...)
return lpeg.S(fold(function (a, b) return a .. b end, ...))
end
local any_character = lpeg.P(1)
local rng = function(s, e) return lpeg.R(s .. e) end
local concat = folder(function (a, b) return a * b end)
local branch = folder(function (a, b) return a + b end)
local one_or_more = function(v) return v ^ 1 end
local two_or_more = function(v) return v ^ 2 end
local any_amount = function(v) return v ^ 0 end
local one_or_no = function(v) return v ^ -1 end
local look_behind = lpeg.B
local look_ahead = function(v) return #v end
local neg_look_ahead = function(v) return -v end
local neg_look_behind = function(v) return -look_behind(v) end
local w = branch(
rng('a', 'z'),
rng('A', 'Z'),
lit('_')
)
local aw = branch(
w,
rng('0', '9')
)
local s = set(' ', '\n', '\t')
local raw_word = concat(w, any_amount(aw))
local right_word = concat(
raw_word,
neg_look_ahead(aw)
)
local word = concat(
neg_look_behind(aw),
right_word
)
local spaces = any_amount(branch(
s,
-- Comments are really handled by preprocessor, so the following is not needed
concat(
lit('/*'),
any_amount(concat(
neg_look_ahead(lit('*/')),
any_character
)),
lit('*/')
),
concat(
lit('//'),
any_amount(concat(
neg_look_ahead(lit('\n')),
any_character
)),
lit('\n')
)
))
local typ_part = concat(
word,
any_amount(concat(
spaces,
lit('*')
)),
spaces
)
local typ = one_or_more(typ_part)
local typ_id = two_or_more(typ_part)
local arg = typ_id -- argument name is swallowed by typ
local pattern = concat(
typ_id, -- return type with function name
spaces,
lit('('),
spaces,
one_or_no(branch( -- function arguments
concat(
arg, -- first argument, does not require comma
any_amount(concat( -- following arguments, start with a comma
spaces,
lit(','),
spaces,
arg,
any_amount(concat(
lit('['),
spaces,
any_amount(aw),
spaces,
lit(']')
))
)),
one_or_no(concat(
spaces,
lit(','),
spaces,
lit('...')
))
),
lit('void') -- also accepts just void
)),
spaces,
lit(')'),
any_amount(concat( -- optional attributes
spaces,
lit('FUNC_ATTR_'),
any_amount(aw),
one_or_no(concat( -- attribute argument
spaces,
lit('('),
any_amount(concat(
neg_look_ahead(lit(')')),
any_character
)),
lit(')')
))
)),
look_ahead(concat( -- definition must be followed by "{"
spaces,
lit('{')
))
)
if fname == '--help' then
print'Usage:'
print()
print' gendeclarations.lua definitions.c static.h non-static.h "cc -E …"'
os.exit()
end
local pipe = io.popen(cpp .. ' -DDO_NOT_DEFINE_EMPTY_ATTRIBUTES ' .. fname, 'r')
local text = pipe:read('*a')
if not pipe:close() then
os.exit(2)
end
local header = [[
#ifndef DEFINE_FUNC_ATTRIBUTES
# define DEFINE_FUNC_ATTRIBUTES
#endif
#include "nvim/func_attr.h"
#undef DEFINE_FUNC_ATTRIBUTES
]]
local footer = [[
#include "nvim/func_attr.h"
]]
local non_static = header
local static = header
local filepattern = '^# %d+ "[^"]-/?([^"/]+)"'
local curfile
init = 0
curfile = nil
neededfile = fname:match('[^/]+$')
while init ~= nil do
init = text:find('\n', init)
if init == nil then
break
end
init = init + 1
if text:sub(init, init) == '#' then
file = text:match(filepattern, init)
if file ~= nil then
curfile = file
end
elseif curfile == neededfile then
s = init
e = pattern:match(text, init)
if e ~= nil then
local declaration = text:sub(s, e - 1)
-- Comments are really handled by preprocessor, so the following is not
-- needed
declaration = declaration:gsub('/%*.-%*/', '')
declaration = declaration:gsub('//.-\n', '\n')
declaration = declaration:gsub('\n', ' ')
declaration = declaration:gsub('%s+', ' ')
declaration = declaration:gsub(' ?%( ?', '(')
declaration = declaration:gsub(' ?%) ?', ')')
declaration = declaration:gsub(' ?, ?', ', ')
declaration = declaration:gsub(' ?(%*+) ?', ' %1')
declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1')
declaration = declaration:gsub(' $', '')
declaration = declaration .. ';\n'
if text:sub(s, s + 5) == 'static' then
static = static .. declaration
else
non_static = non_static .. declaration
end
end
end
end
non_static = non_static .. footer
static = static .. footer
local F
F = io.open(static_fname, 'w')
F:write(static)
F:close()
F = io.open(non_static_fname, 'w')
F:write(non_static)
F:close()

View File

@@ -94,7 +94,9 @@ output:write([[
]])
for i = 1, #headers do
output:write('\n#include "nvim/'..headers[i]..'"')
if headers[i]:sub(-12) ~= '.generated.h' then
output:write('\n#include "nvim/'..headers[i]..'"')
end
end
output:write([[

141
scripts/stripdecls.py Executable file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/python
# vim: set fileencoding=utf-8:
from __future__ import print_function, unicode_literals, division
from clang.cindex import Index, CursorKind
from collections import namedtuple, OrderedDict, defaultdict
import sys
import os
DECL_KINDS = {
CursorKind.FUNCTION_DECL,
}
Strip = namedtuple('Strip', 'start_line start_column end_line end_column')
def main(progname, cfname, only_static, move_all):
only_static = False
cfname = os.path.abspath(os.path.normpath(cfname))
hfname1 = os.path.splitext(cfname)[0] + os.extsep + 'h'
hfname2 = os.path.splitext(cfname)[0] + '_defs' + os.extsep + 'h'
files_to_modify = (cfname, hfname1, hfname2)
index = Index.create()
src_dirname = os.path.join(os.path.dirname(__file__), '..', 'src')
src_dirname = os.path.abspath(os.path.normpath(src_dirname))
relname = os.path.join(src_dirname, 'nvim')
unit = index.parse(cfname, args=('-I' + src_dirname,
'-DUNIX',
'-DEXITFREE',
'-DFEAT_USR_CMDS',
'-DFEAT_CMDL_COMPL',
'-DFEAT_COMPL_FUNC',
'-DPROTO',
'-DUSE_MCH_ERRMSG'))
cursor = unit.cursor
tostrip = defaultdict(OrderedDict)
definitions = set()
for child in cursor.get_children():
if not (child.location and child.location.file):
continue
fname = os.path.abspath(os.path.normpath(child.location.file.name))
if fname not in files_to_modify:
continue
if child.kind not in DECL_KINDS:
continue
if only_static and next(child.get_tokens()).spelling == 'static':
continue
if child.is_definition() and fname == cfname:
definitions.add(child.spelling)
else:
stripdict = tostrip[fname]
assert(child.spelling not in stripdict)
stripdict[child.spelling] = Strip(
child.extent.start.line,
child.extent.start.column,
child.extent.end.line,
child.extent.end.column,
)
for (fname, stripdict) in tostrip.items():
if not move_all:
for name in set(stripdict) - definitions:
stripdict.pop(name)
if not stripdict:
continue
if fname.endswith('.h'):
is_h_file = True
include_line = next(reversed(stripdict.values())).start_line + 1
else:
is_h_file = False
include_line = next(iter(stripdict.values())).start_line
lines = None
generated_existed = os.path.exists(fname + '.generated.h')
with open(fname, 'rb') as F:
lines = list(F)
stripped = []
for name, position in reversed(stripdict.items()):
sl = slice(position.start_line - 1, position.end_line)
if is_h_file:
include_line -= sl.stop - sl.start
stripped += lines[sl]
lines[sl] = ()
if not generated_existed:
lines[include_line:include_line] = [
'#ifdef INCLUDE_GENERATED_DECLARATIONS\n',
'# include "{0}.generated.h"\n'.format(os.path.relpath(fname, relname)),
'#endif\n',
]
with open(fname, 'wb') as F:
F.writelines(lines)
if __name__ == '__main__':
progname = sys.argv[0]
args = sys.argv[1:]
if not args or '--help' in args:
print('Usage:')
print('')
print(' {0} [--static [--all]] file.c...'.format(progname))
print('')
print('Stripts all declarations from file.c, file.h and file_defs.h.')
print('If --static argument is given then only static declarations are')
print('stripped. Declarations are stripped only if corresponding')
print('definition is found unless --all argument was given.')
print('')
print('Note: it is assumed that static declarations starts with "static"')
print(' keyword.')
sys.exit(0 if args else 1)
if args[0] == '--static':
only_static = True
args = args[1:]
else:
only_static = False
if args[0] == '--all':
move_all = True
args = args[1:]
else:
move_all = False
for cfname in args:
print('Processing {0}'.format(cfname))
main(progname, cfname, only_static, move_all)