mirror of
https://github.com/correl/rebar.git
synced 2024-11-23 19:19:54 +00:00
commit
00164b280a
8 changed files with 124 additions and 86 deletions
|
@ -1,7 +0,0 @@
|
||||||
\.beam
|
|
||||||
^rebar$
|
|
||||||
.~
|
|
||||||
\.orig
|
|
||||||
\.swp
|
|
||||||
rt.work/*
|
|
||||||
^.gitignore$
|
|
1
.hgtags
1
.hgtags
|
@ -1 +0,0 @@
|
||||||
e8747041ef63f1b394d3b156c72c5bc12e92ecc4 RELEASE-1
|
|
1
Makefile
1
Makefile
|
@ -10,6 +10,7 @@ clean:
|
||||||
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit
|
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
@rm -f dialyzer_warnings
|
||||||
@rm -rf deps
|
@rm -rf deps
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
|
|
11
THANKS
11
THANKS
|
@ -109,3 +109,14 @@ Daniel White
|
||||||
Martin Schut
|
Martin Schut
|
||||||
Serge Aleynikov
|
Serge Aleynikov
|
||||||
Magnus Henoch
|
Magnus Henoch
|
||||||
|
Artem Teslenko
|
||||||
|
Jeremie Lasalle Ratelle
|
||||||
|
Jose Valim
|
||||||
|
Krzysztof Rutka
|
||||||
|
Mats Cronqvist
|
||||||
|
Matthew Conway
|
||||||
|
Giacomo Olgeni
|
||||||
|
Pedram Nimreezi
|
||||||
|
Sylvain Benner
|
||||||
|
Oliver Ferrigni
|
||||||
|
Dave Thomas
|
||||||
|
|
|
@ -209,7 +209,9 @@
|
||||||
{xref_warnings, false}.
|
{xref_warnings, false}.
|
||||||
|
|
||||||
%% xref checks to run
|
%% xref checks to run
|
||||||
{xref_checks, [exports_not_used, undefined_function_calls]}.
|
{xref_checks, [undefined_function_calls, undefined_functions,
|
||||||
|
locals_not_used, exports_not_used,
|
||||||
|
deprecated_function_calls, deprecated_functions]}.
|
||||||
|
|
||||||
%% Optional custom xref queries (xref manual has details) specified as
|
%% Optional custom xref queries (xref manual has details) specified as
|
||||||
%% {xref_queries, [{query_string(), expected_query_result()},...]}
|
%% {xref_queries, [{query_string(), expected_query_result()},...]}
|
||||||
|
|
|
@ -533,21 +533,7 @@ plugin_modules(Config, PredirsAssoc, FoundModules, MissingModules) ->
|
||||||
|
|
||||||
load_plugin_modules(Config, PredirsAssoc, Modules) ->
|
load_plugin_modules(Config, PredirsAssoc, Modules) ->
|
||||||
Cwd = rebar_utils:get_cwd(),
|
Cwd = rebar_utils:get_cwd(),
|
||||||
PluginDirs = case rebar_config:get_local(Config, plugin_dir, undefined) of
|
PluginDirs = get_all_plugin_dirs(Config, Cwd, PredirsAssoc),
|
||||||
undefined ->
|
|
||||||
% Plugin can be in the project's "plugins" folder
|
|
||||||
[filename:join(Cwd, "plugins")];
|
|
||||||
Dir ->
|
|
||||||
[Dir]
|
|
||||||
end ++
|
|
||||||
% We also want to include this case:
|
|
||||||
% Plugin can be in "plugins" directory of the plugin base directory. For
|
|
||||||
% example, Cwd depends on Plugin, and deps/Plugin/plugins/Plugin.erl is the
|
|
||||||
% plugin.
|
|
||||||
[
|
|
||||||
filename:join(Dir, "plugins") ||
|
|
||||||
Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc)
|
|
||||||
],
|
|
||||||
|
|
||||||
%% Find relevant sources in base_dir and plugin_dir
|
%% Find relevant sources in base_dir and plugin_dir
|
||||||
Erls = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"),
|
Erls = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"),
|
||||||
|
@ -562,12 +548,32 @@ load_plugin_modules(Config, PredirsAssoc, Modules) ->
|
||||||
NotLoaded = [V || V <- Modules, FilterMissing(V)],
|
NotLoaded = [V || V <- Modules, FilterMissing(V)],
|
||||||
{Loaded, NotLoaded}.
|
{Loaded, NotLoaded}.
|
||||||
|
|
||||||
|
get_all_plugin_dirs(Config, Cwd, PredirsAssoc) ->
|
||||||
|
get_plugin_dir(Config, Cwd) ++ get_base_plugin_dirs(Cwd, PredirsAssoc).
|
||||||
|
|
||||||
|
get_plugin_dir(Config, Cwd) ->
|
||||||
|
case rebar_config:get_local(Config, plugin_dir, undefined) of
|
||||||
|
undefined ->
|
||||||
|
%% Plugin can be in the project's "plugins" folder
|
||||||
|
[filename:join(Cwd, "plugins")];
|
||||||
|
Dir ->
|
||||||
|
[Dir]
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% We also want to include this case:
|
||||||
|
%% Plugin can be in "plugins" directory of the plugin base directory.
|
||||||
|
%% For example, Cwd depends on Plugin, and deps/Plugin/plugins/Plugin.erl
|
||||||
|
%% is the plugin.
|
||||||
|
get_base_plugin_dirs(Cwd, PredirsAssoc) ->
|
||||||
|
[filename:join(Dir, "plugins") ||
|
||||||
|
Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc)].
|
||||||
|
|
||||||
%% @doc PredirsAssoc is a dictionary of plugindir -> 'parent' pairs
|
%% @doc PredirsAssoc is a dictionary of plugindir -> 'parent' pairs
|
||||||
%% 'parent' in this case depends on plugin; therefore we have to give
|
%% 'parent' in this case depends on plugin; therefore we have to give
|
||||||
%% all plugins that Cwd ('parent' in this case) depends on.
|
%% all plugins that Cwd ('parent' in this case) depends on.
|
||||||
get_plugin_base_dirs(Cwd, PredirsAssoc) ->
|
get_plugin_base_dirs(Cwd, PredirsAssoc) ->
|
||||||
[PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc),
|
[PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc),
|
||||||
Master =:= Cwd].
|
Master =:= Cwd].
|
||||||
|
|
||||||
is_missing_plugin(Loaded) ->
|
is_missing_plugin(Loaded) ->
|
||||||
fun(Mod) -> not lists:member(Mod, Loaded) end.
|
fun(Mod) -> not lists:member(Mod, Loaded) end.
|
||||||
|
|
|
@ -108,7 +108,8 @@ run_test(TestDir, LogDir, Config, _File) ->
|
||||||
" 2>&1 | tee -a " ++ RawLog
|
" 2>&1 | tee -a " ++ RawLog
|
||||||
end,
|
end,
|
||||||
|
|
||||||
case rebar_utils:sh(Cmd ++ Output, [{env,[{"TESTDIR", TestDir}]}, return_on_error]) of
|
ShOpts = [{env,[{"TESTDIR", TestDir}]}, return_on_error],
|
||||||
|
case rebar_utils:sh(Cmd ++ Output, ShOpts) of
|
||||||
{ok,_} ->
|
{ok,_} ->
|
||||||
%% in older versions of ct_run, this could have been a failure
|
%% in older versions of ct_run, this could have been a failure
|
||||||
%% that returned a non-0 code. Check for that!
|
%% that returned a non-0 code. Check for that!
|
||||||
|
@ -135,11 +136,16 @@ clear_log(LogDir, RawLog) ->
|
||||||
check_success_log(Config, RawLog) ->
|
check_success_log(Config, RawLog) ->
|
||||||
check_log(Config, RawLog, fun(Msg) -> ?CONSOLE("DONE.\n~s\n", [Msg]) end).
|
check_log(Config, RawLog, fun(Msg) -> ?CONSOLE("DONE.\n~s\n", [Msg]) end).
|
||||||
|
|
||||||
check_fail_log(Config, RawLog, Command, {Rc, Output}) ->
|
-type err_handler() :: fun((string()) -> no_return()).
|
||||||
check_log(Config, RawLog, fun(_Msg) ->
|
-spec failure_logger(string(), {integer(), string()}) -> err_handler().
|
||||||
|
failure_logger(Command, {Rc, Output}) ->
|
||||||
|
fun(_Msg) ->
|
||||||
?ABORT("~s failed with error: ~w and output:~n~s~n",
|
?ABORT("~s failed with error: ~w and output:~n~s~n",
|
||||||
[Command, Rc, Output])
|
[Command, Rc, Output])
|
||||||
end).
|
end.
|
||||||
|
|
||||||
|
check_fail_log(Config, RawLog, Command, Result) ->
|
||||||
|
check_log(Config, RawLog, failure_logger(Command, Result)).
|
||||||
|
|
||||||
check_log(Config,RawLog,Fun) ->
|
check_log(Config,RawLog,Fun) ->
|
||||||
{ok, Msg} =
|
{ok, Msg} =
|
||||||
|
|
|
@ -37,6 +37,9 @@
|
||||||
|
|
||||||
-export([xref/2]).
|
-export([xref/2]).
|
||||||
|
|
||||||
|
%% for internal use only
|
||||||
|
-export([info/2]).
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Public API
|
%% Public API
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
@ -58,14 +61,15 @@ xref(Config, _) ->
|
||||||
|
|
||||||
%% Get list of xref checks we want to run
|
%% Get list of xref checks we want to run
|
||||||
ConfXrefChecks = rebar_config:get(Config, xref_checks,
|
ConfXrefChecks = rebar_config:get(Config, xref_checks,
|
||||||
[exports_not_used,
|
[exports_not_used,
|
||||||
undefined_function_calls]),
|
undefined_function_calls]),
|
||||||
|
|
||||||
SupportedXrefs = [undefined_function_calls, undefined_functions,
|
SupportedXrefs = [undefined_function_calls, undefined_functions,
|
||||||
locals_not_used, exports_not_used,
|
locals_not_used, exports_not_used,
|
||||||
deprecated_function_calls, deprecated_functions],
|
deprecated_function_calls, deprecated_functions],
|
||||||
|
|
||||||
XrefChecks = sets:to_list(sets:intersection(sets:from_list(SupportedXrefs),
|
XrefChecks = sets:to_list(sets:intersection(
|
||||||
|
sets:from_list(SupportedXrefs),
|
||||||
sets:from_list(ConfXrefChecks))),
|
sets:from_list(ConfXrefChecks))),
|
||||||
|
|
||||||
%% Run xref checks
|
%% Run xref checks
|
||||||
|
@ -92,17 +96,37 @@ xref(Config, _) ->
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
||||||
|
info(help, xref) ->
|
||||||
|
?CONSOLE(
|
||||||
|
"Run cross reference analysis.~n"
|
||||||
|
"~n"
|
||||||
|
"Valid rebar.config options:~n"
|
||||||
|
" ~p~n"
|
||||||
|
" ~p~n"
|
||||||
|
" ~p~n",
|
||||||
|
[
|
||||||
|
{xref_warnings, false},
|
||||||
|
{xref_checks, [undefined_function_calls, undefined_functions,
|
||||||
|
locals_not_used, exports_not_used,
|
||||||
|
deprecated_function_calls, deprecated_functions]},
|
||||||
|
{xref_queries,
|
||||||
|
[{"(xc - uc) || (xu - x - b"
|
||||||
|
" - (\"mod\":\".*foo\"/\"4\"))",[]}]}
|
||||||
|
]).
|
||||||
|
|
||||||
xref_checks(XrefChecks) ->
|
xref_checks(XrefChecks) ->
|
||||||
XrefWarnCount = lists:foldl(
|
XrefWarnCount = lists:foldl(fun run_xref_check/2, 0, XrefChecks),
|
||||||
fun(XrefCheck, Acc) ->
|
|
||||||
{ok, Results} = xref:analyze(xref, XrefCheck),
|
|
||||||
FilteredResults =filter_xref_results(XrefCheck, Results),
|
|
||||||
lists:foreach(fun(Res) -> display_xrefresult(XrefCheck, Res) end, FilteredResults),
|
|
||||||
Acc + length(FilteredResults)
|
|
||||||
end,
|
|
||||||
0, XrefChecks),
|
|
||||||
XrefWarnCount =:= 0.
|
XrefWarnCount =:= 0.
|
||||||
|
|
||||||
|
run_xref_check(XrefCheck, Acc) ->
|
||||||
|
{ok, Results} = xref:analyze(xref, XrefCheck),
|
||||||
|
FilteredResults =filter_xref_results(XrefCheck, Results),
|
||||||
|
lists:foreach(fun(Res) ->
|
||||||
|
display_xref_result(XrefCheck, Res)
|
||||||
|
end,
|
||||||
|
FilteredResults),
|
||||||
|
Acc + length(FilteredResults).
|
||||||
|
|
||||||
check_query({Query, Value}) ->
|
check_query({Query, Value}) ->
|
||||||
{ok, Answer} = xref:q(xref, Query),
|
{ok, Answer} = xref:q(xref, Query),
|
||||||
case Answer =:= Value of
|
case Answer =:= Value of
|
||||||
|
@ -141,78 +165,75 @@ get_xref_ignorelist(Mod, XrefCheck) ->
|
||||||
_Class:_Error -> []
|
_Class:_Error -> []
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Ignore_xref = keyall(ignore_xref, Attributes),
|
IgnoreXref = keyall(ignore_xref, Attributes),
|
||||||
|
|
||||||
Behaviour_callbacks = case XrefCheck of
|
BehaviourCallbacks = get_behaviour_callbacks(XrefCheck, Attributes),
|
||||||
exports_not_used -> [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes)];
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
|
|
||||||
% And create a flat {M,F,A} list
|
%% And create a flat {M,F,A} list
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(El,Acc) ->
|
fun({F, A}, Acc) -> [{Mod,F,A} | Acc];
|
||||||
case El of
|
({M, F, A}, Acc) -> [{M,F,A} | Acc]
|
||||||
{F, A} -> [{Mod,F,A} | Acc];
|
end, [], lists:flatten([IgnoreXref, BehaviourCallbacks])).
|
||||||
{M, F, A} -> [{M,F,A} | Acc]
|
|
||||||
end
|
|
||||||
end, [],lists:flatten([Ignore_xref, Behaviour_callbacks])).
|
|
||||||
|
|
||||||
keyall(Key, List) ->
|
keyall(Key, List) ->
|
||||||
lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List).
|
lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List).
|
||||||
|
|
||||||
parse_xref_result(XrefResult) ->
|
get_behaviour_callbacks(exports_not_used, Attributes) ->
|
||||||
case XrefResult of
|
[B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes)];
|
||||||
{_, MFAt} -> MFAt;
|
get_behaviour_callbacks(_XrefCheck, _Attributes) ->
|
||||||
MFAt -> MFAt
|
[].
|
||||||
end.
|
|
||||||
|
parse_xref_result({_, MFAt}) -> MFAt;
|
||||||
|
parse_xref_result(MFAt) -> MFAt.
|
||||||
|
|
||||||
filter_xref_results(XrefCheck, XrefResults) ->
|
filter_xref_results(XrefCheck, XrefResults) ->
|
||||||
SearchModules = lists:usort(lists:map(
|
SearchModules = lists:usort(
|
||||||
fun(Res) ->
|
lists:map(
|
||||||
case Res of
|
fun({Mt,_Ft,_At}) -> Mt;
|
||||||
{Mt,_Ft,_At} -> Mt;
|
({{Ms,_Fs,_As},{_Mt,_Ft,_At}}) -> Ms;
|
||||||
{{Ms,_Fs,_As},{_Mt,_Ft,_At}} -> Ms;
|
(_) -> undefined
|
||||||
_ -> undefined
|
end, XrefResults)),
|
||||||
end
|
|
||||||
end, XrefResults)),
|
|
||||||
|
|
||||||
Ignores = lists:flatten([
|
Ignores = lists:flatmap(fun(Module) ->
|
||||||
get_xref_ignorelist(Module,XrefCheck) || Module <- SearchModules]),
|
get_xref_ignorelist(Module, XrefCheck)
|
||||||
|
end, SearchModules),
|
||||||
|
|
||||||
[Result || Result <- XrefResults,
|
[Result || Result <- XrefResults,
|
||||||
not lists:member(parse_xref_result(Result),Ignores)].
|
not lists:member(parse_xref_result(Result), Ignores)].
|
||||||
|
|
||||||
display_xrefresult(Type, XrefResult) ->
|
display_xref_result(Type, XrefResult) ->
|
||||||
{ Source, SMFA, TMFA } = case XrefResult of
|
{ Source, SMFA, TMFA } = case XrefResult of
|
||||||
{MFASource, MFATarget} ->
|
{MFASource, MFATarget} ->
|
||||||
{format_mfa_source(MFASource), format_mfa(MFASource),
|
{format_mfa_source(MFASource),
|
||||||
format_mfa(MFATarget)};
|
format_mfa(MFASource),
|
||||||
MFATarget ->
|
format_mfa(MFATarget)};
|
||||||
{format_mfa_source(MFATarget), format_mfa(MFATarget),
|
MFATarget ->
|
||||||
undefined}
|
{format_mfa_source(MFATarget),
|
||||||
end,
|
format_mfa(MFATarget),
|
||||||
|
undefined}
|
||||||
|
end,
|
||||||
case Type of
|
case Type of
|
||||||
undefined_function_calls ->
|
undefined_function_calls ->
|
||||||
?CONSOLE("~sWarning: ~s calls undefined function ~s (Xref)\n",
|
?CONSOLE("~sWarning: ~s calls undefined function ~s (Xref)\n",
|
||||||
[Source, SMFA, TMFA]);
|
[Source, SMFA, TMFA]);
|
||||||
undefined_functions ->
|
undefined_functions ->
|
||||||
?CONSOLE("~sWarning: ~s is undefined function (Xref)\n",
|
?CONSOLE("~sWarning: ~s is undefined function (Xref)\n",
|
||||||
[Source, SMFA]);
|
[Source, SMFA]);
|
||||||
locals_not_used ->
|
locals_not_used ->
|
||||||
?CONSOLE("~sWarning: ~s is unused local function (Xref)\n",
|
?CONSOLE("~sWarning: ~s is unused local function (Xref)\n",
|
||||||
[Source, SMFA]);
|
[Source, SMFA]);
|
||||||
exports_not_used ->
|
exports_not_used ->
|
||||||
?CONSOLE("~sWarning: ~s is unused export (Xref)\n",
|
?CONSOLE("~sWarning: ~s is unused export (Xref)\n",
|
||||||
[Source, SMFA]);
|
[Source, SMFA]);
|
||||||
deprecated_function_calls ->
|
deprecated_function_calls ->
|
||||||
?CONSOLE("~sWarning: ~s calls deprecated function ~s (Xref)\n",
|
?CONSOLE("~sWarning: ~s calls deprecated function ~s (Xref)\n",
|
||||||
[Source, SMFA, TMFA]);
|
[Source, SMFA, TMFA]);
|
||||||
deprecated_functions ->
|
deprecated_functions ->
|
||||||
?CONSOLE("~sWarning: ~s is deprecated function (Xref)\n",
|
?CONSOLE("~sWarning: ~s is deprecated function (Xref)\n",
|
||||||
[Source, SMFA]);
|
[Source, SMFA]);
|
||||||
Other ->
|
Other ->
|
||||||
?CONSOLE("~sWarning: ~s - ~s xref check: ~s (Xref)\n",
|
?CONSOLE("~sWarning: ~s - ~s xref check: ~s (Xref)\n",
|
||||||
[Source, SMFA, TMFA, Other])
|
[Source, SMFA, TMFA, Other])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
format_mfa({M, F, A}) ->
|
format_mfa({M, F, A}) ->
|
||||||
|
@ -236,7 +257,6 @@ safe_element(N, Tuple) ->
|
||||||
Value
|
Value
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Given a MFA, find the file and LOC where it's defined. Note that
|
%% Given a MFA, find the file and LOC where it's defined. Note that
|
||||||
%% xref doesn't work if there is no abstract_code, so we can avoid
|
%% xref doesn't work if there is no abstract_code, so we can avoid
|
||||||
|
|
Loading…
Reference in a new issue