From 7e71f5071b19415b4b285702e1753c9a82523acb Mon Sep 17 00:00:00 2001 From: Xavier Leroy Date: Fri, 19 Dec 2014 14:47:01 +0100 Subject: Use Unix.create_process instead of Sys.command to run external tools. Revised parsing of compcert.ini file to split arguments into words like POSIX shell does (including quotes). --- lib/Readconfig.mli | 39 +++++++++++++++++++ lib/Readconfig.mll | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 lib/Readconfig.mli create mode 100644 lib/Readconfig.mll diff --git a/lib/Readconfig.mli b/lib/Readconfig.mli new file mode 100644 index 00000000..c81e7786 --- /dev/null +++ b/lib/Readconfig.mli @@ -0,0 +1,39 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, INRIA Paris-Rocquencourt *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Reading configuration files *) + +(* The format of a configuration file is a list of lines + variable=value + The "value" on the right hand side is a list of whitespace-separated + words. Quoting is honored with the same rules as POSIX shell: + \ for multi-line values + single quotes no escapes within + double quotes \$ \` \ \\ \ as escapes + Finally, lines starting with '#' are comments. +*) + +val read_config_file: string -> unit + (** Read (key, value) pairs from the given file name. Raise [Error] + if file is ill-formed. *) + +val key_val: string -> string list option + (** [key_val k] returns the value associated with key [k], if any. + Otherwise, [None] is returned. *) + +exception Error of string * int * string + (** Raised in case of error. + First argument is file name, second argument is line number, + third argument is an explanation of the error. *) diff --git a/lib/Readconfig.mll b/lib/Readconfig.mll new file mode 100644 index 00000000..27ef32cf --- /dev/null +++ b/lib/Readconfig.mll @@ -0,0 +1,111 @@ +{ + +(* Recording key=val bindings *) + +let key_val_tbl : (string, string list) Hashtbl.t = Hashtbl.create 17 + +let key_val key = + try Some(Hashtbl.find key_val_tbl key) with Not_found -> None + +(* Auxiliaries for parsing *) + +let buf = Buffer.create 32 + +let stash inword words = + if inword then begin + let w = Buffer.contents buf in + Buffer.clear buf; + w :: words + end else + words + +(* Error reporting *) + +exception Error of string * int * string + +let error msg lexbuf = + Lexing.(raise (Error(lexbuf.lex_curr_p.pos_fname, + lexbuf.lex_curr_p.pos_lnum, + msg))) + +let ill_formed_line lexbuf = error "Ill-formed line" lexbuf +let unterminated_quote lexbuf = error "Unterminated quote" lexbuf +let lone_backslash lexbuf = error "Lone \\ (backslash) at end of file" lexbuf + +} + +let whitespace = [' ' '\t' '\012' '\r'] +let newline = '\r'* '\n' +let ident = ['A'-'Z' 'a'-'z' '_'] ['A'-'Z' 'a'-'z' '0'-'9' '_' '.']* + +rule begline = parse + | '#' [^ '\n']* ('\n' | eof) + { Lexing.new_line lexbuf; begline lexbuf } + | whitespace* (ident as key) whitespace* '=' + { let words = unquoted false [] lexbuf in + Hashtbl.add key_val_tbl key (List.rev words); + begline lexbuf } + | eof + { () } + | _ + { ill_formed_line lexbuf } + +and unquoted inword words = parse + | '\n' | eof { Lexing.new_line lexbuf; stash inword words } + | whitespace+ { unquoted false (stash inword words) lexbuf } + | '\\' newline { Lexing.new_line lexbuf; unquoted inword words lexbuf } + | '\\' (_ as c) { Buffer.add_char buf c; unquoted true words lexbuf } + | '\\' eof { lone_backslash lexbuf } + | '\'' { singlequote lexbuf; unquoted true words lexbuf } + | '\"' { doublequote lexbuf; unquoted true words lexbuf } + | _ as c { Buffer.add_char buf c; unquoted true words lexbuf } + +and singlequote = parse + | eof { unterminated_quote lexbuf } + | '\'' { () } + | newline { Lexing.new_line lexbuf; + Buffer.add_char buf '\n'; singlequote lexbuf } + | _ as c { Buffer.add_char buf c; singlequote lexbuf } + +and doublequote = parse + | eof { unterminated_quote lexbuf } + | '\"' { () } + | '\\' newline { Lexing.new_line lexbuf; doublequote lexbuf } + | '\\' (['$' '`' '\"' '\\'] as c) + { Buffer.add_char buf c; doublequote lexbuf } + | newline { Lexing.new_line lexbuf; + Buffer.add_char buf '\n'; doublequote lexbuf } + | _ as c { Buffer.add_char buf c; doublequote lexbuf } + +{ + +(* The entry point *) + +let read_config_file filename = + let ic = open_in filename in + let lexbuf = Lexing.from_channel ic in + Lexing.(lexbuf.lex_start_p <- {lexbuf.lex_start_p with pos_fname = filename}); + try + Hashtbl.clear key_val_tbl; + begline lexbuf; + close_in ic + with x -> + close_in ic; raise x + +(* Test harness *) +(* +open Printf + +let _ = + Hashtbl.clear key_val_tbl; + begline (Lexing.from_channel stdin); + Hashtbl.iter + (fun key value -> + printf "%s =" key; + List.iter (fun v -> printf " |%s|" v) value; + printf "\n") + key_val_tbl +*) + +} + -- cgit From c130f4936bad11fd6dab3a5d142b870d2a5f650c Mon Sep 17 00:00:00 2001 From: Xavier Leroy Date: Mon, 29 Dec 2014 11:15:29 +0100 Subject: Use Unix.create_process instead of Sys.command (continued). --- Makefile.extr | 5 +- driver/Configuration.ml | 125 ++++++++++++++++++++++++------------------------ driver/Driver.ml | 88 +++++++++++++++++++++------------- 3 files changed, 122 insertions(+), 96 deletions(-) diff --git a/Makefile.extr b/Makefile.extr index 35ae5f7b..5c9cc8dd 100644 --- a/Makefile.extr +++ b/Makefile.extr @@ -75,9 +75,10 @@ OCAMLLEX=ocamllex -q MODORDER=tools/modorder .depend.extr PARSERS=backend/CMparser.mly cparser/pre_parser.mly -LEXERS=backend/CMlexer.mll cparser/Lexer.mll lib/Tokenize.mll +LEXERS=backend/CMlexer.mll cparser/Lexer.mll \ + lib/Tokenize.mll lib/Readconfig.mll -LIBS=str.cmxa +LIBS=str.cmxa unix.cmxa EXECUTABLES=ccomp ccomp.byte cchecklink cchecklink.byte clightgen clightgen.byte GENERATED=$(PARSERS:.mly=.mli) $(PARSERS:.mly=.ml) $(LEXERS:.mll=.ml) diff --git a/driver/Configuration.ml b/driver/Configuration.ml index e73125f7..0012dc0c 100644 --- a/driver/Configuration.ml +++ b/driver/Configuration.ml @@ -10,86 +10,87 @@ (* *) (* *********************************************************************) +open Printf -let config_vars: (string, string) Hashtbl.t = Hashtbl.create 10 +(* Locate the .ini file, which is either in the same directory as + the executable or in the directory ../share *) +let ini_file_name = + try + Sys.getenv "COMPCERT_CONFIG" + with Not_found -> + let exe_dir = Filename.dirname Sys.executable_name in + let exe_ini = Filename.concat exe_dir "compcert.ini" in + let share_dir = + Filename.concat (Filename.concat exe_dir Filename.parent_dir_name) + "share" in + let share_ini = Filename.concat share_dir "compcert.ini" in + if Sys.file_exists exe_ini then exe_ini + else if Sys.file_exists share_ini then share_ini + else begin + eprintf "Cannot find compcert.ini configuration file.\n"; + exit 2 + end + +(* Read in the .ini file *) -(* Read in the .ini file, which is either in the same directory or in the directory ../share *) let _ = try - let file = - try - let env_file = Sys.getenv "COMPCERT_CONFIG" in - open_in env_file - with Not_found -> - let dir = Sys.getcwd () - and name = Sys.executable_name in - let dirname = if Filename.is_relative name then - Filename.dirname (Filename.concat dir name) - else - Filename.dirname name - in - let share_dir = Filename.concat (Filename.concat dirname Filename.parent_dir_name) "share" in - try - open_in (Filename.concat dirname "compcert.ini") - with Sys_error _ -> - open_in (Filename.concat share_dir "compcert.ini") - in - (try - let ini_line = Str.regexp "\\([^=]+\\)=\\(.+\\)" in - while true do - let line = input_line file in - if Str.string_match ini_line line 0 then - let key = Str.matched_group 1 line - and value = Str.matched_group 2 line in - Hashtbl.add config_vars key value - else - Printf.eprintf "Wrong value %s in .ini file.\n" line - done - with End_of_file -> close_in file) - with Sys_error msg -> Printf.eprintf "Unable to open .ini file\n" + Readconfig.read_config_file ini_file_name + with + | Readconfig.Error(file, lineno, msg) -> + eprintf "Error reading configuration file %s.\n" ini_file_name; + eprintf "%s:%d:%s\n" file lineno msg; + exit 2 + | Sys_error msg -> + eprintf "Error reading configuration file %s.\n" ini_file_name; + eprintf "%s\n" msg; + exit 2 let get_config key = - try - Hashtbl.find config_vars key - with Not_found -> - Printf.eprintf "Configuration option `%s' is not set\n" key; exit 2 + match Readconfig.key_val key with + | Some v -> v + | None -> eprintf "Configuration option `%s' is not set\n" key; exit 2 -let bad_config key v = - Printf.eprintf "Invalid value `%s' for configuation option `%s'\n" v key; exit 2 +let bad_config key vl = + eprintf "Invalid value `%s' for configuration option `%s'\n" + (String.concat " " vl) key; + exit 2 -let prepro = get_config "prepro" -let asm = get_config "asm" -let linker = get_config "linker" -let arch = - let v = get_config "arch" in - (match v with - | "powerpc" - | "arm" - | "ia32" -> () - | _ -> bad_config "arch" v); - v +let get_config_string key = + match get_config key with + | [v] -> v + | vl -> bad_config key vl -let model = get_config "model" -let abi = get_config "abi" -let system = get_config "system" +let get_config_list key = + match get_config key with + | [] -> bad_config key [] + | vl -> vl + +let prepro = get_config_list "prepro" +let asm = get_config_list "asm" +let linker = get_config_list "linker" +let arch = + match get_config_string "arch" with + | "powerpc"|"arm"|"ia32" as a -> a + | v -> bad_config "arch" [v] +let model = get_config_string "model" +let abi = get_config_string "abi" +let system = get_config_string "system" let has_runtime_lib = - match get_config "has_runtime_lib" with + match get_config_string "has_runtime_lib" with | "true" -> true | "false" -> false - | v -> bad_config "has_runtime_lib" v - - + | v -> bad_config "has_runtime_lib" [v] let stdlib_path = if has_runtime_lib then - get_config "stdlib_path" + get_config_string "stdlib_path" else "" - let asm_supports_cfi = - match get_config "asm_supports_cfi" with + match get_config_string "asm_supports_cfi" with | "true" -> true | "false" -> false - | v -> bad_config "asm_supports_cfi" v + | v -> bad_config "asm_supports_cfi" [v] -let version = get_config "version" +let version = get_config_string "version" diff --git a/driver/Driver.ml b/driver/Driver.ml index fec87420..14ce11f4 100644 --- a/driver/Driver.ml +++ b/driver/Driver.ml @@ -20,17 +20,38 @@ open Timing let stdlib_path = ref Configuration.stdlib_path -let command cmd = +(* Invocation of external tools *) + +let command ?stdout args = if !option_v then begin - prerr_string "+ "; prerr_string cmd; prerr_endline "" + eprintf "+ %s" (String.concat " " args); + begin match stdout with + | None -> () + | Some f -> eprintf " > %s" f + end; + prerr_endline "" end; - Sys.command cmd - -let quote_options opts = - String.concat " " (List.rev_map Filename.quote opts) - -let quote_arguments args = - String.concat " " (List.map Filename.quote args) + let argv = Array.of_list args in + assert (Array.length argv > 0); + try + let fd_out = + match stdout with + | None -> Unix.stdout + | Some f -> + Unix.openfile f [Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC] 0o666 in + let pid = + Unix.create_process argv.(0) argv Unix.stdin fd_out Unix.stderr in + let (_, status) = + Unix.waitpid [] pid in + if stdout <> None then Unix.close fd_out; + match status with + | Unix.WEXITED rc -> rc + | Unix.WSIGNALED n | Unix.WSTOPPED n -> + eprintf "Command '%s' killed on a signal.\n" argv.(0); -1 + with Unix.Unix_error(err, fn, param) -> + eprintf "Error executing '%s': %s: %s %s\n" + argv.(0) fn (Unix.error_message err) param; + -1 let safe_remove file = try Sys.remove file with Sys_error _ -> () @@ -68,16 +89,17 @@ let output_filename_default default_file = let preprocess ifile ofile = let output = - if ofile = "-" then "" else sprintf "> %s" ofile in - let cmd = - sprintf "%s -D__COMPCERT__ %s %s %s %s" - Configuration.prepro - (if Configuration.has_runtime_lib - then sprintf "-I%s" !stdlib_path - else "") - (quote_options !prepro_options) - ifile output in - if command cmd <> 0 then begin + if ofile = "-" then None else Some ofile in + let cmd = List.concat [ + Configuration.prepro; + ["-D__COMPCERT__"]; + (if Configuration.has_runtime_lib + then ["-I" ^ !stdlib_path] + else []); + List.rev !prepro_options; + [ifile] + ] in + if command ?stdout:output cmd <> 0 then begin if ofile <> "-" then safe_remove ofile; eprintf "Error during preprocessing.\n"; exit 2 @@ -208,11 +230,13 @@ let compile_cminor_file ifile ofile = (* From asm to object file *) let assemble ifile ofile = - let cmd = - sprintf "%s -o %s %s %s" - Configuration.asm ofile (quote_options !assembler_options) ifile in - let retcode = command cmd in - if retcode <> 0 then begin + let cmd = List.concat [ + Configuration.asm; + ["-o"; ofile]; + List.rev !assembler_options; + [ifile] + ] in + if command cmd <> 0 then begin safe_remove ofile; eprintf "Error during assembling.\n"; exit 2 @@ -221,14 +245,14 @@ let assemble ifile ofile = (* Linking *) let linker exe_name files = - let cmd = - sprintf "%s -o %s %s %s" - Configuration.linker - (Filename.quote exe_name) - (quote_arguments files) - (if Configuration.has_runtime_lib - then sprintf "-L%s -lcompcert" !stdlib_path - else "") in + let cmd = List.concat [ + Configuration.linker; + ["-o"; exe_name]; + files; + (if Configuration.has_runtime_lib + then ["-L" ^ !stdlib_path; "-lcompcert"] + else []) + ] in if command cmd <> 0 then exit 2 (* Processing of a .c file *) -- cgit