diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/functional/ex_cmds/ctrl_c_spec.lua | 62 | ||||
| -rw-r--r-- | test/functional/ex_cmds/global_spec.lua | 74 | ||||
| -rw-r--r-- | test/functional/helpers.lua | 73 | ||||
| -rw-r--r-- | test/functional/legacy/autochdir_spec.lua | 26 | ||||
| -rw-r--r-- | test/functional/plugin/msgpack_spec.lua | 2 | ||||
| -rw-r--r-- | test/functional/plugin/shada_spec.lua | 37 | ||||
| -rw-r--r-- | test/functional/ui/mouse_spec.lua | 14 | ||||
| -rw-r--r-- | test/helpers.lua | 40 | ||||
| -rw-r--r-- | test/unit/formatc.lua | 8 | ||||
| -rw-r--r-- | test/unit/helpers.lua | 15 | ||||
| -rw-r--r-- | test/unit/path_spec.lua | 11 | ||||
| -rw-r--r-- | test/unit/preprocess.lua | 155 |
12 files changed, 320 insertions, 197 deletions
diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua new file mode 100644 index 0000000000..b0acb02000 --- /dev/null +++ b/test/functional/ex_cmds/ctrl_c_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, feed, source = helpers.clear, helpers.feed, helpers.source +local execute = helpers.execute + +if helpers.pending_win32(pending) then return end + +describe("CTRL-C (mapped)", function() + before_each(function() + clear() + end) + + it("interrupts :global", function() + if helpers.skip_fragile(pending, + (os.getenv("TRAVIS") and os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) + then + return + end + + source([[ + set nomore nohlsearch undolevels=-1 + nnoremap <C-C> <NOP> + ]]) + + execute("silent edit! test/functional/fixtures/bigfile.txt") + local screen = Screen.new(52, 6) + screen:attach() + screen:set_default_attr_ids({ + [0] = {foreground = Screen.colors.White, + background = Screen.colors.Red}, + [1] = {bold = true, + foreground = Screen.colors.SeaGreen} + }) + + screen:expect([[ + ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;| + | + ]]) + + local function test_ctrl_c(ms) + feed(":global/^/p<CR>") + helpers.sleep(ms) + feed("<C-C>") + screen:expect([[Interrupt]], nil, nil, nil, true) + end + + -- The test is time-sensitive. Try different sleep values. + local ms_values = {1, 10, 100} + for i, ms in ipairs(ms_values) do + if i < #ms_values then + local status, _ = pcall(test_ctrl_c, ms) + if status then break end + else -- Call the last attempt directly. + test_ctrl_c(ms) + end + end + end) +end) diff --git a/test/functional/ex_cmds/global_spec.lua b/test/functional/ex_cmds/global_spec.lua deleted file mode 100644 index 81a0ef3248..0000000000 --- a/test/functional/ex_cmds/global_spec.lua +++ /dev/null @@ -1,74 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') -local clear, feed, source = helpers.clear, helpers.feed, helpers.source - -if helpers.pending_win32(pending) then return end - -describe(':global', function() - before_each(function() - clear() - end) - - it('is interrupted by mapped CTRL-C', function() - if os.getenv("TRAVIS") and os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN" then - -- XXX: ASAN_UBSAN is too slow to react to the CTRL-C. - pending("", function() end) - return - end - - source([[ - set nomore - set undolevels=-1 - nnoremap <C-C> <NOP> - for i in range(0, 99999) - put ='XXX' - endfor - put ='ZZZ' - 1 - .delete - ]]) - - local screen = Screen.new(52, 6) - screen:attach() - screen:set_default_attr_ids({ - [0] = {foreground = Screen.colors.White, - background = Screen.colors.Red}, - [1] = {bold = true, - foreground = Screen.colors.SeaGreen} - }) - - screen:expect([[ - ^XXX | - XXX | - XXX | - XXX | - XXX | - | - ]]) - - local function test_ctrl_c(ms) - feed(":global/^/p<CR>") - helpers.sleep(ms) - feed("<C-C>") - screen:expect([[ - XXX | - XXX | - XXX | - XXX | - {0:Interrupted} | - Interrupt: {1:Press ENTER or type command to continue}^ | - ]]) - end - - -- The test is time-sensitive. Try with different sleep values. - local ms_values = {10, 50, 100} - for i, ms in ipairs(ms_values) do - if i < #ms_values then - local status, _ = pcall(test_ctrl_c, ms) - if status then break end - else -- Call the last attempt directly. - test_ctrl_c(ms) - end - end - end) -end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index f3332cff4f..5eec3afe65 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -14,6 +14,7 @@ local neq = global_helpers.neq local eq = global_helpers.eq local ok = global_helpers.ok +local start_dir = lfs.currentdir() local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim' local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.', @@ -21,6 +22,9 @@ local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', local mpack = require('mpack') +local tmpname = global_helpers.tmpname +local uname = global_helpers.uname + -- Formulate a path to the directory containing nvim. We use this to -- help run test executables. It helps to keep the tests working, even -- when the build is not in the default location. @@ -334,44 +338,6 @@ local function write_file(name, text, dont_dedent) file:close() end --- Tries to get platform name from $SYSTEM_NAME, uname; fallback is "Windows". -local uname = (function() - local platform = nil - return (function() - if platform then - return platform - end - - platform = os.getenv("SYSTEM_NAME") - if platform then - return platform - end - - local status, f = pcall(io.popen, "uname -s") - if status then - platform = f:read("*l") - else - platform = 'Windows' - end - return platform - end) -end)() - -local function tmpname() - local fname = os.tmpname() - if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then - -- In Windows tmpname() returns a filename starting with - -- special sequence \s, prepend $TEMP path - local tmpdir = os.getenv('TEMP') - return tmpdir..fname - elseif fname:match('^/tmp') and uname() == 'Darwin' then - -- In OS X /tmp links to /private/tmp - return '/private'..fname - else - return fname - end -end - local function source(code) local fname = tmpname() write_file(fname, code) @@ -475,6 +441,12 @@ end local function rmdir(path) local ret, _ = pcall(do_rmdir, path) + if not ret and os_name() == "windows" then + -- Maybe "Permission denied"; try again after changing the nvim + -- process to the top-level directory. + nvim_command([[exe 'cd '.fnameescape(']]..start_dir.."')") + ret, _ = pcall(do_rmdir, path) + end -- During teardown, the nvim process may not exit quickly enough, then rmdir() -- will fail (on Windows). if not ret then -- Try again. @@ -520,12 +492,12 @@ local function create_callindex(func) end -- Helper to skip tests. Returns true in Windows systems. --- pending_func is pending() from busted -local function pending_win32(pending_func) +-- pending_fn is pending() from busted +local function pending_win32(pending_fn) clear() if uname() == 'Windows' then - if pending_func ~= nil then - pending_func('FIXME: Windows', function() end) + if pending_fn ~= nil then + pending_fn('FIXME: Windows', function() end) end return true else @@ -533,6 +505,22 @@ local function pending_win32(pending_func) end end +-- Calls pending() and returns `true` if the system is too slow to +-- run fragile or expensive tests. Else returns `false`. +local function skip_fragile(pending_fn, cond) + if pending_fn == nil or type(pending_fn) ~= type(function()end) then + error("invalid pending_fn") + end + if cond then + pending_fn("skipped (test is fragile on this system)", function() end) + return true + elseif os.getenv("TEST_SKIP_FRAGILE") then + pending_fn("skipped (TEST_SKIP_FRAGILE)", function() end) + return true + end + return false +end + local funcs = create_callindex(nvim_call) local meths = create_callindex(nvim) local uimeths = create_callindex(ui) @@ -601,6 +589,7 @@ return function(after_each) curwinmeths = curwinmeths, curtabmeths = curtabmeths, pending_win32 = pending_win32, + skip_fragile = skip_fragile, tmpname = tmpname, NIL = mpack.NIL, } diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua new file mode 100644 index 0000000000..06f7c1dd11 --- /dev/null +++ b/test/functional/legacy/autochdir_spec.lua @@ -0,0 +1,26 @@ +local lfs = require('lfs') +local helpers = require('test.functional.helpers')(after_each) +local clear, eq = helpers.clear, helpers.eq +local eval, execute = helpers.eval, helpers.execute + +describe('autochdir behavior', function() + local dir = 'Xtest-functional-legacy-autochdir' + + before_each(function() + lfs.mkdir(dir) + clear() + end) + + after_each(function() + helpers.rmdir(dir) + end) + + -- Tests vim/vim/777 without test_autochdir(). + it('sets filename', function() + execute('set acd') + execute('new') + execute('w '..dir..'/Xtest') + eq('Xtest', eval("expand('%')")) + eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]])) + end) +end) diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index c8da8e8f6c..5ba19708cf 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -652,6 +652,8 @@ describe('In autoload/msgpack.vim', function() eval_eq('integer', ('a'):byte(), '\'a\'') eval_eq('integer', 0xAB, '\'«\'') + eval_eq('integer', 0, '\'\\0\'') + eval_eq('integer', 10246567, '\'\\10246567\'') end) it('correctly loads constants', function() diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index b1209a22e9..b543037ae2 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -609,6 +609,18 @@ describe('In autoload/shada.vim', function() 'abc', -1, ]}] ]]):gsub('\n', '')) + -- Regression: NUL separator must be properly supported + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' - separator \'\\0\'', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + '', + 0x0 + ]}] ]]):gsub('\n', '')) end) it('works with register items', function() @@ -837,7 +849,7 @@ describe('In autoload/shada.vim', function() sd2strings_eq({ 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', - ' + n name 20', + ' + n name \'\\20\'', ' + f file name "foo"', ' # Value is negative', ' + l line number -10', @@ -852,7 +864,18 @@ describe('In autoload/shada.vim', function() sd2strings_eq({ 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', - ' + n name 20', + ' + n name 128', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 128, + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \'\\20\'', ' + f file name "foo"', ' # Expected integer', ' + l line number "FOO"', @@ -1123,7 +1146,7 @@ describe('In autoload/shada.vim', function() 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', - ' + n name 20', + ' + n name \'\\20\'', ' # Value is negative', ' + l line number -10', ' # Value is negative', @@ -1138,7 +1161,7 @@ describe('In autoload/shada.vim', function() 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', - ' + n name 20', + ' + n name \'\\20\'', ' # Expected integer', ' + l line number "FOO"', ' # Expected integer', @@ -1932,13 +1955,13 @@ describe('In autoload/shada.vim', function() 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', - ' + f file name 10', + ' + f file name \'\\10\'', ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected binary string', - ' + f file name 20', + ' + f file name \'\\20\'', ' + l line number 1', ' + c column 0', }) @@ -1948,7 +1971,7 @@ describe('In autoload/shada.vim', function() 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', - ' + f file name 10', + ' + f file name \'\\10\'', ' + l line number 1', ' + c column 0', '', diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index b729b0db08..17d949825a 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -153,9 +153,10 @@ describe('Mouse input', function() end) it('in tabline to the left moves tab left', function() - if os.getenv("TRAVIS") and (helpers.os_name() == "osx" - or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN") then - pending("[Fails on Travis macOS, ASAN_UBSAN. #4874]", function() end) + if helpers.skip_fragile(pending, + os.getenv("TRAVIS") and (helpers.os_name() == "osx" + or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) -- #4874 + then return end @@ -257,9 +258,10 @@ describe('Mouse input', function() end) it('out of tabline to the left moves tab left', function() - if os.getenv("TRAVIS") and (helpers.os_name() == "osx" - or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN") then - pending("[Fails on Travis macOS, ASAN_UBSAN. #4874]", function() end) + if helpers.skip_fragile(pending, + os.getenv("TRAVIS") and (helpers.os_name() == "osx" + or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) -- #4874 + then return end diff --git a/test/helpers.lua b/test/helpers.lua index 4c50c7644f..0bc62da5d7 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -52,9 +52,49 @@ local function check_logs() assert(0 == runtime_errors) end +-- Tries to get platform name from $SYSTEM_NAME, uname; fallback is "Windows". +local uname = (function() + local platform = nil + return (function() + if platform then + return platform + end + + platform = os.getenv("SYSTEM_NAME") + if platform then + return platform + end + + local status, f = pcall(io.popen, "uname -s") + if status then + platform = f:read("*l") + else + platform = 'Windows' + end + return platform + end) +end)() + +local function tmpname() + local fname = os.tmpname() + if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then + -- In Windows tmpname() returns a filename starting with + -- special sequence \s, prepend $TEMP path + local tmpdir = os.getenv('TEMP') + return tmpdir..fname + elseif fname:match('^/tmp') and uname() == 'Darwin' then + -- In OS X /tmp links to /private/tmp + return '/private'..fname + else + return fname + end +end + return { eq = eq, neq = neq, ok = ok, check_logs = check_logs, + uname = uname, + tmpname = tmpname, } diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index 00637e0b8d..e288081960 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -219,13 +219,7 @@ local function standalone(...) -- luacheck: ignore Preprocess.add_to_include_path('./../../build/include') Preprocess.add_to_include_path('./../../.deps/usr/include') - local input = Preprocess.preprocess_stream(arg[1]) - local raw = input:read('*all') - input:close() - - if raw == nil then - print("ERROR: Preprocess.preprocess_stream():read() returned empty") - end + local raw = Preprocess.preprocess('', arg[1]) local formatted if #arg == 2 and arg[2] == 'no' then diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 9025c64570..45bbaaeb10 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -45,6 +45,8 @@ local function filter_complex_blocks(body) return table.concat(result, "\n") end +local previous_defines = '' + -- 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. local function cimport(...) @@ -66,17 +68,8 @@ local function cimport(...) return libnvim end - local body = nil - for _ = 1, 10 do - local stream = Preprocess.preprocess_stream(unpack(paths)) - body = stream:read("*a") - stream:close() - if body ~= nil then break end - end - - if body == nil then - print("ERROR: helpers.lua: Preprocess.preprocess_stream():read() returned empty") - end + local body + body, previous_defines = Preprocess.preprocess(previous_defines, unpack(paths)) -- format it (so that the lines are "unique" statements), also filter out -- Objective-C blocks diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 9b76834383..ccaf0228ab 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -336,6 +336,17 @@ describe('more path function', function() eq(FAIL, result) end) + it('fails safely if given length is wrong #5737', function() + local force_expansion = 1 + local filename = 'foo/bar/bazzzzzzz/buz/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a' + local too_short_len = 8 + local buf = cstr(too_short_len, '') + local result = path.vim_FullName(filename, buf, too_short_len, force_expansion) + local expected = string.sub(filename, 1, (too_short_len - 1)) + eq(expected, (ffi.string(buf))) + eq(FAIL, result) + end) + it('uses the filename if the filename is a URL', function() local force_expansion = 1 local filename = 'http://www.neovim.org' diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 10ba997758..8c2a5c73e5 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -7,22 +7,22 @@ local ccs = {} local env_cc = os.getenv("CC") if env_cc then - table.insert(ccs, {path = "/usr/bin/env " .. tostring(env_cc), type = "gcc"}) + table.insert(ccs, {path = {"/usr/bin/env", env_cc}, type = "gcc"}) end if ffi.os == "Windows" then - table.insert(ccs, {path = "cl", type = "msvc"}) + table.insert(ccs, {path = {"cl"}, type = "msvc"}) end -table.insert(ccs, {path = "/usr/bin/env cc", type = "gcc"}) -table.insert(ccs, {path = "/usr/bin/env gcc", type = "gcc"}) -table.insert(ccs, {path = "/usr/bin/env gcc-4.9", type = "gcc"}) -table.insert(ccs, {path = "/usr/bin/env gcc-4.8", type = "gcc"}) -table.insert(ccs, {path = "/usr/bin/env gcc-4.7", type = "gcc"}) -table.insert(ccs, {path = "/usr/bin/env clang", type = "clang"}) -table.insert(ccs, {path = "/usr/bin/env icc", type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "cc"}, type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "gcc"}, type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.9"}, type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.8"}, type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.7"}, type = "gcc"}) +table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"}) +table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"}) -local quote_me = '[^%w%+%-%=%@%_%/]' -- complement (needn't quote) +local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote) local function shell_quote(str) if string.find(str, quote_me) or str == '' then return "'" .. string.gsub(str, "'", [['"'"']]) .. "'" @@ -61,12 +61,12 @@ end -- will produce a string that represents a meta C header file that includes -- all the passed in headers. I.e.: -- --- headerize({"stdio.h", "math.h", true} +-- headerize({"stdio.h", "math.h"}, true) -- produces: -- #include <stdio.h> -- #include <math.h> -- --- headerize({"vim.h", "memory.h", false} +-- headerize({"vim.h", "memory.h"}, false) -- produces: -- #include "vim.h" -- #include "memory.h" @@ -79,8 +79,7 @@ local function headerize(headers, global) end local formatted = {} - for i = 1, #headers do - local hdr = headers[i] + for _, hdr in ipairs(headers) do formatted[#formatted + 1] = "#include " .. tostring(pre) .. tostring(hdr) .. @@ -91,49 +90,77 @@ local function headerize(headers, global) end local Gcc = { + preprocessor_extra_flags = {}, + get_defines_extra_flags = {'-std=c99', '-dM', '-E'}, + get_declarations_extra_flags = {'-std=c99', '-P', '-E'}, +} + +function Gcc:define(name, args, val) + local define = '-D' .. name + if args ~= nil then + define = define .. '(' .. table.concat(args, ',') .. ')' + end + if val ~= nil then + define = define .. '=' .. val + end + self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = define +end + +function Gcc:undefine(name) + self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = ( + '-U' .. name) +end + +function Gcc:init_defines() -- preprocessor flags that will hopefully make the compiler produce C -- declarations that the LuaJIT ffi understands. - preprocessor_extra_flags = { - '-D "aligned(ARGS)="', - '-D "__attribute__(ARGS)="', - '-D "__asm(ARGS)="', - '-D "__asm__(ARGS)="', - '-D "__inline__="', - '-D "EXTERN=extern"', - '-D "INIT(...)="', - '-D_GNU_SOURCE', - '-DINCLUDE_GENERATED_DECLARATIONS', - - -- Needed for FreeBSD - '-D "_Thread_local="', - - -- Needed for macOS Sierra - '-D "_Nullable="', - '-D "_Nonnull="', - '-U__BLOCKS__', - } -} + self:define('aligned', {'ARGS'}, '') + self:define('__attribute__', {'ARGS'}, '') + self:define('__asm', {'ARGS'}, '') + self:define('__asm__', {'ARGS'}, '') + self:define('__inline__', nil, '') + self:define('EXTERN', nil, 'extern') + self:define('INIT', {'...'}, '') + self:define('_GNU_SOURCE') + self:define('INCLUDE_GENERATED_DECLARATIONS') + -- Needed for FreeBSD + self:define('_Thread_local', nil, '') + -- Needed for macOS Sierra + self:define('_Nullable', nil, '') + self:define('_Nonnull', nil, '') + self:undefine('__BLOCKS__') +end function Gcc:new(obj) obj = obj or {} setmetatable(obj, self) self.__index = self + self:init_defines() return obj end function Gcc:add_to_include_path(...) - local paths = {...} - for i = 1, #paths do - local path = paths[i] - local directive = '-I ' .. '"' .. path .. '"' + for i = 1, select('#', ...) do + local path = select(i, ...) local ef = self.preprocessor_extra_flags - ef[#ef + 1] = directive + ef[#ef + 1] = '-I' .. path + end +end + +local function argss_to_cmd(...) + local cmd = '' + for i = 1, select('#', ...) do + for _, arg in ipairs(select(i, ...)) do + cmd = cmd .. ' ' .. shell_quote(arg) + end end + return cmd end -- returns a list of the headers files upon which this file relies function Gcc:dependencies(hdr) - local out = io.popen(tostring(self.path) .. " -M " .. tostring(hdr) .. " 2>&1") + local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1' + local out = io.popen(cmd) local deps = out:read("*a") out:close() if deps then @@ -143,23 +170,51 @@ function Gcc:dependencies(hdr) end end +local function repeated_call(...) + local cmd = argss_to_cmd(...) + for _ = 1, 10 do + local stream = io.popen(cmd) + local ret = stream:read('*a') + stream:close() + if ret then + return ret + end + end + print('ERROR: preprocess.lua: Failed to execute ' .. cmd .. ': nil return after 10 attempts') + return nil +end + -- returns a stream representing a preprocessed form of the passed-in headers. -- Don't forget to close the stream by calling the close() method on it. -function Gcc:preprocess_stream(...) +function Gcc:preprocess(previous_defines, ...) -- create pseudo-header local pseudoheader = headerize({...}, false) - local defines = table.concat(self.preprocessor_extra_flags, ' ') - local cmd = ("echo $hdr | " .. - tostring(self.path) .. - " " .. - tostring(defines) .. - " -std=c99 -P -E -"):gsub('$hdr', shell_quote(pseudoheader)) + local pseudoheader_fname = 'tmp_pseudoheader.h' + local pseudoheader_file = io.open(pseudoheader_fname, 'w') + pseudoheader_file:write(previous_defines) + pseudoheader_file:write("\n") + pseudoheader_file:write(pseudoheader) + pseudoheader_file:flush() + pseudoheader_file:close() + + local defines = repeated_call(self.path, self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname}) + -- lfs = require("lfs") -- print("CWD: #{lfs.currentdir!}") -- print("CMD: #{cmd}") -- io.stderr\write("CWD: #{lfs.currentdir!}\n") -- io.stderr\write("CMD: #{cmd}\n") - return io.popen(cmd) + + local declarations = repeated_call(self.path, self.preprocessor_extra_flags, + self.get_declarations_extra_flags, + {pseudoheader_fname}) + + os.remove(pseudoheader_fname) + + assert(declarations and defines) + return declarations, defines end local Clang = Gcc:new() @@ -197,8 +252,8 @@ return { includes = function(hdr) return cc:dependencies(hdr) end, - preprocess_stream = function(...) - return cc:preprocess_stream(...) + preprocess = function(...) + return cc:preprocess(...) end, add_to_include_path = function(...) return cc:add_to_include_path(...) |