summaryrefslogtreecommitdiff
path: root/src/rebar_deps.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_deps.erl')
-rw-r--r--src/rebar_deps.erl182
1 files changed, 114 insertions, 68 deletions
diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl
index 0852b01..69f5fc1 100644
--- a/src/rebar_deps.erl
+++ b/src/rebar_deps.erl
@@ -1,4 +1,4 @@
-%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
@@ -33,6 +33,7 @@
compile/2,
'check-deps'/2,
'get-deps'/2,
+ 'update-deps'/2,
'delete-deps'/2]).
@@ -46,15 +47,15 @@
%% ===================================================================
preprocess(Config, _) ->
- %% Side effect to set deps_dir globally for all dependencies from top level down.
- %% Means the root deps_dir is honoured or the default used globally
- %% since it will be set on the first time through here
+ %% Side effect to set deps_dir globally for all dependencies from
+ %% top level down. Means the root deps_dir is honoured or the default
+ %% used globally since it will be set on the first time through here
set_global_deps_dir(Config, rebar_config:get_global(deps_dir, [])),
%% Get the list of deps for the current working directory and identify those
%% deps that are available/present.
Deps = rebar_config:get_local(Config, deps, []),
- {AvailableDeps, MissingDeps} = find_deps(Deps),
+ {AvailableDeps, MissingDeps} = find_deps(find, Deps),
?DEBUG("Available deps: ~p\n", [AvailableDeps]),
?DEBUG("Missing deps : ~p\n", [MissingDeps]),
@@ -68,8 +69,8 @@ preprocess(Config, _) ->
case rebar_config:get_global(skip_deps, false) of
"true" ->
lists:foreach(fun (#dep{dir = Dir}) ->
- rebar_core:skip_dir(Dir)
- end, AvailableDeps);
+ rebar_core:skip_dir(Dir)
+ end, AvailableDeps);
_ ->
ok
end,
@@ -93,43 +94,49 @@ compile(Config, AppFile) ->
'check-deps'(Config, _) ->
%% Get the list of immediate (i.e. non-transitive) deps that are missing
Deps = rebar_config:get_local(Config, deps, []),
- case find_deps(Deps) of
+ case find_deps(find, Deps) of
{_, []} ->
%% No missing deps
ok;
{_, MissingDeps} ->
lists:foreach(fun (#dep{app=App, vsn_regex=Vsn, source=Src}) ->
- ?CONSOLE("Dependency not available: ~p-~s (~p)\n",
- [App, Vsn, Src])
- end, MissingDeps),
+ ?CONSOLE("Dependency not available: "
+ "~p-~s (~p)\n", [App, Vsn, Src])
+ end, MissingDeps),
?FAIL
end.
'get-deps'(Config, _) ->
%% Determine what deps are available and missing
Deps = rebar_config:get_local(Config, deps, []),
- {AvailableDeps, MissingDeps} = find_deps(Deps),
+ {_AvailableDeps, MissingDeps} = find_deps(find, Deps),
%% For each missing dep with a specified source, try to pull it.
- PulledDeps0 = [use_source(D) || D <- MissingDeps, D#dep.source /= undefined],
-
- %% For each available dep try to update the source to the specified
- %% version.
- PulledDeps1 = [update_source(D) || D <- AvailableDeps,
- D#dep.source /= undefined],
+ PulledDeps = [use_source(D) || D <- MissingDeps, D#dep.source /= undefined],
%% Add each pulled dep to our list of dirs for post-processing. This yields
%% the necessary transitivity of the deps
- erlang:put(?MODULE, [D#dep.dir || D <- PulledDeps0 ++ PulledDeps1]),
+ erlang:put(?MODULE, [D#dep.dir || D <- PulledDeps]),
+ ok.
+
+'update-deps'(Config, _) ->
+ %% Determine what deps are available and missing
+ Deps = rebar_config:get_local(Config, deps, []),
+ UpdatedDeps = [update_source(D) || D <- find_deps(read, Deps),
+ D#dep.source /= undefined],
+ %% Add each updated dep to our list of dirs for post-processing. This yields
+ %% the necessary transitivity of the deps
+ erlang:put(?MODULE, [D#dep.dir || D <- UpdatedDeps]),
ok.
'delete-deps'(Config, _) ->
%% Delete all the available deps in our deps/ directory, if any
DepsDir = get_deps_dir(),
Deps = rebar_config:get_local(Config, deps, []),
- {AvailableDeps, _} = find_deps(Deps),
- _ = [delete_dep(D) || D <- AvailableDeps,
- lists:prefix(DepsDir, D#dep.dir) == true],
+ {AvailableDeps, _} = find_deps(find, Deps),
+ _ = [delete_dep(D)
+ || D <- AvailableDeps,
+ lists:prefix(DepsDir, D#dep.dir)],
ok.
@@ -141,7 +148,8 @@ compile(Config, AppFile) ->
%% need all deps in same dir and should be the one set by the root rebar.config
%% Sets a default if root config has no deps_dir set
set_global_deps_dir(Config, []) ->
- rebar_config:set_global(deps_dir, rebar_config:get_local(Config, deps_dir, "deps"));
+ rebar_config:set_global(deps_dir,
+ rebar_config:get_local(Config, deps_dir, "deps"));
set_global_deps_dir(_Config, _DepsDir) ->
ok.
@@ -163,35 +171,48 @@ update_deps_code_path([Dep | Rest]) ->
end,
update_deps_code_path(Rest).
-find_deps(Deps) ->
- find_deps(Deps, {[], []}).
-find_deps([], {Avail, Missing}) ->
+find_deps(find=Mode, Deps) ->
+ find_deps(Mode, Deps, {[], []});
+find_deps(read=Mode, Deps) ->
+ find_deps(Mode, Deps, []).
+
+find_deps(find, [], {Avail, Missing}) ->
{lists:reverse(Avail), lists:reverse(Missing)};
-find_deps([App | Rest], Acc) when is_atom(App) ->
- find_deps([{App, ".*", undefined} | Rest], Acc);
-find_deps([{App, VsnRegex} | Rest], Acc) when is_atom(App) ->
- find_deps([{App, VsnRegex, undefined} | Rest], Acc);
-find_deps([{App, VsnRegex, Source} | Rest], {Avail, Missing}) ->
+find_deps(read, [], Deps) ->
+ lists:reverse(Deps);
+find_deps(Mode, [App | Rest], Acc) when is_atom(App) ->
+ find_deps(Mode, [{App, ".*", undefined} | Rest], Acc);
+find_deps(Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) ->
+ find_deps(Mode, [{App, VsnRegex, undefined} | Rest], Acc);
+find_deps(Mode, [{App, VsnRegex, Source} | Rest], Acc) ->
Dep = #dep { app = App,
vsn_regex = VsnRegex,
source = Source },
case is_app_available(App, VsnRegex) of
{true, AppDir} ->
- find_deps(Rest, {[Dep#dep { dir = AppDir } | Avail], Missing});
+ find_deps(Mode, Rest, acc_deps(Mode, avail, Dep, AppDir, Acc));
{false, _} ->
AppDir = filename:join(get_deps_dir(), Dep#dep.app),
case is_app_available(App, VsnRegex, AppDir) of
{true, AppDir} ->
- find_deps(Rest, {[Dep#dep { dir = AppDir } | Avail], Missing});
+ find_deps(Mode, Rest,
+ acc_deps(Mode, avail, Dep, AppDir, Acc));
{false, _} ->
- find_deps(Rest, {Avail, [Dep#dep { dir = AppDir } | Missing]})
+ find_deps(Mode, Rest,
+ acc_deps(Mode, missing, Dep, AppDir, Acc))
end
end;
-find_deps([Other | _Rest], _Acc) ->
+find_deps(_Mode, [Other | _Rest], _Acc) ->
?ABORT("Invalid dependency specification ~p in ~s\n",
[Other, rebar_utils:get_cwd()]).
+acc_deps(find, avail, Dep, AppDir, {Avail, Missing}) ->
+ {[Dep#dep { dir = AppDir } | Avail], Missing};
+acc_deps(find, missing, Dep, AppDir, {Avail, Missing}) ->
+ {Avail, [Dep#dep { dir = AppDir } | Missing]};
+acc_deps(read, _, Dep, AppDir, Acc) ->
+ [Dep#dep { dir = AppDir } | Acc].
delete_dep(D) ->
case filelib:is_dir(D#dep.dir) of
@@ -228,28 +249,34 @@ is_app_available(App, VsnRegex, Path) ->
nomatch ->
?WARN("~s has version ~p; requested regex was ~s\n",
[AppFile, Vsn, VsnRegex]),
- {false, version_mismatch}
+ {false, {version_mismatch,
+ {AppFile,
+ {expected, VsnRegex}, {has, Vsn}}}}
end;
OtherApp ->
- ?WARN("~s has application id ~p; expected ~p\n", [AppFile, OtherApp, App]),
- {false, name_mismatch}
+ ?WARN("~s has application id ~p; expected ~p\n",
+ [AppFile, OtherApp, App]),
+ {false, {name_mismatch,
+ {AppFile, {expected, App}, {has, OtherApp}}}}
end;
false ->
- ?WARN("Expected ~s to be an app dir (containing ebin/*.app), but no .app found.\n",
- [Path]),
- {false, missing_app_file}
+ ?WARN("Expected ~s to be an app dir (containing ebin/*.app), "
+ "but no .app found.\n", [Path]),
+ {false, {missing_app_file, Path}}
end.
use_source(Dep) ->
use_source(Dep, 3).
use_source(Dep, 0) ->
- ?ABORT("Failed to acquire source from ~p after 3 tries.\n", [Dep#dep.source]);
+ ?ABORT("Failed to acquire source from ~p after 3 tries.\n",
+ [Dep#dep.source]);
use_source(Dep, Count) ->
case filelib:is_dir(Dep#dep.dir) of
true ->
- %% Already downloaded -- verify the versioning matches up with our regex
- case is_app_available(Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir) of
+ %% Already downloaded -- verify the versioning matches the regex
+ case is_app_available(Dep#dep.app,
+ Dep#dep.vsn_regex, Dep#dep.dir) of
{true, _} ->
Dir = filename:join(Dep#dep.dir, "ebin"),
ok = filelib:ensure_dir(filename:join(Dir, "dummy")),
@@ -261,7 +288,7 @@ use_source(Dep, Count) ->
%% The app that was downloaded doesn't match up (or had
%% errors or something). For the time being, abort.
?ABORT("Dependency dir ~s failed application validation "
- "with reason ~p.\n", [Dep#dep.dir, Reason])
+ "with reason:~n~p.\n", [Dep#dep.dir, Reason])
end;
false ->
?CONSOLE("Pulling ~p from ~p\n", [Dep#dep.app, Dep#dep.source]),
@@ -273,28 +300,35 @@ use_source(Dep, Count) ->
download_source(AppDir, {hg, Url, Rev}) ->
ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]), [], filename:dirname(AppDir)),
- rebar_utils:sh(?FMT("hg update ~s", [Rev]), [], AppDir);
+ rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("hg update ~s", [Rev]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url}) ->
+ download_source(AppDir, {git, Url, "HEAD"});
+download_source(AppDir, {git, Url, ""}) ->
+ download_source(AppDir, {git, Url, "HEAD"});
download_source(AppDir, {git, Url, {branch, Branch}}) ->
ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]), [], filename:dirname(AppDir)),
- rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [], AppDir);
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [{cd, AppDir}]);
download_source(AppDir, {git, Url, {tag, Tag}}) ->
ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]), [], filename:dirname(AppDir)),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [], AppDir);
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [{cd, AppDir}]);
download_source(AppDir, {git, Url, Rev}) ->
download_source(AppDir, {git, Url, {branch, Rev}});
download_source(AppDir, {bzr, Url, Rev}) ->
ok = filelib:ensure_dir(AppDir),
rebar_utils:sh(?FMT("bzr branch -r ~s ~s ~s",
- [Rev, Url, filename:basename(AppDir)]), [],
- filename:dirname(AppDir));
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]);
download_source(AppDir, {svn, Url, Rev}) ->
ok = filelib:ensure_dir(AppDir),
rebar_utils:sh(?FMT("svn checkout -r ~s ~s ~s",
- [Rev, Url, filename:basename(AppDir)]), [],
- filename:dirname(AppDir)).
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]).
update_source(Dep) ->
%% It's possible when updating a source, that a given dep does not have a
@@ -309,24 +343,31 @@ update_source(Dep) ->
update_source(AppDir, Dep#dep.source),
Dep;
false ->
- ?WARN("Skipping update for ~p: no VCS directory available!\n", [Dep]),
+ ?WARN("Skipping update for ~p: "
+ "no VCS directory available!\n", [Dep]),
Dep
end.
+update_source(AppDir, {git, Url}) ->
+ update_source(AppDir, {git, Url, "HEAD"});
+update_source(AppDir, {git, Url, ""}) ->
+ update_source(AppDir, {git, Url, "HEAD"});
update_source(AppDir, {git, _Url, {branch, Branch}}) ->
- rebar_utils:sh(?FMT("git fetch origin", []), [], AppDir),
- rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [], AppDir);
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), ShOpts);
update_source(AppDir, {git, _Url, {tag, Tag}}) ->
- rebar_utils:sh(?FMT("git fetch --tags origin", []), [], AppDir),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [], AppDir);
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch --tags origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts);
update_source(AppDir, {git, Url, Refspec}) ->
update_source(AppDir, {git, Url, {branch, Refspec}});
update_source(AppDir, {svn, _Url, Rev}) ->
- rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [], AppDir);
+ rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]);
update_source(AppDir, {hg, _Url, Rev}) ->
- rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [], AppDir);
+ rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]);
update_source(AppDir, {bzr, _Url, Rev}) ->
- rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [], AppDir).
+ rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]).
@@ -347,7 +388,8 @@ source_engine_avail({Name, _, _}=Source)
scm_client_vsn(false, _VsnArg, _VsnRegex) ->
false;
scm_client_vsn(Path, VsnArg, VsnRegex) ->
- Info = os:cmd("LANG=C " ++ Path ++ VsnArg),
+ {ok, Info} = rebar_utils:sh(Path ++ VsnArg, [{env, [{"LANG", "C"}]},
+ {use_stdout, false}]),
case re:run(Info, VsnRegex, [{capture, all_but_first, list}]) of
{match, Match} ->
list_to_tuple([list_to_integer(S) || S <- Match]);
@@ -361,13 +403,17 @@ required_scm_client_vsn(bzr) -> {2, 0};
required_scm_client_vsn(svn) -> {1, 6}.
scm_client_vsn(hg) ->
- scm_client_vsn(rebar_utils:find_executable("hg"), " --version", "version (\\d+).(\\d+)");
+ scm_client_vsn(rebar_utils:find_executable("hg"), " --version",
+ "version (\\d+).(\\d+)");
scm_client_vsn(git) ->
- scm_client_vsn(rebar_utils:find_executable("git"), " --version", "git version (\\d+).(\\d+)");
+ scm_client_vsn(rebar_utils:find_executable("git"), " --version",
+ "git version (\\d+).(\\d+)");
scm_client_vsn(bzr) ->
- scm_client_vsn(rebar_utils:find_executable("bzr"), " --version", "Bazaar \\(bzr\\) (\\d+).(\\d+)");
+ scm_client_vsn(rebar_utils:find_executable("bzr"), " --version",
+ "Bazaar \\(bzr\\) (\\d+).(\\d+)");
scm_client_vsn(svn) ->
- scm_client_vsn(rebar_utils:find_executable("svn"), " --version", "svn, version (\\d+).(\\d+)").
+ scm_client_vsn(rebar_utils:find_executable("svn"), " --version",
+ "svn, version (\\d+).(\\d+)").
has_vcs_dir(git, Dir) ->
filelib:is_dir(filename:join(Dir, ".git"));