Mercurial > urweb
comparison src/compiler.sml @ 764:7f653298dd66
C FFI compiler options
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Thu, 30 Apr 2009 17:15:14 -0400 |
parents | 43553c93dd8c |
children | a28982de5645 |
comparison
equal
deleted
inserted
replaced
763:af41ec2f302a | 764:7f653298dd66 |
---|---|
1 (* Copyright (c) 2008, Adam Chlipala | 1 (* Copyright (c) 2008-2009, Adam Chlipala |
2 * All rights reserved. | 2 * All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are met: | 5 * modification, are permitted provided that the following conditions are met: |
6 * | 6 * |
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * POSSIBILITY OF SUCH DAMAGE. | 25 * POSSIBILITY OF SUCH DAMAGE. |
26 *) | 26 *) |
27 | 27 |
28 (* Ur/Web language parser *) | |
29 | |
30 structure Compiler :> COMPILER = struct | 28 structure Compiler :> COMPILER = struct |
31 | 29 |
32 structure UrwebLrVals = UrwebLrValsFn(structure Token = LrParser.Token) | 30 structure UrwebLrVals = UrwebLrValsFn(structure Token = LrParser.Token) |
33 structure Lex = UrwebLexFn(structure Tokens = UrwebLrVals.Tokens) | 31 structure Lex = UrwebLexFn(structure Tokens = UrwebLrVals.Tokens) |
34 structure UrwebP = Join(structure ParserData = UrwebLrVals.ParserData | 32 structure UrwebP = Join(structure ParserData = UrwebLrVals.ParserData |
41 sources : string list, | 39 sources : string list, |
42 exe : string, | 40 exe : string, |
43 sql : string option, | 41 sql : string option, |
44 debug : bool, | 42 debug : bool, |
45 profile : bool, | 43 profile : bool, |
46 timeout : int | 44 timeout : int, |
45 ffi : string list, | |
46 link : string list, | |
47 headers : string list | |
47 } | 48 } |
48 | 49 |
49 type ('src, 'dst) phase = { | 50 type ('src, 'dst) phase = { |
50 func : 'src -> 'dst, | 51 func : 'src -> 'dst, |
51 print : 'dst -> Print.PD.pp_desc | 52 print : 'dst -> Print.PD.pp_desc |
199 | _ => absyn | 200 | _ => absyn |
200 end | 201 end |
201 handle LrParser.ParseError => [], | 202 handle LrParser.ParseError => [], |
202 print = SourcePrint.p_file} | 203 print = SourcePrint.p_file} |
203 | 204 |
204 fun p_job {prefix, database, exe, sql, sources, debug, profile, timeout} = | 205 fun p_job {prefix, database, exe, sql, sources, debug, profile, timeout, ffi, link, headers} = |
205 let | 206 let |
206 open Print.PD | 207 open Print.PD |
207 open Print | 208 open Print |
208 in | 209 in |
209 box [if debug then | 210 box [if debug then |
226 | SOME sql => string ("SQL fle: " ^ sql), | 227 | SOME sql => string ("SQL fle: " ^ sql), |
227 newline, | 228 newline, |
228 string "Timeout: ", | 229 string "Timeout: ", |
229 string (Int.toString timeout), | 230 string (Int.toString timeout), |
230 newline, | 231 newline, |
232 p_list_sep (box []) (fn s => box [string "Ffi", space, string s, newline]) ffi, | |
233 p_list_sep (box []) (fn s => box [string "Header", space, string s, newline]) headers, | |
234 p_list_sep (box []) (fn s => box [string "Link", space, string s, newline]) link, | |
231 string "Sources:", | 235 string "Sources:", |
232 p_list string sources, | 236 p_list string sources, |
233 newline] | 237 newline] |
234 end | 238 end |
235 | 239 |
248 val inf = TextIO.openIn (OS.Path.joinBaseExt {base = filename, ext = SOME "urp"}) | 252 val inf = TextIO.openIn (OS.Path.joinBaseExt {base = filename, ext = SOME "urp"}) |
249 | 253 |
250 fun relify fname = | 254 fun relify fname = |
251 OS.Path.concat (dir, fname) | 255 OS.Path.concat (dir, fname) |
252 handle OS.Path.Path => fname | 256 handle OS.Path.Path => fname |
257 | |
258 val absDir = OS.Path.mkAbsolute {path = dir, relativeTo = OS.FileSys.getDir ()} | |
259 | |
260 fun relifyA fname = OS.Path.mkAbsolute {path = fname, relativeTo = absDir} | |
253 | 261 |
254 fun readSources acc = | 262 fun readSources acc = |
255 case TextIO.inputLine inf of | 263 case TextIO.inputLine inf of |
256 NONE => rev acc | 264 NONE => rev acc |
257 | SOME line => | 265 | SOME line => |
268 end | 276 end |
269 in | 277 in |
270 readSources acc | 278 readSources acc |
271 end | 279 end |
272 | 280 |
273 fun finish (prefix, database, exe, sql, debug, profile, timeout, sources) = | 281 val prefix = ref NONE |
274 {prefix = Option.getOpt (prefix, "/"), | 282 val database = ref NONE |
275 database = database, | 283 val exe = ref NONE |
276 exe = Option.getOpt (exe, OS.Path.joinBaseExt {base = OS.Path.base filename, | 284 val sql = ref NONE |
277 ext = SOME "exe"}), | 285 val debug = ref false |
278 sql = sql, | 286 val profile = ref false |
279 debug = debug, | 287 val timeout = ref NONE |
280 profile = profile, | 288 val ffi = ref [] |
281 timeout = Option.getOpt (timeout, 60), | 289 val link = ref [] |
290 val headers = ref [] | |
291 | |
292 fun finish sources = | |
293 {prefix = Option.getOpt (!prefix, "/"), | |
294 database = !database, | |
295 exe = Option.getOpt (!exe, OS.Path.joinBaseExt {base = OS.Path.base filename, | |
296 ext = SOME "exe"}), | |
297 sql = !sql, | |
298 debug = !debug, | |
299 profile = !profile, | |
300 timeout = Option.getOpt (!timeout, 60), | |
301 ffi = !ffi, | |
302 link = !link, | |
303 headers = !headers, | |
282 sources = sources} | 304 sources = sources} |
283 | 305 |
284 fun read (prefix, database, exe, sql, debug, profile, timeout) = | 306 fun read () = |
285 case TextIO.inputLine inf of | 307 case TextIO.inputLine inf of |
286 NONE => finish (prefix, database, exe, sql, debug, profile, timeout, []) | 308 NONE => finish [] |
287 | SOME "\n" => finish (prefix, database, exe, sql, debug, profile, timeout, readSources []) | 309 | SOME "\n" => finish (readSources []) |
288 | SOME line => | 310 | SOME line => |
289 let | 311 let |
290 val (cmd, arg) = Substring.splitl (fn x => not (Char.isSpace x)) (Substring.full line) | 312 val (cmd, arg) = Substring.splitl (fn x => not (Char.isSpace x)) (Substring.full line) |
291 val cmd = Substring.string (trim cmd) | 313 val cmd = Substring.string (trim cmd) |
292 val arg = Substring.string (trim arg) | 314 val arg = Substring.string (trim arg) |
293 in | 315 in |
294 case cmd of | 316 case cmd of |
295 "prefix" => | 317 "prefix" => |
296 (case prefix of | 318 (case !prefix of |
297 NONE => () | 319 NONE => () |
298 | SOME _ => ErrorMsg.error "Duplicate 'prefix' directive"; | 320 | SOME _ => ErrorMsg.error "Duplicate 'prefix' directive"; |
299 read (SOME arg, database, exe, sql, debug, profile, timeout)) | 321 prefix := SOME arg) |
300 | "database" => | 322 | "database" => |
301 (case database of | 323 (case !database of |
302 NONE => () | 324 NONE => () |
303 | SOME _ => ErrorMsg.error "Duplicate 'database' directive"; | 325 | SOME _ => ErrorMsg.error "Duplicate 'database' directive"; |
304 read (prefix, SOME arg, exe, sql, debug, profile, timeout)) | 326 database := SOME arg) |
305 | "exe" => | 327 | "exe" => |
306 (case exe of | 328 (case !exe of |
307 NONE => () | 329 NONE => () |
308 | SOME _ => ErrorMsg.error "Duplicate 'exe' directive"; | 330 | SOME _ => ErrorMsg.error "Duplicate 'exe' directive"; |
309 read (prefix, database, SOME (relify arg), sql, debug, profile, timeout)) | 331 exe := SOME (relify arg)) |
310 | "sql" => | 332 | "sql" => |
311 (case sql of | 333 (case !sql of |
312 NONE => () | 334 NONE => () |
313 | SOME _ => ErrorMsg.error "Duplicate 'sql' directive"; | 335 | SOME _ => ErrorMsg.error "Duplicate 'sql' directive"; |
314 read (prefix, database, exe, SOME (relify arg), debug, profile, timeout)) | 336 sql := SOME (relify arg)) |
315 | "debug" => read (prefix, database, exe, sql, true, profile, timeout) | 337 | "debug" => debug := true |
316 | "profile" => read (prefix, database, exe, sql, debug, true, timeout) | 338 | "profile" => profile := true |
317 | "timeout" => | 339 | "timeout" => |
318 (case timeout of | 340 (case !timeout of |
319 NONE => () | 341 NONE => () |
320 | SOME _ => ErrorMsg.error "Duplicate 'timeout' directive"; | 342 | SOME _ => ErrorMsg.error "Duplicate 'timeout' directive"; |
321 read (prefix, database, exe, sql, debug, profile, SOME (valOf (Int.fromString arg)))) | 343 timeout := SOME (valOf (Int.fromString arg))) |
322 | _ => (ErrorMsg.error ("Unrecognized command '" ^ cmd ^ "'"); | 344 | "ffi" => ffi := relify arg :: !ffi |
323 read (prefix, database, exe, sql, debug, profile, timeout)) | 345 | "link" => link := relifyA arg :: !link |
346 | "include" => headers := relifyA arg :: !headers | |
347 | _ => ErrorMsg.error ("Unrecognized command '" ^ cmd ^ "'"); | |
348 read () | |
324 end | 349 end |
325 | 350 |
326 val job = read (NONE, NONE, NONE, NONE, false, false, NONE) | 351 val job = read () |
327 in | 352 in |
328 TextIO.closeIn inf; | 353 TextIO.closeIn inf; |
329 Monoize.urlPrefix := #prefix job; | 354 Settings.setUrlPrefix (#prefix job); |
330 CjrPrint.timeout := #timeout job; | 355 Settings.setTimeout (#timeout job); |
356 Settings.setHeaders (#headers job); | |
331 job | 357 job |
332 end, | 358 end, |
333 print = p_job | 359 print = p_job |
334 } | 360 } |
335 | 361 |
337 | 363 |
338 fun capitalize "" = "" | 364 fun capitalize "" = "" |
339 | capitalize s = str (Char.toUpper (String.sub (s, 0))) ^ String.extract (s, 1, NONE) | 365 | capitalize s = str (Char.toUpper (String.sub (s, 0))) ^ String.extract (s, 1, NONE) |
340 | 366 |
341 val parse = { | 367 val parse = { |
342 func = fn {database, sources = fnames, ...} : job => | 368 func = fn {database, sources = fnames, ffi, ...} : job => |
343 let | 369 let |
344 fun nameOf fname = capitalize (OS.Path.file fname) | 370 fun nameOf fname = capitalize (OS.Path.file fname) |
371 | |
372 fun parseFfi fname = | |
373 let | |
374 val mname = nameOf fname | |
375 val urs = OS.Path.joinBaseExt {base = fname, ext = SOME "urs"} | |
376 | |
377 val loc = {file = urs, | |
378 first = ErrorMsg.dummyPos, | |
379 last = ErrorMsg.dummyPos} | |
380 | |
381 val sgn = (Source.SgnConst (#func parseUrs urs), loc) | |
382 in | |
383 (Source.DFfiStr (mname, sgn), loc) | |
384 end | |
345 | 385 |
346 fun parseOne fname = | 386 fun parseOne fname = |
347 let | 387 let |
348 val mname = nameOf fname | 388 val mname = nameOf fname |
349 val ur = OS.Path.joinBaseExt {base = fname, ext = SOME "ur"} | 389 val ur = OS.Path.joinBaseExt {base = fname, ext = SOME "ur"} |
365 val ds = #func parseUr ur | 405 val ds = #func parseUr ur |
366 in | 406 in |
367 (Source.DStr (mname, sgnO, (Source.StrConst ds, loc)), loc) | 407 (Source.DStr (mname, sgnO, (Source.StrConst ds, loc)), loc) |
368 end | 408 end |
369 | 409 |
410 val dsFfi = map parseFfi ffi | |
370 val ds = map parseOne fnames | 411 val ds = map parseOne fnames |
371 in | 412 in |
372 let | 413 let |
373 val final = nameOf (List.last fnames) | 414 val final = nameOf (List.last fnames) |
374 | 415 |
375 val ds = ds @ [(Source.DExport (Source.StrVar final, ErrorMsg.dummySpan), ErrorMsg.dummySpan)] | 416 val ds = dsFfi @ ds |
417 @ [(Source.DExport (Source.StrVar final, ErrorMsg.dummySpan), ErrorMsg.dummySpan)] | |
376 in | 418 in |
377 case database of | 419 case database of |
378 NONE => ds | 420 NONE => ds |
379 | SOME s => (Source.DDatabase s, ErrorMsg.dummySpan) :: ds | 421 | SOME s => (Source.DDatabase s, ErrorMsg.dummySpan) :: ds |
380 end handle Empty => ds | 422 end handle Empty => ds |
603 print = CjrPrint.p_sql CjrEnv.empty | 645 print = CjrPrint.p_sql CjrEnv.empty |
604 } | 646 } |
605 | 647 |
606 val toSqlify = transform sqlify "sqlify" o toMono_opt2 | 648 val toSqlify = transform sqlify "sqlify" o toMono_opt2 |
607 | 649 |
608 fun compileC {cname, oname, ename, libs, profile, debug} = | 650 fun compileC {cname, oname, ename, libs, profile, debug, link = link'} = |
609 let | 651 let |
610 val urweb_o = clibFile "urweb.o" | 652 val urweb_o = clibFile "urweb.o" |
611 val driver_o = clibFile "driver.o" | 653 val driver_o = clibFile "driver.o" |
612 | 654 |
613 val compile = "gcc " ^ Config.gccArgs ^ " -Wstrict-prototypes -Werror -O3 -I include -c " ^ cname ^ " -o " ^ oname | 655 val compile = "gcc " ^ Config.gccArgs ^ " -Wstrict-prototypes -Werror -O3 -I include -c " ^ cname ^ " -o " ^ oname |
622 val (compile, link) = | 664 val (compile, link) = |
623 if debug then | 665 if debug then |
624 (compile ^ " -g", link ^ " -g") | 666 (compile ^ " -g", link ^ " -g") |
625 else | 667 else |
626 (compile, link) | 668 (compile, link) |
669 | |
670 val link = foldl (fn (s, link) => link ^ " " ^ s) link link' | |
627 in | 671 in |
628 if not (OS.Process.isSuccess (OS.Process.system compile)) then | 672 if not (OS.Process.isSuccess (OS.Process.system compile)) then |
629 print "C compilation failed\n" | 673 print "C compilation failed\n" |
630 else if not (OS.Process.isSuccess (OS.Process.system link)) then | 674 else if not (OS.Process.isSuccess (OS.Process.system link)) then |
631 print "C linking failed\n" | 675 print "C linking failed\n" |
687 Print.fprint s (CjrPrint.p_sql CjrEnv.empty file); | 731 Print.fprint s (CjrPrint.p_sql CjrEnv.empty file); |
688 TextIO.closeOut outf | 732 TextIO.closeOut outf |
689 end; | 733 end; |
690 | 734 |
691 compileC {cname = cname, oname = oname, ename = ename, libs = libs, | 735 compileC {cname = cname, oname = oname, ename = ename, libs = libs, |
692 profile = #profile job, debug = #debug job}; | 736 profile = #profile job, debug = #debug job, link = #link job}; |
693 | 737 |
694 cleanup () | 738 cleanup () |
695 end | 739 end |
696 handle ex => (((cleanup ()) handle _ => ()); raise ex) | 740 handle ex => (((cleanup ()) handle _ => ()); raise ex) |
697 end | 741 end |