mirror of
https://github.com/correl/rebar.git
synced 2024-11-27 19:19:53 +00:00
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.
This commit is contained in:
parent
620c4b01c6
commit
cb4599f828
2 changed files with 89 additions and 10 deletions
|
@ -172,8 +172,9 @@ skip_or_process_dir1(AppFile, ModuleSet, Config, CurrentCodePath,
|
||||||
CurrentCodePath, ModuleSet)
|
CurrentCodePath, ModuleSet)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_dir1(Dir, Command, DirSet, Config0, CurrentCodePath,
|
process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
|
||||||
{DirModules, ModuleSetFile}) ->
|
{DirModules, ModuleSetFile}) ->
|
||||||
|
Config0 = rebar_config:set(Config, command, Command),
|
||||||
%% Get the list of modules for "any dir". This is a catch-all list
|
%% Get the list of modules for "any dir". This is a catch-all list
|
||||||
%% of modules that are processed in addition to modules associated
|
%% of modules that are processed in addition to modules associated
|
||||||
%% with this directory type. These any_dir modules are processed
|
%% with this directory type. These any_dir modules are processed
|
||||||
|
|
|
@ -97,8 +97,22 @@ preprocess(Config, _) ->
|
||||||
%% deps-related can be executed on their directories.
|
%% deps-related can be executed on their directories.
|
||||||
NonRawAvailableDeps = [D || D <- AvailableDeps, not D#dep.is_raw],
|
NonRawAvailableDeps = [D || D <- AvailableDeps, not D#dep.is_raw],
|
||||||
|
|
||||||
|
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
|
%% Return all the available dep directories for process
|
||||||
{ok, NewConfig, dep_dirs(NonRawAvailableDeps)}.
|
{ok, NewConfig, dep_dirs(NonRawAvailableDeps)}
|
||||||
|
end.
|
||||||
|
|
||||||
postprocess(Config, _) ->
|
postprocess(Config, _) ->
|
||||||
case rebar_config:get_xconf(Config, ?MODULE, undefined) of
|
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))}.
|
{ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}.
|
||||||
|
|
||||||
'update-deps'(Config, _) ->
|
'update-deps'(Config, _) ->
|
||||||
%% Determine what deps are required
|
{Config2, UpdatedDeps} = update_deps_int(rebar_config:set(Config, depowner, dict:new()), []),
|
||||||
RawDeps = rebar_config:get_local(Config, deps, []),
|
DepOwners = rebar_config:get(Config2, depowner, dict:new()),
|
||||||
{Config1, Deps} = find_deps(Config, read, RawDeps),
|
|
||||||
|
|
||||||
%% Update each dep
|
%% check for conflicting deps
|
||||||
UpdatedDeps = [update_source(Config1, D)
|
[?ERROR("Conflicting dependencies for ~p: ~p~n", [K,
|
||||||
|| D <- Deps, D#dep.source =/= undefined],
|
[{"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
|
%% Add each updated dep to our list of dirs for post-processing. This yields
|
||||||
%% the necessary transitivity of the deps
|
%% the necessary transitivity of the deps
|
||||||
{ok, save_dep_dirs(Config1, UpdatedDeps)}.
|
{ok, save_dep_dirs(Config, UpdatedDeps)}.
|
||||||
|
|
||||||
'delete-deps'(Config, _) ->
|
'delete-deps'(Config, _) ->
|
||||||
%% Delete all the available deps in our deps/ directory, if any
|
%% 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("fossil pull", [{cd, AppDir}]),
|
||||||
rebar_utils:sh(?FMT("fossil update ~s", [Version]), []).
|
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
|
%% Source helper functions
|
||||||
|
|
Loading…
Reference in a new issue