diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 68391e7..1cefe84 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -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, diff --git a/src/rebar_dialyzer.erl b/src/rebar_dialyzer.erl index c725e48..35bb201 100644 --- a/src/rebar_dialyzer.erl +++ b/src/rebar_dialyzer.erl @@ -28,7 +28,7 @@ %% @doc rebar_dialyzer supports the following commands: %% %% A single option plt can be presented in the dialyzer_opts @@ -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), - %% This is the recommended minimal PLT for OTP - %% (see http://www.erlang.org/doc/apps/dialyzer/dialyzer_chapter.html#id2256857). + {ok, _AppName, AppData} = rebar_app_utils:load_app_file(File), + Apps = proplists:get_value(applications, AppData), + 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. diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 04878c3..51b7f17 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -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) -> diff --git a/src/rebar_eunit.erl b/src/rebar_eunit.erl index 6ef7a25..d51c35d 100644 --- a/src/rebar_eunit.erl +++ b/src/rebar_eunit.erl @@ -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 +%% @doc rebar_eunit supports the following commands: +%% +%% The following Global options are supported: +%% +%% Additionally, for projects that have separate folders for the core +%% implementation, and for the unit tests, then the following rebar.config +%% option can be provided: {eunit_compile_opts, [{src_dirs, ["dir"]}]}.. +%% @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 diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl index 136c5cb..f20ce0c 100644 --- a/src/rebar_otp_app.erl +++ b/src/rebar_otp_app.erl @@ -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, []). - diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 5f1612c..7265058 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -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, []). +