neovim/test/unit/helpers.moon
Nicolas Hillegeer d699ccfb0c test: fix the cimport method
This commit will hopefully allow the cimport method to be used just as one
would use #inclue <header.h> in C. It follows the following method:

1. create a pseudoheader file that #include's all the requested header files
2. runs the pseudoheader through the C preprocessor (it will try various
   compilers if available on the system).
3. runs the preprocessed file through a C formatter, which attempts to group
   statements on one line. For example, a struct definition that was
   formerly on several lines will take just one line after formatting. This
   is done so that unique declarations can be detected. Duplicates are thus
   easy to remove.
4. remove lines that are too complex for the LuaJIT C parser (such as:
   Objective-C block syntax, crazy enums defined on linux, ...)
5. remove duplicate declarations
6. pass result to ffi.cdef
2014-04-28 16:17:25 -03:00

125 lines
3.4 KiB
Plaintext

ffi = require 'ffi'
lpeg = require 'lpeg'
formatc = require 'test.unit.formatc'
Set = require 'test.unit.set'
Preprocess = require 'test.unit.preprocess'
-- add some standard header locations
-- TODO(aktau, jszakmeister): optionally pass more header locations via env
Preprocess.add_to_include_path('./src')
Preprocess.add_to_include_path('./.deps/usr/include')
Preprocess.add_to_include_path('./build/config')
if ffi.abi('32bit')
Preprocess.add_to_include_path('/opt/neovim-deps/32/include')
else
Preprocess.add_to_include_path('/opt/neovim-deps/include')
-- load neovim shared library
testlib = os.getenv 'NVIM_TEST_LIB'
unless testlib
testlib = './build/src/libnvim-test.so'
libnvim = ffi.load testlib
trim = (s) ->
s\match'^%s*(.*%S)' or ''
-- a Set that keeps around the lines we've already seen
export cdefs
if cdefs == nil
cdefs = Set!
export imported
if imported == nil
imported = Set!
-- some things are just too complex for the LuaJIT C parser to digest. We
-- usually don't need them anyway.
filter_complex_blocks = (body) ->
result = {}
for line in body\gmatch("[^\r\n]+")
-- remove all lines that contain Objective-C block syntax, the LuaJIT ffi
-- doesn't understand it.
if string.find(line, "(^)", 1, true) ~= nil
continue
if string.find(line, "_ISwupper", 1, true) ~= nil
continue
result[#result + 1] = line
table.concat(result, "\n")
-- use this helper to import C files, you can pass multiple paths at once,
-- this helper will return the C namespace of the nvim library.
-- cimport = (path) ->
cimport = (...) ->
-- filter out paths we've already imported
paths = [path for path in *{...} when not imported\contains(path)]
for path in *paths
imported\add(path)
if #paths == 0
return libnvim
-- preprocess the header
stream = Preprocess.preprocess_stream(unpack(paths))
body = stream\read("*a")
stream\close!
-- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks
body = formatc(body)
body = filter_complex_blocks(body)
-- add the formatted lines to a set
new_cdefs = Set!
for line in body\gmatch("[^\r\n]+")
new_cdefs\add(trim(line))
-- subtract the lines we've already imported from the new lines, then add
-- the new unique lines to the old lines (so they won't be imported again)
new_cdefs\diff(cdefs)
cdefs\union(new_cdefs)
if new_cdefs\size! == 0
-- if there's no new lines, just return
return libnvim
-- request a sorted version of the new lines (same relative order as the
-- original preprocessed file) and feed that to the LuaJIT ffi
new_lines = new_cdefs\to_table!
ffi.cdef(table.concat(new_lines, "\n"))
return libnvim
testinc = os.getenv 'TEST_INCLUDES'
unless testinc
testinc = './build/test/includes/post'
cppimport = (path) ->
return cimport testinc .. '/' .. path
cimport './src/types.h'
-- take a pointer to a C-allocated string and return an interned
-- version while also freeing the memory
internalize = (cdata) ->
ffi.gc cdata, ffi.C.free
return ffi.string cdata
cstr = ffi.typeof 'char[?]'
to_cstr = (string) ->
cstr (string.len string) + 1, string
return {
cimport: cimport
cppimport: cppimport
internalize: internalize
eq: (expected, actual) -> assert.are.same expected, actual
neq: (expected, actual) -> assert.are_not.same expected, actual
ffi: ffi
lib: libnvim
cstr: cstr
to_cstr: to_cstr
}