diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/rebar.app.src | 2 | ||||
-rw-r--r-- | src/rebar3.erl | 5 | ||||
-rw-r--r-- | src/rebar_agent.erl | 22 | ||||
-rw-r--r-- | src/rebar_app_discover.erl | 2 | ||||
-rw-r--r-- | src/rebar_core.erl | 12 | ||||
-rw-r--r-- | src/rebar_dir.erl | 6 | ||||
-rw-r--r-- | src/rebar_erlc_compiler.erl | 7 | ||||
-rw-r--r-- | src/rebar_file_utils.erl | 113 | ||||
-rw-r--r-- | src/rebar_git_resource.erl | 29 | ||||
-rw-r--r-- | src/rebar_hooks.erl | 47 | ||||
-rw-r--r-- | src/rebar_pkg_resource.erl | 3 | ||||
-rw-r--r-- | src/rebar_plugins.erl | 2 | ||||
-rw-r--r-- | src/rebar_prv_common_test.erl | 41 | ||||
-rw-r--r-- | src/rebar_prv_compile.erl | 13 | ||||
-rw-r--r-- | src/rebar_prv_cover.erl | 20 | ||||
-rw-r--r-- | src/rebar_prv_dialyzer.erl | 10 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 221 | ||||
-rw-r--r-- | src/rebar_prv_plugins_upgrade.erl | 5 | ||||
-rw-r--r-- | src/rebar_prv_release.erl | 29 | ||||
-rw-r--r-- | src/rebar_prv_relup.erl | 29 | ||||
-rw-r--r-- | src/rebar_prv_shell.erl | 12 | ||||
-rw-r--r-- | src/rebar_prv_tar.erl | 24 | ||||
-rw-r--r-- | src/rebar_prv_update.erl | 3 | ||||
-rw-r--r-- | src/rebar_relx.erl | 69 | ||||
-rw-r--r-- | src/rebar_utils.erl | 59 |
25 files changed, 461 insertions, 324 deletions
diff --git a/src/rebar.app.src b/src/rebar.app.src index 5abb643..e5d56bb 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.0.0-alpha-6"}, + {vsn, "3.0.0-beta-1"}, {modules, []}, {registered, []}, {applications, [kernel, diff --git a/src/rebar3.erl b/src/rebar3.erl index 5c0559f..a797c46 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -272,4 +272,7 @@ start_and_load_apps() -> application:start(asn1), application:start(public_key), application:start(ssl), - inets:start(). + inets:start(), + inets:start(httpc, [{profile, rebar}]), + rebar_utils:set_httpc_options(). + diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 0432fb8..dc45dcf 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -20,17 +20,17 @@ do(Namespace, Command) when is_atom(Namespace), is_atom(Command) -> gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity). init(State) -> - {ok, Cwd} = file:get_cwd(), + Cwd = rebar_dir:get_cwd(), {ok, #state{state=State, cwd=Cwd}}. handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), {Res, NewRState} = run(default, Command, RState, Cwd), - {reply, Res, MidState#state{state=NewRState}}; + {reply, Res, MidState#state{state=NewRState}, hibernate}; handle_call({cmd, Namespace, Command}, _From, State = #state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), {Res, NewRState} = run(Namespace, Command, RState, Cwd), - {reply, Res, MidState#state{state=NewRState}}; + {reply, Res, MidState#state{state=NewRState}, hibernate}; handle_call(_Call, _From, State) -> {noreply, State}. @@ -48,8 +48,8 @@ terminate(_Reason, _State) -> run(Namespace, Command, RState, Cwd) -> try - case file:get_cwd() of - {ok, Cwd} -> + case rebar_dir:get_cwd() of + Cwd -> Args = [atom_to_list(Namespace), atom_to_list(Command)], CmdState0 = refresh_state(RState, Cwd), CmdState1 = rebar_state:set(CmdState0, task, atom_to_list(Command)), @@ -87,16 +87,22 @@ refresh_paths(RState) -> %% make sure to never reload self; halt()s the VM ) -- [filename:dirname(code:which(?MODULE))], %% Similar to rebar_utils:update_code/1, but also forces a reload - %% of used modules. + %% of used modules. Also forces to reload all of ebin/ instead + %% of just the modules in the .app file, because 'extra_src_dirs' + %% allows to load and compile files that are not to be kept + %% in the app file. lists:foreach(fun(Path) -> Name = filename:basename(Path, "/ebin"), + Files = filelib:wildcard(filename:join([Path, "*.beam"])), + Modules = [list_to_atom(filename:basename(F, ".beam")) + || F <- Files], App = list_to_atom(Name), application:load(App), case application:get_key(App, modules) of undefined -> code:add_patha(Path), ok; - {ok, Modules} -> + {ok, _} -> ?DEBUG("reloading ~p from ~s", [Modules, Path]), code:replace_path(Name, Path), [begin code:purge(M), code:delete(M), code:load_file(M) end @@ -108,5 +114,5 @@ refresh_state(RState, _Dir) -> lists:foldl( fun(F, State) -> F(State) end, rebar3:init_config(), - [fun(S) -> rebar_state:current_profiles(S, rebar_state:current_profiles(RState)) end] + [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end] ). diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index 2eb9d91..9c4a5ff 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -139,7 +139,7 @@ app_dir(AppFile) -> -spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}. create_app_info(AppDir, AppFile) -> - [{application, AppName, AppDetails}] = rebar_file_utils:try_consult(AppFile), + [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile), AppVsn = proplists:get_value(vsn, AppDetails), Applications = proplists:get_value(applications, AppDetails, []), IncludedApplications = proplists:get_value(included_applications, AppDetails, []), diff --git a/src/rebar_core.erl b/src/rebar_core.erl index b1647f0..f097429 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -128,9 +128,15 @@ do([ProviderName | Rest], State) -> {error, Error} catch error:undef -> - %% This should really only happen if a plugin provider doesn't export do/1 - ?DEBUG("Undefined call to provider's do function:~n~p", [erlang:get_stacktrace()]), - ?PRV_ERROR({bad_provider_namespace, ProviderName}); + Stack = erlang:get_stacktrace(), + case Stack of + [{ProviderName, do, [_], _}|_] -> + %% This should really only happen if a plugin provider doesn't export do/1 + ?DEBUG("Undefined call to provider's do/1 function:~n~p", [Stack]), + ?PRV_ERROR({bad_provider_namespace, ProviderName}); + _ -> % re-raise + erlang:raise(error, undef, Stack) + end; error:{badrecord,provider} -> {error, ProviderName} end. diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index e226633..7af94ea 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -100,7 +100,11 @@ local_cache_dir(Dir) -> get_cwd() -> {ok, Dir} = file:get_cwd(), - Dir. + %% On windows cwd may return capital letter for drive, + %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1 + %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize" + %% cwd as soon as it possible. + filename:join([Dir]). template_globals(State) -> filename:join([global_config_dir(State), "templates", "globals"]). diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index b9072a3..87cf352 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -404,10 +404,13 @@ target_base(OutDir, Source) -> -spec compile_mib(file:filename(), file:filename(), rebar_state:t()) -> 'ok'. compile_mib(Source, Target, Config) -> + Dir = rebar_state:dir(Config), ok = filelib:ensure_dir(Target), - ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")), - Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++ + ok = filelib:ensure_dir(filename:join([Dir, "include", "dummy.hrl"])), + Opts = [{outdir, filename:join([Dir, "priv", "mibs"])} + ,{i, [filename:join([Dir, "priv", "mibs"])]}] ++ rebar_state:get(Config, mib_opts, []), + case snmpc:compile(Source, Opts) of {ok, _} -> Mib = filename:rootname(Target), diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index ad30172..b4cdc27 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -36,10 +36,14 @@ write_file_if_contents_differ/2, system_tmpdir/0, system_tmpdir/1, - reset_dir/1]). + reset_dir/1, + touch/1]). -include("rebar.hrl"). + -include_lib("providers/include/providers.hrl"). +-include_lib("kernel/include/file.hrl"). + %% =================================================================== %% Public API @@ -71,9 +75,40 @@ symlink_or_copy(Source, Target) -> {error, eexist} -> ok; {error, _} -> - cp_r([Source], Target) + case os:type() of + {win32, _} -> + S = unicode:characters_to_list(Source), + T = unicode:characters_to_list(Target), + case filelib:is_dir(S) of + true -> + win32_symlink(S, T); + false -> + cp_r([S], T) + end; + _ -> + case filelib:is_dir(Target) of + true -> + ok; + false -> + cp_r([Source], Target) + end + end + end. + +win32_symlink(Source, Target) -> + Res = rebar_utils:sh( + ?FMT("cmd /c mklink /j \"~s\" \"~s\"", + [filename:nativename(Target), filename:nativename(Source)]), + [{use_stdout, false}, return_on_error]), + case win32_ok(Res) of + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to symlink ~s to ~s~n", + [Source, Target]))} end. + %% @doc Remove files and directories. %% Target is a single filename, directoryname or wildcard expression. -spec rm_rf(string()) -> 'ok'. @@ -120,21 +155,32 @@ mv(Source, Dest) -> [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> - {ok, R} = rebar_utils:sh( - ?FMT("move /y \"~s\" \"~s\" 1> nul", - [filename:nativename(Source), - filename:nativename(Dest)]), + Cmd = case filelib:is_dir(Source) of + true -> + ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul", + [filename:nativename(Source), + filename:nativename(Dest)]); + false -> + ?FMT("robocopy /move /s \"~s\" \"~s\" \"~s\" 1> nul", + [filename:nativename(filename:dirname(Source)), + filename:nativename(Dest), + filename:basename(Source)]) + end, + Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), - case R of - [] -> - ok; - _ -> + case win32_ok(Res) of + true -> ok; + false -> {error, lists:flatten( io_lib:format("Failed to move ~s to ~s~n", [Source, Dest]))} end end. +win32_ok({ok, _}) -> true; +win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true; +win32_ok(_) -> false. + delete_each([]) -> ok; delete_each([File | Rest]) -> @@ -186,6 +232,17 @@ reset_dir(Path) -> %% recreate the directory filelib:ensure_dir(filename:join([Path, "dummy.beam"])). + +%% Linux touch but using erlang functions to work in bot *nix os and +%% windows +-spec touch(Path) -> ok | {error, Reason} when + Path :: file:name(), + Reason :: file:posix(). +touch(Path) -> + {ok, A} = file:read_file_info(Path), + ok = file:write_file_info(Path, A#file_info{mtime = calendar:local_time(), + atime = calendar:local_time()}). + %% =================================================================== %% Internal functions %% =================================================================== @@ -198,28 +255,36 @@ delete_each_dir_win32([Dir | Rest]) -> delete_each_dir_win32(Rest). xcopy_win32(Source,Dest)-> - {ok, R} = rebar_utils:sh( - ?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul", - [filename:nativename(Source), filename:nativename(Dest)]), + %% "xcopy \"~s\" \"~s\" /q /y /e 2> nul", Chanegd to robocopy to + %% handle long names. May have issues with older windows. + Cmd = case filelib:is_dir(Source) of + true -> + ?FMT("robocopy \"~s\" \"~s\" /e /is 1> nul", + [filename:nativename(Source), + filename:nativename(Dest)]); + false -> + ?FMT("robocopy \"~s\" \"~s\" \"~s\" /e /is 1> nul", + [filename:nativename(filename:dirname(Source)), + filename:nativename(Dest), + filename:basename(Source)]) + end, + Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), - case length(R) > 0 of - %% when xcopy fails, stdout is empty and and error message is printed - %% to stderr (which is redirected to nul) - true -> ok; - false -> - {error, lists:flatten( - io_lib:format("Failed to xcopy from ~s to ~s~n", - [Source, Dest]))} + case win32_ok(Res) of + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to copy ~s to ~s~n", + [Source, Dest]))} end. cp_r_win32({true, SourceDir}, {true, DestDir}) -> %% from directory to directory - SourceBase = filename:basename(SourceDir), - ok = case file:make_dir(filename:join(DestDir, SourceBase)) of + ok = case file:make_dir(DestDir) of {error, eexist} -> ok; Other -> Other end, - ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase)); + ok = xcopy_win32(SourceDir, DestDir); cp_r_win32({false, Source} = S,{true, DestDir}) -> %% from file to directory cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))}); diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 2d83579..e2f3f69 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -68,6 +68,7 @@ compare_url(Dir, Url) -> CurrentUrl1 = string:strip(string:strip(CurrentUrl, both, $\n), both, $\r), ParsedUrl = parse_git_url(Url), ParsedCurrentUrl = parse_git_url(CurrentUrl1), + ?DEBUG("Comparing git url ~p with ~p", [ParsedUrl, ParsedCurrentUrl]), ParsedCurrentUrl =:= ParsedUrl. parse_git_url("git@" ++ HostPath) -> @@ -76,6 +77,9 @@ parse_git_url("git@" ++ HostPath) -> parse_git_url("git://" ++ HostPath) -> [Host | Path] = string:tokens(HostPath, "/"), {Host, filename:rootname(filename:join(Path), ".git")}; +parse_git_url("http://" ++ HostPath) -> + [Host | Path] = string:tokens(HostPath, "/"), + {Host, filename:rootname(filename:join(Path), ".git")}; parse_git_url("https://" ++ HostPath) -> [Host | Path] = string:tokens(HostPath, "/"), {Host, filename:rootname(filename:join(Path), ".git")}. @@ -109,28 +113,23 @@ download(Dir, {git, Url, Rev}, _State) -> rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]). make_vsn(Dir) -> - {ok, Cwd} = file:get_cwd(), - try - ok = file:set_cwd(Dir), - {Vsn, RawRef, RawCount} = collect_default_refcount(), - {plain, build_vsn_string(Vsn, RawRef, RawCount)} - after - file:set_cwd(Cwd) - end. + {Vsn, RawRef, RawCount} = collect_default_refcount(Dir), + {plain, build_vsn_string(Vsn, RawRef, RawCount)}. %% Internal functions -collect_default_refcount() -> +collect_default_refcount(Dir) -> %% Get the tag timestamp and minimal ref from the system. The %% timestamp is really important from an ordering perspective. AbortMsg1 = "Getting log of git dependency failed in " ++ rebar_dir:get_cwd(), {ok, String} = rebar_utils:sh("git log -n 1 --pretty=format:\"%h\n\" ", [{use_stdout, false}, + {cd, Dir}, {debug_abort_on_error, AbortMsg1}]), RawRef = string:strip(String, both, $\n), - {Tag, TagVsn} = parse_tags(), + {Tag, TagVsn} = parse_tags(Dir), {ok, RawCount} = case Tag of undefined -> @@ -140,7 +139,7 @@ collect_default_refcount() -> {debug_abort_on_error, AbortMsg2}]), rebar_utils:line_count(PatchLines); _ -> - get_patch_count(Tag) + get_patch_count(Dir, Tag) end, {TagVsn, RawRef, RawCount}. @@ -158,8 +157,8 @@ build_vsn_string(Vsn, RawRef, Count) -> integer_to_list(Count), RefTag])) end. -get_patch_count(RawRef) -> - AbortMsg = "Getting rev-list of git dep failed in " ++ rebar_dir:get_cwd(), +get_patch_count(Dir, RawRef) -> + AbortMsg = "Getting rev-list of git dep failed in " ++ Dir, Ref = re:replace(RawRef, "\\s", "", [global]), Cmd = io_lib:format("git rev-list ~s..HEAD", [Ref]), @@ -169,10 +168,10 @@ get_patch_count(RawRef) -> rebar_utils:line_count(PatchLines). -parse_tags() -> +parse_tags(Dir) -> %% Don't abort on error, we want the bad return to be turned into 0.0.0 case rebar_utils:sh("git log --oneline --no-walk --tags --decorate", - [{use_stdout, false}, return_on_error]) of + [{use_stdout, false}, return_on_error, {cd, Dir}]) of {error, _} -> {undefined, "0.0.0"}; {ok, Line} -> diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl index 56c4e91..857336c 100644 --- a/src/rebar_hooks.erl +++ b/src/rebar_hooks.erl @@ -14,12 +14,21 @@ run_all_hooks(Dir, Type, Command, Providers, State) -> run_hooks(Dir, Type, Command, State). run_provider_hooks(Dir, Type, Command, Providers, State) -> + case rebar_state:get(State, provider_hooks, []) of + [] -> + ok; + AllHooks -> + TypeHooks = proplists:get_value(Type, AllHooks, []), + run_provider_hooks(Dir, Type, Command, Providers, TypeHooks, State) + end. + +run_provider_hooks(_Dir, _Type, _Command, _Providers, [], _State) -> + ok; +run_provider_hooks(Dir, Type, Command, Providers, TypeHooks, State) -> PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps), code:add_pathsa(PluginDepsPaths), Providers1 = rebar_state:providers(State), State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers++Providers1), - AllHooks = rebar_state:get(State1, provider_hooks, []), - TypeHooks = proplists:get_value(Type, AllHooks, []), HookProviders = proplists:get_all_values(Command, TypeHooks), case rebar_core:do(HookProviders, State1) of @@ -58,24 +67,24 @@ format_error({bad_provider, Type, Command, Name}) -> %% ERL = ERLANG_ROOT_DIR/bin/erl %% ERLC = ERLANG_ROOT_DIR/bin/erl %% +run_hooks(Dir, pre, Command, State) -> + run_hooks(Dir, pre_hooks, Command, State); +run_hooks(Dir, post, Command, State) -> + run_hooks(Dir, post_hooks, Command, State); run_hooks(Dir, Type, Command, State) -> - Hooks = case Type of - pre -> - rebar_state:get(State, pre_hooks, []); - post -> - rebar_state:get(State, post_hooks, []); - _ -> - [] - end, - - Env = create_env(State), - lists:foreach(fun({_, C, _}=Hook) when C =:= Command -> - apply_hook(Dir, Env, Hook); - ({C, _}=Hook) when C =:= Command -> - apply_hook(Dir, Env, Hook); - (_) -> - continue - end, Hooks). + case rebar_state:get(State, Type, []) of + [] -> + ok; + Hooks -> + Env = create_env(State), + lists:foreach(fun({_, C, _}=Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + ({C, _}=Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + (_) -> + continue + end, Hooks) + end. apply_hook(Dir, Env, {Arch, Command, Hook}) -> case rebar_utils:is_arch(Arch) of diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 59ce0dc..450ff1e 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -95,7 +95,8 @@ make_vsn(_) -> request(Url, ETag) -> case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]}, [{relaxed, true}], - [{body_format, binary}]) of + [{body_format, binary}], + rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> ?DEBUG("Successfully downloaded ~s", [Url]), {"etag", ETag1} = lists:keyfind("etag", 1, Headers), diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 7e12324..3e855de 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -63,7 +63,7 @@ handle_plugins(Profile, Plugins, State, Upgrade) -> handle_plugin(Profile, Plugin, State, Upgrade) -> try - {ok, Apps, State2} = rebar_prv_install_deps:handle_deps(Profile, State, [Plugin], Upgrade), + {Apps, State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State, [Plugin], Upgrade), {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 710922a..2b024cf 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -105,14 +105,18 @@ run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)). run_test_quiet(Opts) -> Pid = self(), + Ref = erlang:make_ref(), LogDir = proplists:get_value(logdir, Opts), - erlang:spawn_monitor(fun() -> + {_, Monitor} = erlang:spawn_monitor(fun() -> {ok, F} = file:open(filename:join([LogDir, "ct.latest.log"]), [write]), true = group_leader(F, self()), - Pid ! ct:run_test(Opts) + Pid ! {Ref, ct:run_test(Opts)} end), - receive Result -> handle_quiet_results(Opts, Result) end. + receive + {Ref, Result} -> handle_quiet_results(Opts, Result); + {'DOWN', Monitor, _, _, Reason} -> handle_results(?PRV_ERROR(Reason)) + end. handle_results(Results) when is_list(Results) -> Result = lists:foldl(fun sum_results/2, {0, 0, {0,0}}, Results), @@ -132,8 +136,6 @@ sum_results({Passed, Failed, {UserSkipped, AutoSkipped}}, handle_quiet_results(_, {error, _} = Result) -> handle_results(Result); -handle_quiet_results(_, {'DOWN', _, _, _, Reason}) -> - handle_results(?PRV_ERROR(Reason)); handle_quiet_results(CTOpts, Results) when is_list(Results) -> _ = [format_result(Result) || Result <- Results], case handle_results(Results) of @@ -345,23 +347,26 @@ 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) -> - case ec_file:is_dir(Path) of - false -> ok; - true -> remove_links1(Path) - end. -remove_links1(Path) -> +remove_links(Path) -> + IsDir = ec_file:is_dir(Path), case ec_file:is_symlink(Path) of - true -> - file:delete(Path); - false -> - lists:foreach(fun(ChildPath) -> - remove_links(ChildPath) - end, sub_dirs(Path)) + 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. -sub_dirs(Path) -> +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]. diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index 6eb8a4f..25d5193 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -126,7 +126,20 @@ copy_app_dirs(State, OldAppDir, AppDir) -> false -> ok end, + filelib:ensure_dir(filename:join(AppDir, "dummy")), + + %% link or copy mibs if it exists + 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/ + filelib:ensure_dir(filename:join([OldAppDir, "priv", "dummy"])), + symlink_or_copy(OldAppDir, AppDir, "mibs"); + false -> + ok + end, + %% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref SrcDirs = rebar_dir:all_src_dirs(State, ["src"], ["test"]), [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs]; diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 6c115b6..8c26521 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -337,16 +337,24 @@ write_coverdata(State, Task) -> ?WARN("Cover data export failed: ~p", [Reason]) end. -verbose(State) -> +command_line_opts(State) -> {Opts, _} = rebar_state:command_parsed_args(State), - case proplists:get_value(verbose, Opts, missing) of - missing -> rebar_state:get(State, cover_print_enabled, false); - Else -> Else + Opts. + +config_opts(State) -> + rebar_state:get(State, cover_opts, []). + +verbose(State) -> + Command = proplists:get_value(verbose, command_line_opts(State), undefined), + Config = proplists:get_value(verbose, config_opts(State), undefined), + case {Command, Config} of + {undefined, undefined} -> false; + {undefined, Verbose} -> Verbose; + {Verbose, _} -> Verbose end. cover_dir(State) -> - rebar_state:get(State, cover_data_dir, filename:join([rebar_dir:base_dir(State), - "cover"])). + filename:join([rebar_dir:base_dir(State), "cover"]). cover_opts(_State) -> [{reset, $r, "reset", boolean, help(reset)}, diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 15f1dac..1cf7b71 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -72,6 +72,7 @@ short_desc() -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> + maybe_fix_env(), ?INFO("Dialyzer starting, this may take a while...", []), code:add_pathsa(rebar_state:code_paths(State, all_deps)), Plt = get_plt(State), @@ -91,6 +92,13 @@ do(State) -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)) end. +%% This is used to workaround dialyzer quirk discussed here +%% https://github.com/rebar/rebar3/pull/489#issuecomment-107953541 +%% Dialyzer gets default plt location wrong way by peeking HOME environment +%% variable which usually is not defined on Windows. +maybe_fix_env() -> + os:putenv("DIALYZER_PLT", filename:join(rebar_dir:home_dir(), ".dialyzer_plt")). + -spec format_error(any()) -> iolist(). format_error({error_processing_apps, Error}) -> io_lib:format("Error in dialyzing apps: ~s", [Error]); @@ -380,7 +388,7 @@ run_dialyzer(State, Opts, Output) -> {check_plt, false} | Opts], ?DEBUG("Running dialyzer with options: ~p~n", [Opts2]), - _ = dialyzer:run(Opts2), + dialyzer:run(Opts2), {0, State} end. diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 768d41a..105bbb1 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -35,10 +35,7 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). --export([handle_deps/3, - handle_deps/4, - handle_deps/5, - +-export([handle_deps_as_profile/4, find_cycles/1, cull_compile/2]). @@ -76,8 +73,8 @@ do(State) -> Profiles = rebar_state:current_profiles(State), ProjectApps = rebar_state:project_apps(State), - {Apps, State1} = - lists:foldl(fun deps_per_profile/2, {[], State}, lists:reverse(Profiles)), + Upgrade = rebar_state:get(State, upgrade, false), + {Apps, State1} = deps_per_profile(Profiles, Upgrade, State), State2 = rebar_state:update_all_deps(State1, Apps), CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps], @@ -119,63 +116,77 @@ format_error({cycles, Cycles}) -> format_error(Reason) -> io_lib:format("~p", [Reason]). --spec handle_deps(atom(), rebar_state:t(), list()) -> - {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(Profile, State, Deps) -> - handle_deps(Profile, State, Deps, false, []). - --spec handle_deps(atom(), rebar_state:t(), list(), list() | boolean()) -> - {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(Profile, State, Deps, Upgrade) when is_boolean(Upgrade) -> - handle_deps(Profile, State, Deps, Upgrade, []); -handle_deps(Profile, State, Deps, Locks) when is_list(Locks) -> - Upgrade = rebar_state:get(State, upgrade, false), - handle_deps(Profile, State, Deps, Upgrade, Locks). - --spec handle_deps(atom(), rebar_state:t(), list(), boolean() | {true, binary(), integer()}, list()) - -> {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(_Profile, State, [], _, _) -> - {ok, [], State}; -handle_deps(Profile, State0, Deps, Upgrade, Locks) -> - %% Split source deps from pkg deps, needed to keep backwards compatibility - DepsDir = profile_dep_dir(State0, Profile), - - {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State0, Locks, 0), - - %% Fetch transitive src deps - {State1, SrcApps, PkgDeps1, Seen} = update_src_deps(Profile, 0, SrcDeps, PkgDeps, [] - ,State0, Upgrade, sets:new(), Locks), - - {Solved, State4} = - case PkgDeps1 of - [] -> - {[], State1}; - _ -> - %% Read in package index and dep graph - {Packages, Graph} = rebar_state:packages(State1), - Registry = rebar_packages:registry(State1), - State2 = rebar_state:packages(rebar_state:registry(State1, Registry) - ,{Packages, Graph}), - - update_pkg_deps(Profile, Packages, PkgDeps1 - ,Graph, Upgrade, Seen, State2, Locks) - end, - - AllDeps = lists:ukeymerge(2 - ,lists:ukeysort(2, SrcApps) - ,lists:ukeysort(2, Solved)), - - {ok, AllDeps, State4}. +%% Allows other providers to install deps in a given profile +%% manually, outside of what is provided by rebar3's deps tuple. +handle_deps_as_profile(Profile, State, Deps, Upgrade) -> + Locks = [], + Level = 0, + DepsDir = profile_dep_dir(State, Profile), + + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level), + AllSrcProfileDeps = [{Profile, SrcDeps, Locks, Level}], + AllPkgProfileDeps = [{Profile, Locks, PkgDeps}], + {AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllSrcProfileDeps, AllPkgProfileDeps, Locks, sets:new(), Upgrade, State), + + handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1). + %% =================================================================== %% Internal functions %% =================================================================== -deps_per_profile(Profile, {Apps, State}) -> +%% finds all the deps in `{deps, ...}` for each profile provided. +deps_per_profile(Profiles, Upgrade, State) -> + Level = 0, + {AllProfileDeps, PkgDeps} = lists:foldl(fun(Profile, {SrcAcc, PkgAcc}) -> + {Src, Pkg} = parse_profile_deps(State, Profile, Level), + {[Src | SrcAcc], [Pkg | PkgAcc]} + end, {[], []}, Profiles), + {AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllProfileDeps, PkgDeps, [], sets:new(), Upgrade, State), + + handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1). + +parse_profile_deps(State, Profile, Level) -> + DepsDir = profile_dep_dir(State, Profile), Locks = rebar_state:get(State, {locks, Profile}, []), - ProfileDeps = rebar_state:get(State, {deps, Profile}, []), - {ok, NewApps, NewState} = handle_deps(Profile, State, ProfileDeps, Locks), - {NewApps++Apps, NewState}. + Deps = rebar_state:get(State, {deps, Profile}, []), + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level), + {{Profile, SrcDeps, Locks, Level}, {Profile, Locks, PkgDeps}}. + +%% Level-order traversal of all dependencies, across profiles. +%% If profiles x,y,z are present, then the traversal will go: +%% x0, y0, z0, x1, y1, z1, ..., xN, yN, zN. +handle_profile_level([], PkgDeps, SrcApps, Seen, _Upgrade, State) -> + {SrcApps, PkgDeps, Seen, State}; +handle_profile_level([{Profile, SrcDeps, Locks, Level} | Rest], PkgDeps, SrcApps, Seen, Upgrade, State) -> + {SrcDeps1, PkgDeps1, SrcApps1, State1, Seen1, Locks1} = + update_src_deps(Profile, Level, SrcDeps, [], SrcApps + ,State, Upgrade, Seen, Locks), + SrcDeps2 = case SrcDeps1 of + [] -> Rest; + _ -> Rest ++ [{Profile, SrcDeps1, Locks1, Level+1}] + end, + handle_profile_level(SrcDeps2, [{Profile, Locks1, PkgDeps1} | PkgDeps], SrcApps1++SrcApps, sets:union(Seen, Seen1), Upgrade, State1). + +handle_profile_pkg_level(PkgDeps, AllApps, Seen, Upgrade, State) -> + %% Read in package index and dep graph + {Packages, Graph} = rebar_state:packages(State), + Registry = rebar_packages:registry(State), + State1 = rebar_state:packages(rebar_state:registry(State, Registry) + ,{Packages, Graph}), + + lists:foldl(fun({_Profile, _, []}, {AllAcc, StateAcc}) -> + {AllAcc, StateAcc}; + ({Profile1, Locks, PkgDeps2}, {AllAcc, StateAcc}) -> + {Solved, StateAcc2} = update_pkg_deps(Profile1, Packages, PkgDeps2 + ,Graph, Upgrade, Seen, StateAcc, Locks), + + AllDeps = lists:ukeymerge(2 + ,lists:ukeysort(2, AllAcc) + ,lists:ukeysort(2, Solved)), + + {AllDeps, StateAcc2} + end, {AllApps, State1}, PkgDeps). find_cycles(Apps) -> case rebar_digraph:compile_order(Apps) of @@ -279,19 +290,15 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) -> -spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {rebar_state:t(), list(), list(), sets:set(binary())}. update_src_deps(Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) -> - case lists:foldl( - fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) -> - update_src_dep(AppInfo, Profile, Level, - SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, - Upgrade, SeenAcc, Locks, LocksAcc) - end, - {[], PkgDeps, SrcApps, State, Seen, Locks}, - rebar_utils:sort_deps(SrcDeps)) of - {[], NewPkgDeps, NewSrcApps, State1, Seen1, _NewLocks} -> - {State1, NewSrcApps, NewPkgDeps, Seen1}; - {NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Seen1, NewLocks} -> - update_src_deps(Profile, Level+1, NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Upgrade, Seen1, NewLocks) - end. + lists:foldl( + fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) -> + update_src_dep(AppInfo, Profile, Level, + SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, + Upgrade, SeenAcc, Locks, LocksAcc) + end, + {[], PkgDeps, SrcApps, State, Seen, Locks}, + rebar_utils:sort_deps(SrcDeps)). + update_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> %% If not seen, add to list of locks to write out @@ -314,7 +321,7 @@ profile_dep_dir(State, Profile) -> _ -> rebar_dir:deps_dir(State) end. -update_seen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> +update_seen_src_dep(AppInfo, _Profile, _Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> Name = rebar_app_info:name(AppInfo), %% If seen from lock file or user requested an upgrade %% don't print warning about skipping @@ -323,44 +330,32 @@ update_seen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, U false when not Upgrade -> warn_skip_deps(AppInfo, State); true -> ok end, - %% scan for app children here if upgrading - case Upgrade of - false -> - {SrcDeps, PkgDeps, SrcApps, State, Seen, Locks}; - true -> - {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, NewLocks} - = handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State, Locks), - {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, Seen, NewLocks} - end. + {SrcDeps, PkgDeps, SrcApps, State, Seen, Locks}. update_unseen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) -> {NewSeen, State1} = maybe_lock(Profile, AppInfo, Seen, State, Level), {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewLocks} = case Upgrade of - true -> - handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State1, Locks); - _ -> - {_, AppInfo1} = maybe_fetch(AppInfo, Profile, false, Seen, State1), - handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State1, Locks) - end, + true -> + handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State1, Seen, Locks); + _ -> + {_, AppInfo1} = maybe_fetch(AppInfo, Profile, false, Seen, State1), + handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State1, Locks) + end, {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewSeen, NewLocks}. -handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> +handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Seen, Locks) -> Name = rebar_app_info:name(AppInfo), case lists:keyfind(Name, 1, Locks) of false -> - case maybe_fetch(AppInfo, Profile, true, sets:new(), State) of - {true, AppInfo1} -> - handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State, Locks); - {false, AppInfo1} -> - {[AppInfo1|SrcDeps], PkgDeps, SrcApps, State, Locks} - end; + {_, AppInfo1} = maybe_fetch(AppInfo, Profile, true, Seen, State), + handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State, Locks); _StillLocked -> - {[AppInfo|SrcDeps], PkgDeps, SrcApps, State, Locks} + handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State, Locks) end. handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> @@ -414,41 +409,27 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) -> false -> case rebar_app_discover:find_app(AppDir, all) of false -> - case already_in_default(AppInfo, State) of - false -> - case fetch_app(AppInfo, AppDir, State) of - true -> - maybe_symlink_default(State, Profile, AppDir, AppInfo), - {true, update_app_info(AppDir, AppInfo)}; - Other -> - {Other, AppInfo} - end; - {true, FoundApp} -> - %% Preserve the state we created with overrides - AppState = rebar_app_info:state(AppInfo), - FoundApp1 = rebar_app_info:state(FoundApp, AppState), - symlink_dep(rebar_app_info:dir(FoundApp1), AppDir), - {true, FoundApp1} + case fetch_app(AppInfo, AppDir, State) of + true -> + maybe_symlink_default(State, Profile, AppDir, AppInfo), + {true, update_app_info(AppDir, AppInfo)}; + Other -> + {Other, AppInfo} end; {true, AppInfo1} -> %% Preserve the state we created with overrides AppState = rebar_app_info:state(AppInfo), AppInfo2 = rebar_app_info:state(AppInfo1, AppState), - maybe_symlink_default(State, Profile, AppDir, AppInfo2), case sets:is_element(rebar_app_info:name(AppInfo), Seen) of true -> {false, AppInfo2}; false -> + maybe_symlink_default(State, Profile, AppDir, AppInfo2), {maybe_upgrade(AppInfo, AppDir, Upgrade, State), AppInfo2} end end end. -already_in_default(AppInfo, State) -> - Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)), - DefaultAppDir = filename:join([rebar_state:get(State, base_dir, []), "default", "lib", Name]), - rebar_app_discover:find_app(DefaultAppDir, all). - needs_symlinking(State, Profile) -> case {rebar_state:current_profiles(State), Profile} of {[default], default} -> @@ -504,6 +485,7 @@ parse_deps(DepsDir, Deps, State, Locks, Level) -> end, {[], []}, Deps). parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) -> + %% Versioned Package dependency CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of {ok, _App} -> @@ -514,6 +496,7 @@ parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]} end; parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) -> + %% Unversioned package dependency {PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State), CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl index 5ccd054..02c185f 100644 --- a/src/rebar_prv_plugins_upgrade.erl +++ b/src/rebar_prv_plugins_upgrade.erl @@ -62,10 +62,7 @@ upgrade(Plugin, State) -> ?PRV_ERROR({not_found, Plugin}); {ok, P, Profile} -> State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR), - {ok, Apps, _State2} = rebar_prv_install_deps:handle_deps(Profile - ,State1 - ,[P] - ,true), + {Apps, _State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State1, [P], true), {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl index dc58047..2cf9b23 100644 --- a/src/rebar_prv_release.erl +++ b/src/rebar_prv_release.erl @@ -32,34 +32,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["release" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - try - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State} - catch - throw:T -> - {error, {rlx_prv_release, T}} - end. + rebar_relx:do(rlx_prv_release, "release", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_relup.erl b/src/rebar_prv_relup.erl index aed12a6..a4cd8ae 100644 --- a/src/rebar_prv_relup.erl +++ b/src/rebar_prv_relup.erl @@ -33,34 +33,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["relup" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - try - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State} - catch - throw:T -> - {error, {rlx_prv_release, T}} - end. + rebar_relx:do(rlx_prv_release, "relup", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 84ad723..3c6369a 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -90,9 +90,10 @@ shell(State) -> %% their application masters never gets the new group leader (held in %% their internal state) maybe_boot_apps(State), - rebar_agent:start_link(State), - %% this call never returns (until user quits shell) - timer:sleep(infinity). + simulate_proc_lib(), + true = register(rebar_agent, self()), + {ok, GenState} = rebar_agent:init(State), + gen_server:enter_loop(rebar_agent, [], GenState, {local, rebar_agent}, hibernate). info() -> "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n". @@ -145,6 +146,11 @@ maybe_boot_apps(State) -> boot_apps(Apps) end. +simulate_proc_lib() -> + FakeParent = spawn_link(fun() -> timer:sleep(infinity) end), + put('$ancestors', [FakeParent]), + put('$initial_call', {rebar_agent, init, 1}). + setup_name(State) -> {Opts, _} = rebar_state:command_parsed_args(State), case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of diff --git a/src/rebar_prv_tar.erl b/src/rebar_prv_tar.erl index 17d7b0b..b3a12c0 100644 --- a/src/rebar_prv_tar.erl +++ b/src/rebar_prv_tar.erl @@ -32,29 +32,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["tar" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State}. + rebar_relx:do(rlx_prv_release, "tar", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 6838bab..64fe65e 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -43,7 +43,8 @@ do(State) -> Url = rebar_state:get(State, rebar_packages_cdn, "https://s3.amazonaws.com/s3.hex.pm/registry.ets.gz"), {ok, _RequestId} = httpc:request(get, {Url, []}, - [], [{stream, TmpFile}, {sync, true}]), + [], [{stream, TmpFile}, {sync, true}], + rebar), {ok, Data} = file:read_file(TmpFile), Unzipped = zlib:gunzip(Data), ok = file:write_file(HexFile, Unzipped), diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl new file mode 100644 index 0000000..a3adedd --- /dev/null +++ b/src/rebar_relx.erl @@ -0,0 +1,69 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et + +-module(rebar_relx). + +-export([do/4, + format_error/1]). + +-include("rebar.hrl"). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec do(atom(), string(), atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(Module, Command, Provider, State) -> + Options = rebar_state:command_args(State), + DepsDir = rebar_dir:deps_dir(State), + ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), + LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, + [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), + OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), + AllOptions = string:join([Command | Options], " "), + Cwd = rebar_state:dir(State), + Providers = rebar_state:providers(State), + rebar_hooks:run_all_hooks(Cwd, pre, Provider, Providers, State), + try + case rebar_state:get(State, relx, []) of + [] -> + relx:main([{lib_dirs, LibDirs} + ,{output_dir, OutputDir} + ,{caller, api}], AllOptions); + Config -> + Config1 = update_config(Config), + relx:main([{lib_dirs, LibDirs} + ,{config, Config1} + ,{output_dir, OutputDir} + ,{caller, api}], AllOptions) + end, + rebar_hooks:run_all_hooks(Cwd, post, Provider, Providers, State), + {ok, State} + catch + throw:T -> + {error, {Module, T}} + end. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). + +%% To handle profiles rebar3 expects the provider to use the first entry +%% in a configuration key-value list as the value of a key if dups exist. +%% This does not work with relx. Some config options must not lose their +%% order (release which has an extends option is one). So here we pull out +%% options that are special so we can reverse the rest so what we expect +%% from a rebar3 profile is what we get on the relx side. +-define(SPECIAL_KEYS, [release, vm_args, sys_config, overlay_vars, lib_dirs]). + +update_config(Config) -> + {Special, Other} = + lists:foldl(fun(Tuple, {SpecialAcc, OtherAcc}) when is_tuple(Tuple) -> + case lists:member(element(1, Tuple), ?SPECIAL_KEYS) of + true -> + {[Tuple | SpecialAcc], OtherAcc}; + false -> + {SpecialAcc, [Tuple | OtherAcc]} + end + end, {[], []}, Config), + lists:reverse(Special) ++ Other. diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index cc59ed0..ebdf0fe 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -56,7 +56,8 @@ wordsize/0, tup_umerge/2, tup_sort/1, - line_count/1]). + line_count/1, + set_httpc_options/0]). %% for internal use only -export([otp_release/0]). @@ -127,11 +128,13 @@ sh_send(Command0, String, Options0) -> PortSettings = proplists:get_all_values(port_settings, Options) ++ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], Port = open_port({spawn, Command}, PortSettings), - - %% allow us to send some data to the shell command's STDIN - %% Erlang doesn't let us get any reply after sending an EOF, though... - Port ! {self(), {command, String}}, - port_close(Port). + try + %% allow us to send some data to the shell command's STDIN + %% Erlang doesn't let us get any reply after sending an EOF, though... + Port ! {self(), {command, String}} + after + port_close(Port) + end. %% %% Options = [Option] -- defaults to [use_stdout, abort_on_error] @@ -154,15 +157,19 @@ sh(Command0, Options0) -> Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options, []))), PortSettings = proplists:get_all_values(port_settings, Options) ++ - [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], + [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof], ?DEBUG("Port Cmd: ~s\nPort Opts: ~p\n", [Command, PortSettings]), Port = open_port({spawn, Command}, PortSettings), - case sh_loop(Port, OutputHandler, []) of - {ok, _Output} = Ok -> - Ok; - {error, {_Rc, _Output}=Err} -> - ErrorHandler(Command, Err) + try + case sh_loop(Port, OutputHandler, []) of + {ok, _Output} = Ok -> + Ok; + {error, {_Rc, _Output}=Err} -> + ErrorHandler(Command, Err) + end + after + port_close(Port) end. find_files(Dir, Regex) -> @@ -435,10 +442,14 @@ sh_loop(Port, Fun, Acc) -> sh_loop(Port, Fun, Fun(Line ++ "\n", Acc)); {Port, {data, {noeol, Line}}} -> sh_loop(Port, Fun, Fun(Line, Acc)); - {Port, {exit_status, 0}} -> - {ok, lists:flatten(lists:reverse(Acc))}; - {Port, {exit_status, Rc}} -> - {error, {Rc, lists:flatten(lists:reverse(Acc))}} + {Port, eof} -> + Data = lists:flatten(lists:reverse(Acc)), + receive + {Port, {exit_status, 0}} -> + {ok, Data}; + {Port, {exit_status, Rc}} -> + {error, {Rc, Data}} + end end. beam_to_mod(Dir, Filename) -> @@ -657,3 +668,19 @@ maybe_ends_in_comma(H) -> "," ++ Flag -> lists:reverse(Flag); _ -> false end. + +get_http_vars(Scheme) -> + GlobalConfigFile = rebar_dir:global_config(), + Config = rebar_config:consult_file(GlobalConfigFile), + proplists:get_value(Scheme, Config, []). + +set_httpc_options() -> + set_httpc_options(https_proxy, get_http_vars(https_proxy)), + set_httpc_options(proxy, get_http_vars(http_proxy)). + +set_httpc_options(_, []) -> + ok; + +set_httpc_options(Scheme, Proxy) -> + {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). |