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