mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 06:48:17 +00:00

Unit tests never need to declare globals, only access them. In the main code base this is handled by including "vim.h". If a file wants to declare globals (in the case of neovim that's only main.c), it #define's EXTERN and includes "vim.h". Otherwise, a file just includes "vim.h" (that's the majority case). Since we want to be able to run unit tests without including "vim.h", we predefine "EXTERN" to mean extern. That way, we don't have to include "vim.h".
159 lines
4.4 KiB
Plaintext
159 lines
4.4 KiB
Plaintext
-- helps managing loading different headers into the LuaJIT ffi. Untested on
|
|
-- windows, will probably need quite a bit of adjustment to run there.
|
|
|
|
ffi = require("ffi")
|
|
|
|
ccs = {}
|
|
|
|
env_cc = os.getenv("CC")
|
|
if env_cc
|
|
table.insert(ccs, {path: "/usr/bin/env #{env_cc}", type: "gcc"})
|
|
|
|
if ffi.os == "Windows"
|
|
table.insert(ccs, {path: "cl", type: "msvc"})
|
|
|
|
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"})
|
|
|
|
quote_me = '[^%w%+%-%=%@%_%/]' -- complement (needn't quote)
|
|
shell_quote = (str) ->
|
|
if string.find(str, quote_me) or str == '' then
|
|
"'" .. string.gsub(str, "'", [['"'"']]) .. "'"
|
|
else
|
|
str
|
|
|
|
-- parse Makefile format dependencies into a Lua table
|
|
parse_make_deps = (deps) ->
|
|
-- remove line breaks and line concatenators
|
|
deps = deps\gsub("\n", "")\gsub("\\", "")
|
|
|
|
-- remove the Makefile "target:" element
|
|
deps = deps\gsub(".+:", "")
|
|
|
|
-- remove redundant spaces
|
|
deps = deps\gsub(" +", " ")
|
|
|
|
-- split according to token (space in this case)
|
|
headers = {}
|
|
for token in deps\gmatch("[^%s]+")
|
|
-- headers[token] = true
|
|
headers[#headers + 1] = token
|
|
|
|
-- resolve path redirections (..) to normalize all paths
|
|
for i, v in ipairs(headers)
|
|
-- double dots (..)
|
|
headers[i] = v\gsub("/[^/%s]+/%.%.", "")
|
|
|
|
-- single dot (.)
|
|
headers[i] = v\gsub("%./", "")
|
|
|
|
headers
|
|
|
|
-- 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}
|
|
-- produces:
|
|
-- #include <stdio.h>
|
|
-- #include <math.h>
|
|
--
|
|
-- headerize({"vim.h", "memory.h", false}
|
|
-- produces:
|
|
-- #include "vim.h"
|
|
-- #include "memory.h"
|
|
headerize = (headers, global) ->
|
|
pre = '"'
|
|
post = pre
|
|
if global
|
|
pre = "<"
|
|
post = ">"
|
|
|
|
formatted = ["#include #{pre}#{hdr}#{post}" for hdr in *headers]
|
|
table.concat(formatted, "\n")
|
|
|
|
class Gcc
|
|
-- 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'
|
|
}
|
|
|
|
new: (path) =>
|
|
@path = path
|
|
|
|
add_to_include_path: (...) =>
|
|
paths = {...}
|
|
for path in *paths
|
|
directive = '-I ' .. '"' .. path .. '"'
|
|
@@preprocessor_extra_flags[#@@preprocessor_extra_flags + 1] = directive
|
|
|
|
-- returns a list of the headers files upon which this file relies
|
|
dependencies: (hdr) =>
|
|
out = io.popen("#{@path} -M #{hdr} 2>&1")
|
|
deps = out\read("*a")
|
|
out\close!
|
|
|
|
if deps
|
|
parse_make_deps(deps)
|
|
else
|
|
nil
|
|
|
|
-- 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.
|
|
preprocess_stream: (...) =>
|
|
paths = {...}
|
|
-- create pseudo-header
|
|
pseudoheader = headerize(paths, false)
|
|
defines = table.concat(@@preprocessor_extra_flags, ' ')
|
|
cmd = ("echo $hdr | #{@path} #{defines} -std=c99 -P -E -")\gsub('$hdr', shell_quote(pseudoheader))
|
|
-- lfs = require("lfs")
|
|
-- print("CWD: #{lfs.currentdir!}")
|
|
-- print("CMD: #{cmd}")
|
|
-- io.stderr\write("CWD: #{lfs.currentdir!}\n")
|
|
-- io.stderr\write("CMD: #{cmd}\n")
|
|
io.popen(cmd)
|
|
|
|
class Clang extends Gcc
|
|
class Msvc extends Gcc
|
|
|
|
type_to_class = {
|
|
"gcc": Gcc,
|
|
"clang": Clang,
|
|
"msvc": Msvc
|
|
}
|
|
|
|
find_best_cc = (ccs) ->
|
|
for _, meta in pairs(ccs)
|
|
version = io.popen("#{meta.path} -v 2>&1")
|
|
version\close!
|
|
if version
|
|
return type_to_class[meta.type](meta.path)
|
|
nil
|
|
|
|
-- find the best cc. If os.exec causes problems on windows (like popping up
|
|
-- a console window) we might consider using something like this:
|
|
-- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec
|
|
cc = nil
|
|
if cc == nil
|
|
cc = find_best_cc(ccs)
|
|
|
|
return {
|
|
includes: (hdr) -> cc\dependencies(hdr)
|
|
preprocess_stream: (...) -> cc\preprocess_stream(...)
|
|
add_to_include_path: (...) -> cc\add_to_include_path(...)
|
|
}
|