parsav  Diff

Differences From Artifact [ce122c09dc]:

To Artifact [fcc06cebed]:


2
3
4
5
6
7
8





9
10
11
12
13
14
15
..
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
..
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
...
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
...
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293
294
295
...
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
333
334
335
336
337
338
...
345
346
347
348
349
350
351
352
353
354
355
356





357
358
359
360
361
362
363
364
365
366
367
368
369
370

local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')

lib = {
	init = {};





	loadlib = function(name,hdr)
		local p = config.pkg[name]
		-- for _,v in pairs(p.dylibs) do
		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
		-- end
		return terralib.includec(p.incdir .. '/' .. hdr)
	end;
................................................................................
		return macro(function(v,...)
			for ty,fn in pairs(tbl) do
				if v.tree.type == ty then return fn(v,...) end
			end
			return (tbl[false])(v,...)
		end)
	end;
	emit = function(...)
		local code = {}
		for i,v in ipairs{...} do
			if type(v) == 'string' or type(v) == 'number' then
				local str = tostring(v)
				code[#code+1] = `lib.io.send(2, str, [#str])


			elseif v.tree:is 'constant' then
				local str = tostring(v:asvalue())
				code[#code+1] = `lib.io.send(2, str, [#str])
			else
				code[#code+1] = quote var n = v in
					lib.io.send(2, n, lib.str.sz(n)) end
			end
		end
		code[#code+1] = `lib.io.send(2, '\n', 1)
		return code
	end;































	trn = macro(function(cond, i, e)
		return quote
			var c: bool = [cond]
			var r: i.tree.type
			if c == true then r = i else r = e end
		in r end
	end);
................................................................................
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};
	str = {
		sz = terralib.externfunction('strlen', rawstring -> intptr);
		cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
		ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
		cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
		ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
		ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
		fmt = terralib.externfunction('asprintf',
			terralib.types.funcpointer({&rawstring},{int},true));
	};
	copy = function(tbl)
		local new = {}
		for k,v in pairs(tbl) do new[k] = v end
		setmetatable(new, getmetatable(tbl))
		return new
	end;
	mem = {
		zero = macro(function(r)
			return quote
				for i = 0, [r.tree.type.N] do r[i] = 0 end
			end
		end);
		heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
		heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
		heapf = terralib.externfunction('free', &opaque -> {});
		cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
		heapa = macro(function(ty, sz)
			local p = lib.mem.ptr(ty:astype())
			return `p {
				ptr = [&ty:astype()](lib.mem.heapa_raw(sizeof(ty) * sz));
				ct = sz;
			}
		end)
	};
}





local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
	end
end
local defrep = function(level,n,code)
	return macro(function(...)
		local q = lib.emit(noise_header(code,n), ...)
		return quote
			if noise >= level then [q] end
		end
	end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(noise_header('31','fatal'), ...)
	return quote
		[q]
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
................................................................................
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty }
	for i, name in ipairs(tbl) do
		o[name] = i
	end
	return o
end
lib.mem.ptr = terralib.memoize(function(ty)
	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
	t.entries = {
		{'ptr', &ty};
		{'ct', intptr};
	}

	t.ptr_basetype = ty
	local recurse = false
	if ty:isstruct() then
		if ty.methods.free then recurse = true end


	end
	t.methods = {
		free = terra(self: &t): bool



			[recurse and quote
				self.ptr:free()
			end or {}]
			if self.ct > 0 then
				lib.mem.heapf(self.ptr)

				self.ct = 0
				return true
			end
			return false
		end;
		init = terra(self: &t, newct: intptr): bool
			var nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
		resize = terra(self: &t, newct: intptr): bool
			var nv: &ty
			if self.ct > 0
				then nv = [&ty](lib.mem.heapr_raw(self.ptr, sizeof(ty) * newct))
				else nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			end
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
	}
	return t
end)
lib.mem.vec = terralib.memoize(function(ty)
	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
	v.entries = {
		{field = 'storage', type = lib.mem.ptr(ty)};
		{field = 'sz', type = intptr};
		{field = 'run', type = intptr};
	}
	local terra biggest(a: intptr, b: intptr)
		if a > b then return a else return b end






	end
	terra v:assure(n: intptr)
		if self.storage.ct < n then
			self.storage:resize(biggest(n, self.storage.ct + self.run))
		end

	end
	v.methods = {
		init = terra(self: &v, run: intptr): bool
			if not self.storage:init(run) then return false end
			self.run = run
			self.sz = 0







			return true
		end;
		new = terra(self: &v): &ty
			self:assure(self.sz + 1)
			self.sz = self.sz + 1
			return self.storage.ptr + (self.sz - 1)
		end;
		push = terra(self: &v, val: ty)
			self:assure(self.sz + 1)
			self.storage.ptr[self.sz] = val
			self.sz = self.sz + 1
		end;
		free = terra(self: &v) self.storage:free() end;
		last = terra(self: &v, idx: intptr): &ty
			if self.sz > idx then
				return self.storage.ptr + (self.sz - (idx+1))
			else lib.bail('vector underrun!') end
		end;

		crush = terra(self: &v)
			self.storage:resize(self.sz)
			return self.storage






		end;
	}
	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
		if self.sz > idx then
			return self.storage.ptr + idx
		else lib.bail('vector overrun!') end
	end
	return v 
end)

lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.file = terralib.loadfile('file.t')()

lib.math = terralib.loadfile('math.t')()
lib.crypt = terralib.loadfile('crypt.t')()
lib.http = terralib.loadfile('http.t')()
lib.tpl = terralib.loadfile('tpl.t')()
lib.string = terralib.loadfile('string.t')()
lib.store = terralib.loadfile('store.t')()


local be = {}
for _, b in pairs { 'pgsql' } do
	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
end
lib.store.backends = global(`array([be]))

lib.cmdparse = terralib.loadfile('cmdparse.t')()
lib.srv = terralib.loadfile('srv.t')()

................................................................................

local pemdump = macro(function(pub, kp)
	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
	return quote
		var buf: lib.crypt.pemfile
		lib.mem.zero(buf)
		lib.crypt.pem(pub, &kp, buf)

		lib.io.send(1, msg, [#msg])
		lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
		lib.io.send(1, '\n', 1)
	end
end)

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end
................................................................................
	end
	noise = 1
end

local options = lib.cmdparse {
	version = {'V', 'display information about the binary build and exit'};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'}

}

terra entry(argc: int, argv: &rawstring): int


	noise_init()
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)


	var mode: options
	mode:parse(argc,argv)
	if mode.version then
		version()
		return 0
	end
	if mode.help then
		lib.io.send(1,  [options.helptxt], [#options.helptxt])
		return 0
	end


	var srv: lib.srv






	srv:start('backend.conf')


	lib.report('listening for requests')
	while true do
		srv:poll()
	end
	srv:shutdown()

	return 0
................................................................................
end

if bflag('dump-config','C') then
	print(util.dump(config))
	os.exit(0)
end

local emit = print
if bflag('quiet','q') then emit = function() end end

local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}





if config.posix then
	linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
emit('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
		main = entry
	},
	linkargs,
	config.tgttrip and terralib.newtarget {
		Triple = config.tgttrip;
		CPU = config.tgtcpu;
		FloatABIHard = config.tgthf;
	} or nil)







>
>
>
>
>







 







|





>
>








|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
|
<
<
<
<
<
<
<
<






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
>
>
>
>











|









|







 







|
|
|
|
|
<
>
|
|
|
|
>
>
|
<
<
>
>
>
|
<
<
<
<
>
|
|
|
<
|
<
<
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
|
<
<
<

>
|
<
<
<
<
<
>
>
>
>
>
>
>
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
<
>
>
>
>
>
>
|
<
<
<
<
<

|
|








<
>
|
|
|
<
<
<
|
>

|







 







>
|
|
|







 







|
>



>
>





>

|
|
|
<
<
<
|
|
|
|
>
>
|
>
>
>
>
>
>
|
>
>







 







|
<
<


>
>
>
>
>




|









2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
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
..
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
...
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
...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
...
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
...
331
332
333
334
335
336
337
338


339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359

local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')

lib = {
	init = {};
	load = function(lst)
		for _, l in pairs(lst) do
			lib[l] = terralib.loadfile(l .. '.t')()
		end
	end;
	loadlib = function(name,hdr)
		local p = config.pkg[name]
		-- for _,v in pairs(p.dylibs) do
		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
		-- end
		return terralib.includec(p.incdir .. '/' .. hdr)
	end;
................................................................................
		return macro(function(v,...)
			for ty,fn in pairs(tbl) do
				if v.tree.type == ty then return fn(v,...) end
			end
			return (tbl[false])(v,...)
		end)
	end;
	emit_unitary = function(fd,...)
		local code = {}
		for i,v in ipairs{...} do
			if type(v) == 'string' or type(v) == 'number' then
				local str = tostring(v)
				code[#code+1] = `lib.io.send(2, str, [#str])
			elseif type(v) == 'table' and #v == 2 then
				code[#code+1] = `lib.io.send(2, [v[1]], [v[2]])
			elseif v.tree:is 'constant' then
				local str = tostring(v:asvalue())
				code[#code+1] = `lib.io.send(2, str, [#str])
			else
				code[#code+1] = quote var n = v in
					lib.io.send(2, n, lib.str.sz(n)) end
			end
		end
		code[#code+1] = `lib.io.send(fd, '\n', 1)
		return code
	end;
	emitv = function(fd,...)
		local vec = {}
		local defs = {}
		for i,v in ipairs{...} do
			local str, ct
			if type(v) == 'table' and v.tree and not (v.tree:is 'constant') then
				if v.tree.type.convertible == 'tuple' then
					str = `v._0
					ct = `v._1
				else
					local n = symbol(v.tree.type)
					defs[#defs + 1] = quote var [n] = v end
					str = n
					ct = `lib.str.sz(n)
				end
			else
				if type(v) == 'string' or type(v) == 'number' then
					str = tostring(v) 
				else--if v.tree:is 'constant' then
					str = tostring(v:asvalue())
				end
				ct = ct or #str
			end
			vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque](str), iov_len = ct}
		end
		vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1}
		return quote
			[defs]
			var strs = array( [vec] )
		in lib.uio.writev(fd, strs, [#vec]) end
	end;
	trn = macro(function(cond, i, e)
		return quote
			var c: bool = [cond]
			var r: i.tree.type
			if c == true then r = i else r = e end
		in r end
	end);
................................................................................
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};

	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };








	copy = function(tbl)
		local new = {}
		for k,v in pairs(tbl) do new[k] = v end
		setmetatable(new, getmetatable(tbl))
		return new
	end;















}



if config.posix then
	lib.uio = terralib.includec 'sys/uio.h';
	lib.emit = lib.emitv -- use more efficient call where available
else lib.emit = lib.emit_unitary end

local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
	end
end
local defrep = function(level,n,code)
	return macro(function(...)
		local q = lib.emit(2, noise_header(code,n), ...)
		return quote
			if noise >= level then [q] end
		end
	end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(2, noise_header('31','fatal'), ...)
	return quote
		[q]
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
................................................................................
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty }
	for i, name in ipairs(tbl) do
		o[name] = i
	end
	return o
end
lib.set = function(tbl)
	local bytes = math.ceil(#tbl / 8)
	local o = {}
	for i, name in ipairs(tbl) do o[name] = i end
	local struct set { _store: uint8[bytes] }

	local struct bit { _v: intptr _set: &set}
	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
	set.members = tbl
	set.name = string.format('set<%s>', table.concat(tbl, '|'))
	set.metamethods.__entrymissing = macro(function(val, obj)
		if o[val] == nil then error('value ' .. val .. ' not in set') end
		return `bit { _v=[o[val] - 1], _set = &obj }
	end)


	set.methods.dump = macro(function(self)
		local q = quote lib.io.say('dumping set:\n') end
		for i,v in ipairs(tbl) do
			q = quote




				[q]
				if [bool](self.[v])
					then lib.io.say([' - ' .. v .. ': true\n'])
					else lib.io.say([' - ' .. v .. ': false\n'])

				end







			end





		end







		return q
	end)









	set.metamethods.__add = macro(function(self,other)
		local new = symbol(set)
		local q = quote var [new] new:clear() end
		for i = 0, bytes - 1 do
			q = quote [q]
				new._store[i] = self._store[i] or other._store[i]
			end



		end
		return quote [q] in new end
	end)





	bit.metamethods.__cast = function(from,to,e)
		local q = quote var s = e
			in (s._set._store[s._v/8] and (1 << s._v % 8)) end
		if to == bit then error('casting to bit is not meaningful')
		elseif to == bool then return `([q] ~= 0)
		elseif to:isintegral() then return q
		elseif from == bit then error('cannot cast bit to ' .. tostring(to))
		else return nil end
	end
















	bit.metamethods.__apply = terra(self: &bit): bool return @self end
	bit.metamethods.__lshift = terra(self: &bit, hl: bool)


		var byte = self._v / 8
		var bit = self._v % 8
		if hl then
			self._set._store[byte] = self._set._store[byte] or (1 << bit)
		else
			self._set._store[byte] = self._set._store[byte] and not (1 << bit)
		end





	end
	return set
end

lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')


lib.load {
	'mem', 'str', 'file', 'math', 'crypt';
	'http', 'tpl', 'store';



}

local be = {}
for _, b in pairs(config.backends) do
	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
end
lib.store.backends = global(`array([be]))

lib.cmdparse = terralib.loadfile('cmdparse.t')()
lib.srv = terralib.loadfile('srv.t')()

................................................................................

local pemdump = macro(function(pub, kp)
	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
	return quote
		var buf: lib.crypt.pemfile
		lib.mem.zero(buf)
		lib.crypt.pem(pub, &kp, buf)
		lib.emit(msg, buf, '\n')
		--lib.io.send(1, msg, [#msg])
		--lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
		--lib.io.send(1, '\n', 1)
	end
end)

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end
................................................................................
	end
	noise = 1
end

local options = lib.cmdparse {
	version = {'V', 'display information about the binary build and exit'};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'};
	backend_file = {'b', 'init from specified backend file', 1};
}

terra entry(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

	noise_init()
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
	var srv: lib.srv

	do var mode: options
		mode:parse(argc,argv) defer mode:free()
		if mode.version then version() return 0 end



		if mode.help then
			lib.io.send(1,  [options.helptxt], [#options.helptxt])
			return 0
		end
		var cnf: rawstring
		if mode.backend_file ~= 0
			then if mode.arglist.ct >= mode.backend_file
					then cnf = mode.arglist.ptr[mode.backend_file - 1]
					else lib.bail('bad invocation, backend file not specified') end
			else cnf = lib.proc.getenv('parsav_backend_file')
		end
		if cnf == nil then cnf = "backend.conf" end

		srv:start(cnf)
	end

	lib.report('listening for requests')
	while true do
		srv:poll()
	end
	srv:shutdown()

	return 0
................................................................................
end

if bflag('dump-config','C') then
	print(util.dump(config))
	os.exit(0)
end

local holler = print


local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}

if bflag('quiet','q') then holler = function() end end
if bflag('asan','s') then linkargs[#linkargs+1] = '-fsanitize=address' end
if bflag('lsan','S') then linkargs[#linkargs+1] = '-fsanitize=leak' end

if config.posix then
	linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
holler('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
		main = entry
	},
	linkargs,
	config.tgttrip and terralib.newtarget {
		Triple = config.tgttrip;
		CPU = config.tgtcpu;
		FloatABIHard = config.tgthf;
	} or nil)