Provide package support for eunit; build_plt now includes listed applications; begin including -spec annotations

This commit is contained in:
theiw 2010-02-05 17:34:38 +00:00
parent 30a3816c86
commit 788bd3df72
6 changed files with 85 additions and 49 deletions

View file

@ -188,7 +188,7 @@ process_dir(Dir, ParentConfig, Commands) ->
%% Get the list of modules for "any dir". This is a catch-all list of modules
%% that are processed in addion to modules associated with this directory
%5 type. These any_dir modules are processed FIRST.
%% type. These any_dir modules are processed FIRST.
{ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
Modules = AnyDirModules ++ DirModules,

View file

@ -28,7 +28,7 @@
%% @doc rebar_dialyzer supports the following commands:
%% <ul>
%% <li>analyze (essentially "dialyzer -r ebin")</li>
%% <li>build_plt (essentially "dialyzer --build_plt -r &rt;stdlib_lib&lt; &rt;kernel_lib&lt; &rt;mnesia_lib&lt;")</li>
%% <li>build_plt (essentially "dialyzer --build_plt -r &lt;app_dirs&gt;")</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>
@ -59,8 +59,8 @@
%% @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),
analyze(Config, File) ->
Plt = plt_path(Config, File),
case dialyzer:plt_info(Plt) of
{ok, _} ->
try dialyzer:run([{files_rec, ["ebin"]}, {init_plt, Plt}]) of
@ -82,17 +82,14 @@ analyze(Config, _File) ->
%% @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),
build_plt(Config, File) ->
Plt = plt_path(Config, File),
{ok, _AppName, AppData} = rebar_app_utils:load_app_file(File),
Apps = proplists:get_value(applications, AppData),
%% This is the recommended minimal PLT for OTP
%% (see http://www.erlang.org/doc/apps/dialyzer/dialyzer_chapter.html#id2256857).
Warnings = dialyzer:run([{analysis_type, plt_build},
{files_rec, [
filename:join(code:lib_dir(stdlib), "ebin"),
filename:join(code:lib_dir(kernel), "ebin"),
filename:join(code:lib_dir(mnesia), "ebin")
]},
{files_rec, app_dirs(Apps)},
{output_plt, Plt}]),
case Warnings of
[] ->
@ -105,8 +102,8 @@ build_plt(Config, _File) ->
%% @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),
check_plt(Config, File) ->
Plt = 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]);
@ -123,8 +120,15 @@ check_plt(Config, _File) ->
%% Internal functions
%% ===================================================================
%% @doc Obtain the library paths for the supplied applications.
%% @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()]) -> void()
%% @spec output_warnings(Warnings::[warning()]) -> none()
-spec(output_warnings(Warnings::[warning()]) -> none()).
output_warnings(Warnings) ->
lists:foreach(fun(Warning) ->
@ -133,13 +137,14 @@ 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{}) -> string()
-spec(plt_path(Config::#config{}) -> string()).
plt_path(Config) ->
%% @spec plt_path(Config::#config{}, File::string()) -> string()
-spec(plt_path(Config::#config{}, File::string()) -> string()).
plt_path(Config, File) ->
{ok, AppName, _AppData} = rebar_app_utils:load_app_file(File),
DialyzerOpts = rebar_config:get(Config, dialyzer_opts, []),
case proplists:get_value(plt, DialyzerOpts) of
undefined ->
filename:join(os:getenv("HOME"), ".dialyzer_plt");
filename:join(os:getenv("HOME"), "." ++ atom_to_list(AppName) ++ "_dialyzer_plt");
Plt ->
Plt
end.

View file

@ -4,7 +4,7 @@
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
@ -38,12 +38,14 @@
%% Public API
%% ===================================================================
-spec compile(Config::#config{}, AppFile::string()) -> 'ok'.
compile(Config, _AppFile) ->
doterl_compile(Config, "ebin"),
rebar_base_compiler:run(Config, rebar_config:get_list(Config, mib_first_files, []),
"mibs", ".mib", "priv/mibs", ".bin",
fun compile_mib/3).
-spec clean(Config::#config{}, AppFile::string()) -> 'ok'.
clean(_Config, _AppFile) ->
%% TODO: This would be more portable if it used Erlang to traverse
%% the dir structure and delete each file; however it would also
@ -64,26 +66,33 @@ clean(_Config, _AppFile) ->
%% .erl Compilation API (externally used by only eunit)
%% ===================================================================
-spec doterl_compile(Config::#config{}, OutDir::string()) -> 'ok'.
doterl_compile(Config, OutDir) ->
doterl_compile(Config, OutDir, []).
doterl_compile(Config, OutDir, MoreSources) ->
FirstErls = rebar_config:get_list(Config, erl_first_files, []),
RestErls = [Source || Source <- rebar_utils:find_files("src", ".*\\.erl\$") ++ MoreSources,
ErlOpts = rebar_config:get(Config, erl_opts, []),
%% Support the src_dirs option allowing multiple directories to
%% contain erlang source. This might be used, for example, should eunit tests be
%% separated from the core application source.
SrcDirs = src_dirs(proplists:append_values(src_dirs, ErlOpts)),
RestErls = [Source || Source <- gather_src(SrcDirs, []) ++ MoreSources,
lists:member(Source, FirstErls) == false],
rebar_base_compiler:run(Config, FirstErls, RestErls,
fun(S, C) -> internal_erl_compile(S, C, OutDir) end).
%% ===================================================================
%% Internal functions
%% ===================================================================
-spec include_path(Source::string(), Config::#config{}) -> [string()].
include_path(Source, Config) ->
ErlOpts = rebar_config:get(Config, erl_opts, []),
[filename:dirname(Source)] ++ proplists:get_all_values(i, ErlOpts).
-spec inspect(Source::string(), IncludePath::[string()]) -> {string(), [string()]}.
inspect(Source, IncludePath) ->
ModuleDefault = filename:basename(Source, ".erl"),
case epp:open(Source, IncludePath) of
@ -94,9 +103,10 @@ inspect(Source, IncludePath) ->
{ModuleDefault, []}
end.
-spec inspect_epp(Epp::pid(), Module::string(), Includes::[string()]) -> {string(), [string()]}.
inspect_epp(Epp, Module, Includes) ->
case epp:parse_erl_form(Epp) of
{ok, {attribute, _, module, ActualModule}} when is_list(ActualModule) ->
{ok, {attribute, _, module, ActualModule}} ->
%% If the module name includes package info, we get a list of atoms...
case is_list(ActualModule) of
true ->
@ -116,12 +126,13 @@ inspect_epp(Epp, Module, Includes) ->
inspect_epp(Epp, Module, Includes)
end.
-spec needs_compile(Source::string(), Target::string(), Hrls::[string()]) -> boolean().
needs_compile(Source, Target, Hrls) ->
TargetLastMod = filelib:last_modified(Target),
lists:any(fun(I) -> TargetLastMod < filelib:last_modified(I) end,
[Source] ++ Hrls).
-spec internal_erl_compile(Source::string(), Config::#config{}, Outdir::string()) -> 'ok' | 'skipped'.
internal_erl_compile(Source, Config, Outdir) ->
%% Determine the target name and includes list by inspecting the source file
{Module, Hrls} = inspect(Source, include_path(Source, Config)),
@ -154,6 +165,7 @@ internal_erl_compile(Source, Config, Outdir) ->
skipped
end.
-spec compile_mib(Source::string(), Target::string(), Config::#config{}) -> 'ok'.
compile_mib(Source, Target, Config) ->
ok = rebar_utils:ensure_dir(Target),
Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++
@ -165,9 +177,22 @@ compile_mib(Source, Target, Config) ->
?FAIL
end.
gather_src([], Srcs) ->
Srcs;
gather_src([Dir|Rest], Srcs) ->
gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, ".*\\.erl\$")).
-spec src_dirs(SrcDirs::[string()]) -> [string()].
src_dirs([]) ->
["src"];
src_dirs(SrcDirs) ->
SrcDirs ++ src_dirs([]).
-spec dirs(Dir::string()) -> [string()].
dirs(Dir) ->
[F || F <- filelib:wildcard(filename:join([Dir, "*"])), filelib:is_dir(F)].
-spec delete_dir(Dir::string(), Subdirs::[string()]) -> 'ok' | {'error', atom()}.
delete_dir(Dir, []) ->
file:del_dir(Dir);
delete_dir(Dir, Subdirs) ->

View file

@ -4,7 +4,7 @@
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
@ -24,14 +24,21 @@
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% -------------------------------------------------------------------
%%
%% Targets:
%% eunit - runs eunit tests
%% clean - remove .eunit directory
%%
%% Global options:
%% verbose=1 - show extra output from the eunit test
%% suite="foo"" - runs test/foo_tests.erl
%% @author Dave Smith <dizzyd@dizzyd.com>
%% @doc rebar_eunit supports the following commands:
%% <ul>
%% <li>eunit - runs eunit tests</li>
%% <li>clean - remove .eunit directory</li>
%% </ul>
%% The following Global options are supported:
%% <ul>
%% <li>verbose=1 - show extra output from the eunit test</li>
%% <li>suite="foo"" - runs test/foo_tests.erl</li>
%% </ul>
%% Additionally, for projects that have separate folders for the core
%% implementation, and for the unit tests, then the following <code>rebar.config</code>
%% option can be provided: <code>{eunit_compile_opts, [{src_dirs, ["dir"]}]}.</code>.
%% @copyright 2009, 2010 Dave Smith
%% -------------------------------------------------------------------
-module(rebar_eunit).
@ -61,9 +68,7 @@ eunit(Config, _File) ->
%% with that scan and causes any cover compilation info to be lost. So,
%% we do it by hand. :(
%%
%% TODO: Not currently compatible with package modules
Modules = [list_to_atom(filename:basename(N, ".beam")) ||
N <- filelib:wildcard("*.beam", ?EUNIT_DIR)],
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- rebar_utils:beams(?EUNIT_DIR)],
%% TODO: If there are other wildcards specified in eunit_sources, compile them

View file

@ -131,7 +131,7 @@ validate_modules(AppName, undefined) ->
validate_modules(AppName, Mods) ->
%% Construct two sets -- one for the actual .beam files in ebin/ and one for the modules
%% listed in the .app file
EbinSet = ordsets:from_list([beam_to_mod(N) || N <- beams()]),
EbinSet = ordsets:from_list([rebar_utils:beam_to_mod("ebin", N) || N <- rebar_utils:beams("ebin")]),
ModSet = ordsets:from_list(Mods),
%% Identify .beam files listed in the .app, but not present in ebin/
@ -155,12 +155,3 @@ validate_modules(AppName, Mods) ->
[AppName, Msg2]),
?FAIL
end.
beam_to_mod(Filename) ->
["ebin" | Rest] = filename:split(Filename),
list_to_atom(filename:basename(string:join(Rest, "."), ".beam")).
beams() ->
filelib:fold_files("ebin", ".*\.beam\$", true,
fun(F, Acc) -> [F | Acc] end, []).

View file

@ -4,7 +4,7 @@
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
@ -33,7 +33,8 @@
sh_failfast/2,
find_files/2,
now_str/0,
ensure_dir/1]).
ensure_dir/1,
beam_to_mod/2, beams/1]).
-include("rebar.hrl").
@ -132,3 +133,12 @@ sh_loop(Port) ->
{Port, {exit_status, Rc}} ->
{error, Rc}
end.
beam_to_mod(Dir, Filename) ->
[Dir | Rest] = filename:split(Filename),
list_to_atom(filename:basename(string:join(Rest, "."), ".beam")).
beams(Dir) ->
filelib:fold_files(Dir, ".*\.beam\$", true,
fun(F, Acc) -> [F | Acc] end, []).