Skip to content

Commit

Permalink
Implement CompilationDatabase.split (issue #624)
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Mar 9, 2022
1 parent 6958d4b commit f368050
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 4 deletions.
49 changes: 45 additions & 4 deletions src/util/compilationDatabase.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,54 @@ type command_object = {
command: string option [@default None];
arguments: string list option [@default None];
output: string option [@default None];
} [@@deriving yojson]
} [@@deriving yojson, show]

type t = command_object list [@@deriving yojson]
type t = command_object list [@@deriving yojson, show]

let parse_file filename =
Result.get_ok (of_yojson (Yojson.Safe.from_file filename))

(* TODO: allow quoted .c file name? *)
let command_c_regexp = Str.regexp " +\\([^ ]+\\.c\\)\\b"
let argument_c_regexp = Str.regexp ".*\\.c$"

let split (obj: command_object): command_object list =
match obj.command, obj.arguments with
| Some command, None ->
let rec map_command start =
match Str.search_forward command_c_regexp command start with
| match_begin ->
let match_end = Str.match_end () in
let init = Str.string_before command match_begin in
let tail = Str.string_after command match_end in
let matched_string = Str.matched_string command in (* must do before global_replace! *)
let file' = Str.matched_group 1 command in (* must do before global_replace! *)
let init' = Str.global_replace command_c_regexp "" init in
let tail' = Str.global_replace command_c_regexp "" tail in
let command' = init' ^ matched_string ^ tail' in
{obj with command = Some command'; file = file'} :: map_command match_end
| exception Not_found ->
[]
in
map_command 0
| None, Some arguments ->
let is_c s = Str.string_match argument_c_regexp s 0 in
List.filteri_map (fun i argument ->
if is_c argument then (
let (init, tail) = List.split_at i arguments in
let init' = List.filter (Fun.negate is_c) init in
let tail' = List.filter (Fun.negate is_c) tail in
let arguments' = init' @ argument :: tail' in
Some {obj with arguments = Some arguments'; file = argument}
)
else
None
) arguments
| Some _, Some _ ->
failwith ("CompilationDatabase.split: both command and arguments specified for " ^ obj.file)
| None, None ->
failwith ("CompilationDatabase.split: neither command nor arguments specified for " ^ obj.file)

let command_o_regexp = Str.regexp "-o +[^ ]+"
let command_program_regexp = Str.regexp "^ *\\([^ ]+\\)"

Expand Down Expand Up @@ -73,9 +114,9 @@ let load_and_preprocess ~all_cppflags filename =
let preprocess_arguments = all_cppflags @ "-E" :: preprocess_arguments in
Filename.quote_command arguments_program preprocess_arguments
| Some _, Some _ ->
failwith "CompilationDatabase.preprocess: both command and arguments specified for " ^ file
failwith ("CompilationDatabase.preprocess: both command and arguments specified for " ^ file)
| None, None ->
failwith "CompilationDatabase.preprocess: neither command nor arguments specified for " ^ file
failwith ("CompilationDatabase.preprocess: neither command nor arguments specified for " ^ file)
in
let cwd = reroot obj.directory in
if GobConfig.get_bool "dbg.verbose" then
Expand Down
1 change: 1 addition & 0 deletions unittest/mainTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ let all_tests = ("" >:::
MapDomainTest.test ();
SolverTest.test ();
LvalTest.test ();
CompilationDatabaseTest.tests;
(* etc *)
"domaintest" >::: QCheck_ounit.to_ounit2_test_list Maindomaintest.all_testsuite
])
Expand Down
89 changes: 89 additions & 0 deletions unittest/util/compilationDatabaseTest.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
open OUnit2

let command_object_from_string s =
s
|> Yojson.Safe.from_string
|> CompilationDatabase.command_object_of_yojson
|> Result.get_ok

let test_split_arguments _ =
let obj = command_object_from_string {json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"arguments": [
"gcc",
"-DANSWER=42",
"-Ilib/",
"main.c",
"lib/lib.c"
],
"file": "lib/lib.c"
}
|json}
in
let actual_split = CompilationDatabase.split obj in
let expected_split = List.map command_object_from_string [
{json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"arguments": [
"gcc",
"-DANSWER=42",
"-Ilib/",
"main.c"
],
"file": "main.c"
}
|json};
{json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"arguments": [
"gcc",
"-DANSWER=42",
"-Ilib/",
"lib/lib.c"
],
"file": "lib/lib.c"
}
|json};
]
in
assert_equal ~printer:CompilationDatabase.show expected_split actual_split

let test_split_command _ =
let obj = command_object_from_string {json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"command": "gcc -DANSWER=42 -Ilib/ main.c lib/lib.c",
"file": "lib/lib.c"
}
|json}
in
let actual_split = CompilationDatabase.split obj in
let expected_split = List.map command_object_from_string [
{json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"command": "gcc -DANSWER=42 -Ilib/ main.c",
"file": "main.c"
}
|json};
{json|
{
"directory": "/home/simmo/Desktop/goblint-action-test/makefile-project",
"command": "gcc -DANSWER=42 -Ilib/ lib/lib.c",
"file": "lib/lib.c"
}
|json};
]
in
assert_equal ~printer:CompilationDatabase.show expected_split actual_split

let tests =
"compilationDatabaseTest" >::: [
"split" >::: [
"arguments" >:: test_split_arguments;
"command" >:: test_split_command;
];
]

0 comments on commit f368050

Please sign in to comment.