summaryrefslogtreecommitdiff
path: root/src/rebar_deps.erl
diff options
context:
space:
mode:
authorAndrew Thompson <andrew@hijacked.us>2013-09-19 17:36:02 -0400
committerAndrew Thompson <andrew@hijacked.us>2013-09-20 01:24:07 -0400
commitcb4599f828f0ec13a4ea18bcb1b0de4938d8dc7e (patch)
treea1615f3ab8f6a81fc6df2ccff7ab569025ef6c08 /src/rebar_deps.erl
parent620c4b01c6e59d47e92ea069f8510b8cb482ebae (diff)
Make update-deps traverse deps breadth-first, top-down
This ensures that deps of deps are updated AFTER the dep listing them is, so that a complicated project with many layers of deps will be updated correctly. Any new deps encountered along the way are also cloned, and THEIR deps are also evaluated. Also added was conflict detection, if a dep has differing versions or source information, inherited from different places, that will be logged at the end of update-deps, along with the origin of each conflicting dep.
Diffstat (limited to 'src/rebar_deps.erl')
-rw-r--r--src/rebar_deps.erl98
1 files changed, 88 insertions, 10 deletions
diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl
index be6283d..82c5061 100644
--- a/src/rebar_deps.erl
+++ b/src/rebar_deps.erl
@@ -97,8 +97,22 @@ preprocess(Config, _) ->
%% deps-related can be executed on their directories.
NonRawAvailableDeps = [D || D <- AvailableDeps, not D#dep.is_raw],
- %% Return all the available dep directories for process
- {ok, NewConfig, dep_dirs(NonRawAvailableDeps)}.
+ case rebar_config:get(Config, command, undefined) of
+ 'update-deps' ->
+ %% Skip ALL of the dep folders, we do this because we don't want
+ %% any other calls to preprocess() for update-deps beyond the
+ %% toplevel directory. They aren't actually harmful, but they slow
+ %% things down unnecessarily.
+ NewConfig2 = lists:foldl(fun(D, Acc) ->
+ rebar_config:set_skip_dir(Acc, D#dep.dir)
+ end, NewConfig, collect_deps(rebar_utils:get_cwd(),NewConfig)),
+ %% Return the empty list, as we don't want anything processed before
+ %% us.
+ {ok, NewConfig2, []};
+ _ ->
+ %% Return all the available dep directories for process
+ {ok, NewConfig, dep_dirs(NonRawAvailableDeps)}
+ end.
postprocess(Config, _) ->
case rebar_config:get_xconf(Config, ?MODULE, undefined) of
@@ -169,17 +183,23 @@ do_check_deps(Config) ->
{ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}.
'update-deps'(Config, _) ->
- %% Determine what deps are required
- RawDeps = rebar_config:get_local(Config, deps, []),
- {Config1, Deps} = find_deps(Config, read, RawDeps),
-
- %% Update each dep
- UpdatedDeps = [update_source(Config1, D)
- || D <- Deps, D#dep.source =/= undefined],
+ {Config2, UpdatedDeps} = update_deps_int(rebar_config:set(Config, depowner, dict:new()), []),
+ DepOwners = rebar_config:get(Config2, depowner, dict:new()),
+
+ %% check for conflicting deps
+ [?ERROR("Conflicting dependencies for ~p: ~p~n", [K,
+ [{"From: " ++ string:join(dict:fetch(D,
+ DepOwners),
+ ", "),
+ {D#dep.vsn_regex,
+ D#dep.source}} || D <- V]]) ||
+ {K, V} <- dict:to_list(lists:foldl(fun(Dep, Acc) ->
+ dict:append(Dep#dep.app, Dep, Acc)
+ end, dict:new(), UpdatedDeps)), length(V) > 1],
%% Add each updated dep to our list of dirs for post-processing. This yields
%% the necessary transitivity of the deps
- {ok, save_dep_dirs(Config1, UpdatedDeps)}.
+ {ok, save_dep_dirs(Config, UpdatedDeps)}.
'delete-deps'(Config, _) ->
%% Delete all the available deps in our deps/ directory, if any
@@ -566,6 +586,64 @@ update_source1(AppDir, {fossil, _Url, Version}) ->
rebar_utils:sh("fossil pull", [{cd, AppDir}]),
rebar_utils:sh(?FMT("fossil update ~s", [Version]), []).
+%% Recursively update deps, this is not done via rebar's usual dep traversal as
+%% that is the wrong order (tips are updated before branches). Instead we do a
+%% traverse the deps at each level completely before traversing *their* deps.
+%% This allows updates to actually propogate down the tree, rather than fail to
+%% flow up the tree, which was the previous behaviour.
+update_deps_int(Config0, UDD) ->
+ %% Determine what deps are required
+ ConfDir = filename:basename(rebar_utils:get_cwd()),
+ RawDeps = rebar_config:get_local(Config0, deps, []),
+ {Config1, Deps} = find_deps(Config0, read, RawDeps),
+
+ %% Update each dep
+ UpdatedDeps = [update_source(Config1, D)
+ || D <- Deps, D#dep.source =/= undefined, not lists:member(D, UDD)],
+
+ lists:foldl(fun(Dep, {Config, Updated}) ->
+ {true, AppDir} = get_deps_dir(Config, Dep#dep.app),
+ Config2 = case has_vcs_dir(element(1, Dep#dep.source), AppDir) of
+ false ->
+ %% If the dep did not exist (maybe it was added)
+ %% clone it. We'll traverse ITS deps below. and
+ %% clone them if needed.
+ {C1, _D1} = use_source(Config, Dep),
+ C1;
+ true ->
+ Config
+ end,
+ ok = file:set_cwd(AppDir),
+ Config3 = rebar_config:new(Config2),
+ %% track where a dep comes from...
+ Config4 = rebar_config:set(Config3, depowner,
+ dict:append(Dep, ConfDir,
+ rebar_config:get(Config3,
+ depowner,
+ dict:new()))),
+
+ {Config5, Res} = update_deps_int(Config4, Updated),
+ {Config5, lists:umerge(lists:sort(Res),
+ lists:sort(Updated))}
+ end, {Config1, lists:umerge(lists:sort(UpdatedDeps),
+ lists:sort(UDD))}, UpdatedDeps).
+
+%% Recursively walk the deps and build a list of them.
+collect_deps(Dir, C) ->
+ case file:set_cwd(Dir) of
+ ok ->
+ Config = rebar_config:new(C),
+ RawDeps = rebar_config:get_local(Config, deps, []),
+ {Config1, Deps} = find_deps(Config, read, RawDeps),
+
+ lists:flatten(Deps ++ [begin
+ {true, AppDir} = get_deps_dir(Config1, Dep#dep.app),
+ collect_deps(AppDir, C)
+ end || Dep <- Deps]);
+ _ ->
+ []
+ end.
+
%% ===================================================================
%% Source helper functions