forked from thenumbernine/include-lua
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinclude-list.lua
More file actions
332 lines (300 loc) · 9.75 KB
/
include-list.lua
File metadata and controls
332 lines (300 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
-- TODO chop this file up into per-OS files for system including , and per-lib files
-- mapping from c includes to luajit ffi/ includes
-- this is used for automated generation
-- this is also used during generation for swapping out #includes with require()'s of already-generated files
--[[
TODO an exhaustive way to generate all with the least # of intermediate files could be
- go down the list
- for each file, generate
- as you reach includes, see if any previous have requested the same include.
- if so then generate that include, and restart the process.
--]]
local ffi = require 'ffi'
local template = require 'template'
local path = require 'ext.path'
local assert = require 'ext.assert'
local string = require 'ext.string'
local table = require 'ext.table'
local io = require 'ext.io'
local os = require 'ext.os'
local tolua = require 'ext.tolua'
local util = require 'include.util'
local safegsub = util.safegsub
local removeEnum = util.removeEnum
local includeList = table()
-- files found in multiple OS's will go in [os]/[path]
-- and then in just [path] will be a file that determines the file based on os (and arch?)
--[[
OS-specific bindings
each should have:
1) internal includes that vary per OS
2) external includes that make up the API of requests for `require 'ffi.req' 'c.whatever'` <=> `#include <whatever.h>`
https://stackoverflow.com/a/2029106/2714073
--]]
if ffi.os == 'Windows' then
includeList:append(require 'include.include-list-windows')
end
if ffi.os == 'Linux' then
includeList:append(require 'include.include-list-linux')
end
if ffi.os == 'OSX' then
includeList:append(require 'include.include-list-osx')
end
if ffi.os == 'Android' then
includeList:append(require 'include.include-list-android')
end
if ffi.os == 'Haiku' then
includeList:append(require 'include.include-list-haiku')
end
assert(#includeList > 0, "I didn't find your OS "..tostring(ffi.os))
includeList:append(table{
-- these come from external libraries (so I don't put them in the c/ subfolder)
-- apt install libarchive-dev
{
inc='<archive.h>',
moreincs = {
'<archive_entry.h>',
},
out='archive.lua',
final = function(code)
code = code .. [[
return require 'ffi.load' 'archive'
]]
return code
end,
},
-- apt install libffi-dev
{
inc = '<ffi.h>',
out = 'libffi.lua',
pkgconfig = 'libffi', -- points to /System stuff, not homebrew...
final = function(code)
code = removeEnum(code, 'FFI_64_BIT_MAX = 9223372036854775807')
code = [[
-- WARNING, this is libffi, not luajit ffi
-- will that make my stupid ?/?.lua LUA_PATH rule screw things up? if so then move this file ... or rename it to libffi.lua or something
]] .. code .. [[
return require 'ffi.load' 'ffi'
]]
return code
end,
},
-- apt install libhdf5-dev
-- depends: inttypes.h
{
inc = '<hdf5.h>',
out = 'hdf5.lua',
pkgconfig = 'hdf5',
final = function(code)
-- old header comment:
-- for gcc / ubuntu looks like off_t is defined in either unistd.h or stdio.h, and either are set via testing/setting __off_t_defined
-- in other words, the defs in here are getting more and more conditional ...
-- pretty soon a full set of headers + full preprocessor might be necessary
-- TODO regen this on Windows and compare?
code = code .. [[
return require 'ffi.load' 'hdf5' -- pkg-config --libs hdf5
]]
return code
end,
-- ffi.load override information
-- TODO somehow insert this into ffi/load.lua without destroying the file
-- don't modify require 'ffi.load' within 'ffi.hdf5', since the whole point of fif.load is for the user to provide overrides to the lib loc that the header needs.
ffiload = {
hdf5 = {Linux = '/usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.so'},
},
},
-- these external files are per-OS
-- maybe eventually all .h's will be?
-- depends on complex.h
{inc='<cblas.h>', out='cblas.lua', final=function(code)
code = [[
]] .. code .. [[
return require 'ffi.load' 'openblas'
]]
return code
end},
{
inc = '<lapack.h>',
out = 'lapack.lua',
pkgconfig = 'lapack',
final = function(code)
-- needs lapack_int replaced with int, except the enum def line
-- the def is conditional, but i think this is the right eval ...
code = safegsub(code, 'enum { lapack_int = 0 };', 'typedef int32_t lapack_int;')
--[[
#if defined(LAPACK_ILP64)
#define lapack_int int64_t
#else
#define lapack_int int32_t
#endif
--]]
-- preproc on this generate a *LOT* of `enum { LAPACK_lsame_base = 0 };`
-- they are generated from macro calls to LAPACK_GLOBAL
-- which is defined as
-- #define LAPACK_GLOBAL(lcname,UCNAME) lcname##_
-- ... soo ... I need to not gen enums for macros that do string manipulation or whatever
code = safegsub(code, 'enum { LAPACK_[_%w]+ = 0 };', '')
code = safegsub(code, '\n\n', '\n')
code = code .. [[
return require 'ffi.load' 'lapack'
]]
return code
end,
},
{
inc = '<lapacke.h>',
out = 'lapacke.lua',
pkgconfig = 'lapacke',
final = function(code)
code = code .. [[
return require 'ffi.load' 'lapacke'
]]
return code
end,
},
{
inc = '<pulse/pulseaudio.h>',
out = 'pulse.lua',
final = function(code)
-- so this spits out enums for both enums and #define's
-- that runs us into trouble sometimes ...
local lines = string.split(code, '\n')
local definedEnums = {}
for i=1,#lines do
local line = lines[i]
if line:match'^typedef enum' then
for w in line:gmatch'%S+' do
if w:match'^PA_' then
if w:match',$' then w = w:sub(1,-2) end
--io.stderr:write('defining typedef enum '..w..'\n')
definedEnums[w] = true
end
end
end
local prefix, enumName = line:match'^(.*)enum { (.*) = 0 };$'
if enumName then
--io.stderr:write('found enum=0 name '..enumName..'\n')
if definedEnums[enumName] then
--io.stderr:write('...removing\n')
lines[i] = prefix
end
end
end
code = lines:concat'\n'
-- undefs of static inline functions ...
for f in ([[PA_CONTEXT_IS_GOOD PA_STREAM_IS_GOOD PA_SINK_IS_OPENED PA_SOURCE_IS_OPENED]]):gmatch'%S+' do
code = removeStaticInlineFunction(code, f)
code = safegsub(code, 'enum { '..f..' = 0 };', '')
end
return code
end,
},
{
inc = '<tensorflow/c/c_api.h>',
out = 'tensorflow.lua',
-- tensorflow is failing when it includes <string.h>, which is funny because I already generate <string.h> above
-- something in it is pointing to <secure/_string.h>, which is redefining memcpy ... which is breaking my parser (tho it shouldnt break, but i don't want to fix it)
skipincs = (ffi.os == 'OSX') and {'<string.h>'} or {},
final = function(code)
code = code .. [[
return require 'ffi.load' 'tensorflow'
]]
return code
end,
},
})
-- now detect any duplicate #include paths and make sure they are going to distinct os-specific destination file names
-- and in those cases, add in a splitting file that redirects to the specific OS
local detectDups = {}
for _,inc in ipairs(includeList) do
detectDups[inc.inc] = detectDups[inc.inc] or {}
local det = detectDups[inc.inc][inc.os or 'all']
if det then
print("got two entries that have matching include name, and at least one is not os-specific: "..tolua{
det,
inc,
})
end
detectDups[inc.inc][inc.os or 'all'] = inc
end
for incname, det in pairs(detectDups) do
if type(det) == 'table' then
local keys = table.keys(det)
-- if we had more than 1 key
if #keys > 1
then
local base
for os,inc in pairs(det) do
assert(inc.os, "have a split file and one entry doesn't have an os... "..tolua(inc))
local incbase = inc.out:match('^'..inc.os..'/(.*)$')
if not incbase then
error("expected os "..tolua(inc.os).." prefix, bad formatted out for "..tolua(inc))
end
if base == nil then
base = incbase
else
assert(incbase == base, "for split file, assumed output is [os]/[path] ,but didn't get it for "..tolua(inc))
end
end
--[=[ add in the split file
includeList:insert{
inc = incname,
out = base,
-- TODO this assumes it is Windows vs all, and 'all' is stored in Linux ...
-- TODO autogen by keys. non-all <-> if os == $key, all <-> else, no 'all' present <-> else error "idk your os" unless you wanna have Linux the default
forcecode = template([[
local ffi = require 'ffi'
if ffi.os == 'Windows' then
return require 'ffi.Windows.<?=req?>'
else
return require 'ffi.Linux.<?=req?>'
end
]], {
req = (assert(base:match'(.*)%.lua', 'expcted inc.out to be ext .lua')
:gsub('/', '.')),
})
}
--]=]
end
end
end
-- remove all those that pertain to other os/arch
includeList = includeList:filter(function(inc)
if inc.os ~= nil and inc.os ~= ffi.os then return end
if inc.arch ~= nil and inc.arch ~= ffi.arch then return end
return true
end)
local class = require 'ext.class'
local IncludeFile = class()
function IncludeFile:setupPkgConfig()
if self.hasSetupPkgConfig then return end
self.hasSetupPkgConfig = true
if self.pkgconfig
-- pkgconfig doesn't work on windows so rather than try and fail a lot ...
and ffi.os ~= 'Windows'
then
local out = string.trim(io.readproc('pkg-config --cflags '..self.pkgconfig))
local flags = string.split(out, '%s+')
-- HERE process -I -D flags
for _,f in ipairs(flags) do
if f:sub(1,2) == '-D' then
assert.gt(#f, 2, 'TODO handle -D <macro>')
self.macros = self.macros or table()
self.macros:insert(f:sub(3))
elseif f:sub(1,2) == '-I' then
assert.gt(#f, 2, 'TODO handle -I <incdir>')
self.includedirs = table(self.includedirs)
self.includedirs:insert(f:sub(3))
else
error("pkg-config '"..self.pkgconfig.."' has unknown flag "..tostring(f)..'\n'
..'pkg-config output: '..out)
end
end
end
end
for i,inc in ipairs(includeList) do
assert(not getmetatable(inc), 'index='..i..' inc='..tostring(inc.inc))
setmetatable(inc, IncludeFile)
end
includeList.IncludeFile = IncludeFile -- what a mess
return includeList