|
|
@ -27,9 +27,9 @@ |
|
|
|
%% @author Dave Smith <dizzyd@dizzyd.com> |
|
|
|
%% @doc rebar_dialyzer supports the following commands: |
|
|
|
%% <ul> |
|
|
|
%% <li>analyze (essentially "dialyzer -r ebin")</li> |
|
|
|
%% <li>build_plt (essentially "dialyzer --build_plt -r <app_dirs>")</li> |
|
|
|
%% <li>check_plt (essentially "dialyzer --check_plt")</li> |
|
|
|
%% <li>dialyze (essentially "dialyzer ebin" or "dialyzer --src src")</li> |
|
|
|
%% <li>build-plt (essentially "dialyzer --build_plt -r <app_dirs>")</li> |
|
|
|
%% <li>check-plt (essentially "dialyzer --check_plt")</li> |
|
|
|
%% </ul> |
|
|
|
%% A single option <code>plt</code> can be presented in the <code>dialyzer_opts</code> |
|
|
|
%% options in <code>rebar.config</code>. If it is present, it is used as the PLT for the |
|
|
@ -44,9 +44,9 @@ |
|
|
|
%% ------------------------------------------------------------------- |
|
|
|
-module(rebar_dialyzer). |
|
|
|
|
|
|
|
-export([analyze/2, |
|
|
|
build_plt/2, |
|
|
|
check_plt/2]). |
|
|
|
-export([dialyze/2, |
|
|
|
'build-plt'/2, |
|
|
|
'check-plt'/2]). |
|
|
|
|
|
|
|
-include("rebar.hrl"). |
|
|
|
|
|
|
@ -57,21 +57,39 @@ |
|
|
|
%% =================================================================== |
|
|
|
|
|
|
|
%% @doc Perform static analysis on the contents of the ebin directory. |
|
|
|
%% @spec analyze(Config::#config{}, File::string()) -> ok |
|
|
|
-spec(analyze(Config::#config{}, File::string()) -> ok). |
|
|
|
analyze(Config, File) -> |
|
|
|
Plt = plt_path(Config, File), |
|
|
|
%% @spec dialyze(Config::#config{}, File::string()) -> ok |
|
|
|
-spec dialyze(Config::#config{}, File::string()) -> ok. |
|
|
|
dialyze(Config, File) -> |
|
|
|
Plt = existing_plt_path(Config, File), |
|
|
|
case dialyzer:plt_info(Plt) of |
|
|
|
{ok, _} -> |
|
|
|
try dialyzer:run([{files_rec, ["ebin"]}, {init_plt, Plt}]) of |
|
|
|
FromSrc = proplists:get_bool(src, rebar_config:get(Config, |
|
|
|
dialyzer_opts, |
|
|
|
[])), |
|
|
|
DialyzerOpts0 = case FromSrc of |
|
|
|
true -> |
|
|
|
[{files_rec, ["src"]}, {init_plt, Plt}, |
|
|
|
{from, src_code}]; |
|
|
|
false -> |
|
|
|
[{files_rec, ["ebin"]}, {init_plt, Plt}] |
|
|
|
end, |
|
|
|
WarnOpts = warnings(Config), |
|
|
|
DialyzerOpts = case WarnOpts of |
|
|
|
[] -> DialyzerOpts0; |
|
|
|
_ -> [{warnings, WarnOpts}|DialyzerOpts0] |
|
|
|
end, |
|
|
|
?DEBUG("DialyzerOpts: ~p~n", [DialyzerOpts]), |
|
|
|
try dialyzer:run(DialyzerOpts) of |
|
|
|
Warnings -> output_warnings(Warnings) |
|
|
|
catch |
|
|
|
throw:{dialyzer_error, Reason} -> |
|
|
|
?ABORT("~s~n", [Reason]) |
|
|
|
end; |
|
|
|
{error, no_such_file} -> |
|
|
|
?ABORT("The PLT ~s does not exist. Please perform the build_plt command to ~n" |
|
|
|
"produce the initial PLT. Be aware this operation may take several minutes.", [Plt]); |
|
|
|
?ABORT("The PLT ~s does not exist. Please perform the build-plt " |
|
|
|
"command to ~n" |
|
|
|
"produce the initial PLT. Be aware that this operation may " |
|
|
|
"take several minutes.~n", [Plt]); |
|
|
|
{error, read_error} -> |
|
|
|
?ABORT("Unable to read PLT ~n~n", [Plt]); |
|
|
|
{error, not_valid} -> |
|
|
@ -80,29 +98,30 @@ analyze(Config, File) -> |
|
|
|
ok. |
|
|
|
|
|
|
|
%% @doc Build the PLT. |
|
|
|
%% @spec build_plt(Config::#config{}, File::string()) -> ok |
|
|
|
-spec(build_plt(Config::#config{}, File::string()) -> ok). |
|
|
|
build_plt(Config, File) -> |
|
|
|
Plt = plt_path(Config, File), |
|
|
|
%% @spec build-plt(Config::#config{}, File::string()) -> ok |
|
|
|
-spec 'build-plt'(Config::#config{}, File::string()) -> ok. |
|
|
|
'build-plt'(Config, File) -> |
|
|
|
Plt = new_plt_path(Config, File), |
|
|
|
|
|
|
|
Apps = rebar_app_utils:app_applications(File), |
|
|
|
|
|
|
|
?DEBUG("Build PLT ~s including following apps:~n~p~n", [Plt, Apps]), |
|
|
|
Warnings = dialyzer:run([{analysis_type, plt_build}, |
|
|
|
{files_rec, app_dirs(Apps)}, |
|
|
|
{output_plt, Plt}]), |
|
|
|
case Warnings of |
|
|
|
[] -> |
|
|
|
?INFO("The built PLT can be found in ~s", [Plt]); |
|
|
|
?INFO("The built PLT can be found in ~s~n", [Plt]); |
|
|
|
_ -> |
|
|
|
output_warnings(Warnings) |
|
|
|
end, |
|
|
|
ok. |
|
|
|
|
|
|
|
%% @doc Check whether the PLT is up-to-date (rebuilding it if not). |
|
|
|
%% @spec check_plt(Config::#config{}, File::string()) -> ok |
|
|
|
-spec(check_plt(Config::#config{}, File::string()) -> ok). |
|
|
|
check_plt(Config, File) -> |
|
|
|
Plt = plt_path(Config, File), |
|
|
|
%% @spec check-plt(Config::#config{}, File::string()) -> ok |
|
|
|
-spec 'check-plt'(Config::#config{}, File::string()) -> ok. |
|
|
|
'check-plt'(Config, File) -> |
|
|
|
Plt = existing_plt_path(Config, File), |
|
|
|
try dialyzer:run([{analysis_type, plt_check}, {init_plt, Plt}]) of |
|
|
|
[] -> |
|
|
|
?CONSOLE("The PLT ~s is up-to-date~n", [Plt]); |
|
|
@ -121,14 +140,14 @@ check_plt(Config, File) -> |
|
|
|
|
|
|
|
%% @doc Obtain the library paths for the supplied applications. |
|
|
|
%% @spec app_dirs(Apps::[atom()]) -> [string()] |
|
|
|
-spec class="p">(app_dirs(Apps::[atom()]) -> [string()]). |
|
|
|
-spec app_dirs(Apps::[atom()]) -> [string()]. |
|
|
|
app_dirs(Apps) -> |
|
|
|
[filename:join(Path, "ebin") || |
|
|
|
Path <- lists:map(fun(App) -> code:lib_dir(App) end, Apps), erlang:is_list(Path)]. |
|
|
|
|
|
|
|
%% @doc Render the warnings on the console. |
|
|
|
%% @spec output_warnings(Warnings::[warning()]) -> 'ok' |
|
|
|
-spec class="p">(output_warnings(Warnings::[warning()]) -> 'ok'). |
|
|
|
-spec output_warnings(Warnings::[warning()]) -> 'ok'. |
|
|
|
output_warnings(Warnings) -> |
|
|
|
lists:foreach(fun(Warning) -> |
|
|
|
?CONSOLE("~s", [dialyzer:format_warning(Warning)]) |
|
|
@ -136,14 +155,52 @@ output_warnings(Warnings) -> |
|
|
|
|
|
|
|
%% @doc If the plt option is present in rebar.config return its value, otherwise |
|
|
|
%% return $HOME/.dialyzer_plt. |
|
|
|
%% @spec plt_path(Config::#config{}, File::string()) -> string() |
|
|
|
-spec class="p">(plt_path(Config::#config{}, File::string()) -> string()). |
|
|
|
plt_path(Config, File) -> |
|
|
|
%% @spec new_plt_path(Config::#config{}, File::string()) -> string() |
|
|
|
-spec new_plt_path(Config::#config{}, File::string()) -> string(). |
|
|
|
new_plt_path(Config, File) -> |
|
|
|
AppName = rebar_app_utils:app_name(File), |
|
|
|
DialyzerOpts = rebar_config:get(Config, dialyzer_opts, []), |
|
|
|
case proplists:get_value(plt, DialyzerOpts) of |
|
|
|
undefined -> |
|
|
|
filename:join(os:getenv("HOME"), "." ++ atom_to_list(AppName) ++ "_dialyzer_plt"); |
|
|
|
filename:join(os:getenv("HOME"), |
|
|
|
"." ++ atom_to_list(AppName) ++ "_dialyzer_plt"); |
|
|
|
Plt -> |
|
|
|
Plt |
|
|
|
end. |
|
|
|
|
|
|
|
%% @doc If the plt option is present in rebar.config and the file exists |
|
|
|
%% return its value or if ~/.AppName_dialyzer_plt exists return that. |
|
|
|
%% Otherwise return ~/.dialyzer_plt if it exists or abort. |
|
|
|
%% @spec existing_plt_path(Config::#config{}, File::string()) -> string() |
|
|
|
-spec existing_plt_path(Config::#config{}, File::string()) -> string(). |
|
|
|
existing_plt_path(Config, File) -> |
|
|
|
AppName = rebar_app_utils:app_name(File), |
|
|
|
DialyzerOpts = rebar_config:get(Config, dialyzer_opts, []), |
|
|
|
Home = os:getenv("HOME"), |
|
|
|
case proplists:get_value(plt, DialyzerOpts) of |
|
|
|
undefined -> |
|
|
|
AppPlt = filename:join(Home, "." ++ atom_to_list(AppName) |
|
|
|
++ "_dialyzer_plt"), |
|
|
|
case filelib:is_regular(AppPlt) of |
|
|
|
true -> |
|
|
|
AppPlt; |
|
|
|
false -> |
|
|
|
HomePlt = filename:join(Home, ".dialyzer_plt"), |
|
|
|
case filelib:is_regular(HomePlt) of |
|
|
|
true -> |
|
|
|
HomePlt; |
|
|
|
false -> |
|
|
|
?ABORT("No PLT found~n", []) |
|
|
|
end |
|
|
|
end; |
|
|
|
Plt -> |
|
|
|
Plt |
|
|
|
end. |
|
|
|
|
|
|
|
%% @doc If the warnings option is present in rebar.config return its value, |
|
|
|
%% otherwise return []. |
|
|
|
%% @spec warnings(Config::#config{}) -> list(). |
|
|
|
-spec warnings(Config::#config{}) -> list(). |
|
|
|
warnings(Config) -> |
|
|
|
DialyzerOpts = rebar_config:get(Config, dialyzer_opts, []), |
|
|
|
proplists:get_value(warnings, DialyzerOpts, []). |