-module(rebar3_lfe_utils). -export([config/2, copy_app_src/1, copy_app_src/2, out_dir/0, out_dir/1, include_dir/0, include_dir/1, ensure_dir/1, get_apps/1, get_first_files/2, get_src_dirs/2, relative_out_dir/1, relative/1, lfe_config/1, first_value/2, update_app_file/1, app_name/1, app_name_str/1, set_diff/2, debug_get_value/4, run_prehooks/2]). config(OutDir, Config) -> Key = lfe_opts, Defaults = [{outdir, OutDir}] ++ rebar_opts:erl_opts(Config) ++ [{i, include_dir()}, return, verbose], case dict:is_key(Key, Config) of true -> dict:append_list(Key, Defaults, Config); false -> dict:store(Key, Defaults, Config) end. copy_app_src(AppInfo) -> copy_app_src(AppInfo, []). copy_app_src(AppInfo, State) -> rebar_api:debug("\t\tEntered copy_app_src/1 ...", []), AppOutDir = rebar_app_info:out_dir(AppInfo), AppSrcFile = rebar_app_info:app_file_src(AppInfo), AppFile = app_file(AppOutDir, AppSrcFile, State), rebar_api:debug("\t\tAppOutDir: ~p", [AppOutDir]), rebar_api:debug("\t\tAppSrcFile: ~p", [AppSrcFile]), rebar_api:debug("\t\tAppFile: ~p", [AppFile]), rebar_api:debug("\t\tCopying ~p to ~p ...", [AppSrcFile, AppFile]), copy_file(AppSrcFile, AppFile). app_file(OutDir, SrcFile, State) -> %% Rebar screwed us over, here: they changed the arity of %% rebar_app_utils:app_src_to_app between their 3.15 and 3.16 release, %% so we have to do crazy here: UtilsExports = proplists:get_value(exports, rebar_app_utils:module_info()), App2AppArity = proplists:get_value(app_src_to_app, UtilsExports), case App2AppArity of 2 -> rebar_app_utils:app_src_to_app(OutDir, SrcFile); 3 -> rebar_app_utils:app_src_to_app(OutDir, SrcFile, State); A -> {error, io_lib:format("Unexpected arity: rebar_app_utils:app_src_to_app/~p", [A])} end. copy_file(Src, Dst) -> case file:copy(Src, Dst) of {ok, BytesCopied} -> rebar_api:debug("\t\tCopied ~p bytes.", [BytesCopied]); {error, Reason} -> rebar_api:error("\t\tFailed to copy ~p: ~p", [Src, Reason]) end. out_dir() -> "ebin". out_dir(AppDir) -> filename:join(AppDir, "ebin"). relative_out_dir(AppInfo) -> filename:join(rebar_app_info:out_dir(AppInfo), "ebin"). include_dir() -> "include". include_dir(AppDir) -> filename:join(AppDir, "include"). -spec ensure_dir(file:dirname()) -> ok. ensure_dir(OutDir) -> %% Make sure that ebin/ exists and is on the path ok = filelib:ensure_dir(filename:join(OutDir, "dummy.beam")), AbsOutDir = filename:absname(OutDir), rebar_api:debug("\t\tAdding ~p to path ...", [AbsOutDir]), true = code:add_patha(AbsOutDir), ok. get_apps(State) -> case rebar_state:current_app(State) of undefined -> rebar_api:debug("\tCurrent app state is undefined ...", []), rebar_state:project_apps(State); AppInfo -> rebar_api:debug("\tConverting current app state to list ...", []), [AppInfo] end. get_first_files(Opts, AppDir) -> Dirs = rebar_opts:get(Opts, lfe_first_files, []), [filename:join(AppDir, Dir) || Dir <- Dirs]. get_src_dirs(AppDir, Dirs) -> rebar_api:debug("\tDirs: ~p", [Dirs]), lists:usort([filename:join(AppDir, DirName) || DirName <- Dirs]). relative(Filename) -> {ok, Cwd} = file:get_cwd(), re:replace(Filename, Cwd, ".", [{return,list}]). lfe_config(State) -> rebar_state:get(State, lfe, []). first_value([], _) -> no_value; first_value([Fun | Rest], State) -> case Fun(State) of no_value -> first_value(Rest, State); Value -> Value end. %% @doc Updates the list of modules in the .app file for the specified %% directory. %% %% The .app file will be update to include all modules in its %% `modules' entry. The modules listed are resolved by looking for all %% files with the extension `.beam' in `Dir'. -spec update_app_file(file:name()) -> ok. update_app_file(Dir) -> case rebar_utils:find_files(Dir, ".app$", false) of [AppFile] -> {ok, [{application, AppName, AppDetail0}]} = file:consult(AppFile), BeamPaths = rebar_utils:find_files(Dir, ".beam$", false), Modules = [ list_to_atom(filename:basename(Path, ".beam")) || Path <- BeamPaths ], AppDetail1 = lists:keyreplace( modules , 1 , AppDetail0 , {modules, Modules} ), SpecBefore = io_lib:format("~p.\n", [{application, AppName, AppDetail0}]), SpecAfter = io_lib:format("~p.\n", [{application, AppName, AppDetail1}]), rebar_api:debug("Updating app file for ~p", [AppName]), rebar_api:debug("~p.app (BEFORE):~n~s", [AppName, SpecBefore]), rebar_api:debug("~p.app (AFTER):~n~s", [AppName, SpecAfter]), ok = rebar_file_utils:write_file_if_contents_differ(AppFile, SpecAfter, utf8), ok; [] -> ok end. app_name(AppInfo) -> rebar_api:debug("Getting app name (atom) ...", []), AppName = erlang:list_to_atom(app_name_str(AppInfo)), rebar_api:debug("AppName: ~p", [AppName]), AppName. app_name_str(AppInfo) -> rebar_api:debug("Getting app name (string) ...", []), AppName = rebar_app_info:name(AppInfo), rebar_api:debug("AppName: ~p", [AppName]), case erlang:is_binary(AppName) of true -> erlang:binary_to_list(AppName); false -> rebar_api:error("Unexpected type for app name: ~p", [AppName]), "unknown" end. set_diff(BigList, SmallList) -> sets:to_list(sets:subtract(sets:from_list(BigList), sets:from_list(SmallList))). debug_get_value(Key, List, Default, Description) -> case proplists:get_value(Key, List, Default) of Default -> Default; Value -> rebar_api:debug(Description, []), Value end. run_prehooks(State, Provider) -> Providers = rebar_state:providers(State), Cwd = rebar_dir:get_cwd(), rebar_hooks:run_project_and_app_hooks(Cwd, pre, Provider, Providers, State).