From 05092202b35430f2b682fd40b7b5f1b3c773bb04 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Thu, 18 Aug 2016 23:52:34 +0200 Subject: 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. --- inttest/port/port_rt.erl | 1 + src/rebar_port_compiler.erl | 64 ++++++++++++++++++++++++++++++++++++--------- 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) -> -- cgit v1.2.1