Procházet zdrojové kódy

Merge pull request #783 from sile/dialyzer_include_all_deps_option

Add plt_include_all_deps dialyzer option
pull/809/head
Fred Hebert před 9 roky
rodič
revize
493d290a3f
5 změnil soubory, kde provedl 163 přidání a 13 odebrání
  1. +1
    -0
      rebar.config.sample
  2. +6
    -0
      src/rebar.hrl
  3. +47
    -11
      src/rebar_prv_dialyzer.erl
  4. +71
    -2
      test/rebar_dialyzer_SUITE.erl
  5. +38
    -0
      test/rebar_localfs_resource.erl

+ 1
- 0
rebar.config.sample Zobrazit soubor

@ -99,6 +99,7 @@
{dialyzer, [
{warnings, [underspecs, no_return]},
{get_warnings, true},
{plt_apps, top_level_deps}, % top_level_deps | all_deps
{plt_extra_apps, []},
{plt_location, local}, % local | "/my/file/name"
{plt_prefix, "rebar3"},

+ 6
- 0
src/rebar.hrl Zobrazit soubor

@ -43,6 +43,12 @@
-type rebar_digraph() :: digraph().
-endif.
-ifdef(namespaced_types).
-type rebar_set() :: sets:set().
-else.
-type rebar_set() :: set().
-endif.
-define(GRAPH_VSN, 2).
-type v() :: {digraph:vertex(), term()} | 'false'.
-type e() :: {digraph:vertex(), digraph:vertex()}.

+ 47
- 11
src/rebar_prv_dialyzer.erl Zobrazit soubor

@ -44,25 +44,29 @@ desc() ->
"options `dialyzer` in rebar.config:\n"
"`warnings` - a list of dialyzer warnings\n"
"`get_warnings` - display warnings when altering a PLT file (boolean)\n"
"`plt_extra_apps` - a list of applications to include in the PLT file*\n"
"`plt_apps` - the strategy for determining the applications which included "
"in the PLT file, `top_level_deps` to include just the direct dependencies "
"or `all_deps` to include all nested dependencies*\n"
"`plt_extra_apps` - a list of applications to include in the PLT file**\n"
"`plt_location` - the location of the PLT file, `local` to store in the "
"profile's base directory (default) or a custom directory.\n"
"`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"**\n"
"`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"***\n"
"`base_plt_apps` - a list of applications to include in the base "
"PLT file***\n"
"PLT file****\n"
"`base_plt_location` - the location of base PLT file, `global` to store in "
"$HOME/.cache/rebar3 (default) or a custom directory***\n"
"$HOME/.cache/rebar3 (default) or a custom directory****\n"
"`base_plt_prefix` - the prefix to the base PLT file, defaults to "
"\"rebar3\"** ***\n"
"\"rebar3\"*** ****\n"
"\n"
"For example, to warn on unmatched returns: \n"
"{dialyzer, [{warnings, [unmatched_returns]}]}.\n"
"\n"
"*The applications in `dialyzer_base_plt_apps` and any `applications` and "
"`included_applications` listed in their .app files will be added to the "
"list.\n"
"**PLT files are named \"<prefix>_<otp_release>_plt\".\n"
"***The base PLT is a PLT containing the core applications often required "
"*The direct dependent applications are listed in `applications` and "
"`included_applications` of their .app files.\n"
"**The applications in `base_plt_apps` will be added to the "
"list. \n"
"***PLT files are named \"<prefix>_<otp_release>_plt\".\n"
"****The base PLT is a PLT containing the core applications often required "
"for a project's PLT. One base PLT is created per OTP version and "
"stored in `base_plt_location`. A base PLT is used to build project PLTs."
"\n".
@ -178,7 +182,12 @@ proj_plt_files(State) ->
PltApps = get_config(State, plt_extra_apps, []),
Apps = rebar_state:project_apps(State),
DepApps = lists:flatmap(fun rebar_app_info:applications/1, Apps),
get_plt_files(BasePltApps ++ PltApps ++ DepApps, Apps).
DepApps1 =
case get_config(State, plt_apps, top_level_deps) of
top_level_deps -> DepApps;
all_deps -> collect_nested_dependent_apps(DepApps)
end,
get_plt_files(BasePltApps ++ PltApps ++ DepApps1, Apps).
default_plt_apps() ->
[erts,
@ -442,3 +451,30 @@ no_warnings() ->
get_config(State, Key, Default) ->
Config = rebar_state:get(State, dialyzer, []),
proplists:get_value(Key, Config, Default).
-spec collect_nested_dependent_apps([atom()]) -> [atom()].
collect_nested_dependent_apps(RootApps) ->
Deps = lists:foldl(fun collect_nested_dependent_apps/2, sets:new(), RootApps),
sets:to_list(Deps).
-spec collect_nested_dependent_apps(atom(), rebar_set()) -> rebar_set().
collect_nested_dependent_apps(App, Seen) ->
case sets:is_element(App, Seen) of
true ->
Seen;
false ->
Seen1 = sets:add_element(App, Seen),
case code:lib_dir(App) of
{error, _} ->
throw({unknown_application, App});
AppDir ->
case rebar_app_discover:find_app(AppDir, all) of
false ->
throw({unknown_application, App});
{true, AppInfo} ->
lists:foldl(fun collect_nested_dependent_apps/2,
Seen1,
rebar_app_info:applications(AppInfo))
end
end
end.

+ 71
- 2
test/rebar_dialyzer_SUITE.erl Zobrazit soubor

@ -7,7 +7,8 @@
all/0,
update_base_plt/1,
update_app_plt/1,
build_release_plt/1]).
build_release_plt/1,
plt_apps_option/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@ -38,7 +39,7 @@ init_per_testcase(Testcase, Config) ->
rebar_test_utils:init_rebar_state(Config)].
all() ->
[update_base_plt, update_app_plt, build_release_plt].
[update_base_plt, update_app_plt, build_release_plt, plt_apps_option].
update_base_plt(Config) ->
AppDir = ?config(apps, Config),
@ -130,6 +131,58 @@ build_release_plt(Config) ->
{ok, PltFiles} = plt_files(Plt),
?assertEqual(ErtsFiles, PltFiles).
plt_apps_option(Config) ->
AppDir = ?config(apps, Config),
RebarConfig = ?config(rebar_config, Config),
Plt = ?config(plt, Config),
State = ?config(state, Config),
%% Create applications
Name1 = rebar_test_utils:create_random_name("app1_"),
Vsn1 = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(filename:join([AppDir,"deps",Name1]), Name1, Vsn1,
[]),
App1 = ec_cnv:to_atom(Name1),
Name2 = rebar_test_utils:create_random_name("app2_"),
Vsn2 = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(filename:join([AppDir,"deps",Name2]), Name2, Vsn2,
[App1]), % App2 depends on App1
App2 = ec_cnv:to_atom(Name2),
Name3 = rebar_test_utils:create_random_name("app3_"), % the project application
Vsn3 = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(AppDir, Name3, Vsn3,
[App2]), % App3 depends on App2
%% Dependencies settings
State1 = rebar_state:add_resource(State, {localfs, rebar_localfs_resource}),
Config1 = [{state, State1} | Config],
RebarConfig1 = merge_config(
[{deps,
[
{App1, {localfs, filename:join([AppDir,"deps",Name1])}},
{App2, {localfs, filename:join([AppDir,"deps",Name2])}}
]}],
RebarConfig),
%% Dialyzer: plt_apps = top_level_deps (default)
rebar_test_utils:run_and_check(Config1, RebarConfig1, ["dialyzer"],
{ok, [{app, Name3}]}),
%% NOTE: `erts` is included in `base_plt_apps`
{ok, PltFiles1} = plt_files(Plt),
?assertEqual([App2, erts], get_apps_from_beam_files(PltFiles1)),
%% Dialyzer: plt_apps = all_deps
RebarConfig2 = merge_config([{dialyzer, [{plt_apps, all_deps}]}],
RebarConfig1),
rebar_test_utils:run_and_check(Config1, RebarConfig2, ["dialyzer"],
{ok, [{app, Name3}]}),
{ok, PltFiles2} = plt_files(Plt),
?assertEqual([App1, App2, erts], get_apps_from_beam_files(PltFiles2)).
%% Helpers
erts_files() ->
@ -157,3 +210,19 @@ alter_plt(Plt) ->
{init_plt, Plt},
{files, [code:which(dialyzer)]}]),
ok.
-spec merge_config(Config, Config) -> Config when
Config :: [{term(), term()}].
merge_config(NewConfig, OldConfig) ->
dict:to_list(
rebar_opts:merge_opts(dict:from_list(NewConfig),
dict:from_list(OldConfig))).
-spec get_apps_from_beam_files(string()) -> [atom()].
get_apps_from_beam_files(BeamFiles) ->
lists:usort(
[begin
AppNameVsn = filename:basename(filename:dirname(filename:dirname(File))),
[AppName | _] = string:tokens(AppNameVsn ++ "-", "-"),
ec_cnv:to_atom(AppName)
end || File <- BeamFiles]).

+ 38
- 0
test/rebar_localfs_resource.erl Zobrazit soubor

@ -0,0 +1,38 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%%
%% @doc A localfs custom resource (for testing purposes only)
%%
%% ```
%% {deps, [
%% %% Application files are copied from "/path/to/app_name"
%% {app_name, {localfs, "/path/to/app_name", undefined}}
%% ]}.
%% '''
-module(rebar_localfs_resource).
-behaviour(rebar_resource).
-export([lock/2
,download/3
,needs_update/2
,make_vsn/1]).
-include_lib("eunit/include/eunit.hrl").
lock(AppDir, {localfs, Path, _Ref}) ->
lock(AppDir, {localfs, Path});
lock(_AppDir, {localfs, Path}) ->
{localfs, Path, undefined}.
needs_update(_AppDir, _Resource) ->
false.
download(AppDir, {localfs, Path, _Ref}, State) ->
download(AppDir, {localfs, Path}, State);
download(AppDir, {localfs, Path}, _State) ->
ok = rebar_file_utils:cp_r(filelib:wildcard(Path ++ "/*"), AppDir),
{ok, undefined}.
make_vsn(_AppDir) ->
{plain, "undefined"}.

Načítá se…
Zrušit
Uložit