diff options
-rw-r--r-- | rebar.config | 1 | ||||
-rw-r--r-- | src/rebar_erlc_compiler.erl | 258 | ||||
-rw-r--r-- | src/rebar_prv_clean.erl | 7 | ||||
-rw-r--r-- | src/rebar_prv_common_test.erl | 44 | ||||
-rw-r--r-- | src/rebar_prv_compile.erl | 119 | ||||
-rw-r--r-- | src/rebar_prv_cover.erl | 32 | ||||
-rw-r--r-- | src/rebar_prv_eunit.erl | 167 | ||||
-rw-r--r-- | src/rebar_utils.erl | 9 | ||||
-rw-r--r-- | test/rebar_compile_SUITE.erl | 633 | ||||
-rw-r--r-- | test/rebar_cover_SUITE.erl | 87 | ||||
-rw-r--r-- | test/rebar_ct_SUITE.erl | 1 | ||||
-rw-r--r-- | test/rebar_eunit_SUITE.erl | 18 | ||||
-rw-r--r-- | test/rebar_file_utils_SUITE.erl | 6 | ||||
-rw-r--r-- | test/rebar_src_dirs_SUITE.erl | 21 | ||||
-rw-r--r-- | test/rebar_test_utils.erl | 7 |
15 files changed, 1107 insertions, 303 deletions
diff --git a/rebar.config b/rebar.config index deff2cc..6eed68f 100644 --- a/rebar.config +++ b/rebar.config @@ -18,6 +18,7 @@ {"rebar/priv/templates/*", "_build/default/lib/"}]}. {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + {platform_define, "^R1[4|5]", no_list_dir_all}, no_debug_info, warnings_as_errors]}. diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 06d09bd..5075f0f 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -26,8 +26,9 @@ %% ------------------------------------------------------------------- -module(rebar_erlc_compiler). --export([compile/1, - compile/3, +-export([compile/1, compile/2, compile/3, + compile_dir/3, compile_dir/4, + compile_dirs/5, clean/1]). -include("rebar.hrl"). @@ -38,14 +39,22 @@ -type erlc_info_v() :: {digraph:vertex(), term()} | 'false'. -type erlc_info_e() :: {digraph:vertex(), digraph:vertex()}. -type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e()), list(string())}. --record(erlcinfo, - { - vsn = ?ERLCINFO_VSN :: pos_integer(), - info = {[], [], []} :: erlc_info() - }). +-record(erlcinfo, { + vsn = ?ERLCINFO_VSN :: pos_integer(), + info = {[], [], []} :: erlc_info() +}). +-type compile_opts() :: [compile_opt()]. +-type compile_opt() :: {recursive, boolean()}. + +-record(compile_opts, { + recursive = true +}). + +-define(DEFAULT_OUTDIR, "ebin"). -define(RE_PREFIX, "^[^._]"). + %% =================================================================== %% Public API %% =================================================================== @@ -80,106 +89,189 @@ %% 'old_inets'}]}. %% --spec compile(rebar_app_info:t()) -> 'ok'. -compile(AppInfo) -> +%% @equiv compile(AppInfo, []). + +-spec compile(rebar_app_info:t()) -> ok. +compile(AppInfo) when is_tuple(AppInfo), element(1, AppInfo) == app_info_t -> + compile(AppInfo, []). + +%% @doc compile an individual application. + +-spec compile(rebar_app_info:t(), compile_opts()) -> ok. +compile(AppInfo, CompileOpts) when is_tuple(AppInfo), element(1, AppInfo) == app_info_t -> Dir = ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)), - compile(rebar_app_info:opts(AppInfo), Dir, filename:join([Dir, "ebin"])). + RebarOpts = rebar_app_info:opts(AppInfo), --spec compile(rebar_dict(), file:name(), file:name()) -> 'ok'. -compile(Opts, Dir, OutDir) -> - rebar_base_compiler:run(Opts, + rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) - || File <- rebar_opts:get(Opts, xrl_first_files, [])]), + || File <- rebar_opts:get(RebarOpts, xrl_first_files, [])]), filename:join(Dir, "src"), ".xrl", filename:join(Dir, "src"), ".erl", fun compile_xrl/3), - rebar_base_compiler:run(Opts, + rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) - || File <- rebar_opts:get(Opts, yrl_first_files, [])]), + || File <- rebar_opts:get(RebarOpts, yrl_first_files, [])]), filename:join(Dir, "src"), ".yrl", filename:join(Dir, "src"), ".erl", fun compile_yrl/3), - rebar_base_compiler:run(Opts, + rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) - || File <- rebar_opts:get(Opts, mib_first_files, [])]), + || File <- rebar_opts:get(RebarOpts, mib_first_files, [])]), filename:join(Dir, "mibs"), ".mib", filename:join([Dir, "priv", "mibs"]), ".bin", fun compile_mib/3), - doterl_compile(Opts, Dir, OutDir). --spec clean(file:filename()) -> 'ok'. -clean(AppDir) -> - MibFiles = rebar_utils:find_files(filename:join(AppDir, "mibs"), ?RE_PREFIX".*\\.mib\$"), + SrcDirs = lists:map(fun(SrcDir) -> filename:join(Dir, SrcDir) end, + rebar_dir:src_dirs(RebarOpts, ["src"])), + OutDir = filename:join(Dir, outdir(RebarOpts)), + compile_dirs(RebarOpts, Dir, SrcDirs, OutDir, CompileOpts), + + ExtraDirs = rebar_dir:extra_src_dirs(RebarOpts), + F = fun(D) -> + case ec_file:is_dir(filename:join([Dir, D])) of + true -> compile_dirs(RebarOpts, Dir, [D], D, CompileOpts); + false -> ok + end + end, + lists:foreach(F, lists:map(fun(SrcDir) -> filename:join(Dir, SrcDir) end, ExtraDirs)). + +%% @hidden +%% these are kept for backwards compatibility but they're bad functions with +%% bad interfaces you probably shouldn't use +%% State/RebarOpts have to have src_dirs set and BaseDir must be the parent +%% directory of those src_dirs + +-spec compile(rebar_dict() | rebar_state:t(), file:name(), file:name()) -> ok. +compile(State, BaseDir, OutDir) when is_tuple(State), element(1, State) == state_t -> + compile(rebar_state:opts(State), BaseDir, OutDir, [{recursive, false}]); +compile(RebarOpts, BaseDir, OutDir) -> + compile(RebarOpts, BaseDir, OutDir, [{recursive, false}]). + +%% @hidden + +-spec compile(rebar_dict() | rebar_state:t(), file:name(), file:name(), compile_opts()) -> ok. +compile(State, BaseDir, OutDir, CompileOpts) when is_tuple(State), element(1, State) == state_t -> + compile(rebar_state:opts(State), BaseDir, OutDir, CompileOpts); +compile(RebarOpts, BaseDir, OutDir, CompileOpts) -> + SrcDirs = lists:map(fun(SrcDir) -> filename:join(BaseDir, SrcDir) end, + rebar_dir:src_dirs(RebarOpts, ["src"])), + compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, CompileOpts), + + ExtraDirs = rebar_dir:extra_src_dirs(RebarOpts), + F = fun(D) -> + case ec_file:is_dir(filename:join([BaseDir, D])) of + true -> compile_dirs(RebarOpts, BaseDir, [D], D, CompileOpts); + false -> ok + end + end, + lists:foreach(F, lists:map(fun(SrcDir) -> filename:join(BaseDir, SrcDir) end, ExtraDirs)). + +%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, [{recursive, false}]). + +-spec compile_dir(rebar_dict() | rebar_state:t(), file:name(), file:name()) -> ok. +compile_dir(State, BaseDir, Dir) when is_tuple(State), element(1, State) == state_t -> + compile_dir(rebar_state:opts(State), BaseDir, Dir, [{recursive, false}]); +compile_dir(RebarOpts, BaseDir, Dir) -> + compile_dir(RebarOpts, BaseDir, Dir, [{recursive, false}]). + +%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, Opts). + +-spec compile_dir(rebar_dict() | rebar_state:t(), file:name(), file:name(), compile_opts()) -> ok. +compile_dir(State, BaseDir, Dir, Opts) when is_tuple(State), element(1, State) == state_t -> + compile_dirs(rebar_state:opts(State), BaseDir, [Dir], Dir, Opts); +compile_dir(RebarOpts, BaseDir, Dir, Opts) -> + compile_dirs(RebarOpts, BaseDir, [Dir], Dir, Opts). + +%% @doc compile a list of directories with the given opts. + +-spec compile_dirs(rebar_dict() | rebar_state:t(), + file:filename(), + [file:filename()], + file:filename(), + compile_opts()) -> ok. +compile_dirs(State, BaseDir, Dirs, OutDir, CompileOpts) when is_tuple(State), element(1, State) == state_t -> + compile_dirs(rebar_state:opts(State), BaseDir, Dirs, OutDir, CompileOpts); +compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> + CompileOpts = parse_opts(Opts), + + ErlOpts = rebar_opts:erl_opts(RebarOpts), + Recursive = CompileOpts#compile_opts.recursive, + AllErlFiles = gather_src(SrcDirs, Recursive), + + %% Make sure that outdir is on the path + ok = filelib:ensure_dir(filename:join(OutDir, "dummy.beam")), + true = code:add_patha(filename:absname(OutDir)), + + G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles, BaseDir, OutDir), + + NeededErlFiles = needed_files(G, ErlOpts, BaseDir, OutDir, AllErlFiles), + {ErlFirstFiles, ErlOptsFirst} = erl_first_files(RebarOpts, ErlOpts, BaseDir, NeededErlFiles), + {DepErls, OtherErls} = lists:partition( + fun(Source) -> digraph:in_degree(G, Source) > 0 end, + [File || File <- NeededErlFiles, not lists:member(File, ErlFirstFiles)]), + SubGraph = digraph_utils:subgraph(G, DepErls), + DepErlsOrdered = digraph_utils:topsort(SubGraph), + FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered), + try + rebar_base_compiler:run( + RebarOpts, FirstErls, OtherErls, + fun(S, C) -> + ErlOpts1 = case lists:member(S, ErlFirstFiles) of + true -> ErlOptsFirst; + false -> ErlOpts + end, + internal_erl_compile(C, BaseDir, S, OutDir, ErlOpts1) + end) + after + true = digraph:delete(SubGraph), + true = digraph:delete(G) + end, + ok. + +%% @doc remove compiled artifacts from an AppDir. + +-spec clean(rebar_app_info:t()) -> 'ok'. +clean(AppInfo) -> + AppDir = rebar_app_info:out_dir(AppInfo), + + MibFiles = rebar_utils:find_files(filename:join([AppDir, "mibs"]), ?RE_PREFIX".*\\.mib\$"), MIBs = [filename:rootname(filename:basename(MIB)) || MIB <- MibFiles], rebar_file_utils:delete_each( [filename:join([AppDir, "include",MIB++".hrl"]) || MIB <- MIBs]), - lists:foreach(fun(F) -> ok = rebar_file_utils:rm_rf(F) end, - [filename:join(AppDir, "ebin/*.beam"), filename:join(AppDir, "priv/mibs/*.bin")]), + ok = rebar_file_utils:rm_rf(filename:join([AppDir, "priv/mibs/*.bin"])), - YrlFiles = rebar_utils:find_files(filename:join(AppDir, "src"), ?RE_PREFIX".*\\.[x|y]rl\$"), + YrlFiles = rebar_utils:find_files(filename:join([AppDir, "src"]), ?RE_PREFIX".*\\.[x|y]rl\$"), rebar_file_utils:delete_each( [ binary_to_list(iolist_to_binary(re:replace(F, "\\.[x|y]rl$", ".erl"))) || F <- YrlFiles ]), + BinDirs = ["ebin"|rebar_dir:extra_src_dirs(rebar_app_info:opts(AppInfo))], + ok = clean_dirs(AppDir, BinDirs), + %% Delete the build graph, if any - rebar_file_utils:rm_rf(erlcinfo_file(AppDir)), + rebar_file_utils:rm_rf(erlcinfo_file(AppDir)). +clean_dirs(_AppDir, []) -> ok; +clean_dirs(AppDir, [Dir|Rest]) -> + ok = rebar_file_utils:rm_rf(filename:join([AppDir, Dir, "*.beam"])), %% Erlang compilation is recursive, so it's possible that we have a nested %% directory structure in ebin with .beam files within. As such, we want - %% to scan whatever is left in the ebin/ directory for sub-dirs which + %% to scan whatever is left in the app's out_dir directory for sub-dirs which %% satisfy our criteria. - BeamFiles = rebar_utils:find_files(filename:join(AppDir, "ebin"), ?RE_PREFIX".*\\.beam\$"), + BeamFiles = rebar_utils:find_files(filename:join([AppDir, Dir]), ?RE_PREFIX".*\\.beam\$"), rebar_file_utils:delete_each(BeamFiles), - lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs(filename:join(AppDir, "ebin"))), - ok. + lists:foreach(fun(D) -> delete_dir(D, dirs(D)) end, dirs(filename:join([AppDir, Dir]))), + clean_dirs(AppDir, Rest). + %% =================================================================== %% Internal functions %% =================================================================== --spec doterl_compile(rebar_dict(), file:filename(), file:filename()) -> ok. -doterl_compile(Opts, Dir, ODir) -> - ErlOpts = rebar_opts:erl_opts(Opts), - doterl_compile(Opts, Dir, ODir, [], ErlOpts). - -doterl_compile(Opts, Dir, OutDir, MoreSources, ErlOpts) -> - ?DEBUG("erl_opts ~p", [ErlOpts]), - %% 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 = [filename:join(Dir, X) || X <- rebar_dir:all_src_dirs(Opts, ["src"], [])], - AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources, - - %% Make sure that ebin/ exists and is on the path - ok = filelib:ensure_dir(filename:join(OutDir, "dummy.beam")), - true = code:add_patha(filename:absname(OutDir)), - - OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir), - - G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles, Dir, OutDir), - - NeededErlFiles = needed_files(G, ErlOpts, Dir, OutDir1, AllErlFiles), - {ErlFirstFiles, ErlOptsFirst} = erl_first_files(Opts, ErlOpts, Dir, NeededErlFiles), - {DepErls, OtherErls} = lists:partition( - fun(Source) -> digraph:in_degree(G, Source) > 0 end, - [File || File <- NeededErlFiles, not lists:member(File, ErlFirstFiles)]), - SubGraph = digraph_utils:subgraph(G, DepErls), - DepErlsOrdered = digraph_utils:topsort(SubGraph), - FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered), - ?DEBUG("Files to compile first: ~p", [FirstErls]), - try - rebar_base_compiler:run( - Opts, FirstErls, OtherErls, - fun(S, C) -> - ErlOpts1 = case lists:member(S, ErlFirstFiles) of - true -> ErlOptsFirst; - false -> ErlOpts - end, - internal_erl_compile(C, Dir, S, OutDir1, ErlOpts1) - end) - after - true = digraph:delete(SubGraph), - true = digraph:delete(G) - end, - ok. +gather_src(Dirs, Recursive) -> + gather_src(Dirs, [], Recursive). +gather_src([], Srcs, _Recursive) -> Srcs; +gather_src([Dir|Rest], Srcs, Recursive) -> + gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, ?RE_PREFIX".*\\.erl\$", Recursive), Recursive). + %% Get files which need to be compiled first, i.e. those specified in erl_first_files %% and parse_transform options. Also produce specific erl_opts for these first %% files, so that yet to be compiled parse transformations are excluded from it. @@ -487,11 +579,7 @@ compile_xrl_yrl(_Opts, Source, Target, AllOpts, Mod) -> needs_compile(Source, Target) -> filelib:last_modified(Source) > filelib:last_modified(Target). -gather_src([], Srcs) -> - Srcs; -gather_src([Dir|Rest], Srcs) -> - gather_src( - Rest, Srcs ++ rebar_utils:find_files(Dir, ?RE_PREFIX".*\\.erl\$")). + -spec dirs(file:filename()) -> [file:filename()]. dirs(Dir) -> @@ -620,3 +708,13 @@ check_file(File) -> false -> ?ABORT("File ~p is missing, aborting\n", [File]); true -> File end. + +outdir(RebarOpts) -> + ErlOpts = rebar_opts:erl_opts(RebarOpts), + proplists:get_value(outdir, ErlOpts, ?DEFAULT_OUTDIR). + +parse_opts(Opts) -> parse_opts(Opts, #compile_opts{}). + +parse_opts([], CompileOpts) -> CompileOpts; +parse_opts([{recursive, Recursive}|Rest], CompileOpts) when Recursive == true; Recursive == false -> + parse_opts(Rest, CompileOpts#compile_opts{recursive = Recursive}). diff --git a/src/rebar_prv_clean.erl b/src/rebar_prv_clean.erl index a2b537a..6694dc0 100644 --- a/src/rebar_prv_clean.erl +++ b/src/rebar_prv_clean.erl @@ -49,6 +49,7 @@ do(State) -> Cwd = rebar_dir:get_cwd(), rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), clean_apps(State, Providers, ProjectApps), + clean_extras(State), rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), {ok, State}. @@ -66,10 +67,14 @@ clean_apps(State, Providers, Apps) -> ?INFO("Cleaning out ~s...", [rebar_app_info:name(AppInfo)]), AppDir = rebar_app_info:dir(AppInfo), AppInfo1 = rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, AppInfo, State), - rebar_erlc_compiler:clean(rebar_app_info:out_dir(AppInfo1)), + rebar_erlc_compiler:clean(AppInfo1), rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, AppInfo1, State) end || AppInfo <- Apps]. +clean_extras(State) -> + BaseDir = rebar_dir:base_dir(State), + rebar_file_utils:rm_rf(filename:join([BaseDir, "extras"])). + handle_args(State) -> {Args, _} = rebar_state:command_parsed_args(State), All = proplists:get_value(all, Args, false), diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 9155d3b..1f4c02d 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -285,26 +285,7 @@ find_suite_dirs(Suites) -> copy(State, Dir) -> From = reduce_path(Dir), - case From == rebar_state:dir(State) of - true -> throw({error, suite_at_project_root}); - false -> ok - end, - case retarget_path(State, From) of - %% directory lies outside of our project's file structure so - %% don't copy it - From -> From; - Target -> - %% recursively delete any symlinks in the target directory - %% if it exists so we don't smash files in the linked dirs - case ec_file:is_dir(Target) of - true -> remove_links(Target); - false -> ok - end, - case rebar_file_utils:symlink_or_copy(From, Target) of - exists -> Target; - ok -> Target - end - end. + retarget_path(State, From). compile_dir(State, Dir) -> NewState = replace_src_dirs(State, [filename:absname(Dir)]), @@ -350,29 +331,6 @@ reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest); reduce_path([], [".."|Rest]) -> reduce_path([], Rest); reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest). - -remove_links(Path) -> - IsDir = ec_file:is_dir(Path), - case ec_file:is_symlink(Path) of - true when IsDir -> - delete_dir_link(Path); - false when IsDir -> - lists:foreach(fun(ChildPath) -> - remove_links(ChildPath) - end, dir_entries(Path)); - _ -> file:delete(Path) - end. - -delete_dir_link(Path) -> - case os:type() of - {unix, _} -> file:delete(Path); - {win32, _} -> file:del_dir(Path) - end. - -dir_entries(Path) -> - {ok, SubDirs} = file:list_dir(Path), - [filename:join(Path, SubDir) || SubDir <- SubDirs]. - replace_src_dirs(State, Dirs) -> %% replace any `src_dirs` with the test dirs ErlOpts = rebar_state:get(State, erl_opts, []), diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index 9811de9..2996aee 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -52,8 +52,10 @@ do(State) -> ProjectApps2 = build_apps(State, Providers, ProjectApps1), State2 = rebar_state:project_apps(State, ProjectApps2), - ProjAppsPaths = [filename:join(rebar_app_info:out_dir(X), "ebin") || X <- ProjectApps2], - State3 = rebar_state:code_paths(State2, all_deps, DepsPaths ++ ProjAppsPaths), + %% projects with structures like /apps/foo,/apps/bar,/test + build_extra_dirs(State, ProjectApps2), + + State3 = update_code_paths(State2, ProjectApps2, DepsPaths), rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State2), case rebar_state:has_all_artifacts(State3) of @@ -82,6 +84,30 @@ build_app(State, Providers, AppInfo) -> copy_app_dirs(AppInfo, AppDir, OutDir), compile(State, Providers, AppInfo). +build_extra_dirs(State, Apps) -> + BaseDir = rebar_state:dir(State), + F = fun(App) -> rebar_app_info:dir(App) == BaseDir end, + %% check that this app hasn't already been dealt with + case lists:any(F, Apps) of + false -> + ProjOpts = rebar_state:opts(State), + Extras = rebar_dir:extra_src_dirs(ProjOpts, []), + [build_extra_dir(State, Dir) || Dir <- Extras]; + true -> ok + end. + +build_extra_dir(_State, []) -> ok; +build_extra_dir(State, Dir) -> + case ec_file:is_dir(filename:join([rebar_state:dir(State), Dir])) of + true -> + BaseDir = filename:join([rebar_dir:base_dir(State), "extras"]), + OutDir = filename:join([BaseDir, Dir]), + filelib:ensure_dir(filename:join([OutDir, "dummy.beam"])), + copy(rebar_state:dir(State), BaseDir, Dir), + rebar_erlc_compiler:compile_dir(State, BaseDir, OutDir); + false -> ok + end. + compile(State, AppInfo) -> compile(State, rebar_state:providers(State), AppInfo). @@ -104,6 +130,33 @@ compile(State, Providers, AppInfo) -> %% Internal functions %% =================================================================== +update_code_paths(State, ProjectApps, DepsPaths) -> + ProjAppsPaths = paths_for_apps(ProjectApps), + ExtrasPaths = paths_for_extras(State, ProjectApps), + rebar_state:code_paths(State, all_deps, DepsPaths ++ ProjAppsPaths ++ ExtrasPaths). + +paths_for_apps(Apps) -> paths_for_apps(Apps, []). + +paths_for_apps([], Acc) -> Acc; +paths_for_apps([App|Rest], Acc) -> + {_SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_app_info:opts(App)), + Paths = [filename:join([rebar_app_info:out_dir(App), Dir]) || Dir <- ["ebin"|ExtraDirs]], + FilteredPaths = lists:filter(fun ec_file:is_dir/1, Paths), + paths_for_apps(Rest, Acc ++ FilteredPaths). + +paths_for_extras(State, Apps) -> + F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end, + %% check that this app hasn't already been dealt with + case lists:any(F, Apps) of + false -> paths_for_extras(State); + true -> [] + end. + +paths_for_extras(State) -> + {_SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_state:opts(State)), + Paths = [filename:join([rebar_dir:base_dir(State), "extras", Dir]) || Dir <- ExtraDirs], + lists:filter(fun ec_file:is_dir/1, Paths). + has_all_artifacts(AppInfo1) -> case rebar_app_info:has_all_artifacts(AppInfo1) of {false, File} -> @@ -120,17 +173,17 @@ copy_app_dirs(AppInfo, OldAppDir, AppDir) -> %% copy all files from ebin if it exists case filelib:is_dir(EbinDir) of true -> - OutEbin = filename:join(AppDir, "ebin"), - filelib:ensure_dir(filename:join(OutEbin, "dummy.beam")), - rebar_file_utils:cp_r(filelib:wildcard(filename:join(EbinDir, "*")), OutEbin); + OutEbin = filename:join([AppDir, "ebin"]), + filelib:ensure_dir(filename:join([OutEbin, "dummy.beam"])), + rebar_file_utils:cp_r(filelib:wildcard(filename:join([EbinDir, "*"])), OutEbin); false -> ok end, - filelib:ensure_dir(filename:join(AppDir, "dummy")), + filelib:ensure_dir(filename:join([AppDir, "dummy"])), %% link or copy mibs if it exists - case filelib:is_dir(filename:join(OldAppDir, "mibs")) of + case filelib:is_dir(filename:join([OldAppDir, "mibs"])) of true -> %% If mibs exist it means we must ensure priv exists. %% mibs files are compiled to priv/mibs/ @@ -139,15 +192,57 @@ copy_app_dirs(AppInfo, OldAppDir, AppDir) -> false -> ok end, - + {SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_app_info:opts(AppInfo)), %% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref - SrcDirs = rebar_dir:all_src_dirs(rebar_app_info:opts(AppInfo), ["src"], []), - [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs]; + [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs], + %% copy all extra_src_dirs as they build into themselves and linking means they + %% are shared across profiles + [copy(OldAppDir, AppDir, Dir) || Dir <- ExtraDirs]; false -> ok end. symlink_or_copy(OldAppDir, AppDir, Dir) -> - Source = filename:join(OldAppDir, Dir), - Target = filename:join(AppDir, Dir), + Source = filename:join([OldAppDir, Dir]), + Target = filename:join([AppDir, Dir]), rebar_file_utils:symlink_or_copy(Source, Target). + +copy(OldAppDir, AppDir, Dir) -> + Source = filename:join([OldAppDir, Dir]), + Target = filename:join([AppDir, Dir]), + case ec_file:is_dir(Source) of + true -> copy(Source, Target); + false -> ok + end. + +%% TODO: use ec_file:copy/2 to do this, it preserves timestamps and +%% may prevent recompilation of files in extra dirs +copy(Source, Target) -> + %% important to do this so no files are copied onto themselves + %% which truncates them to zero length on some platforms + ok = delete_if_symlink(Target), + ok = filelib:ensure_dir(filename:join([Target, "dummy.beam"])), + {ok, Files} = rebar_utils:list_dir(Source), + case [filename:join([Source, F]) || F <- Files] of + [] -> ok; + Paths -> rebar_file_utils:cp_r(Paths, Target) + end. + +delete_if_symlink(Path) -> + case ec_file:is_symlink(Path) of + true -> file:delete(Path); + false -> ok + end. + +resolve_src_dirs(Opts) -> + SrcDirs = rebar_dir:src_dirs(Opts, ["src"]), + ExtraDirs = rebar_dir:extra_src_dirs(Opts, []), + normalize_src_dirs(SrcDirs, ExtraDirs). + +%% remove duplicates and make sure no directories that exist +%% in src_dirs also exist in extra_src_dirs +normalize_src_dirs(SrcDirs, ExtraDirs) -> + S = lists:usort(SrcDirs), + E = lists:usort(ExtraDirs), + {S, lists:subtract(E, S)}. + diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index fc7457e..0b9b9bb 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -103,7 +103,7 @@ analyze(State) -> get_all_coverdata(CoverDir) -> ok = filelib:ensure_dir(filename:join([CoverDir, "dummy.log"])), - {ok, Files} = file:list_dir(CoverDir), + {ok, Files} = rebar_utils:list_dir(CoverDir), rebar_utils:filtermap(fun(FileName) -> case filename:extension(FileName) == ".coverdata" of true -> {true, filename:join([CoverDir, FileName])}; @@ -277,8 +277,9 @@ strip_coverdir(File) -> cover_compile(State, apps) -> Apps = filter_checkouts(rebar_state:project_apps(State)), - AppDirs = app_ebin_dirs(Apps, []), - cover_compile(State, AppDirs); + AppDirs = app_dirs(Apps), + ExtraDirs = extra_src_dirs(State, Apps), + cover_compile(State, AppDirs ++ ExtraDirs); cover_compile(State, Dirs) -> %% start the cover server if necessary {ok, CoverPid} = start_cover(), @@ -290,9 +291,30 @@ cover_compile(State, Dirs) -> compile([], Acc) -> lists:reverse(Acc); compile([Dir|Rest], Acc) -> + ?INFO("covering ~p", [Dir]), Result = cover:compile_beam_directory(Dir), compile(Rest, [Result|Acc]). +app_dirs(Apps) -> + lists:foldl(fun app_ebin_dirs/2, [], Apps). + +app_ebin_dirs(App, Acc) -> + AppDir = rebar_app_info:ebin_dir(App), + ExtraDirs = rebar_dir:extra_src_dirs(rebar_app_info:opts(App), []), + OutDir = rebar_app_info:out_dir(App), + [filename:join([OutDir, D]) || D <- [AppDir|ExtraDirs]] ++ Acc. + +extra_src_dirs(State, Apps) -> + BaseDir = rebar_state:dir(State), + F = fun(App) -> rebar_app_info:dir(App) == BaseDir end, + %% check that this app hasn't already been dealt with + Extras = case lists:any(F, Apps) of + false -> rebar_dir:extra_src_dirs(rebar_state:opts(State), []); + true -> [] + end, + OutDir = rebar_dir:base_dir(State), + [filename:join([OutDir, "extras", D]) || D <- Extras]. + filter_checkouts(Apps) -> filter_checkouts(Apps, []). filter_checkouts([], Acc) -> lists:reverse(Acc); @@ -302,10 +324,6 @@ filter_checkouts([App|Rest], Acc) -> false -> filter_checkouts(Rest, [App|Acc]) end. -app_ebin_dirs([], Acc) -> Acc; -app_ebin_dirs([App|Rest], Acc) -> - app_ebin_dirs(Rest, [rebar_app_info:ebin_dir(App)|Acc]). - start_cover() -> case cover:start() of {ok, Pid} -> {ok, Pid}; diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 959be3f..71a1063 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -9,7 +9,7 @@ do/1, format_error/1]). %% exported solely for tests --export([compile/1, prepare_tests/1, eunit_opts/1]). +-export([compile/2, prepare_tests/1, eunit_opts/1]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -39,14 +39,15 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - case compile(State) of + Tests = prepare_tests(State), + case compile(State, Tests) of %% successfully compiled apps - {ok, S} -> do_tests(S); + {ok, S} -> do(S, Tests); %% this should look like a compiler error, not an eunit error Error -> Error end. -do_tests(State) -> +do(State, Tests) -> ?INFO("Performing EUnit tests...", []), rebar_utils:update_code(rebar_state:code_paths(State, all_deps)), @@ -56,9 +57,9 @@ do_tests(State) -> Cwd = rebar_dir:get_cwd(), rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - case prepare_tests(State) of - {ok, Tests} -> - case do_tests(State, Tests) of + case Tests of + {ok, T} -> + case run_tests(State, T) of {ok, State1} -> %% Run eunit provider posthooks rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1), @@ -73,11 +74,12 @@ do_tests(State) -> Error end. -do_tests(State, Tests) -> +run_tests(State, Tests) -> + T = translate_paths(State, Tests), EUnitOpts = resolve_eunit_opts(State), - ?DEBUG("eunit_tests ~p", [Tests]), - ?DEBUG("eunit_opts ~p", [EUnitOpts]), - Result = eunit:test(Tests, EUnitOpts), + ?INFO("eunit_tests ~p", [T]), + ?INFO("eunit_opts ~p", [EUnitOpts]), + Result = eunit:test(T, EUnitOpts), ok = maybe_write_coverdata(State), case handle_results(Result) of {error, Reason} -> @@ -114,38 +116,80 @@ safe_define_test_macro(Opts) -> false -> [{d, 'TEST'}] ++ Opts end. -compile(State) -> - %% inject `eunit_first_files` and `eunit_compile_opts` into the applications to be compiled - NewState = inject_eunit_state(State), +compile(State, {ok, Tests}) -> + %% inject `eunit_first_files`, `eunit_compile_opts` and any + %% directories required by tests into the applications + NewState = inject_eunit_state(State, Tests), case rebar_prv_compile:do(NewState) of %% successfully compiled apps {ok, S} -> ok = maybe_cover_compile(S), - ok = maybe_compile_bare_testdir(S), {ok, S}; %% this should look like a compiler error, not an eunit error Error -> Error - end. + end; +%% maybe compile even in the face of errors? +compile(_State, Error) -> Error. -inject_eunit_state(State) -> +inject_eunit_state(State, Tests) -> Apps = project_apps(State), - ModdedApps = lists:map(fun(App) -> inject(State, App) end, Apps), - rebar_state:project_apps(State, ModdedApps). - -inject(State, App) -> + ModdedApps = lists:map(fun(App) -> + NewOpts = inject(rebar_app_info:opts(App), State), + rebar_app_info:opts(App, NewOpts) + end, Apps), + NewOpts = inject(rebar_state:opts(State), State), + NewState = rebar_state:opts(State, NewOpts), + test_dirs(NewState, ModdedApps, Tests). + +inject(Opts, State) -> %% append `eunit_compile_opts` to app defined `erl_opts` - ErlOpts = rebar_app_info:get(App, erl_opts, []), + ErlOpts = rebar_opts:get(Opts, erl_opts, []), EUnitOpts = rebar_state:get(State, eunit_compile_opts, []), - NewOpts = EUnitOpts ++ ErlOpts, + NewErlOpts = EUnitOpts ++ ErlOpts, %% append `eunit_first_files` to app defined `erl_first_files` - FirstFiles = rebar_app_info:get(App, erl_first_files, []), + FirstFiles = rebar_opts:get(Opts, erl_first_files, []), EUnitFirstFiles = rebar_state:get(State, eunit_first_files, []), NewFirstFiles = EUnitFirstFiles ++ FirstFiles, - %% insert the new keys into the app - lists:foldl(fun({K, V}, NewApp) -> rebar_app_info:set(NewApp, K, V) end, - App, - [{erl_opts, NewOpts}, {erl_first_files, NewFirstFiles}]). + %% insert the new keys into the opts + lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, + Opts, + [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). + +test_dirs(State, Apps, []) -> rebar_state:project_apps(State, Apps); +test_dirs(State, Apps, [{dir, Dir}|Rest]) -> + %% insert `Dir` into an app if relative, or the base state if not + %% app relative but relative to the root or not at all if outside + %% project scope + {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), + test_dirs(NewState, NewApps, Rest); +test_dirs(State, Apps, [{file, File}|Rest]) -> + Dir = filename:dirname(File), + {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), + test_dirs(NewState, NewApps, Rest); +test_dirs(State, Apps, [_|Rest]) -> test_dirs(State, Apps, Rest). + +maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of + {ok, Path} -> + Opts = inject_test_dir(rebar_app_info:opts(App), Path), + {State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest}; + {error, badparent} -> + maybe_inject_test_dir(State, AppAcc ++ [App], Rest, Dir) + end; +maybe_inject_test_dir(State, AppAcc, [], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of + {ok, Path} -> + Opts = inject_test_dir(rebar_state:opts(State), Path), + {rebar_state:opts(State, Opts), AppAcc}; + {error, badparent} -> + {State, AppAcc} + end. + +inject_test_dir(Opts, Dir) -> + %% append specified test targets to app defined `extra_src_dirs` + ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), + rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). test_defined([{d, 'TEST'}|_]) -> true; test_defined([{d, 'TEST', true}|_]) -> true; @@ -212,7 +256,7 @@ default_tests(State, Apps) -> %% included or `test` does not exist false -> lists:reverse(Tests); %% need to add `test` dir at root to dirs to be included - true -> lists:reverse([{dir, filename:join([rebar_dir:base_dir(State), "test"])}|Tests]) + true -> lists:reverse([{dir, BareTest}|Tests]) end. set_apps([], Acc) -> Acc; @@ -268,14 +312,14 @@ validate_app(State, [App|Rest], AppName) -> false -> validate_app(State, Rest, AppName) end. -validate_dir(_State, Dir) -> - case ec_file:is_dir(Dir) of +validate_dir(State, Dir) -> + case ec_file:is_dir(filename:join([rebar_state:dir(State), Dir])) of true -> ok; false -> {error, lists:concat(["Directory `", Dir, "' not found."])} end. -validate_file(_State, File) -> - case ec_file:exists(File) of +validate_file(State, File) -> + case ec_file:exists(filename:join([rebar_state:dir(State), File])) of true -> ok; false -> {error, lists:concat(["File `", File, "' not found."])} end. @@ -302,6 +346,31 @@ set_verbose(Opts) -> false -> [verbose] ++ Opts end. +translate_paths(State, Tests) -> translate_paths(State, Tests, []). + +translate_paths(_State, [], Acc) -> lists:reverse(Acc); +translate_paths(State, [{dir, Dir}|Rest], Acc) -> + Apps = project_apps(State), + translate_paths(State, Rest, [translate(State, Apps, Dir)|Acc]); +translate_paths(State, [{file, File}|Rest], Acc) -> + Dir = filename:dirname(File), + Apps = project_apps(State), + translate_paths(State, Rest, [translate(State, Apps, Dir)|Acc]); +translate_paths(State, [Test|Rest], Acc) -> + translate_paths(State, Rest, [Test|Acc]). + +translate(State, [App|Rest], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of + {ok, Path} -> {dir, filename:join([rebar_app_info:out_dir(App), Path])}; + {error, badparent} -> translate(State, Rest, Dir) + end; +translate(State, [], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of + {ok, Path} -> {dir, filename:join([rebar_dir:base_dir(State), "extras", Path])}; + %% not relative, leave as is + {error, badparent} -> {dir, Dir} + end. + maybe_cover_compile(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), State1 = case proplists:get_value(cover, RawOpts, false) of @@ -310,14 +379,6 @@ maybe_cover_compile(State) -> end, rebar_prv_cover:maybe_cover_compile(State1). -maybe_cover_compile(State, Dir) -> - {RawOpts, _} = rebar_state:command_parsed_args(State), - State1 = case proplists:get_value(cover, RawOpts, false) of - true -> rebar_state:set(State, cover_enabled, true); - false -> State - end, - rebar_prv_cover:maybe_cover_compile(State1, [Dir]). - maybe_write_coverdata(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), State1 = case proplists:get_value(cover, RawOpts, false) of @@ -326,30 +387,6 @@ maybe_write_coverdata(State) -> end, rebar_prv_cover:maybe_write_coverdata(State1, ?PROVIDER). -maybe_compile_bare_testdir(State) -> - Apps = project_apps(State), - BareTest = filename:join([rebar_state:dir(State), "test"]), - F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end, - case ec_file:is_dir(BareTest) andalso not lists:any(F, Apps) of - true -> - ErlOpts = rebar_state:get(State, erl_opts, []), - EUnitOpts = rebar_state:get(State, eunit_compile_opts, []), - NewErlOpts = safe_define_test_macro(EUnitOpts ++ ErlOpts), - %% append `eunit_first_files` to app defined `erl_first_files` - FirstFiles = rebar_state:get(State, erl_first_files, []), - EUnitFirstFiles = rebar_state:get(State, eunit_first_files, []), - NewFirstFiles = EUnitFirstFiles ++ FirstFiles, - Opts = rebar_state:opts(State), - NewOpts = lists:foldl(fun({K, V}, Dict) -> rebar_opts:set(Dict, K, V) end, - Opts, - [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}, {src_dirs, ["test"]}]), - OutDir = filename:join([rebar_dir:base_dir(State), "test"]), - filelib:ensure_dir(filename:join([OutDir, "dummy.beam"])), - rebar_erlc_compiler:compile(NewOpts, rebar_state:dir(State), OutDir), - maybe_cover_compile(State, [OutDir]); - false -> ok - end. - handle_results(ok) -> ok; handle_results(error) -> {error, unknown_error}; diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index ccdf960..4fd4bd1 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -65,7 +65,8 @@ escape_double_quotes_weak/1, check_min_otp_version/1, check_blacklisted_otp_versions/1, - info_useless/2]). + info_useless/2, + list_dir/1]). %% for internal use only -export([otp_release/0]). @@ -774,3 +775,9 @@ info_useless(Old, New) -> || Name <- Old, not lists:member(Name, New)], ok. + +-ifdef(no_list_dir_all). +list_dir(Dir) -> file:list_dir(Dir). +-else. +list_dir(Dir) -> file:list_dir_all(Dir). +-endif. diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index cc37441..1c2c527 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -5,12 +5,23 @@ end_per_suite/1, init_per_testcase/2, end_per_testcase/2, + init_per_group/2, + end_per_group/2, all/0, - build_basic_app/1, - build_release_apps/1, - build_checkout_apps/1, - build_checkout_deps/1, - build_all_srcdirs/1, + groups/0, + build_basic_app/1, paths_basic_app/1, clean_basic_app/1, + build_release_apps/1, paths_release_apps/1, clean_release_apps/1, + build_checkout_apps/1, paths_checkout_apps/1, + build_checkout_deps/1, paths_checkout_deps/1, + build_basic_srcdirs/1, paths_basic_srcdirs/1, + build_release_srcdirs/1, paths_release_srcdirs/1, + build_unbalanced_srcdirs/1, paths_unbalanced_srcdirs/1, + build_basic_extra_dirs/1, paths_basic_extra_dirs/1, clean_basic_extra_dirs/1, + build_release_extra_dirs/1, paths_release_extra_dirs/1, clean_release_extra_dirs/1, + build_unbalanced_extra_dirs/1, paths_unbalanced_extra_dirs/1, + build_extra_dirs_in_project_root/1, + paths_extra_dirs_in_project_root/1, + clean_extra_dirs_in_project_root/1, recompile_when_hrl_changes/1, recompile_when_opts_change/1, dont_recompile_when_opts_dont_change/1, @@ -35,116 +46,590 @@ suite() -> []. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_testcase(_, Config) -> - rebar_test_utils:init_rebar_state(Config). - -end_per_testcase(_, _Config) -> - catch meck:unload(). - all() -> - [build_basic_app, build_release_apps, - build_checkout_apps, build_checkout_deps, - build_all_srcdirs, recompile_when_hrl_changes, - recompile_when_opts_change, dont_recompile_when_opts_dont_change, - dont_recompile_yrl_or_xrl, delete_beam_if_source_deleted, + [{group, basic_app}, {group, release_apps}, + {group, checkout_apps}, {group, checkout_deps}, + {group, basic_srcdirs}, {group, release_srcdirs}, {group, unbalanced_srcdirs}, + {group, basic_extras}, {group, release_extras}, {group, unbalanced_extras}, + {group, root_extras}, + recompile_when_hrl_changes, recompile_when_opts_change, + dont_recompile_when_opts_dont_change, dont_recompile_yrl_or_xrl, + delete_beam_if_source_deleted, deps_in_path, checkout_priority, highest_version_of_pkg_dep, parse_transform_test, erl_first_files_test, mib_test, umbrella_mib_first_test, only_default_transitive_deps, - clean_all, override_deps, profile_override_deps, build_more_sources]. - -build_basic_app(Config) -> - AppDir = ?config(apps, Config), - - Name = rebar_test_utils:create_random_name("app1_"), + clean_all, override_deps, profile_override_deps]. + +groups() -> + [{basic_app, [], [build_basic_app, paths_basic_app, clean_basic_app]}, + {release_apps, [], [build_release_apps, paths_release_apps, clean_release_apps]}, + {checkout_apps, [], [build_checkout_apps, paths_checkout_apps]}, + {checkout_deps, [], [build_checkout_deps, paths_checkout_deps]}, + {basic_srcdirs, [], [build_basic_srcdirs, paths_basic_srcdirs]}, + {release_srcdirs, [], [build_release_srcdirs, + paths_release_srcdirs]}, + {unbalanced_srcdirs, [], [build_unbalanced_srcdirs, + paths_unbalanced_srcdirs]}, + {basic_extras, [], [build_basic_extra_dirs, + paths_basic_extra_dirs, + clean_basic_extra_dirs]}, + {release_extras, [], [build_release_extra_dirs, + paths_release_extra_dirs, + clean_release_extra_dirs]}, + {unbalanced_extras, [], [build_unbalanced_extra_dirs, + paths_unbalanced_extra_dirs]}, + {root_extras, [], [build_extra_dirs_in_project_root, + paths_extra_dirs_in_project_root, + clean_extra_dirs_in_project_root]}]. + +init_per_group(basic_app, Config) -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "basic_app_"), + AppDir = ?config(apps, NewConfig), + + Name = rebar_test_utils:create_random_name("app1"), Vsn = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + [{app_names, [Name]}, {vsns, [Vsn]}|NewConfig]; - rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}). - -build_release_apps(Config) -> - AppDir = ?config(apps, Config), +init_per_group(release_apps, Config) -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "release_apps_"), + AppDir = ?config(apps, NewConfig), Name1 = rebar_test_utils:create_random_name("relapp1_"), Vsn1 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(filename:join([AppDir,"apps",Name1]), Name1, Vsn1, [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("relapp2_"), Vsn2 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(filename:join([AppDir,"apps",Name2]), Name2, Vsn2, [kernel, stdlib]), + + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]; - rebar_test_utils:run_and_check( - Config, [], ["compile"], - {ok, [{app, Name1}, {app, Name2}]} - ). +init_per_group(checkout_apps, Config) -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "checkout_apps_"), + AppDir = ?config(apps, NewConfig), + CheckoutsDir = ?config(checkouts, NewConfig), -build_checkout_apps(Config) -> - AppDir = ?config(apps, Config), - CheckoutsDir = ?config(checkouts, Config), Name1 = rebar_test_utils:create_random_name("checkapp1_"), Vsn1 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name1, Vsn1, [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("checkapp2_"), Vsn2 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(filename:join([CheckoutsDir,Name2]), Name2, Vsn2, [kernel, stdlib]), - rebar_test_utils:run_and_check( - Config, [], ["compile"], - {ok, [{app, Name1}, {checkout, Name2}]} - ). + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]; -build_checkout_deps(Config) -> - AppDir = ?config(apps, Config), - CheckoutsDir = ?config(checkouts, Config), +init_per_group(checkout_deps, Config) -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "checkout_deps_"), + AppDir = ?config(apps, NewConfig), + CheckoutsDir = ?config(checkouts, NewConfig), DepsDir = filename:join([AppDir, "_build", "default", "lib"]), + Name1 = rebar_test_utils:create_random_name("checkapp1_"), Vsn1 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name1, Vsn1, [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("checkapp2_"), Vsn2 = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(filename:join([CheckoutsDir,Name2]), Name2, Vsn2, [kernel, stdlib]), + rebar_test_utils:create_app(filename:join([DepsDir,Name2]), Name2, Vsn1, [kernel, stdlib]), + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]; + +init_per_group(Group, Config) when Group == basic_srcdirs; Group == basic_extras -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "basic_srcdirs_"), + AppDir = ?config(apps, NewConfig), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ExtraSrc = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name]), + + ok = filelib:ensure_dir(filename:join([AppDir, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "extra", io_lib:format("~ts_extra.erl", [Name])]), + ExtraSrc), + + [{app_names, [Name]}, {vsns, [Vsn]}|NewConfig]; + +init_per_group(Group, Config) when Group == release_srcdirs; Group == release_extras -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "release_srcdirs_"), + AppDir = ?config(apps, NewConfig), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + ExtraOne = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name1]), + + ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name1, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "apps", Name1, "extra", + io_lib:format("~ts_extra.erl", [Name1])]), + ExtraOne), + + ExtraTwo = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name2]), + + ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name2, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "apps", Name2, "extra", + io_lib:format("~ts_extra.erl", [Name2])]), + ExtraTwo), + + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]; + +init_per_group(Group, Config) when Group == unbalanced_srcdirs; Group == unbalanced_extras -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "unbalanced_srcdirs_"), + AppDir = ?config(apps, NewConfig), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + ExtraOne = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name1]), + + ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name1, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "apps", Name1, "extra", + io_lib:format("~ts_extra.erl", [Name1])]), + ExtraOne), + + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]; + +init_per_group(root_extras, Config) -> + NewConfig = rebar_test_utils:init_rebar_state(Config, "root_extras_"), + AppDir = ?config(apps, NewConfig), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + Extra = <<"-module(extra).\n-export([ok/0]).\nok() -> ok.\n">>, + + ok = filelib:ensure_dir(filename:join([AppDir, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "extra", "extra.erl"]), Extra), + + [{app_names, [Name1, Name2]}, {vsns, [Vsn1, Vsn2]}|NewConfig]. + +end_per_group(_Group, _Config) -> + ok. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_, Config) -> + case ?config(apps, Config) of + undefined -> rebar_test_utils:init_rebar_state(Config); + _ -> Config + end. + +end_per_testcase(_, _Config) -> + catch meck:unload(). + + + +%% test cases + +build_basic_app(Config) -> + [Name] = ?config(app_names, Config), + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}). + +build_release_apps(Config) -> + [Name1, Name2] = ?config(app_names, Config), + rebar_test_utils:run_and_check( + Config, [], ["compile"], + {ok, [{app, Name1}, {app, Name2}]} + ). + +build_checkout_apps(Config) -> + [Name1, Name2] = ?config(app_names, Config), + rebar_test_utils:run_and_check( + Config, [], ["compile"], + {ok, [{app, Name1}, {checkout, Name2}]} + ). + +build_checkout_deps(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + [_, Vsn2] = ?config(vsns, Config), + Deps = [{list_to_atom(Name2), Vsn2, {git, "", ""}}], {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, Deps}])), - {ok, State} = rebar_test_utils:run_and_check( + rebar_test_utils:run_and_check( Config, RebarConfig, ["compile"], {ok, [{app, Name1}, {checkout, Name2}]} + ). + +build_basic_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + ExtraBeam = filename:join([AppDir, "_build", "default", "lib", Name, "ebin", + io_lib:format("~ts_extra.beam", [Name])]), + %% check the extra src_dir was copied/linked into the _build dir + ExtraDir = filename:join([AppDir, "_build", "default", "lib", Name, "extra"]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name}, {file, ExtraBeam}, {dir, ExtraDir}]} + ). + +build_release_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + Extra1Beam = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin", + io_lib:format("~ts_extra.beam", [Name1])]), + Extra2Beam = filename:join([AppDir, "_build", "default", "lib", Name2, "ebin", + io_lib:format("~ts_extra.beam", [Name2])]), + + %% check the extra src_dir was copied/linked into the _build dir + Extra1Dir = filename:join([AppDir, "_build", "default", "lib", Name1, "extra"]), + Extra2Dir = filename:join([AppDir, "_build", "default", "lib", Name2, "extra"]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name1}, {app, Name2}, + {file, Extra1Beam}, {file, Extra2Beam}, + {dir, Extra1Dir}, {dir, Extra2Dir}]} + ). + +build_unbalanced_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + Extra1Beam = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin", + io_lib:format("~ts_extra.beam", [Name1])]), + + %% check the extra src_dir was copied/linked into the _build dir + Extra1Dir = filename:join([AppDir, "_build", "default", "lib", Name1, "extra"]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name1}, {app, Name2}, {file, Extra1Beam}, {dir, Extra1Dir}]} + ), + + %% check no extra src_dir were copied/linked into the _build dir + Extra2Dir = filename:join([AppDir, "_build", "default", "lib", Name2, "extra"]), + false = filelib:is_dir(Extra2Dir), + %% check only expected beams are in the ebin dir + {ok, Files} = rebar_utils:list_dir(filename:join([AppDir, "_build", "default", "lib", Name2, "ebin"])), + lists:all(fun(Beam) -> lists:member(Beam, [Name2 ++ ".app", "not_a_real_src_" ++ Name2 ++ ".beam"]) end, + Files). + +build_basic_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + ExtraBeam = filename:join([AppDir, "_build", "default", "lib", Name, "extra", + io_lib:format("~ts_extra.beam", [Name])]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name}, {file, ExtraBeam}]} + ). + +build_release_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + Extra1Beam = filename:join([AppDir, "_build", "default", "lib", Name1, "extra", + io_lib:format("~ts_extra.beam", [Name1])]), + Extra2Beam = filename:join([AppDir, "_build", "default", "lib", Name2, "extra", + io_lib:format("~ts_extra.beam", [Name2])]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name1}, {app, Name2}, {file, Extra1Beam}, {file, Extra2Beam}]} + ). + +build_unbalanced_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + Extra1Beam = filename:join([AppDir, "_build", "default", "lib", Name1, "extra", + io_lib:format("~ts_extra.beam", [Name1])]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name1}, {app, Name2}, {file, Extra1Beam}]} ), + + %% check no extra src_dir were copied/linked into the _build dir + false = filelib:is_dir(filename:join([AppDir, "_build", "default", "lib", Name2, "extra"])), + %% check only expected beams are in the ebin dir + {ok, Files} = rebar_utils:list_dir(filename:join([AppDir, "_build", "default", "lib", Name2, "ebin"])), + lists:all(fun(Beam) -> lists:member(Beam, [Name2 ++ ".app", "not_a_real_src_" ++ Name2 ++ ".beam"]) end, + Files). + +build_extra_dirs_in_project_root(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + %% check a beam corresponding to the src in the extra src_dir exists + ExtraBeam = filename:join([AppDir, "_build", "default", "extras", "extra", "extra.beam"]), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name1}, {app, Name2}, {file, ExtraBeam}]} + ). + +paths_basic_app(Config) -> + [Name] = ?config(app_names, Config), + [Vsn] = ?config(vsns, Config), + + {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + ok = application:load(list_to_atom(Name)), + Loaded = application:loaded_applications(), + {_, _, Vsn} = lists:keyfind(list_to_atom(Name), 1, Loaded). + +paths_release_apps(Config) -> + [Name1, Name2] = ?config(app_names, Config), + [Vsn1, Vsn2] = ?config(vsns, Config), + + {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return), + code:add_paths(rebar_state:code_paths(State, all_deps)), + ok = application:load(list_to_atom(Name1)), ok = application:load(list_to_atom(Name2)), Loaded = application:loaded_applications(), + {_, _, Vsn1} = lists:keyfind(list_to_atom(Name1), 1, Loaded), {_, _, Vsn2} = lists:keyfind(list_to_atom(Name2), 1, Loaded). -build_all_srcdirs(Config) -> +paths_checkout_apps(Config) -> + [Name1, _Name2] = ?config(app_names, Config), + [Vsn1, _Vsn2] = ?config(vsns, Config), + + {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + ok = application:load(list_to_atom(Name1)), + Loaded = application:loaded_applications(), + {_, _, Vsn1} = lists:keyfind(list_to_atom(Name1), 1, Loaded). + +paths_checkout_deps(Config) -> AppDir = ?config(apps, Config), + [_Name1, Name2] = ?config(app_names, Config), + [_Vsn1, Vsn2] = ?config(vsns, Config), + + %% rebar_test_utils:init_rebar_state/1,2 uses rebar_state:new/3 which + %% maybe incorrectly sets deps to [] (based on `rebar.lock`) instead of + %% to the checkapps + %% until that is sorted out the lock file has to be removed before + %% this test will pass + file:delete(filename:join([AppDir, "rebar.lock"])), + + {ok, RebarConfig} = file:consult(filename:join([AppDir, "rebar.config"])), + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + ok = application:load(list_to_atom(Name2)), + Loaded = application:loaded_applications(), + {_, _, Vsn2} = lists:keyfind(list_to_atom(Name2), 1, Loaded). + +paths_basic_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name] = ?config(app_names, Config), RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], - Name = rebar_test_utils:create_random_name("app1_"), - Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), - ExtraSrc = <<"-module(extra_src).\n" - "-export([ok/0]).\n" - "ok() -> ok.\n">>, + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name]))), + {module, Mod} = code:ensure_loaded(Mod), - ok = filelib:ensure_dir(filename:join([AppDir, "extra", "dummy"])), - ok = file:write_file(filename:join([AppDir, "extra", "extra_src.erl"]), ExtraSrc), + Expect = filename:join([AppDir, "_build", "default", "lib", Name, "ebin", + io_lib:format("~ts_extra.beam", [Name])]), + Expect = code:which(Mod). - rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), +paths_release_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), - %% check a beam corresponding to the src in the extra src_dir exists in ebin - EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - true = filelib:is_file(filename:join([EbinDir, "extra_src.beam"])), + RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], - %% check the extra src_dir was linked into the _build dir - true = filelib:is_dir(filename:join([AppDir, "_build", "default", "lib", Name, "extra"])). + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {module, Mod1} = code:ensure_loaded(Mod1), + Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {module, Mod2} = code:ensure_loaded(Mod2), + + ExpectOne = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin", + io_lib:format("~ts_extra.beam", [Name1])]), + ExpectOne = code:which(Mod1), + ExpectTwo = filename:join([AppDir, "_build", "default", "lib", Name2, "ebin", + io_lib:format("~ts_extra.beam", [Name2])]), + ExpectTwo = code:which(Mod2). + +paths_unbalanced_srcdirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{src_dirs, ["src", "extra"]}]}], + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {module, Mod1} = code:ensure_loaded(Mod1), + Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {error, nofile} = code:ensure_loaded(Mod2), + + ExpectOne = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin", + io_lib:format("~ts_extra.beam", [Name1])]), + ExpectOne = code:which(Mod1). + +paths_basic_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name]))), + {module, Mod} = code:ensure_loaded(Mod), + + Expect = filename:join([AppDir, "_build", "default", "lib", Name, "extra", + io_lib:format("~ts_extra.beam", [Name])]), + Expect = code:which(Mod). + +paths_release_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {module, Mod1} = code:ensure_loaded(Mod1), + Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {module, Mod2} = code:ensure_loaded(Mod2), + + ExpectOne = filename:join([AppDir, "_build", "default", "lib", Name1, "extra", + io_lib:format("~ts_extra.beam", [Name1])]), + ExpectOne = code:which(Mod1), + ExpectTwo = filename:join([AppDir, "_build", "default", "lib", Name2, "extra", + io_lib:format("~ts_extra.beam", [Name2])]), + ExpectTwo = code:which(Mod2). + + +paths_unbalanced_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {module, Mod1} = code:ensure_loaded(Mod1), + Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {error, nofile} = code:ensure_loaded(Mod2), + + ExpectOne = filename:join([AppDir, "_build", "default", "lib", Name1, "extra", + io_lib:format("~ts_extra.beam", [Name1])]), + ExpectOne = code:which(Mod1). + +paths_extra_dirs_in_project_root(Config) -> + AppDir = ?config(apps, Config), + + RebarConfig = [{erl_opts, [{extra_src_dirs, ["extra"]}]}], + + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + code:add_paths(rebar_state:code_paths(State, all_deps)), + {module, extra} = code:ensure_loaded(extra), + + Expect = filename:join([AppDir, "_build", "default", "extras", "extra", "extra.beam"]), + Expect = code:which(extra). + +clean_basic_app(Config) -> + [Name] = ?config(app_names, Config), + + rebar_test_utils:run_and_check(Config, [], ["clean"], {ok, [{app, Name, invalid}]}). + +clean_release_apps(Config) -> + [Name1, Name2] = ?config(app_names, Config), + + rebar_test_utils:run_and_check(Config, [], ["clean"], + {ok, [{app, Name1, invalid}, {app, Name2, invalid}]}). + +clean_basic_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name] = ?config(app_names, Config), + + rebar_test_utils:run_and_check(Config, [], ["clean"], {ok, [{app, Name, invalid}]}), + + Beam = lists:flatten(io_lib:format("~ts_extra", [Name])), + false = ec_file:exists(filename:join([AppDir, "_build", "default", "lib", Name, "extras", Beam])). + +clean_release_extra_dirs(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + rebar_test_utils:run_and_check(Config, [], ["clean"], + {ok, [{app, Name1, invalid}, {app, Name2, invalid}]}), + + Beam1 = lists:flatten(io_lib:format("~ts_extra", [Name1])), + false = ec_file:exists(filename:join([AppDir, "_build", "default", "lib", Name1, "extras", Beam1])), + Beam2 = lists:flatten(io_lib:format("~ts_extra", [Name2])), + false = ec_file:exists(filename:join([AppDir, "_build", "default", "lib", Name2, "extras", Beam2])). + +clean_extra_dirs_in_project_root(Config) -> + AppDir = ?config(apps, Config), + [Name1, Name2] = ?config(app_names, Config), + + rebar_test_utils:run_and_check(Config, [], ["clean"], + {ok, [{app, Name1, invalid}, {app, Name2, invalid}]}), + + false = ec_file:exists(filename:join([AppDir, "_build", "default", "extras"])). recompile_when_hrl_changes(Config) -> AppDir = ?config(apps, Config), @@ -167,7 +652,7 @@ recompile_when_hrl_changes(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - {ok, Files} = file:list_dir(EbinDir), + {ok, Files} = rebar_utils:list_dir(EbinDir), ModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- Files, filename:extension(F) == ".beam"], @@ -178,7 +663,7 @@ recompile_when_hrl_changes(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), - {ok, NewFiles} = file:list_dir(EbinDir), + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- NewFiles, filename:extension(F) == ".beam"], @@ -194,7 +679,7 @@ recompile_when_opts_change(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - {ok, Files} = file:list_dir(EbinDir), + {ok, Files} = rebar_utils:list_dir(EbinDir), ModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- Files, filename:extension(F) == ".beam"], @@ -204,7 +689,7 @@ recompile_when_opts_change(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), - {ok, NewFiles} = file:list_dir(EbinDir), + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- NewFiles, filename:extension(F) == ".beam"], @@ -220,7 +705,7 @@ dont_recompile_when_opts_dont_change(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - {ok, Files} = file:list_dir(EbinDir), + {ok, Files} = rebar_utils:list_dir(EbinDir), ModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- Files, filename:extension(F) == ".beam"], @@ -228,7 +713,7 @@ dont_recompile_when_opts_dont_change(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), - {ok, NewFiles} = file:list_dir(EbinDir), + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- NewFiles, filename:extension(F) == ".beam"], @@ -331,7 +816,8 @@ deps_in_path(Config) -> %% find pkg name in there ?assertNotEqual([], [Path || Path <- code:get_path(), {match, _} <- [re:run(Path, PkgName)]]), - code:set_path(StartPaths), + + true = code:set_path(lists:filter(fun(P) -> ec_file:exists(P) end, StartPaths)), %% Make sure apps we look for are not visible again %% Hope not to find src name ?assertEqual([], [Path || Path <- code:get_path(), @@ -638,10 +1124,10 @@ clean_all(Config) -> ), %% Clean all - rebar_test_utils:run_and_check( - Config, RConf, ["clean", "--all"], - {ok, [{app, Name, invalid}, {app, DepName, invalid}, {app, PkgName, invalid}]} - ). + rebar_test_utils:run_and_check(Config, [], ["clean", "--all"], + {ok, [{app, Name, invalid}, + {app, DepName, invalid}, + {app, PkgName, invalid}]}). override_deps(Config) -> mock_git_resource:mock([{deps, [{some_dep, "0.0.1"},{other_dep, "0.0.1"}]}]), @@ -680,3 +1166,4 @@ profile_override_deps(Config) -> Config, RebarConfig, ["as", "a", "compile"], {ok, [{dep, "some_dep"},{dep_not_exist, "other_dep"}]} ). + diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index 1fae92c..ba078c2 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -7,6 +7,9 @@ all/0, flag_coverdata_written/1, config_coverdata_written/1, + basic_extra_src_dirs/1, + release_extra_src_dirs/1, + root_extra_src_dirs/1, index_written/1, flag_verbose/1, config_verbose/1]). @@ -29,6 +32,8 @@ init_per_testcase(_, Config) -> all() -> [flag_coverdata_written, config_coverdata_written, + basic_extra_src_dirs, release_extra_src_dirs, + root_extra_src_dirs, index_written, flag_verbose, config_verbose]. @@ -62,6 +67,88 @@ config_coverdata_written(Config) -> true = filelib:is_file(filename:join([AppDir, "_build", "test", "cover", "eunit.coverdata"])). +basic_extra_src_dirs(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("cover_extra_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ExtraSrc = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name]), + + ok = filelib:ensure_dir(filename:join([AppDir, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "extra", io_lib:format("~ts_extra.erl", [Name])]), + ExtraSrc), + + RebarConfig = [{erl_opts, [{d, some_define}]}, {extra_src_dirs, ["extra"]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--cover"], + {ok, [{app, Name}]}), + + Mod = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name]))), + {file, _} = cover:is_compiled(Mod). + +release_extra_src_dirs(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + ExtraOne = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name1]), + + ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name1, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "apps", Name1, "extra", + io_lib:format("~ts_extra.erl", [Name1])]), + ExtraOne), + + ExtraTwo = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name2]), + + ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name2, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "apps", Name2, "extra", + io_lib:format("~ts_extra.erl", [Name2])]), + ExtraTwo), + + RebarConfig = [{erl_opts, [{d, some_define}]}, {extra_src_dirs, ["extra"]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--cover"], + {ok, [{app, Name1}, {app, Name2}]}), + + Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {file, _} = cover:is_compiled(Mod1), + Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {file, _} = cover:is_compiled(Mod2). + +root_extra_src_dirs(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + Extra = <<"-module(extra).\n-export([ok/0]).\nok() -> ok.\n">>, + + ok = filelib:ensure_dir(filename:join([AppDir, "extra", "dummy"])), + ok = file:write_file(filename:join([AppDir, "extra", "extra.erl"]), Extra), + + RebarConfig = [{erl_opts, [{d, some_define}]}, {extra_src_dirs, ["extra"]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--cover"], + {ok, [{app, Name1}, {app, Name2}]}), + + {file, _} = cover:is_compiled(extra). + index_written(Config) -> AppDir = ?config(apps, Config), diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 95a411f..cdd3774 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -204,6 +204,7 @@ multi_app_default_beams(Config) -> File3 = filename:join([AppDir, "_build", "test", + "extras", "test", "extras_SUITE.beam"]), diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index 903bd96..609be51 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -125,7 +125,9 @@ basic_app_files(Config) -> AppDir = ?config(apps, Config), lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "basic_app", "ebin", F])) end, - ["basic_app.app", "basic_app.beam", "basic_app_tests.beam", "basic_app_tests_helper.beam"]). + ["basic_app.app", "basic_app.beam"]), + lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "basic_app", "test", F])) end, + ["basic_app_tests.beam", "basic_app_tests_helper.beam"]). %% check that the correct tests are exported from modules for project %% note that this implies `TEST` is set correctly @@ -171,10 +173,14 @@ multi_app_files(Config) -> AppDir = ?config(apps, Config), lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "multi_app_bar", "ebin", F])) end, - ["multi_app_bar.app", "multi_app_bar.beam", "multi_app_bar_tests.beam", "multi_app_bar_tests_helper.beam"]), + ["multi_app_bar.app", "multi_app_bar.beam"]), lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "multi_app_baz", "ebin", F])) end, - ["multi_app_baz.app", "multi_app_baz.beam", "multi_app_baz_tests.beam", "multi_app_baz_tests_helper.beam"]), - lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "test", F])) end, + ["multi_app_baz.app", "multi_app_baz.beam"]), + lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "multi_app_bar", "test", F])) end, + ["multi_app_bar_tests.beam", "multi_app_bar_tests_helper.beam"]), + lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "lib", "multi_app_baz", "test", F])) end, + ["multi_app_baz_tests.beam", "multi_app_baz_tests_helper.beam"]), + lists:foreach(fun(F) -> true = ec_file:exists(filename:join([AppDir, "_build", "test", "extras", "test", F])) end, ["multi_app_tests.beam", "multi_app_tests_helper.beam"]). %% check that the correct tests are exported from modules for project @@ -202,7 +208,9 @@ multi_app_testset(Config) -> AppDir = ?config(apps, Config), Result = ?config(result, Config), - Set = {ok, [{application, multi_app_bar}, {application, multi_app_baz}, {dir, filename:join([AppDir, "_build", "test", "test"])}]}, + Set = {ok, [{application, multi_app_bar}, + {application, multi_app_baz}, + {dir, filename:join([AppDir, "test"])}]}, Set = rebar_prv_eunit:prepare_tests(Result). diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl index 4324a5f..a061325 100644 --- a/test/rebar_file_utils_SUITE.erl +++ b/test/rebar_file_utils_SUITE.erl @@ -64,7 +64,7 @@ reset_nonexistent_dir(Config) -> ?assertNot(filelib:is_dir(TmpDir)), ok = rebar_file_utils:reset_dir(TmpDir), ?assert(filelib:is_dir(TmpDir)), - {ok, []} = file:list_dir(TmpDir). + {ok, []} = rebar_utils:list_dir(TmpDir). reset_empty_dir(Config) -> TmpDir = ?config(tmpdir, Config), @@ -73,7 +73,7 @@ reset_empty_dir(Config) -> ?assert(filelib:is_dir(TmpDir)), ok = rebar_file_utils:reset_dir(TmpDir), ?assert(filelib:is_dir(TmpDir)), - {ok, []} = file:list_dir(TmpDir). + {ok, []} = rebar_utils:list_dir(TmpDir). reset_dir(Config) -> TmpDir = ?config(tmpdir, Config), @@ -86,7 +86,7 @@ reset_dir(Config) -> ["a", "b", "c"]), ok = rebar_file_utils:reset_dir(TmpDir), ?assert(filelib:is_dir(TmpDir)), - {ok, []} = file:list_dir(TmpDir). + {ok, []} = rebar_utils:list_dir(TmpDir). path_from_ancestor(_Config) -> ?assertEqual({ok, "foo/bar/baz"}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/")), diff --git a/test/rebar_src_dirs_SUITE.erl b/test/rebar_src_dirs_SUITE.erl index e29dcf0..5a00515 100644 --- a/test/rebar_src_dirs_SUITE.erl +++ b/test/rebar_src_dirs_SUITE.erl @@ -132,9 +132,9 @@ build_basic_app(Config) -> rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), - %% check that `extra.erl` was compiled to the `ebin` dir - Ebin = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - true = filelib:is_file(filename:join([Ebin, "extra.beam"])), + %% check that `extra.erl` was compiled to the `extra` dir + ExtraOut = filename:join([AppDir, "_build", "default", "lib", Name, "extra"]), + true = filelib:is_file(filename:join([ExtraOut, "extra.beam"])), %% check that `extra.erl` is not in the `modules` key of the app {ok, App} = file:consult(filename:join([AppDir, @@ -176,11 +176,11 @@ build_multi_apps(Config) -> ), %% check that `extraX.erl` was compiled to the `ebin` dir - Ebin1 = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin"]), - true = filelib:is_file(filename:join([Ebin1, "extra1.beam"])), + ExtraOut1 = filename:join([AppDir, "_build", "default", "lib", Name1, "extra"]), + true = filelib:is_file(filename:join([ExtraOut1, "extra1.beam"])), - Ebin2 = filename:join([AppDir, "_build", "default", "lib", Name2, "ebin"]), - true = filelib:is_file(filename:join([Ebin2, "extra2.beam"])), + ExtraOut2 = filename:join([AppDir, "_build", "default", "lib", Name2, "extra"]), + true = filelib:is_file(filename:join([ExtraOut2, "extra2.beam"])), %% check that `extraX.erl` is not in the `modules` key of the app {ok, App1} = file:consult(filename:join([AppDir, @@ -221,10 +221,9 @@ src_dir_takes_precedence_over_extra(Config) -> rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), - %% check that `extra.erl` was compiled to the `ebin` dir - %% check that `extraX.erl` was compiled to the `ebin` dir - Ebin = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - true = filelib:is_file(filename:join([Ebin, "extra.beam"])), + %% check that `extra.erl` was compiled to the `extra` dir + ExtraOut = filename:join([AppDir, "_build", "default", "lib", Name, "extra"]), + true = filelib:is_file(filename:join([ExtraOut, "extra.beam"])), %% check that `extra.erl` is in the `modules` key of the app {ok, App} = file:consult(filename:join([AppDir, diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index ca5e91a..3943db7 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -334,7 +334,7 @@ check_results(AppDir, Expected, ProfileRun) -> {ok, RelxState3} = rlx_prv_rel_discover:do(RelxState2), LibDir = filename:join([ReleaseDir, Name, "lib"]), - {ok, RelLibs} = file:list_dir(LibDir), + {ok, RelLibs} = rebar_utils:list_dir(LibDir), IsSymLinkFun = fun(X) -> ec_file:is_symlink(filename:join(LibDir, X)) @@ -357,6 +357,9 @@ check_results(AppDir, Expected, ProfileRun) -> ; ({file, Filename}) -> ct:pal("Filename: ~s", [Filename]), ?assert(filelib:is_file(Filename)) + ; ({dir, Dirname}) -> + ct:pal("Directory: ~s", [Dirname]), + ?assert(filelib:is_dir(Dirname)) end, Expected). write_src_file(Dir, Name) -> @@ -422,7 +425,7 @@ get_app_metadata(Name, Vsn, Deps) -> package_app(AppDir, DestDir, PkgName) -> Name = PkgName++".tar", - {ok, Fs} = file:list_dir(AppDir), + {ok, Fs} = rebar_utils:list_dir(AppDir), ok = erl_tar:create(filename:join(DestDir, "contents.tar.gz"), lists:zip(Fs, [filename:join(AppDir,F) || F <- Fs]), [compressed]), |