summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuncer Ayaz <tuncer.ayaz@gmail.com>2016-08-18 23:52:34 +0200
committerTuncer Ayaz <tuncer.ayaz@gmail.com>2016-08-19 14:17:30 +0200
commit05092202b35430f2b682fd40b7b5f1b3c773bb04 (patch)
tree4e8b4e0f5ddbce52f83d0ed9cb728a563b8835ff
parentb62370b97b27296e8a6cfff325995454cc47ef4f (diff)
downloadrebar-05092202b35430f2b682fd40b7b5f1b3c773bb04.tar.gz
port_compiler: generate clang compilation db
In order for newer clang tools to work, they require the presence of a compilation database in the form of compile_commands.json. Therefore, we adapt port_compiler to write such a file.
-rw-r--r--inttest/port/port_rt.erl1
-rw-r--r--src/rebar_port_compiler.erl64
2 files changed, 52 insertions, 13 deletions
diff --git a/inttest/port/port_rt.erl b/inttest/port/port_rt.erl
index c910e0e..afe991e 100644
--- a/inttest/port/port_rt.erl
+++ b/inttest/port/port_rt.erl
@@ -48,6 +48,7 @@ run(_Dir) ->
%% test.so is created during first compile
?assertEqual(0, filelib:last_modified("priv/test.so")),
?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
+ ?assertMatch(true, filelib:is_regular("compile_commands.json")),
TestSo1 = filelib:last_modified("priv/test" ++
shared_library_file_extension(os:type())),
?assert(TestSo1 > 0),
diff --git a/src/rebar_port_compiler.erl b/src/rebar_port_compiler.erl
index 5c612a8..10f8f69 100644
--- a/src/rebar_port_compiler.erl
+++ b/src/rebar_port_compiler.erl
@@ -211,27 +211,65 @@ replace_extension(File, OldExt, NewExt) ->
%%
compile_sources(Config, Specs, SharedEnv) ->
- lists:foldl(
- fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) ->
- Env = proplists:get_value(env, Opts, SharedEnv),
- compile_each(Config, Sources, Type, Env, NewBins)
- end, [], Specs).
-
-compile_each(_Config, [], _Type, _Env, NewBins) ->
- lists:reverse(NewBins);
-compile_each(Config, [Source | Rest], Type, Env, NewBins) ->
+ {Res, Db} =
+ lists:foldl(
+ fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) ->
+ Env = proplists:get_value(env, Opts, SharedEnv),
+ compile_each(Config, Sources, Type, Env, {NewBins, []})
+ end, [], Specs),
+ %% Rewrite clang compile commands database file only if something
+ %% was compiled.
+ case Res of
+ [] ->
+ ok;
+ _ ->
+ {ok, ClangDbFile} = file:open("compile_commands.json", [write]),
+ ok = io:fwrite(ClangDbFile, "[~n", []),
+ lists:foreach(fun(E) -> ok = io:fwrite(ClangDbFile, E, []) end, Db),
+ ok = io:fwrite(ClangDbFile, "]~n", []),
+ ok = file:close(ClangDbFile)
+ end,
+ Res.
+
+compile_each(_Config, [], _Type, _Env, {NewBins, CDB}) ->
+ {lists:reverse(NewBins), lists:reverse(CDB)};
+compile_each(Config, [Source | Rest], Type, Env, {NewBins, CDB}) ->
Ext = filename:extension(Source),
Bin = replace_extension(Source, Ext, ".o"),
+ Template = select_compile_template(Type, compiler(Ext)),
+ Cmd = expand_command(Template, Env, Source, Bin),
+ %% Omit all variables from cmd, and use that as cmd in
+ %% CDB, because otherwise clang-* will complain about it.
+ CDBCmd = string:join(
+ lists:filter(
+ fun("$"++_) -> false;
+ (_) -> true
+ end,
+ string:tokens(Cmd, " ")),
+ " "),
+ Cwd = rebar_utils:get_cwd(),
+ %% If there are more source files, make sure we end the CDB entry
+ %% with a comma.
+ CDBEntSep = case Rest of
+ [] -> "~n";
+ _ -> ",~n"
+ end,
+ %% CDB entry
+ CDBEnt = ?FMT("{ \"file\" : ~p~n"
+ ", \"directory\" : ~p~n"
+ ", \"command\" : ~p~n}"
+ "~s",
+ [Source, Cwd, CDBCmd, CDBEntSep]
+ ),
case needs_compile(Source, Bin) of
true ->
- Template = select_compile_template(Type, compiler(Ext)),
- Cmd = expand_command(Template, Env, Source, Bin),
ShOpts = [{env, Env}, return_on_error, {use_stdout, false}],
exec_compiler(Config, Source, Cmd, ShOpts),
- compile_each(Config, Rest, Type, Env, [Bin | NewBins]);
+ compile_each(Config, Rest, Type, Env,
+ {[Bin | NewBins], [CDBEnt | CDB]});
false ->
?INFO("Skipping ~s\n", [Source]),
- compile_each(Config, Rest, Type, Env, NewBins)
+ compile_each(Config, Rest, Type, Env, {NewBins, [CDBEnt, CDB]})
end.
exec_compiler(Config, Source, Cmd, ShOpts) ->