Browse Source

Merge pull request #1596 from ferd/local-apps-override-deps

Allow top-level apps to take precedence over deps
pull/1602/head
Fred Hebert 7 years ago
committed by GitHub
parent
commit
2939110eb8
3 changed files with 58 additions and 8 deletions
  1. +9
    -1
      src/rebar_app_utils.erl
  2. +20
    -6
      src/rebar_prv_install_deps.erl
  3. +29
    -1
      test/rebar_deps_SUITE.erl

+ 9
- 1
src/rebar_app_utils.erl View File

@ -34,6 +34,7 @@
validate_application_info/2,
parse_deps/5,
parse_deps/6,
expand_deps_sources/2,
dep_to_app/7,
format_error/1]).
@ -227,7 +228,7 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) ->
not_found ->
rebar_app_info:new(Parent, Name, Vsn, Dir, [])
end,
update_source(AppInfo0, Source, State)
rebar_app_info:source(AppInfo0, Source)
end,
C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
AppInfo1 = rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), C),
@ -238,6 +239,13 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) ->
AppInfo5 = rebar_app_info:profiles(AppInfo4, [default]),
rebar_app_info:is_lock(AppInfo5, IsLock).
%% @doc Takes a given application app_info record along with the project.
%% If the app is a package, resolve and expand the package definition.
-spec expand_deps_sources(rebar_app_info:t(), rebar_state:t()) ->
rebar_app_info:t().
expand_deps_sources(Dep, State) ->
update_source(Dep, rebar_app_info:source(Dep), State).
%% @doc sets the source for a given dependency or app along with metadata
%% around version if required.
-spec update_source(rebar_app_info:t(), Source, rebar_state:t()) ->

+ 20
- 6
src/rebar_prv_install_deps.erl View File

@ -140,7 +140,9 @@ handle_deps_as_profile(Profile, State, Deps, Upgrade) ->
DepsDir = profile_dep_dir(State, Profile),
Deps1 = rebar_app_utils:parse_deps(DepsDir, Deps, State, Locks, Level),
ProfileLevelDeps = [{Profile, Deps1, Level}],
handle_profile_level(ProfileLevelDeps, [], sets:new(), Upgrade, Locks, State).
RootSeen = sets:from_list([rebar_app_info:name(AppInfo)
|| AppInfo <- rebar_state:project_apps(State)]),
handle_profile_level(ProfileLevelDeps, [], RootSeen, RootSeen, Upgrade, Locks, State).
%% ===================================================================
%% Internal functions
@ -153,7 +155,9 @@ deps_per_profile(Profiles, Upgrade, State) ->
Deps = lists:foldl(fun(Profile, DepAcc) ->
[parsed_profile_deps(State, Profile, Level) | DepAcc]
end, [], Profiles),
handle_profile_level(Deps, [], sets:new(), Upgrade, Locks, State).
RootSeen = sets:from_list([rebar_app_info:name(AppInfo)
|| AppInfo <- rebar_state:project_apps(State)]),
handle_profile_level(Deps, [], RootSeen, RootSeen, Upgrade, Locks, State).
parsed_profile_deps(State, Profile, Level) ->
ParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, []),
@ -162,17 +166,27 @@ parsed_profile_deps(State, Profile, Level) ->
%% 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([], Apps, _Seen, _Upgrade, _Locks, State) ->
%%
%% There are two 'seen' sets: one for the top-level apps (`RootSeen') and
%% one for all dependencies (`Seen'). The former is used to know when
%% to skip the resolving of dependencies altogether (since they're already
%% top-level apps), while the latter is used to prevent reprocessing
%% deps more than one.
handle_profile_level([], Apps, _RootSeen, _Seen, _Upgrade, _Locks, State) ->
{Apps, State};
handle_profile_level([{Profile, Deps, Level} | Rest], Apps, Seen, Upgrade, Locks, State) ->
handle_profile_level([{Profile, Deps, Level} | Rest], Apps, RootSeen, Seen, Upgrade, Locks, State) ->
Deps0 = [rebar_app_utils:expand_deps_sources(Dep, State)
|| Dep <- Deps,
%% skip top-level apps being double-declared
not sets:is_element(rebar_app_info:name(Dep), RootSeen)],
{Deps1, Apps1, State1, Seen1} =
update_deps(Profile, Level, Deps, Apps
update_deps(Profile, Level, Deps0, Apps
,State, Upgrade, Seen, Locks),
Deps2 = case Deps1 of
[] -> Rest;
_ -> Rest ++ [{Profile, Deps1, Level+1}]
end,
handle_profile_level(Deps2, Apps1, sets:union(Seen, Seen1), Upgrade, Locks, State1).
handle_profile_level(Deps2, Apps1, RootSeen, sets:union(Seen, Seen1), Upgrade, Locks, State1).
find_cycles(Apps) ->
case rebar_digraph:compile_order(Apps) of

+ 29
- 1
test/rebar_deps_SUITE.erl View File

@ -7,7 +7,7 @@ all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock,
http_proxy_settings, https_proxy_settings,
http_os_proxy_settings, https_os_proxy_settings,
semver_matching_lt, semver_matching_lte, semver_matching_gt,
valid_version, {group, git}, {group, pkg}].
valid_version, top_override, {group, git}, {group, pkg}].
groups() ->
[{all, [], [flat, pick_highest_left, pick_highest_right,
@ -47,6 +47,8 @@ init_per_testcase(newly_added_dep, Config) ->
rebar_test_utils:init_rebar_state(Config);
init_per_testcase(sub_app_deps, Config) ->
rebar_test_utils:init_rebar_state(Config);
init_per_testcase(top_override, Config) ->
rebar_test_utils:init_rebar_state(Config);
init_per_testcase(http_proxy_settings, Config) ->
%% Create private rebar.config
Priv = ?config(priv_dir, Config),
@ -255,6 +257,32 @@ circular1(Config) -> run(Config).
circular2(Config) -> run(Config).
circular_skip(Config) -> run(Config).
%% Test that a top-level application overtakes dependencies, and
%% works even if said deps do not exist.
top_override(Config) ->
AppDir = ?config(apps, Config),
ct:pal("dir: ~p", [AppDir]),
Name1 = rebar_test_utils:create_random_name("sub_app1_"),
Name2 = rebar_test_utils:create_random_name("sub_app2_"),
SubAppsDir1 = filename:join([AppDir, "apps", Name1]),
SubAppsDir2 = filename:join([AppDir, "apps", Name2]),
Vsn = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(SubAppsDir1, Name1, Vsn, [kernel, stdlib]),
rebar_test_utils:create_app(SubAppsDir2, Name2, Vsn, [kernel, stdlib]),
rebar_test_utils:create_config(
SubAppsDir1,
[{deps, [list_to_atom(Name2)]}]
),
rebar_test_utils:create_config(
SubAppsDir2,
[{deps, [{list_to_atom(Name1),
{git, "https://example.org", {branch, "master"}}}]}]
),
rebar_test_utils:run_and_check(
Config, [], ["compile"],
{ok, [{app, Name1}, {app,Name2}]}
).
%% Test that the deps of project apps that have their own rebar.config
%% are included, but that top level rebar.config deps take precedence
sub_app_deps(Config) ->

Loading…
Cancel
Save