From 4f6aa5e408be33b04ff9a64db7ba416a5d994f24 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Thu, 27 Oct 2016 04:32:24 -0700 Subject: [PATCH] [clang] Parse compilation database coming from xcpretty Reviewed By: jvillard Differential Revision: D4058793 fbshipit-source-id: 4b6a06d --- infer/src/clang/ClangQuotes.re | 4 +- infer/src/clang/ClangQuotes.rei | 5 +- infer/src/clang/CompilationDatabase.ml | 54 +++++++++++++------ infer/src/clang/CompilationDatabase.mli | 2 +- .../integration/BuckCompilationDatabase.ml | 18 +------ 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/infer/src/clang/ClangQuotes.re b/infer/src/clang/ClangQuotes.re index 857b0f84d..7dbc00e2d 100644 --- a/infer/src/clang/ClangQuotes.re +++ b/infer/src/clang/ClangQuotes.re @@ -13,10 +13,12 @@ open! Utils; /** quoting style of the arguments */ type style = | EscapedDoubleQuotes /** the arguments should be enclosed in "double quotes" and are already escaped */ - | SingleQuotes /** the arguments should be enclosed in 'single quotes' and have to be escaped */; + | SingleQuotes /** the arguments should be enclosed in 'single quotes' and have to be escaped */ + | EscapedNoQuotes /** the arguments should not be enclosed in quotes and are already escaped */; let quote style => switch style { + | EscapedNoQuotes => (fun s => s) | EscapedDoubleQuotes => (fun s => "\"" ^ s ^ "\"") | SingleQuotes => let map = ( diff --git a/infer/src/clang/ClangQuotes.rei b/infer/src/clang/ClangQuotes.rei index a666b7051..97b2c7c5f 100644 --- a/infer/src/clang/ClangQuotes.rei +++ b/infer/src/clang/ClangQuotes.rei @@ -8,10 +8,11 @@ */ /* module for escaping clang arguments on the command line and put them into files */ -/** quoting style */ +/** quoting style of the arguments */ type style = | EscapedDoubleQuotes /** the arguments should be enclosed in "double quotes" and are already escaped */ - | SingleQuotes /** the arguments should be enclosed in 'single quotes' and have to be escaped */; + | SingleQuotes /** the arguments should be enclosed in 'single quotes' and have to be escaped */ + | EscapedNoQuotes /** the arguments should not be enclosed in quotes and are already escaped */; let quote: style => string => string; diff --git a/infer/src/clang/CompilationDatabase.ml b/infer/src/clang/CompilationDatabase.ml index 2dcde2f51..91671f673 100644 --- a/infer/src/clang/CompilationDatabase.ml +++ b/infer/src/clang/CompilationDatabase.ml @@ -12,7 +12,7 @@ open! Utils type compilation_data = { dir : string; command : string; - args : string list; + args : string; } type t = compilation_data StringMap.t ref @@ -24,31 +24,51 @@ let iter database f = StringMap.iter f !database let find database key = StringMap.find key !database +let parse_command_and_arguments command_and_arguments = + let regexp = Str.regexp "[^\\][ ]" in + let index = Str.search_forward regexp command_and_arguments 0 in + let command = Str.string_before command_and_arguments (index+1) in + let arguments = Str.string_after command_and_arguments (index+1) in + command, arguments + (** Parse the compilation database json file into the compilationDatabase map. The json file consists of an array of json objects that contain the file to be compiled, the directory to be compiled in, and the compilation command as a list and as a string. We pack this information into the compilationDatabase map, and remove the clang invocation part, because we will use a clang wrapper. *) let decode_json_file (database : t) should_add_file json_path = + let exit_format_error () = + failwith ("Json file doesn't have the expected format") in let json = Yojson.Basic.from_file json_path in - let parse_argument compilation_argument = - match compilation_argument with - | `String arg -> arg - | _ -> failwith ("Json file doesn't have the expected format") in + let get_dir el = + match el with + | ("directory", `String dir) -> Some dir + | _ -> None in + let get_file el = + match el with + | ("file", `String file) -> Some file + | _ -> None in + let get_cmd el = + match el with + | ("command", `String cmd) -> Some cmd + | _ -> None in let rec parse_json json = match json with | `List arguments -> IList.iter parse_json arguments - | `Assoc [ ("directory", `String dir); - ("file", `String file_path); - ("arguments", `List compilation_arguments); - ("command", `String _) ] -> - (match IList.map parse_argument compilation_arguments with - | [] -> failwith ("Command cannot be empty") - | command :: args when should_add_file file_path -> - let compilation_data = { dir; command; args;} in - database := StringMap.add file_path compilation_data !database - | _ -> ()) - | _ -> - failwith ("Json file doesn't have the expected format") in + | `Assoc l -> + let dir = match IList.find_map_opt get_dir l with + | Some dir -> dir + | None -> exit_format_error () in + let file = match IList.find_map_opt get_file l with + | Some file -> file + | None -> exit_format_error () in + let cmd = match IList.find_map_opt get_cmd l with + | Some cmd -> cmd + | None -> exit_format_error () in + let command, args = parse_command_and_arguments cmd in + if should_add_file file then + let compilation_data = { dir; command; args;} in + database := StringMap.add file compilation_data !database + | _ -> exit_format_error () in parse_json json diff --git a/infer/src/clang/CompilationDatabase.mli b/infer/src/clang/CompilationDatabase.mli index ebd24d187..1efdee6e3 100644 --- a/infer/src/clang/CompilationDatabase.mli +++ b/infer/src/clang/CompilationDatabase.mli @@ -14,7 +14,7 @@ type t type compilation_data = { dir : string; command : string; - args : string list; + args : string; } val empty : unit -> t diff --git a/infer/src/integration/BuckCompilationDatabase.ml b/infer/src/integration/BuckCompilationDatabase.ml index 59f8c45d0..37a252a47 100644 --- a/infer/src/integration/BuckCompilationDatabase.ml +++ b/infer/src/integration/BuckCompilationDatabase.ml @@ -81,27 +81,13 @@ let swap_command cmd = (* The command in the compilation database json emitted by buck can only be clang or clang++ *) failwithf "Unexpected command name in Buck compilation database: %s" cmd -(** We have to replace the .o files because the path in buck-out doesn't exist at this point. - Moreover, in debug mode we create debug files in the place where the .o files are created, - so having all that in the results directory is convenient for finding the files and for - scanning the directory for running clang_frontend_stats. *) -let replace_clang_arg arg = - if Filename.check_suffix arg ".o" then - let dir = Config.results_dir // Config.clang_build_output_dir_name in - let abbrev_source_file = DB.source_file_encoding (DB.source_file_from_string arg) in - [dir // abbrev_source_file] - (* Doing this argument manipulation here rather than in the wrappers because it seems to - be needed only with this integration.*) - else if (arg = "-include-pch") || (Filename.check_suffix arg ".gch") then [] - else [arg] - let run_compilation_file compilation_database file = try let compilation_data = CompilationDatabase.find compilation_database file in let wrapper_cmd = swap_command compilation_data.command in - let replaced_args = IList.map replace_clang_arg compilation_data.args |> IList.flatten in let arg_file = - ClangQuotes.mk_arg_file "buck_clang_args_" ClangQuotes.SingleQuotes replaced_args in + ClangQuotes.mk_arg_file + "cdb_clang_args_" ClangQuotes.EscapedNoQuotes [compilation_data.args] in let args = Array.of_list [wrapper_cmd; "@" ^ arg_file] in let env = Array.append (Unix.environment())