diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 273aafc..0000000 --- a/.hgignore +++ /dev/null @@ -1,7 +0,0 @@ -\.beam -^rebar$ -.~ -\.orig -\.swp -rt.work/* -^.gitignore$ diff --git a/.hgtags b/.hgtags deleted file mode 100644 index 79d0ccc..0000000 --- a/.hgtags +++ /dev/null @@ -1 +0,0 @@ -e8747041ef63f1b394d3b156c72c5bc12e92ecc4 RELEASE-1 diff --git a/Makefile b/Makefile index 1144d5e..1aeb6ad 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ clean: @rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit distclean: clean + @rm -f dialyzer_warnings @rm -rf deps debug: diff --git a/THANKS b/THANKS index 1be44e5..95cc493 100644 --- a/THANKS +++ b/THANKS @@ -109,3 +109,14 @@ Daniel White Martin Schut Serge Aleynikov 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 diff --git a/rebar.config.sample b/rebar.config.sample index d8d61fc..97b5a02 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -209,7 +209,9 @@ {xref_warnings, false}. %% 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 %% {xref_queries, [{query_string(), expected_query_result()},...]} diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 61e8412..fcfa62a 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -533,21 +533,7 @@ plugin_modules(Config, PredirsAssoc, FoundModules, MissingModules) -> load_plugin_modules(Config, PredirsAssoc, Modules) -> Cwd = rebar_utils:get_cwd(), - PluginDirs = 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. - [ - filename:join(Dir, "plugins") || - Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc) - ], + PluginDirs = get_all_plugin_dirs(Config, Cwd, PredirsAssoc), %% Find relevant sources in base_dir and plugin_dir 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)], {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 %% 'parent' in this case depends on plugin; therefore we have to give %% all plugins that Cwd ('parent' in this case) depends on. get_plugin_base_dirs(Cwd, PredirsAssoc) -> [PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc), - Master =:= Cwd]. + Master =:= Cwd]. is_missing_plugin(Loaded) -> fun(Mod) -> not lists:member(Mod, Loaded) end. diff --git a/src/rebar_ct.erl b/src/rebar_ct.erl index 9951f8e..66b9d76 100644 --- a/src/rebar_ct.erl +++ b/src/rebar_ct.erl @@ -108,7 +108,8 @@ run_test(TestDir, LogDir, Config, _File) -> " 2>&1 | tee -a " ++ RawLog 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,_} -> %% in older versions of ct_run, this could have been a failure %% that returned a non-0 code. Check for that! @@ -135,11 +136,16 @@ clear_log(LogDir, RawLog) -> check_success_log(Config, RawLog) -> check_log(Config, RawLog, fun(Msg) -> ?CONSOLE("DONE.\n~s\n", [Msg]) end). -check_fail_log(Config, RawLog, Command, {Rc, Output}) -> - check_log(Config, RawLog, fun(_Msg) -> +-type err_handler() :: fun((string()) -> no_return()). +-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", [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) -> {ok, Msg} = diff --git a/src/rebar_xref.erl b/src/rebar_xref.erl index 6120ada..eaf6d03 100644 --- a/src/rebar_xref.erl +++ b/src/rebar_xref.erl @@ -37,6 +37,9 @@ -export([xref/2]). +%% for internal use only +-export([info/2]). + %% =================================================================== %% Public API %% =================================================================== @@ -58,14 +61,15 @@ xref(Config, _) -> %% Get list of xref checks we want to run ConfXrefChecks = rebar_config:get(Config, xref_checks, - [exports_not_used, - undefined_function_calls]), + [exports_not_used, + undefined_function_calls]), SupportedXrefs = [undefined_function_calls, undefined_functions, - locals_not_used, exports_not_used, - deprecated_function_calls, deprecated_functions], + locals_not_used, exports_not_used, + 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))), %% Run xref checks @@ -92,17 +96,37 @@ xref(Config, _) -> %% 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) -> - XrefWarnCount = lists:foldl( - 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 = lists:foldl(fun run_xref_check/2, 0, XrefChecks), 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}) -> {ok, Answer} = xref:q(xref, Query), case Answer =:= Value of @@ -141,78 +165,75 @@ get_xref_ignorelist(Mod, XrefCheck) -> _Class:_Error -> [] end, - Ignore_xref = keyall(ignore_xref, Attributes), + IgnoreXref = keyall(ignore_xref, Attributes), - Behaviour_callbacks = case XrefCheck of - exports_not_used -> [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes)]; - _ -> [] - end, + BehaviourCallbacks = get_behaviour_callbacks(XrefCheck, Attributes), - % And create a flat {M,F,A} list + %% And create a flat {M,F,A} list lists:foldl( - fun(El,Acc) -> - case El of - {F, A} -> [{Mod,F,A} | Acc]; - {M, F, A} -> [{M,F,A} | Acc] - end - end, [],lists:flatten([Ignore_xref, Behaviour_callbacks])). + fun({F, A}, Acc) -> [{Mod,F,A} | Acc]; + ({M, F, A}, Acc) -> [{M,F,A} | Acc] + end, [], lists:flatten([IgnoreXref, BehaviourCallbacks])). keyall(Key, List) -> lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List). -parse_xref_result(XrefResult) -> - case XrefResult of - {_, MFAt} -> MFAt; - MFAt -> MFAt - end. +get_behaviour_callbacks(exports_not_used, Attributes) -> + [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes)]; +get_behaviour_callbacks(_XrefCheck, _Attributes) -> + []. + +parse_xref_result({_, MFAt}) -> MFAt; +parse_xref_result(MFAt) -> MFAt. filter_xref_results(XrefCheck, XrefResults) -> - SearchModules = lists:usort(lists:map( - fun(Res) -> - case Res of - {Mt,_Ft,_At} -> Mt; - {{Ms,_Fs,_As},{_Mt,_Ft,_At}} -> Ms; - _ -> undefined - end - end, XrefResults)), + SearchModules = lists:usort( + lists:map( + fun({Mt,_Ft,_At}) -> Mt; + ({{Ms,_Fs,_As},{_Mt,_Ft,_At}}) -> Ms; + (_) -> undefined + end, XrefResults)), - Ignores = lists:flatten([ - get_xref_ignorelist(Module,XrefCheck) || Module <- SearchModules]), + Ignores = lists:flatmap(fun(Module) -> + get_xref_ignorelist(Module, XrefCheck) + end, SearchModules), [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 - {MFASource, MFATarget} -> - {format_mfa_source(MFASource), format_mfa(MFASource), - format_mfa(MFATarget)}; - MFATarget -> - {format_mfa_source(MFATarget), format_mfa(MFATarget), - undefined} - end, + {MFASource, MFATarget} -> + {format_mfa_source(MFASource), + format_mfa(MFASource), + format_mfa(MFATarget)}; + MFATarget -> + {format_mfa_source(MFATarget), + format_mfa(MFATarget), + undefined} + end, case Type of undefined_function_calls -> ?CONSOLE("~sWarning: ~s calls undefined function ~s (Xref)\n", - [Source, SMFA, TMFA]); + [Source, SMFA, TMFA]); undefined_functions -> ?CONSOLE("~sWarning: ~s is undefined function (Xref)\n", - [Source, SMFA]); + [Source, SMFA]); locals_not_used -> ?CONSOLE("~sWarning: ~s is unused local function (Xref)\n", - [Source, SMFA]); + [Source, SMFA]); exports_not_used -> ?CONSOLE("~sWarning: ~s is unused export (Xref)\n", - [Source, SMFA]); + [Source, SMFA]); deprecated_function_calls -> ?CONSOLE("~sWarning: ~s calls deprecated function ~s (Xref)\n", - [Source, SMFA, TMFA]); + [Source, SMFA, TMFA]); deprecated_functions -> ?CONSOLE("~sWarning: ~s is deprecated function (Xref)\n", - [Source, SMFA]); + [Source, SMFA]); Other -> ?CONSOLE("~sWarning: ~s - ~s xref check: ~s (Xref)\n", - [Source, SMFA, TMFA, Other]) + [Source, SMFA, TMFA, Other]) end. format_mfa({M, F, A}) -> @@ -236,7 +257,6 @@ safe_element(N, Tuple) -> Value end. - %% %% 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