diff options
Diffstat (limited to 'src/rebar_erlc_compiler.erl')
-rw-r--r-- | src/rebar_erlc_compiler.erl | 258 |
1 files changed, 178 insertions, 80 deletions
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}). |