diff options
Diffstat (limited to 'test/unit/preprocess.moon')
-rw-r--r-- | test/unit/preprocess.moon | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/test/unit/preprocess.moon b/test/unit/preprocess.moon new file mode 100644 index 0000000000..88580476b2 --- /dev/null +++ b/test/unit/preprocess.moon @@ -0,0 +1,155 @@ +-- 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 acording 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_GNU_SOURCE' + } + + 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(...) +} |