From 2d2aed627f747533e2a40f44dd813160e582c7e2 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Mon, 25 Oct 2010 22:07:32 +0200 Subject: [PATCH] Refactor Dialyzer support to make it more usable --- priv/shell-completion/bash/rebar | 4 +- rebar.config.sample | 7 +- src/rebar_core.erl | 6 +- src/rebar_dialyzer.erl | 113 +++++++++++++++++++++++-------- 4 files changed, 95 insertions(+), 35 deletions(-) diff --git a/priv/shell-completion/bash/rebar b/priv/shell-completion/bash/rebar index 840c376..91c48dd 100644 --- a/priv/shell-completion/bash/rebar +++ b/priv/shell-completion/bash/rebar @@ -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=" diff --git a/rebar.config.sample b/rebar.config.sample index 2074805..6933076 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -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 == diff --git a/src/rebar_core.erl b/src/rebar_core.erl index bf37c87..a89510c 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -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 diff --git a/src/rebar_dialyzer.erl b/src/rebar_dialyzer.erl index de78a05..d2b89aa 100644 --- a/src/rebar_dialyzer.erl +++ b/src/rebar_dialyzer.erl @@ -27,9 +27,9 @@ %% @author Dave Smith %% @doc rebar_dialyzer supports the following commands: %% %% A single option plt can be presented in the dialyzer_opts %% options in rebar.config. 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, []).