|
|
|
(*
|
|
|
|
* Copyright (c) 2016 - present Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*)
|
|
|
|
|
|
|
|
open! IStd
|
|
|
|
module L = Logging
|
|
|
|
|
|
|
|
type compilation_data = {dir: string; command: string; args: string}
|
|
|
|
|
|
|
|
type t = compilation_data SourceFile.Map.t ref
|
|
|
|
|
|
|
|
let empty () = ref SourceFile.Map.empty
|
|
|
|
|
|
|
|
let get_size database = SourceFile.Map.cardinal !database
|
|
|
|
|
|
|
|
let iter database f = SourceFile.Map.iter f !database
|
|
|
|
|
|
|
|
let filter_compilation_data database ~f =
|
|
|
|
SourceFile.Map.filter (fun s _ -> f s) !database |> SourceFile.Map.bindings |> List.map ~f:snd
|
|
|
|
|
|
|
|
let find database key = SourceFile.Map.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) json_format =
|
|
|
|
let json_path =
|
|
|
|
match json_format
|
|
|
|
with `Raw x | `Escaped x -> x
|
|
|
|
in
|
|
|
|
let to_string s =
|
|
|
|
match json_format with
|
|
|
|
| `Raw _
|
|
|
|
-> s
|
|
|
|
| `Escaped _
|
|
|
|
-> Utils.with_process_in (Printf.sprintf "/bin/sh -c 'printf \"%%s\" %s'" s)
|
|
|
|
In_channel.input_line_exn
|
|
|
|
|> fst
|
|
|
|
in
|
|
|
|
L.(debug Capture Quiet) "parsing compilation database from %s@\n" json_path ;
|
|
|
|
let exit_format_error () = L.(die ExternalError) "Json file doesn't have the expected format" in
|
|
|
|
let json = Yojson.Basic.from_file json_path in
|
|
|
|
let get_dir el = match el with "directory", `String dir -> Some (to_string dir) | _ -> None in
|
|
|
|
let get_file el = match el with "file", `String file -> Some (to_string 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
|
|
|
|
-> List.iter ~f:parse_json arguments
|
|
|
|
| `Assoc l
|
|
|
|
-> let dir =
|
|
|
|
match List.find_map ~f:get_dir l with Some dir -> dir | None -> exit_format_error ()
|
|
|
|
in
|
|
|
|
let file =
|
|
|
|
match List.find_map ~f:get_file l with Some file -> file | None -> exit_format_error ()
|
|
|
|
in
|
|
|
|
let cmd =
|
|
|
|
match List.find_map ~f:get_cmd l with Some cmd -> cmd | None -> exit_format_error ()
|
|
|
|
in
|
|
|
|
let command, args = parse_command_and_arguments cmd in
|
|
|
|
let compilation_data = {dir; command; args} in
|
|
|
|
let abs_file = if Filename.is_relative file then dir ^/ file else file in
|
|
|
|
let source_file = SourceFile.from_abs_path abs_file in
|
|
|
|
database := SourceFile.Map.add source_file compilation_data !database
|
|
|
|
| _
|
|
|
|
-> exit_format_error ()
|
|
|
|
in
|
|
|
|
parse_json json
|
|
|
|
|
|
|
|
let from_json_files db_json_files =
|
|
|
|
let db = empty () in
|
|
|
|
List.iter ~f:(decode_json_file db) db_json_files ;
|
|
|
|
L.(debug Capture Quiet) "created database with %d entries@\n" (get_size db) ;
|
|
|
|
db
|