mirror of
https://github.com/correl/rebar.git
synced 2024-11-14 19:19:30 +00:00
Refactor Dialyzer support to make it more usable
This commit is contained in:
parent
7ddd4bdaad
commit
2d2aed627f
4 changed files with 95 additions and 35 deletions
|
@ -8,8 +8,8 @@ _rebar()
|
|||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
sopts="-h -c -v -V -f -j"
|
||||
lopts=" --help --commands --verbose --force --jobs= --version"
|
||||
cmdsnvars="analyze build_plt check_plt check-deps clean compile \
|
||||
create create-app create-node ct doc delete-deps eunit \
|
||||
cmdsnvars="build-plt check-plt check-deps clean compile \
|
||||
create create-app create-node ct dialyze doc delete-deps eunit \
|
||||
get-deps generate help list-templates version xref \
|
||||
case= force=1 jobs= suite= verbose=1 appid= skip_deps=1 \
|
||||
template= template_dir="
|
||||
|
|
|
@ -76,8 +76,11 @@
|
|||
|
||||
%% == Dialyzer ==
|
||||
|
||||
%% Options for running the dialyzer, right now only `plt' is supported
|
||||
{dialyzer_opts, []}.
|
||||
%% Options for running dialyzer
|
||||
%% {plt, PltFile}
|
||||
%% 'src': run Dialyzer on the source files as in 'dialyzer --src'
|
||||
%% {warnings, [WarnOpts]}: turn on/off Dialyzer warnings
|
||||
{warnings_opts, [{plt, PltFile}, {warnings, [WarnOpts]}, src]}.
|
||||
|
||||
%% == Cleanup ==
|
||||
|
||||
|
|
|
@ -200,9 +200,9 @@ help() ->
|
|||
%%
|
||||
commands() ->
|
||||
S = <<"
|
||||
analyze Analyze with Dialyzer
|
||||
build_plt Build Dialyzer PLT
|
||||
check_plt Check Dialyzer PLT
|
||||
dialyze Analyze with Dialyzer
|
||||
build-plt Build Dialyzer PLT
|
||||
check-plt Check Dialyzer PLT
|
||||
|
||||
clean Clean
|
||||
compile Compile sources
|
||||
|
|
|
@ -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(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(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(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, []).
|
||||
|
|
Loading…
Reference in a new issue