summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--README.md1
-rw-r--r--priv/shell-completion/bash/rebar37
-rw-r--r--priv/shell-completion/fish/rebar3.fish6
-rw-r--r--priv/shell-completion/zsh/_rebar36
-rw-r--r--rebar.config3
-rw-r--r--rebar.lock6
-rw-r--r--src/rebar.app.src8
-rw-r--r--src/rebar3.erl1
-rw-r--r--src/rebar_agent.erl2
-rw-r--r--src/rebar_api.erl2
-rw-r--r--src/rebar_app_discover.erl89
-rw-r--r--src/rebar_app_info.erl10
-rw-r--r--src/rebar_app_utils.erl3
-rw-r--r--src/rebar_core.erl4
-rw-r--r--src/rebar_digraph.erl209
-rw-r--r--src/rebar_erlc_compiler.erl7
-rw-r--r--src/rebar_prv_common_test.erl8
-rw-r--r--src/rebar_prv_deps.erl36
-rw-r--r--src/rebar_prv_deps_tree.erl84
-rw-r--r--src/rebar_prv_install_deps.erl239
-rw-r--r--src/rebar_prv_lock.erl26
-rw-r--r--src/rebar_prv_packages.erl10
-rw-r--r--src/rebar_prv_shell.erl2
-rw-r--r--src/rebar_prv_update.erl11
-rw-r--r--src/rebar_prv_upgrade.erl17
-rw-r--r--src/rebar_prv_xref.erl2
-rw-r--r--src/rebar_state.erl11
-rw-r--r--src/rebar_utils.erl3
-rw-r--r--test/mock_git_resource.erl7
-rw-r--r--test/rebar_compile_SUITE.erl3
-rw-r--r--test/rebar_deps_SUITE.erl35
-rw-r--r--test/rebar_install_deps_SUITE.erl218
-rw-r--r--test/rebar_plugins_SUITE.erl6
-rw-r--r--test/rebar_profiles_SUITE.erl15
-rw-r--r--test/rebar_test_utils.erl79
-rw-r--r--test/rebar_upgrade_SUITE.erl12
37 files changed, 789 insertions, 400 deletions
diff --git a/.travis.yml b/.travis.yml
index d1163c4..a4d4498 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
sudo: false
language: erlang
+install: "true"
otp_release:
- 18.0
- 17.5
diff --git a/README.md b/README.md
index ae93f40..0d24dda 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,7 @@ locations ([hex.pm](http://hex.pm), git, hg, and so on).
| report | Report on environment and versions for bug reports |
| shell | Run shell with project apps in path |
| tar | Package release into tarball |
+| tree | Print dependency tree |
| unlock | Unlock dependencies |
| update | Update package index |
| upgrade | Fetch latest version of dep |
diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3
index 4e28d3d..40009b7 100644
--- a/priv/shell-completion/bash/rebar3
+++ b/priv/shell-completion/bash/rebar3
@@ -15,6 +15,7 @@ _rebar3()
compile \
cover \
ct \
+ deps \
dialyzer \
do \
edoc \
@@ -29,6 +30,7 @@ _rebar3()
report \
shell \
tar \
+ tree \
unlock \
update \
upgrade \
@@ -77,6 +79,8 @@ _rebar3()
--basic_html \
--ct_hooks \
--verbose"
+ elif [[ ${prev} == deps ]] ; then
+ :
elif [[ ${prev} == dialyzer ]] ; then
sopts="-u -s"
lopts="--update-plt --succ-typings"
@@ -168,6 +172,9 @@ _rebar3()
--system_libs \
--version \
--root"
+ elif [[ ${prev} == tree ]] ; then
+ sopts="-v"
+ lopts="--verbose"
elif [[ ${prev} == update ]] ; then
:
elif [[ ${prev} == upgrade ]] ; then
diff --git a/priv/shell-completion/fish/rebar3.fish b/priv/shell-completion/fish/rebar3.fish
index ad3dad9..df1697e 100644
--- a/priv/shell-completion/fish/rebar3.fish
+++ b/priv/shell-completion/fish/rebar3.fish
@@ -50,6 +50,7 @@ end
## report Provide a crash report to be sent to the rebar3 issues page.
## shell Run shell with project apps and deps in path.
## tar Tar archive of release built of project.
+## tree Print dependency tree.
## unlock Unlock dependencies.
## update Update package index.
## upgrade Upgrade dependencies.
@@ -149,6 +150,10 @@ complete -f -c 'rebar3' -n '__fish_rebar3_using_command tar' -l system_libs
complete -f -c 'rebar3' -n '__fish_rebar3_using_command tar' -l version -d "Print relx version"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command tar' -s r -l root -d "The project root directory"
+complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a tree -d "Print depdency tree."
+
+complete -f -c 'rebar3' -n '__fish_rebar3_needs_command tree' -s v -l verbose -d "Print repo and branch/tag/ref for git and hg deps."
+
complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a unlock -d "Unlock dependencies."
complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a update -d "Update package index."
@@ -158,4 +163,3 @@ complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a upgrade -d "Upgrade
complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a version -d "Print version for rebar and current Erlang."
complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a xref -d "Run cross reference analysis."
-
diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3
index b03b7c9..04575bc 100644
--- a/priv/shell-completion/zsh/_rebar3
+++ b/priv/shell-completion/zsh/_rebar3
@@ -191,6 +191,11 @@ _rebar3 () {
'(-r --root)'{-r,--root}'[The project root directory]:system libs:_files -/' \
&& ret=0
;;
+ (tree)
+ _arguments \
+ '(-v --verbose)'{-v,--verbose}'[Print repo and branch/tag/ref for git and hg deps]' \
+ && ret=0
+ ;;
(unlock)
_arguments \
'*: :_rebar3_list_deps' \
@@ -236,6 +241,7 @@ _rebar3_tasks() {
'report:Provide a crash report to be sent to the rebar3 issues page.'
'shell:Run shell with project apps and deps in path.'
'tar:Tar archive of release built of project.'
+ 'tree:Print dependency tree.'
'unlock:Unlock dependencies.'
'update:Update package index.'
'upgrade:Upgrade dependencies.'
diff --git a/rebar.config b/rebar.config
index 87a842e..2ce40e2 100644
--- a/rebar.config
+++ b/rebar.config
@@ -20,7 +20,8 @@
no_debug_info,
warnings_as_errors]}.
-{dialyzer_plt_apps, [common_test, dialyzer, eunit, snmp]}.
+%% Use OTP 18+ when dialyzing rebar3
+{dialyzer, [{warnings, [unknown]}]}.
%% Profiles
{profiles, [{test, [
diff --git a/rebar.lock b/rebar.lock
index 88304a9..fbf62de 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,6 +1,6 @@
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.3">>},0},
- {<<"providers">>,{pkg,<<"providers">>,<<"1.4.1">>},0},
{<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.15.0">>},0},
+ {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0},
+ {<<"providers">>,{pkg,<<"providers">>,<<"1.4.1">>},0},
{<<"relx">>,{pkg,<<"relx">>,<<"3.4.0">>},0},
- {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0},
- {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}].
+ {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}].
diff --git a/src/rebar.app.src b/src/rebar.app.src
index 9d0e027..4ef493b 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -15,10 +15,15 @@
tools,
eunit,
common_test,
+ dialyzer,
+ public_key,
+ edoc,
+ snmp,
+ getopt,
erlware_commons,
providers,
bbmustache,
- ssl_verify_hostname,
+ ssl_verify_hostname,
relx,
inets]},
{env, [
@@ -36,6 +41,7 @@
rebar_prv_compile,
rebar_prv_cover,
rebar_prv_deps,
+ rebar_prv_deps_tree,
rebar_prv_dialyzer,
rebar_prv_do,
rebar_prv_edoc,
diff --git a/src/rebar3.erl b/src/rebar3.erl
index 46082f0..8004443 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -43,6 +43,7 @@
%% ====================================================================
%% escript Entry point
+-spec main(list()) -> no_return().
main(Args) ->
try run(Args) of
{ok, _State} ->
diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl
index dc45dcf..2b69812 100644
--- a/src/rebar_agent.erl
+++ b/src/rebar_agent.erl
@@ -104,7 +104,7 @@ refresh_paths(RState) ->
ok;
{ok, _} ->
?DEBUG("reloading ~p from ~s", [Modules, Path]),
- code:replace_path(Name, Path),
+ code:replace_path(App, Path),
[begin code:purge(M), code:delete(M), code:load_file(M) end
|| M <- Modules]
end
diff --git a/src/rebar_api.erl b/src/rebar_api.erl
index ec9f85e..6ebc500 100644
--- a/src/rebar_api.erl
+++ b/src/rebar_api.erl
@@ -21,10 +21,12 @@
%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Interrupts program flow
+-spec abort() -> no_return().
abort() -> ?FAIL.
%% @doc like {@link error/2}, except it also raises an
%% exception to interrupt program flow.
+-spec abort(string(), list()) -> no_return().
abort(Str, Args) -> ?ABORT(Str, Args).
%% @doc Prints to the console, including a newline
diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl
index c5a79a6..e81a323 100644
--- a/src/rebar_app_discover.erl
+++ b/src/rebar_app_discover.erl
@@ -16,7 +16,25 @@ do(State, LibDirs) ->
Apps = find_apps(Dirs, all),
ProjectDeps = rebar_state:deps_names(State),
DepsDir = rebar_dir:deps_dir(State),
+ CurrentProfiles = rebar_state:current_profiles(State),
+
+ %% There may be a top level src which is an app and there may not
+ %% Find it here if there is, otherwise define the deps parent as root
+ TopLevelApp = define_root_app(Apps, State),
+
+ %% Handle top level deps
+ State1 = lists:foldl(fun(Profile, StateAcc) ->
+ ProfileDeps = rebar_state:get(StateAcc, {deps, Profile}, []),
+ ProfileDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_sort(ProfileDeps)),
+ ParsedDeps = parse_profile_deps(Profile
+ ,TopLevelApp
+ ,ProfileDeps2
+ ,StateAcc
+ ,StateAcc),
+ rebar_state:set(StateAcc, {parsed_deps, Profile}, ParsedDeps)
+ end, State, lists:reverse(CurrentProfiles)),
+ %% Handle sub project apps deps
%% Sort apps so we get the same merged deps config everytime
SortedApps = rebar_utils:sort_deps(Apps),
lists:foldl(fun(AppInfo, StateAcc) ->
@@ -33,7 +51,19 @@ do(State, LibDirs) ->
?INFO("Ignoring ~s", [Name]),
StateAcc
end
- end, State, SortedApps).
+ end, State1, SortedApps).
+
+define_root_app(Apps, State) ->
+ RootDir = rebar_dir:root_dir(State),
+ case ec_lists:find(fun(X) ->
+ ec_file:real_dir_path(rebar_app_info:dir(X)) =:=
+ ec_file:real_dir_path(RootDir)
+ end, Apps) of
+ {ok, App} ->
+ rebar_app_info:name(App);
+ error ->
+ root
+ end.
format_error({module_list, File}) ->
io_lib:format("Error reading module list from ~p~n", [File]);
@@ -51,24 +81,46 @@ merge_deps(AppInfo, State) ->
rebar_state:apply_profiles(
rebar_state:new(reset_hooks(rebar_state:opts(State, Default)), C,
rebar_app_info:dir(AppInfo)), CurrentProfiles), Name),
+ AppState1 = rebar_state:overrides(AppState, rebar_state:get(AppState, overrides, [])),
- rebar_utils:check_min_otp_version(rebar_state:get(AppState, minimum_otp_vsn, undefined)),
- rebar_utils:check_blacklisted_otp_versions(rebar_state:get(AppState, blacklisted_otp_vsns, [])),
+ rebar_utils:check_min_otp_version(rebar_state:get(AppState1, minimum_otp_vsn, undefined)),
+ rebar_utils:check_blacklisted_otp_versions(rebar_state:get(AppState1, blacklisted_otp_vsns, [])),
- AppState1 = rebar_state:set(AppState, artifacts, []),
- AppInfo1 = rebar_app_info:state(AppInfo, AppState1),
+ AppState2 = rebar_state:set(AppState1, artifacts, []),
+ AppInfo1 = rebar_app_info:state(AppInfo, AppState2),
State1 = lists:foldl(fun(Profile, StateAcc) ->
- AppProfDeps = rebar_state:get(AppState, {deps, Profile}, []),
- TopLevelProfDeps = rebar_state:get(StateAcc, {deps, Profile}, []),
- ProfDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_umerge(
- rebar_utils:tup_sort(TopLevelProfDeps)
- ,rebar_utils:tup_sort(AppProfDeps))),
- rebar_state:set(StateAcc, {deps, Profile}, ProfDeps2)
+ handle_profile(Profile, Name, AppState1, StateAcc)
end, State, lists:reverse(CurrentProfiles)),
{AppInfo1, State1}.
+handle_profile(Profile, Name, AppState, State) ->
+ {TopSrc, TopPkg} = rebar_state:get(State, {parsed_deps, Profile}, {[], []}),
+ TopLevelProfileDeps = rebar_state:get(State, {deps, Profile}, []),
+ AppProfileDeps = rebar_state:get(AppState, {deps, Profile}, []),
+ AppProfileDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_sort(AppProfileDeps)),
+ ProfileDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_umerge(
+ rebar_utils:tup_sort(TopLevelProfileDeps)
+ ,rebar_utils:tup_sort(AppProfileDeps2))),
+ State1 = rebar_state:set(State, {deps, Profile}, ProfileDeps2),
+
+ %% Only deps not also specified in the top level config need
+ %% to be included in the parsed deps
+ NewDeps = ProfileDeps2 -- TopLevelProfileDeps,
+ {ParsedSrc, ParsedPkg} = parse_profile_deps(Profile, Name, NewDeps, AppState, State1),
+ rebar_state:set(State1, {parsed_deps, Profile}, {TopSrc++ParsedSrc, TopPkg++ParsedPkg}).
+
+parse_profile_deps(Profile, Name, Deps, AppState, State) ->
+ DepsDir = rebar_prv_install_deps:profile_dep_dir(State, Profile),
+ Locks = rebar_state:get(State, {locks, Profile}, []),
+ rebar_prv_install_deps:parse_deps(Name
+ ,DepsDir
+ ,Deps
+ ,AppState
+ ,Locks
+ ,1).
+
project_app_config(AppInfo, State) ->
C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
Dir = rebar_app_info:dir(AppInfo),
@@ -138,7 +190,7 @@ find_app(AppDir, Validate) ->
app_dir(AppFile) ->
filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
--spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
+-spec create_app_info(file:name(), file:name()) -> rebar_app_info:t().
create_app_info(AppDir, AppFile) ->
[{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile),
AppVsn = proplists:get_value(vsn, AppDetails),
@@ -208,16 +260,11 @@ try_handle_app_src_file(_, _AppDir, _AppSrcFile, valid) ->
try_handle_app_src_file(_, AppDir, [File], Validate) when Validate =:= invalid
; Validate =:= all ->
AppInfo = create_app_info(AppDir, File),
- case AppInfo of
- {error, Reason} ->
- throw({error, {invalid_app_file, File, Reason}});
+ case filename:extension(File) of
+ ".script" ->
+ {true, rebar_app_info:app_file_src_script(AppInfo, File)};
_ ->
- case filename:extension(File) of
- ".script" ->
- {true, rebar_app_info:app_file_src_script(AppInfo, File)};
- _ ->
- {true, rebar_app_info:app_file_src(AppInfo, File)}
- end
+ {true, rebar_app_info:app_file_src(AppInfo, File)}
end;
try_handle_app_src_file(_, _AppDir, Other, _Validate) ->
throw({error, {multiple_app_files, Other}}).
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index 6962c5a..7e31f6d 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -17,6 +17,8 @@
app_file/2,
app_details/1,
app_details/2,
+ parent/1,
+ parent/2,
original_vsn/1,
original_vsn/2,
ebin_dir/1,
@@ -54,6 +56,7 @@
app_file :: file:filename_all() | undefined,
config :: rebar_state:t() | undefined,
original_vsn :: binary() | string() | undefined,
+ parent :: binary() | root,
app_details=[] :: list(),
applications=[] :: list(),
deps=[] :: list(),
@@ -203,6 +206,13 @@ app_details(#app_info_t{app_details=AppDetails}) ->
app_details(AppInfo=#app_info_t{}, AppDetails) ->
AppInfo#app_info_t{app_details=AppDetails}.
+parent(#app_info_t{parent=Parent}) ->
+ Parent.
+
+-spec parent(t(), binary() | root) -> t().
+parent(AppInfo=#app_info_t{}, Parent) ->
+ AppInfo#app_info_t{parent=Parent}.
+
-spec original_vsn(t()) -> string().
original_vsn(#app_info_t{original_vsn=Vsn}) ->
Vsn.
diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl
index 92c3ff8..e9745c3 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -94,7 +94,8 @@ format_error(Error) ->
%% Internal functions
%% ===================================================================
--spec has_all_beams(file:filename_all(), list()) -> true | providers:error().
+-spec has_all_beams(file:filename_all(), [module()]) ->
+ true | ?PRV_ERROR({missing_module, module()}).
has_all_beams(EbinDir, [Module | ModuleList]) ->
BeamFile = filename:join([EbinDir,
ec_cnv:to_list(Module) ++ ".beam"]),
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index f097429..ba82a59 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -61,7 +61,7 @@ process_namespace(State, Command) ->
{ok, rebar_state:namespace(State, default), Command}
end.
--spec process_command(rebar_state:t(), atom()) -> {ok, rebar_state:t()} | {error, string()}.
+-spec process_command(rebar_state:t(), atom()) -> {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}.
process_command(State, Command) ->
%% ? rebar_prv_install_deps:setup_env(State),
Providers = rebar_state:providers(State),
@@ -103,7 +103,7 @@ process_command(State, Command) ->
end
end.
--spec do([{atom(), atom()}], rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+-spec do([{atom(), atom()}], rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}.
do([], State) ->
{ok, State};
do([ProviderName | Rest], State) ->
diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl
index e989fdc..4ece286 100644
--- a/src/rebar_digraph.erl
+++ b/src/rebar_digraph.erl
@@ -2,10 +2,9 @@
-export([compile_order/1
,restore_graph/1
+ ,cull_deps/2
,cull_deps/3
- ,cull_deps/4
,subgraph/2
- ,print_solution/2
,format_error/1]).
-include("rebar.hrl").
@@ -18,20 +17,23 @@ compile_order(Apps) ->
Deps = all_apps_deps(App),
add(Graph, {Name, Deps})
end, Apps),
- case digraph_utils:topsort(Graph) of
- false ->
- case digraph_utils:is_acyclic(Graph) of
- true ->
- {error, no_sort};
- false ->
- Cycles = lists:sort(
- [lists:sort(Comp) || Comp <- digraph_utils:strong_components(Graph),
- length(Comp)>1]),
- {error, {cycles, Cycles}}
- end;
- V ->
- {ok, names_to_apps(lists:reverse(V), Apps)}
- end.
+ Order =
+ case digraph_utils:topsort(Graph) of
+ false ->
+ case digraph_utils:is_acyclic(Graph) of
+ true ->
+ {error, no_sort};
+ false ->
+ Cycles = lists:sort(
+ [lists:sort(Comp) || Comp <- digraph_utils:strong_components(Graph),
+ length(Comp)>1]),
+ {error, {cycles, Cycles}}
+ end;
+ V ->
+ {ok, names_to_apps(lists:reverse(V), Apps)}
+ end,
+ true = digraph:delete(Graph),
+ Order.
add(Graph, {PkgName, Deps}) ->
case digraph:vertex(Graph, PkgName) of
@@ -70,76 +72,13 @@ restore_graph({Vs, Es}) ->
%% Pick packages to fullfill dependencies
%% The first dep while traversing the graph is chosen and any conflicting
%% dep encountered later on is ignored.
+cull_deps(Graph, Vertices) ->
+ cull_deps(Graph, Vertices, sets:new()).
-cull_deps(Graph, Vertices, Level) ->
- {ok, LvlVertices, Discarded, _} = cull_deps(Graph, Vertices, Level, none),
- {ok, LvlVertices, Discarded}.
-
-cull_deps(Graph, Vertices, Level, SolutionGraph) ->
- cull_deps(Graph,
- Vertices,
- Level+1,
- lists:foldl(fun({Key, _}, Levels) ->
- dict:store(Key, Level, Levels)
- end, dict:new(), Vertices),
- lists:foldl(fun({Key, _}=N, Solution) ->
- dict:store(Key, N, Solution)
- end, dict:new(), Vertices),
- [],
- SolutionGraph).
-
-cull_deps(_Graph, [], _Level, Levels, Solution, Discarded, SolutionGraph) ->
- {_, Vertices} = lists:unzip(dict:to_list(Solution)),
- LvlVertices = [{App,Vsn,dict:fetch(App,Levels)} || {App,Vsn} <- Vertices],
- {ok, LvlVertices, Discarded, SolutionGraph};
-cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded, SolutionGraph) ->
- {NV, NS, LS, DS} =
- lists:foldl(fun(V, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}) ->
- OutNeighbors = lists:keysort(1, digraph:out_neighbours(Graph, V)),
- lists:foldl(fun({Key, _}=N, {NewVertices1, SolutionAcc1, LevelsAcc1, DiscardedAcc1}) ->
- case dict:find(Key, SolutionAcc1) of
- {ok, N} -> % already seen
- {NewVertices1, SolutionAcc1, LevelsAcc1, DiscardedAcc1};
- {ok, _} -> % conflict resolution!
- {NewVertices1, SolutionAcc1, LevelsAcc1, [N|DiscardedAcc1]};
- error ->
- add_to_solution_graph(N, V, SolutionGraph),
- {[N | NewVertices1],
- dict:store(Key, N, SolutionAcc1),
- dict:store(Key, Level, LevelsAcc1),
- DiscardedAcc1}
- end
- end, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}, OutNeighbors)
- end, {[], Solution, Levels, Discarded}, lists:keysort(1, Vertices)),
- cull_deps(Graph, NV, Level+1, LS, NS, DS, SolutionGraph).
-
-subgraph(Graph, Vertices) ->
- digraph_utils:subgraph(Graph, Vertices).
-
-add_to_solution_graph(_, _, none) ->
- ok;
-add_to_solution_graph(N, V, SolutionGraph) ->
- NewV = digraph:add_vertex(SolutionGraph, N),
- digraph:add_edge(SolutionGraph, V, NewV).
-
-print_solution(Graph, Deps) ->
- SolutionGraph = digraph:new(),
- [digraph:add_vertex(SolutionGraph, V) || V <- Deps],
- cull_deps(Graph, Deps, 0, SolutionGraph),
- print_solution(SolutionGraph, Deps, 0).
-
-print_solution(_, [], _) ->
- ok;
-print_solution(SolutionGraph, [{N, V} | Vertices], 0) ->
- ?CONSOLE("~s-~s", [N, V]),
- OutNeighbors = lists:keysort(1, digraph:out_neighbours(SolutionGraph, {N,V})),
- print_solution(SolutionGraph, OutNeighbors, 4),
- print_solution(SolutionGraph, Vertices, 0);
-print_solution(SolutionGraph, [{N, V} | Vertices], Indent) ->
- ?CONSOLE("~s~s-~s", [[" " || _ <- lists:seq(0, Indent)], N, V]),
- OutNeighbors = lists:keysort(1, digraph:out_neighbours(SolutionGraph, {N,V})),
- print_solution(SolutionGraph, OutNeighbors, Indent+4),
- print_solution(SolutionGraph, Vertices, Indent).
+cull_deps(Graph, Vertices, Seen) ->
+ Vertices1 = lists:keysort(2, Vertices),
+ {Solution, Levels, Discarded} = {dict:new(), dict:new(), []},
+ cull_deps(Graph, Vertices1, Levels, Solution, Seen, Discarded).
format_error(no_solution) ->
io_lib:format("No solution for packages found.", []).
@@ -148,6 +87,106 @@ format_error(no_solution) ->
%% Internal Functions
%%====================================================================
+cull_deps(_Graph, [], Levels, Solution, _, Discarded) ->
+ {_, Vertices} = lists:unzip(dict:to_list(Solution)),
+ LvlVertices = [{Profile, {Parent, App, Vsn, dict:fetch(App, Levels)}}
+ || {Profile, {Parent,App,Vsn}} <- Vertices],
+ {ok, LvlVertices, Discarded};
+cull_deps(Graph, [{Profile, Level, Vs} | Vertices], Levels, Solution, Seen, Discarded) ->
+ {NV, NS, LS, DS} =
+ lists:foldl(fun({Parent, Name, Vsn}, {Acc, SolutionAcc, LevelsAcc, DiscardedAcc}) ->
+ {SolutionAcc1, LevelsAcc1, DiscardedAcc1} =
+ maybe_add_to_solution(Profile, Level, Name, {Name, Vsn}, Parent
+ ,SolutionAcc
+ ,LevelsAcc, Seen, DiscardedAcc),
+ OutNeighbors = digraph:out_neighbours(Graph, {Name,Vsn}),
+ {NewVertices, DiscardedAcc2} = handle_neighbors(Profile, Level, Name
+ ,OutNeighbors, Acc, SolutionAcc1
+ ,Seen, DiscardedAcc1),
+ {NewVertices, SolutionAcc1, LevelsAcc1, DiscardedAcc2}
+ end, {[], Solution, Levels, Discarded}, Vs),
+ NewVertices = combine_profile_levels(Vertices, NV),
+ cull_deps(Graph, NewVertices, LS, NS, Seen, DS).
+
+%% Combine lists of deps that have the same profile and level
+combine_profile_levels(Vertices, NewVertices) ->
+ V = lists:foldl(fun({Profile, Level, Vs}, Acc) ->
+ case ec_lists:find(fun({P, L, _}) ->
+ P =:= Profile andalso L =:= Level
+ end, Acc) of
+ {ok, {_, _, OldVs}=Old} ->
+ lists:delete(Old, Acc)++[{Profile, Level, lists:keysort(1, OldVs++Vs)}];
+ error ->
+ Acc++[{Profile, Level, Vs}]
+ end
+ end, Vertices, NewVertices),
+ lists:keysort(2, V).
+
+%% For each outgoing edge of a dep check if it should be added to the solution
+%% and add it to the list of vertices to do the same for
+handle_neighbors(Profile, Level, Parent, OutNeighbors, Vertices
+ ,Solution, Seen, Discarded) ->
+ case lists:foldl(fun({Name, Vsn}=Value, {NewVertices, Discarded1}) ->
+ case dict:find(Name, Solution) of
+ {ok, {Profile, {Parent, Name, Vsn}}} -> % already seen
+ {NewVertices,
+ Discarded1};
+ {ok, _} -> % conflict resolution!
+ %% Warn on different version
+ {NewVertices,
+ [Value|Discarded1]};
+ error ->
+ %% We check Seen separately because we don't care
+ %% to warn if the exact same version of a package
+ %% was already part of the solution but we do
+ %% if it was simply seen in source deps
+ case sets:is_element(Name, Seen) of
+ true ->
+ {NewVertices,
+ [Value|Discarded1]};
+ false ->
+ {[{Parent, Name, Vsn} | NewVertices],
+ Discarded1}
+ end
+ end
+ end, {[], Discarded}, OutNeighbors) of
+ {[], DiscardedAcc2} ->
+ {Vertices, DiscardedAcc2};
+ {NewVertices1, DiscardedAcc2} ->
+ {Vertices++[{Profile, Level+1, NewVertices1}] ,DiscardedAcc2}
+ end.
+
+maybe_add_to_solution(Profile, Level, Key, {Name, Vsn}=Value, Parent
+ ,Solution, Levels, Seen, Discarded) ->
+ case dict:find(Key, Solution) of
+ {ok, {Profile, {Parent, Name, Vsn}}} -> % already seen
+ {Solution,
+ Levels,
+ Discarded};
+ {ok, _} -> % conflict resolution!
+ %% Warn on different version
+ {Solution,
+ Levels,
+ [Value|Discarded]};
+ error ->
+ %% We check Seen separately because we don't care to warn if the exact
+ %% same version of a package was already part of the solution but we do
+ %% if it was simply seen in source deps
+ case sets:is_element(Name, Seen) of
+ true ->
+ {Solution,
+ Levels,
+ [Value|Discarded]};
+ false ->
+ {dict:store(Key, {Profile, {Parent, Name, Vsn}}, Solution),
+ dict:store(Key, Level, Levels),
+ Discarded}
+ end
+ end.
+
+subgraph(Graph, Vertices) ->
+ digraph_utils:subgraph(Graph, Vertices).
+
-spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
names_to_apps(Names, Apps) ->
[element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 87cf352..4c76429 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -165,7 +165,8 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) ->
{DepErls, OtherErls} = lists:partition(
fun(Source) -> digraph:in_degree(G, Source) > 0 end,
[File || File <- NeededErlFiles, not lists:member(File, ErlFirstFiles)]),
- DepErlsOrdered = digraph_utils:topsort(digraph_utils:subgraph(G, DepErls)),
+ SubGraph = digraph_utils:subgraph(G, DepErls),
+ DepErlsOrdered = digraph_utils:topsort(SubGraph),
FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered),
?DEBUG("Files to compile first: ~p", [FirstErls]),
rebar_base_compiler:run(
@@ -177,6 +178,8 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) ->
end,
internal_erl_compile(C, Dir, S, OutDir1, ErlOpts1)
end),
+ true = digraph:delete(SubGraph),
+ true = digraph:delete(G),
ok.
%% Get files which need to be compiled first, i.e. those specified in erl_first_files
@@ -382,7 +385,7 @@ expand_file_names(Files, Dirs) ->
end, Files).
--spec internal_erl_compile(rebar_config:config(), file:filename(), file:filename(),
+-spec internal_erl_compile(rebar_state:t(), file:filename(), file:filename(),
file:filename(), list()) -> ok | {ok, any()} | {error, any(), any()}.
internal_erl_compile(Config, Dir, Module, OutDir, ErlOpts) ->
Target = target_base(OutDir, Module) ++ ".beam",
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 2b024cf..1165631 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -361,10 +361,10 @@ remove_links(Path) ->
end.
delete_dir_link(Path) ->
- case os:type() of
- {unix, _} -> file:delete(Path);
- {win32, _} -> file:del_dir(Path)
- end.
+ case os:type() of
+ {unix, _} -> file:delete(Path);
+ {win32, _} -> file:del_dir(Path)
+ end.
dir_entries(Path) ->
{ok, SubDirs} = file:list_dir(Path),
diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl
index dc356a8..9ff2bfa 100644
--- a/src/rebar_prv_deps.erl
+++ b/src/rebar_prv_deps.erl
@@ -24,25 +24,16 @@ init(State) ->
{short_desc, "List dependencies"},
{desc, "List dependencies. Those not matching lock files "
"are followed by an asterisk (*)."},
- {opts, [{tree, $t, "tree", undefined, "Display package dependencies in tree format (git and hg deps not supported)."}]}])),
+ {opts, []}])),
{ok, State1}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- case display_tree(State) of
- true ->
- {_Packages, Graph} = rebar_state:packages(State),
- List = merge_deps_per_profile(State),
- {_SrcDeps, PkgDeps} = rebar_prv_install_deps:parse_deps("", List, State, [], 0),
- rebar_digraph:print_solution(Graph, PkgDeps),
- {ok, State};
- false ->
- Profiles = rebar_state:current_profiles(State),
- List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
- || Profile <- Profiles],
- [display(State, Profile, Deps) || {Profile, Deps} <- List],
- {ok, State}
- end.
+ Profiles = rebar_state:current_profiles(State),
+ List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
+ || Profile <- Profiles],
+ [display(State, Profile, Deps) || {Profile, Deps} <- List],
+ {ok, State}.
-spec format_error(any()) -> iolist().
format_error(Reason) ->
@@ -91,7 +82,6 @@ dedup([Dep|Deps], [Name|DepNames]) ->
name(T) when is_tuple(T) -> element(1, T);
name(B) when is_binary(B) -> B.
-
display_deps(State, Deps) ->
lists:foreach(fun(Dep) -> display_dep(State, Dep) end, Deps).
@@ -125,17 +115,3 @@ display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Leve
?CONSOLE("~s~s (locked ~s source)", [Name, NeedsUpdate, type(Source)]).
type(Source) when is_tuple(Source) -> element(1, Source).
-
-display_tree(State) ->
- {Args, _} = rebar_state:command_parsed_args(State),
- proplists:get_value(tree, Args, false).
-
-merge_deps_per_profile(State) ->
- Profiles = rebar_state:current_profiles(State),
- lists:foldl(fun(Profile, Deps) ->
- D = rebar_utils:deps_to_binary(rebar_state:get(State, {deps, Profile}, [])),
- D1 = rebar_utils:tup_sort(D),
- rebar_utils:tup_dedup(
- rebar_utils:tup_umerge(D1
- ,Deps))
- end, [], Profiles).
diff --git a/src/rebar_prv_deps_tree.erl b/src/rebar_prv_deps_tree.erl
new file mode 100644
index 0000000..d429c52
--- /dev/null
+++ b/src/rebar_prv_deps_tree.erl
@@ -0,0 +1,84 @@
+-module(rebar_prv_deps_tree).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, tree).
+-define(DEPS, [lock]).
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ State1 = rebar_state:add_provider(
+ State,
+ providers:create([{name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 tree"},
+ {short_desc, "Print dependency tree."},
+ {desc, ""},
+ {opts, [{verbose, $v, "verbose", undefined, "Print repo and branch/tag/ref for git and hg deps"}]}])),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ {Args, _} = rebar_state:command_parsed_args(State),
+ Verbose = proplists:get_value(verbose, Args, false),
+ print_deps_tree(rebar_state:all_deps(State), Verbose, State),
+ {ok, State}.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+%% Internal functions
+
+print_deps_tree(SrcDeps, Verbose, State) ->
+ D = lists:foldl(fun(App, Dict) ->
+ Name = rebar_app_info:name(App),
+ Vsn = rebar_app_info:original_vsn(App),
+ Source = rebar_app_info:source(App),
+ Parent = rebar_app_info:parent(App),
+ dict:append_list(Parent, [{Name, Vsn, Source}], Dict)
+ end, dict:new(), SrcDeps),
+ ProjectAppNames = [{rebar_app_info:name(App)
+ ,rebar_app_info:original_vsn(App)
+ ,project} || App <- rebar_state:project_apps(State)],
+ case dict:find(root, D) of
+ {ok, Children} ->
+ print_children(-1, lists:keysort(1, Children++ProjectAppNames), D, Verbose);
+ error ->
+ print_children(-1, lists:keysort(1, ProjectAppNames), D, Verbose)
+ end.
+
+print_children(_, [], _, _) ->
+ ok;
+print_children(Indent, [{Name, Vsn, Source} | Rest], Dict, Verbose) ->
+
+ [io:format("| ") || _ <- lists:seq(0, Indent, 2)],
+ io:format("|- "),
+ io:format("~s-~s (~s)~n", [Name, Vsn, type(Source, Verbose)]),
+ case dict:find(Name, Dict) of
+ {ok, Children} ->
+ print_children(Indent+2, lists:keysort(1, Children), Dict, Verbose),
+ print_children(Indent, Rest, Dict, Verbose);
+ error ->
+ print_children(Indent, Rest, Dict, Verbose)
+ end.
+
+type(project, _) ->
+ "project app";
+type(Source, Verbose) when is_tuple(Source) ->
+ case {element(1, Source), Verbose} of
+ {pkg, _} ->
+ "hex package";
+ {Other, false} ->
+ io_lib:format("~s repo", [Other]);
+ {_, true} ->
+ io_lib:format("~s", [element(2, Source)])
+ end.
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index b590d03..11a4250 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -37,6 +37,8 @@
-export([handle_deps_as_profile/4,
parse_deps/5,
+ parse_deps/6,
+ profile_dep_dir/2,
find_cycles/1,
cull_compile/2]).
@@ -125,11 +127,15 @@ handle_deps_as_profile(Profile, State, Deps, Upgrade) ->
DepsDir = profile_dep_dir(State, Profile),
{SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level),
AllSrcProfileDeps = [{Profile, SrcDeps, Locks, Level}],
- AllPkgProfileDeps = [{Profile, Locks, PkgDeps, Level}],
+ AllPkgProfileDeps = case PkgDeps of
+ [] ->
+ [];
+ _ ->
+ [{Profile, Level, PkgDeps}]
+ end,
{AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllSrcProfileDeps, AllPkgProfileDeps, Locks, sets:new(), Upgrade, State),
- handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1).
-
+ handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, [], State1).
%% ===================================================================
%% Internal functions
@@ -139,19 +145,21 @@ handle_deps_as_profile(Profile, State, Deps, Upgrade) ->
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]}
+ case parse_profile_deps(State, Profile, Level) of
+ {Src, {_, _, []}} ->
+ {[Src | SrcAcc], PkgAcc};
+ {Src, Pkg} ->
+ {[Src | SrcAcc], [Pkg | PkgAcc]}
+ end
end, {[], []}, Profiles),
{AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllProfileDeps, PkgDeps, [], sets:new(), Upgrade, State),
-
- handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1).
+ Locks = rebar_state:get(State, {locks, default}, []),
+ handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, Locks, State1).
parse_profile_deps(State, Profile, Level) ->
- DepsDir = profile_dep_dir(State, Profile),
Locks = rebar_state:get(State, {locks, Profile}, []),
- Deps = rebar_state:get(State, {deps, Profile}, []),
- {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level),
- {{Profile, SrcDeps, Locks, Level}, {Profile, Locks, PkgDeps, Level}}.
+ {SrcDeps, PkgDeps} = rebar_state:get(State, {parsed_deps, Profile}, {[], []}),
+ {{Profile, SrcDeps, Locks, Level}, {Profile, Level, PkgDeps}}.
%% Level-order traversal of all dependencies, across profiles.
%% If profiles x,y,z are present, then the traversal will go:
@@ -166,28 +174,37 @@ handle_profile_level([{Profile, SrcDeps, Locks, Level} | Rest], PkgDeps, SrcApps
[] -> Rest;
_ -> Rest ++ [{Profile, SrcDeps1, Locks1, Level+1}]
end,
- handle_profile_level(SrcDeps2, [{Profile, Locks1, PkgDeps1, Level+1} | PkgDeps], SrcApps1++SrcApps, sets:union(Seen, Seen1), Upgrade, State1).
-
-handle_profile_pkg_level(PkgDeps, AllApps, Seen, Upgrade, State) ->
+ handle_profile_level(SrcDeps2, case PkgDeps1 of
+ [] ->
+ PkgDeps;
+ _ ->
+ [{Profile, Level+1, PkgDeps1} | PkgDeps]
+ end, SrcApps1, sets:union(Seen, Seen1), Upgrade, State1).
+
+handle_profile_pkg_level([], AllApps, _Seen, _Upgrade, _Locks, State) ->
+ {AllApps, State};
+handle_profile_pkg_level(PkgDeps, AllApps, Seen, Upgrade, Locks, 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, Level}, {AllAcc, StateAcc}) ->
- {Solved, StateAcc2} = update_pkg_deps(Profile1, Packages, PkgDeps2
- ,Graph, Upgrade, Seen, StateAcc, Locks
- ,Level),
-
- AllDeps = lists:ukeymerge(2
- ,lists:ukeysort(2, AllAcc)
- ,lists:ukeysort(2, Solved)),
-
- {AllDeps, StateAcc2}
- end, {AllApps, State1}, PkgDeps).
+ S = case rebar_digraph:cull_deps(Graph, lists:keysort(2, PkgDeps), Seen) of
+ {ok, [], []} ->
+ throw({rebar_digraph, no_solution});
+ {ok, [], Discarded} ->
+ [warn_skip_pkg(Pkg, State) || Pkg <- Discarded, not(pkg_locked(Pkg, Locks))],
+ [];
+ {ok, Solution, []} ->
+ Solution;
+ {ok, Solution, Discarded} ->
+ [warn_skip_pkg(Pkg, State) || Pkg <- Discarded, not(pkg_locked(Pkg, Locks))],
+ Solution
+ end,
+
+ {PkgApps, State2} = update_pkg_deps(S, Packages, Upgrade, Seen, State1, Locks),
+ {AllApps++PkgApps, State2}.
find_cycles(Apps) ->
case rebar_digraph:compile_order(Apps) of
@@ -199,36 +216,17 @@ find_cycles(Apps) ->
cull_compile(TopSortedDeps, ProjectApps) ->
lists:dropwhile(fun not_needs_compile/1, TopSortedDeps -- ProjectApps).
-update_pkg_deps(Profile, Packages, PkgDeps, Graph, Upgrade, Seen, State, Locks, Level) ->
- case PkgDeps of
- [] -> %% No pkg deps
- {[], State};
- PkgDeps ->
- %% Find pkg deps needed
- S = case rebar_digraph:cull_deps(Graph, PkgDeps, Level) of
- {ok, [], _} ->
- throw({rebar_digraph, no_solution});
- {ok, Solution, []} ->
- Solution;
- {ok, Solution, Discarded} ->
- [warn_skip_pkg(Pkg, State) || Pkg <- Discarded, not(pkg_locked(Pkg, Locks))],
- Solution
- end,
- update_pkg_deps(Profile, S, Packages, Upgrade, Seen, State, Locks)
- end.
-
-pkg_locked({Name, _, _}, Locks) ->
+pkg_locked({_, Name, _, _}, Locks) ->
pkg_locked(Name, Locks);
-pkg_locked({Name, _}, Locks) ->
+pkg_locked({_, Name, _}, Locks) ->
pkg_locked(Name, Locks);
pkg_locked(Name, Locks) ->
false =/= lists:keyfind(Name, 1, Locks).
-update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, Locks) ->
- %% Create app_info record for each pkg dep
- DepsDir = profile_dep_dir(State, Profile),
+update_pkg_deps(Pkgs, Packages, Upgrade, Seen, State, Locks) ->
{Solved, _, State1}
- = lists:foldl(fun(Pkg, {Acc, SeenAcc, StateAcc}) ->
+ = lists:foldl(fun({Profile, Pkg}, {Acc, SeenAcc, StateAcc}) ->
+ DepsDir = profile_dep_dir(State, Profile),
handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, Locks, StateAcc)
end, {[], Seen, State}, Pkgs),
{Solved, State1}.
@@ -236,14 +234,19 @@ update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, Locks) ->
handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, Locks, State) ->
IsLock = pkg_locked(Pkg, Locks),
AppInfo = package_to_app(DepsDir, Packages, Pkg, IsLock, State),
- Deps = rebar_app_info:deps(AppInfo),
- Level = rebar_app_info:dep_level(AppInfo),
- {NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level),
- {_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState),
- {AppInfo2, _, _, _, _} =
- handle_dep(NewState, Profile, DepsDir, AppInfo1, Locks, Level),
- AppInfo3 = rebar_app_info:deps(AppInfo2, Deps),
- {[AppInfo3 | Fetched], NewSeen, NewState}.
+ case sets:is_element(rebar_app_info:name(AppInfo), Seen) of
+ true ->
+ {Fetched, Seen, State};
+ false ->
+ Deps = rebar_app_info:deps(AppInfo),
+ Level = rebar_app_info:dep_level(AppInfo),
+ {NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level),
+ {_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState),
+ {AppInfo2, _, _, _, _} =
+ handle_dep(NewState, Profile, DepsDir, AppInfo1, Locks, Level),
+ AppInfo3 = rebar_app_info:deps(AppInfo2, Deps),
+ {[AppInfo3 | Fetched], NewSeen, NewState}
+ end.
maybe_lock(Profile, AppInfo, Seen, State, Level) ->
Name = rebar_app_info:name(AppInfo),
@@ -271,7 +274,7 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) ->
{sets:add_element(Name, Seen), State}
end.
-package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) ->
+package_to_app(DepsDir, Packages, {Parent, Name, Vsn, Level}, IsLock, State) ->
case dict:find({Name, Vsn}, Packages) of
error ->
case rebar_packages:check_registry(Name, Vsn, State) of
@@ -282,14 +285,14 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) ->
end;
{ok, PkgDeps} ->
Source = {pkg, Name, Vsn},
- AppInfo = new_dep(DepsDir, Name, Vsn, Source, IsLock, State),
+ AppInfo = new_dep(root, DepsDir, Name, Vsn, Source, IsLock, State),
AppInfo1 = rebar_app_info:dep_level(rebar_app_info:deps(AppInfo, PkgDeps), Level),
BaseDir = rebar_state:get(State, base_dir, []),
AppState1 = rebar_state:set(rebar_app_info:state(AppInfo1), base_dir, BaseDir),
- rebar_app_info:state(AppInfo1, AppState1)
+ rebar_app_info:parent(rebar_app_info:state(AppInfo1, AppState1), Parent)
end.
--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())}.
+-spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {list(), list(), list(), rebar_state:t(), sets:set(binary()), list()}.
update_src_deps(Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) ->
lists:foldl(
fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) ->
@@ -371,14 +374,14 @@ handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) ->
,NewLocks}.
-spec handle_dep(rebar_state:t(), atom(), file:filename_all(), rebar_app_info:t(), list(), integer()) ->
- {rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()], [integer()]}.
+ {rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()], [integer()], rebar_state:t()}.
handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) ->
Profiles = rebar_state:current_profiles(State),
Name = rebar_app_info:name(AppInfo),
- C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
-
+ %% Deps may be under a sub project app, find it and use its state if so
S = rebar_app_info:state(AppInfo),
+ C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
S1 = rebar_state:new(S, C, rebar_app_info:dir(AppInfo)),
S2 = rebar_state:apply_overrides(S1, Name),
@@ -399,7 +402,7 @@ handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) ->
NewLocks = [{DepName, Source, LockLevel+Level} ||
{DepName, Source, LockLevel} <- rebar_state:get(S5, {locks, default}, [])],
AppInfo3 = rebar_app_info:deps(AppInfo2, rebar_state:deps_names(Deps)),
- {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S5, Locks, Level+1),
+ {SrcDeps, PkgDeps} = parse_deps(rebar_app_info:name(AppInfo3), DepsDir, Deps, S5, Locks, Level+1),
{AppInfo3, SrcDeps, PkgDeps, Locks++NewLocks, State}.
-spec maybe_fetch(rebar_app_info:t(), atom(), boolean(),
@@ -413,23 +416,22 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
false ->
case rebar_app_discover:find_app(AppDir, all) 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 = fetch_app(AppInfo, AppDir, State),
+ maybe_symlink_default(State, Profile, AppDir, AppInfo),
+ {true, update_app_info(AppDir, AppInfo)};
{true, AppInfo1} ->
%% Preserve the state we created with overrides
AppState = rebar_app_info:state(AppInfo),
- AppInfo2 = rebar_app_info:state(AppInfo1, AppState),
+ Parent = rebar_app_info:parent(AppInfo),
+ Source = rebar_app_info:source(AppInfo),
+ AppInfo2 = rebar_app_info:parent(rebar_app_info:state(AppInfo1, AppState), Parent),
+ AppInfo3 = rebar_app_info:source(AppInfo2, Source),
case sets:is_element(rebar_app_info:name(AppInfo), Seen) of
true ->
- {false, AppInfo2};
+ {false, AppInfo3};
false ->
- maybe_symlink_default(State, Profile, AppDir, AppInfo2),
- {maybe_upgrade(AppInfo, AppDir, Upgrade, State), AppInfo2}
+ maybe_symlink_default(State, Profile, AppDir, AppInfo3),
+ {maybe_upgrade(AppInfo, AppDir, Upgrade, State), AppInfo3}
end
end
end.
@@ -477,8 +479,11 @@ make_relative_to_root(State, Path) when is_list(Path) ->
Root = rebar_dir:root_dir(State),
rebar_dir:make_relative_path(Path, Root).
--spec parse_deps(binary(), list(), rebar_state:t(), list(), integer()) -> {[rebar_app_info:t()], [pkg_dep()]}.
+-spec parse_deps(binary(), list(), rebar_state:t(), list(), integer()) -> {[rebar_app_info:t()], [tuple()]}.
parse_deps(DepsDir, Deps, State, Locks, Level) ->
+ parse_deps(root, DepsDir, Deps, State, Locks, Level).
+
+parse_deps(Parent, DepsDir, Deps, State, Locks, Level) ->
lists:foldl(fun(Dep, Acc) ->
Name = case Dep of
Dep when is_tuple(Dep) ->
@@ -488,68 +493,69 @@ parse_deps(DepsDir, Deps, State, Locks, Level) ->
end,
case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of
false ->
- parse_dep(Dep, Acc, DepsDir, false, State);
+ parse_dep(Parent, Dep, Acc, DepsDir, false, State);
LockedDep ->
LockedLevel = element(3, LockedDep),
case LockedLevel > Level of
true ->
- parse_dep(Dep, Acc, DepsDir, false, State);
+ parse_dep(Parent, Dep, Acc, DepsDir, false, State);
false ->
- parse_dep(LockedDep, Acc, DepsDir, true, State)
+ parse_dep(Parent, LockedDep, Acc, DepsDir, true, State)
end
end
end, {[], []}, Deps).
-parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) ->
+parse_dep(Parent, {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} ->
- Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
+ Dep = new_dep(root, DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
- {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
+ {SrcDepsAcc, [parse_goal(Parent
+ ,ec_cnv:to_binary(Name)
,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}
end;
-parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) ->
+parse_dep(Parent, Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name); is_binary(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
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
+ Dep = new_dep(root, DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
- {SrcDepsAcc, [{PkgName, PkgVsn} | PkgDepsAcc]}
+ {SrcDepsAcc, [{Parent, PkgName, PkgVsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
+parse_dep(Parent, {Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(Parent, DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
+parse_dep(Parent, {Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(Parent, DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+parse_dep(Parent, {Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
- Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
+ Dep = new_dep(Parent, DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_integer(Level) ->
+parse_dep(Parent, {_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_integer(Level) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
+ Dep = new_dep(root, DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
- {SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]}
+ {SrcDepsAcc, [{Parent, Name, Vsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source)
- , is_integer(Level) ->
- Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
+parse_dep(Parent, {Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source)
+ , is_integer(Level) ->
+ Dep = new_dep(Parent, DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep(Dep, _, _, _, _) ->
+parse_dep(_, Dep, _, _, _, _) ->
throw(?PRV_ERROR({parse_dep, Dep})).
-new_dep(DepsDir, Name, Vsn, Source, IsLock, State) ->
+new_dep(Parent, DepsDir, Name, Vsn, Source, IsLock, State) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
{ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of
{ok, App} ->
@@ -570,30 +576,28 @@ new_dep(DepsDir, Name, Vsn, Source, IsLock, State) ->
ParentOverrides = rebar_state:overrides(State),
Dep1 = rebar_app_info:state(Dep,
rebar_state:overrides(S, ParentOverrides++Overrides)),
- rebar_app_info:is_lock(rebar_app_info:source(Dep1, Source), IsLock).
+ AppInfo = rebar_app_info:is_lock(rebar_app_info:source(Dep1, Source), IsLock),
+ rebar_app_info:parent(AppInfo, Parent).
fetch_app(AppInfo, AppDir, State) ->
?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
Source = rebar_app_info:source(AppInfo),
- case rebar_fetch:download_source(AppDir, Source, State) of
- true ->
- true;
- Error ->
- throw(Error)
- end.
+ true = rebar_fetch:download_source(AppDir, Source, State).
%% This is called after the dep has been downloaded and unpacked, if it hadn't been already.
%% So this is the first time for newly downloaded apps that its .app/.app.src data can
%% be read in an parsed.
update_app_info(AppDir, AppInfo) ->
{ok, Found} = rebar_app_info:discover(AppDir),
+ Parent = rebar_app_info:parent(AppInfo),
+ Source = rebar_app_info:source(AppInfo),
AppDetails = rebar_app_info:app_details(Found),
Applications = proplists:get_value(applications, AppDetails, []),
IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
AppInfo1 = rebar_app_info:applications(
rebar_app_info:app_details(AppInfo, AppDetails),
IncludedApplications++Applications),
- rebar_app_info:valid(AppInfo1, false).
+ rebar_app_info:source(rebar_app_info:parent(rebar_app_info:valid(AppInfo1, false), Parent), Source).
maybe_upgrade(AppInfo, AppDir, Upgrade, State) ->
Source = rebar_app_info:source(AppInfo),
@@ -602,12 +606,7 @@ maybe_upgrade(AppInfo, AppDir, Upgrade, State) ->
case rebar_fetch:needs_update(AppDir, Source, State) of
true ->
?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
- case rebar_fetch:download_source(AppDir, Source, State) of
- true ->
- true;
- Error ->
- throw(Error)
- end;
+ true = rebar_fetch:download_source(AppDir, Source, State);
false ->
case Upgrade of
true ->
@@ -621,13 +620,13 @@ maybe_upgrade(AppInfo, AppDir, Upgrade, State) ->
false
end.
--spec parse_goal(binary(), binary()) -> pkg_dep().
-parse_goal(Name, Constraint) ->
+-spec parse_goal(binary() | root, binary(), binary()) -> {binary(), binary()} | {binary(), binary(), binary()}.
+parse_goal(Parent, Name, Constraint) ->
case re:run(Constraint, "([^\\d]*)(\\d.*)", [{capture, [1,2], binary}]) of
{match, [<<>>, Vsn]} ->
- {Name, Vsn};
+ {Parent, Name, Vsn};
{match, [Op, Vsn]} ->
- {Name, Vsn, binary_to_atom(Op, utf8)};
+ {Parent, Name, Vsn, binary_to_atom(Op, utf8)};
nomatch ->
throw(?PRV_ERROR({bad_constraint, Name, Constraint}))
end.
diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl
index 1844934..8578979 100644
--- a/src/rebar_prv_lock.erl
+++ b/src/rebar_prv_lock.erl
@@ -29,18 +29,24 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- OldLocks = rebar_state:get(State, {locks, default}, []),
- Locks = build_locks(State),
- Dir = rebar_state:dir(State),
- file:write_file(filename:join(Dir, ?LOCK_FILE),
- io_lib:format("~p.~n", [Locks])),
- State1 = rebar_state:set(State, {locks, default}, Locks),
+ %% Only lock default profile run
+ case rebar_state:current_profiles(State) of
+ [default] ->
+ OldLocks = rebar_state:get(State, {locks, default}, []),
+ Locks = lists:keysort(1, build_locks(State)),
+ Dir = rebar_state:dir(State),
+ file:write_file(filename:join(Dir, ?LOCK_FILE),
+ io_lib:format("~p.~n", [Locks])),
+ State1 = rebar_state:set(State, {locks, default}, Locks),
- OldLockNames = [element(1,L) || L <- OldLocks],
- NewLockNames = [element(1,L) || L <- Locks],
- rebar_utils:info_useless(OldLockNames, NewLockNames),
+ OldLockNames = [element(1,L) || L <- OldLocks],
+ NewLockNames = [element(1,L) || L <- Locks],
+ rebar_utils:info_useless(OldLockNames, NewLockNames),
- {ok, State1}.
+ {ok, State1};
+ _ ->
+ {ok, State}
+ end.
-spec format_error(any()) -> iolist().
format_error(Reason) ->
diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl
index 9a40734..880d4a6 100644
--- a/src/rebar_prv_packages.erl
+++ b/src/rebar_prv_packages.erl
@@ -27,13 +27,9 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- case rebar_packages:get_packages(State) of
- {Dict, _} ->
- print_packages(Dict),
- {ok, State};
- error ->
- ?PRV_ERROR(load_registry_fail)
- end.
+ {Dict, _} = rebar_packages:get_packages(State),
+ print_packages(Dict),
+ {ok, State}.
-spec format_error(any()) -> iolist().
format_error(load_registry_fail) ->
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index 431b3e2..d965e1d 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -274,7 +274,7 @@ find_config_relx(State) ->
consult_config(State, Filename)
end.
--spec consult_config(rebar_state:t(), string()) -> {ok, [tuple()]}|{error, tuple()}.
+-spec consult_config(rebar_state:t(), string()) -> [tuple()].
consult_config(State, Filename) ->
Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
?DEBUG("Loading configuration from ~p", [Fullpath]),
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index 64fe65e..55c70e2 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -15,6 +15,15 @@
-define(PROVIDER, update).
-define(DEPS, []).
+%% Ignore warning of digraph opaque type when running dialyzer
+-dialyzer({no_opaque, do/1}).
+-dialyzer({no_opaque, write_registry/3}).
+
+%% Ignoring the opaque type warning won't stop dialyzer from warning of
+%% no return for functions that had the opaque type warnings
+-dialyzer({no_return, do/1}).
+-dialyzer({no_return, write_registry/3}).
+
%% ===================================================================
%% Public API
%% ===================================================================
@@ -50,6 +59,7 @@ do(State) ->
ok = file:write_file(HexFile, Unzipped),
{Dict, Graph} = hex_to_graph(HexFile),
write_registry(Dict, Graph, State),
+ true = digraph:delete(Graph),
ok
catch
_E:C ->
@@ -63,6 +73,7 @@ do(State) ->
format_error(package_index_write) ->
"Failed to write package index.".
+-spec write_registry(rebar_dict(), {digraph, ets:tid(), ets:tid(), ets:tid(), any()}, rebar_state:t()) -> ok | {error, atom()}.
write_registry(Dict, {digraph, Edges, Vertices, Neighbors, _}, State) ->
RegistryDir = rebar_packages:package_dir(State),
filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index 3a371ca..f49eafe 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -53,18 +53,21 @@ do(State) ->
{Locks0, _Unlocks0} ->
Deps0 = top_level_deps(Deps, Locks),
State1 = rebar_state:set(State, {deps, default}, Deps0),
- State2 = rebar_state:set(State1, {locks, default}, Locks0),
- State3 = rebar_state:set(State2, upgrade, true),
- UpdatedLocks = [L || L <- rebar_state:lock(State3),
+ DepsDir = rebar_prv_install_deps:profile_dep_dir(State, default),
+ D = rebar_prv_install_deps:parse_deps(root, DepsDir, Deps0, State1, Locks0, 0),
+ State2 = rebar_state:set(State1, {parsed_deps, default}, D),
+ State3 = rebar_state:set(State2, {locks, default}, Locks0),
+ State4 = rebar_state:set(State3, upgrade, true),
+ UpdatedLocks = [L || L <- rebar_state:lock(State4),
lists:keymember(rebar_app_info:name(L), 1, Locks0)],
- Res = rebar_prv_install_deps:do(rebar_state:lock(State3, UpdatedLocks)),
+ Res = rebar_prv_install_deps:do(rebar_state:lock(State4, UpdatedLocks)),
case Res of
- {ok, State4} ->
+ {ok, State5} ->
rebar_utils:info_useless(
[element(1,Lock) || Lock <- Locks],
- [rebar_app_info:name(App) || App <- rebar_state:lock(State4)]
+ [rebar_app_info:name(App) || App <- rebar_state:lock(State5)]
),
- rebar_prv_lock:do(State4);
+ rebar_prv_lock:do(State5);
_ ->
Res
end
diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl
index 623e946..f600273 100644
--- a/src/rebar_prv_xref.erl
+++ b/src/rebar_prv_xref.erl
@@ -88,7 +88,7 @@ desc() ->
" - (\"mod\":\".*foo\"/\"4\"))",[]}]}
]).
--spec prepare(rebar_state:t()) -> {[file:filename()], [atom()]}.
+-spec prepare(rebar_state:t()) -> [atom()].
prepare(State) ->
{ok, _} = xref:start(xref),
ok = xref:set_library_path(xref, code_path(State)),
diff --git a/src/rebar_state.erl b/src/rebar_state.erl
index 8c43496..59a9588 100644
--- a/src/rebar_state.erl
+++ b/src/rebar_state.erl
@@ -171,7 +171,7 @@ default(State, Opts) ->
format_error({profile_not_list, Profile, Other}) ->
io_lib:format("Profile config must be a list but for profile '~p' config given as:~n~p", [Profile, Other]).
--spec has_all_artifacts(rebar_app_info:t()) -> true | providers:error().
+-spec has_all_artifacts(#state_t{}) -> true | {false, file:filename()}.
has_all_artifacts(State) ->
Artifacts = rebar_state:get(State, artifacts, []),
Dir = rebar_dir:base_dir(State),
@@ -188,6 +188,7 @@ all(Dir, [File|Artifacts]) ->
all(Dir, Artifacts)
end.
+-spec code_paths(#state_t{}, atom()) -> [file:filename()].
code_paths(#state_t{code_paths=CodePaths}, Key) ->
case dict:find(Key, CodePaths) of
{ok, CodePath} ->
@@ -196,9 +197,11 @@ code_paths(#state_t{code_paths=CodePaths}, Key) ->
[]
end.
+-spec code_paths(#state_t{}, atom(), [file:filename()]) -> #state_t{}.
code_paths(State=#state_t{code_paths=CodePaths}, Key, CodePath) ->
State#state_t{code_paths=dict:store(Key, CodePath, CodePaths)}.
+-spec update_code_paths(#state_t{}, atom(), [file:filename()]) -> #state_t{}.
update_code_paths(State=#state_t{code_paths=CodePaths}, Key, CodePath) ->
case dict:is_key(Key, CodePaths) of
true ->
@@ -451,15 +454,15 @@ registry(#state_t{registry=Registry}) ->
registry(State, Registry) ->
State#state_t{registry=Registry}.
--spec resources(t()) -> rebar_resource:resource().
+-spec resources(t()) -> [{rebar_resource:type(), module()}].
resources(#state_t{resources=Resources}) ->
Resources.
--spec resources(t(), [rebar_resource:resource()]) -> t().
+-spec resources(t(), [{rebar_resource:type(), module()}]) -> t().
resources(State, NewResources) ->
State#state_t{resources=NewResources}.
--spec add_resource(t(), rebar_resource:resource()) -> t().
+-spec add_resource(t(), {rebar_resource:type(), module()}) -> t().
add_resource(State=#state_t{resources=Resources}, Resource) ->
State#state_t{resources=[Resource | Resources]}.
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index c7128fc..02a2262 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -510,6 +510,7 @@ log_msg_and_abort(Message) ->
?ABORT(Message, [])
end.
+-spec debug_log_msg_and_abort(string()) -> err_handler().
debug_log_msg_and_abort(Message) ->
fun(Command, {Rc, Output}) ->
?DEBUG("sh(~s)~n"
@@ -683,7 +684,7 @@ update_code(Paths) ->
code:add_patha(Path),
ok;
{ok, Modules} ->
- code:replace_path(Name, Path),
+ code:replace_path(App, Path),
[begin code:purge(M), code:delete(M) end || M <- Modules]
end
end, Paths).
diff --git a/test/mock_git_resource.erl b/test/mock_git_resource.erl
index d8f747b..0f4aff6 100644
--- a/test/mock_git_resource.erl
+++ b/test/mock_git_resource.erl
@@ -20,7 +20,8 @@ mock() -> mock([]).
| {override_vsn, [{App, Vsn}]}
| {deps, [{App, [Dep]}]},
App :: string(),
- Dep :: {App, string(), {git, string()} | {git, string(), term()}},
+ Dep :: {App, string(), {git, string()} | {git, string(), term()}}
+ | {pkg, App, term()},
Vsn :: string().
mock(Opts) ->
meck:new(?MOD, [no_link]),
@@ -46,8 +47,8 @@ mock_lock(_) ->
case Git of
{git, Url, {tag, Ref}} -> {git, Url, {ref, Ref}};
{git, Url, {ref, Ref}} -> {git, Url, {ref, Ref}};
- {git, Url} -> {git, Url, {ref, "fake-ref"}};
- {git, Url, _} -> {git, Url, {ref, "fake-ref"}}
+ {git, Url} -> {git, Url, {ref, "0.0.0"}};
+ {git, Url, _} -> {git, Url, {ref, "0.0.0"}}
end
end).
diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl
index 0aaa899..1d5aab8 100644
--- a/test/rebar_compile_SUITE.erl
+++ b/test/rebar_compile_SUITE.erl
@@ -544,7 +544,8 @@ only_default_transitive_deps(Config) ->
GitDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}]),
PkgName = rebar_test_utils:create_random_name("pkg1_"),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(GitDeps)},
+ {SrcDeps, _} = rebar_test_utils:flat_deps(GitDeps),
+ mock_git_resource:mock([{deps, SrcDeps},
{config, [{profiles, [{test, [{deps, [list_to_atom(PkgName)]}]}]}]}]),
mock_pkg_resource:mock([{pkgdeps, [{{iolist_to_binary(PkgName), <<"0.1.0">>}, []}]}]),
diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl
index 004d50b..04018fc 100644
--- a/test/rebar_deps_SUITE.erl
+++ b/test/rebar_deps_SUITE.erl
@@ -171,30 +171,11 @@ setup_project(Case, Config0, Deps) ->
rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
TopDeps = rebar_test_utils:top_level_deps(Deps),
RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
- case DepsType of
- git ->
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
- pkg ->
- mock_pkg_resource:mock([{pkgdeps, flat_pkgdeps(Deps)}])
- end,
+ {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
[{rebarconfig, RebarConf} | Config].
-flat_pkgdeps([]) -> [];
-flat_pkgdeps([{{pkg, Name, Vsn}, Deps} | Rest]) ->
- [{{iolist_to_binary(Name),iolist_to_binary(Vsn)}, rebar_test_utils:top_level_deps(Deps)}]
- ++
- flat_pkgdeps(Deps)
- ++
- flat_pkgdeps(Rest).
-
-app_vsn([]) -> [];
-app_vsn([{Source, Deps} | Rest]) ->
- {Name, Vsn} = case Source of
- {pkg, N, V} -> {N,V};
- {N,V,_Ref} -> {N,V}
- end,
- [{Name, Vsn}] ++ app_vsn(Deps) ++ app_vsn(Rest).
-
mock_warnings() ->
%% just let it do its thing, we check warnings through
%% the call log.
@@ -217,7 +198,8 @@ sub_app_deps(Config) ->
Deps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
,{"b", "1.0.0", []}
,{"b", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]),
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("sub_app1_"),
Vsn = rebar_test_utils:create_random_vsn(),
@@ -239,9 +221,10 @@ sub_app_deps(Config) ->
newly_added_dep(Config) ->
AppDir = ?config(apps, Config),
Deps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
- ,{"b", "1.0.0", [{"c", "1.0.0", []}]}
- ,{"c", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]),
+ ,{"b", "1.0.0", [{"c", "1.0.0", []}]}
+ ,{"c", "2.0.0", []}]),
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("app_"),
Vsn = rebar_test_utils:create_random_vsn(),
diff --git a/test/rebar_install_deps_SUITE.erl b/test/rebar_install_deps_SUITE.erl
index be42e68..66c762e 100644
--- a/test/rebar_install_deps_SUITE.erl
+++ b/test/rebar_install_deps_SUITE.erl
@@ -4,16 +4,23 @@
-include_lib("eunit/include/eunit.hrl").
-include_lib("kernel/include/file.hrl").
-all() -> [{group, git}, {group, pkg}].
+all() -> [{group, git}, {group, pkg}, {group, mixed}].
groups() ->
- [{all, [], [flat, pick_highest_left, pick_highest_right,
- pick_smallest1, pick_smallest2,
- circular1, circular2, circular_skip,
- fail_conflict, default_profile, nondefault_profile,
- nondefault_pick_highest]},
- {git, [], [{group, all}]},
- {pkg, [], [{group, all}]}].
+ [{unique, [], [flat, pick_highest_left, pick_highest_right,
+ pick_smallest1, pick_smallest2,
+ circular1, circular2, circular_skip,
+ fail_conflict, default_profile, nondefault_profile,
+ nondefault_pick_highest]},
+ {git, [], [{group, unique}]},
+ {pkg, [], [{group, unique}]},
+ {mixed, [], [
+ m_flat1, m_flat2, m_circular1, m_circular2, m_circular3,
+ m_pick_source1, m_pick_source2, m_pick_source3,
+ m_pick_source4, m_pick_source5, m_source_to_pkg,
+ m_pkg_level1, m_pkg_level2, m_pkg_level3, m_pkg_level3_alpha_order
+ ]}
+ ].
init_per_suite(Config) ->
application:start(meck),
@@ -26,19 +33,33 @@ init_per_group(git, Config) ->
[{deps_type, git} | Config];
init_per_group(pkg, Config) ->
[{deps_type, pkg} | Config];
+init_per_group(mixed, Config) ->
+ [{deps_type, mixed} | Config];
init_per_group(_, Config) ->
Config.
end_per_group(_, Config) ->
Config.
-init_per_testcase(Case, Config) ->
+init_per_testcase(Case, Config) when is_atom(Case) ->
+ DepsType = ?config(deps_type, Config),
+ init_per_testcase({DepsType, Case}, Config);
+init_per_testcase({mixed, Case}, Config) ->
+ {Deps, Warnings, Expect} = mdeps(Case),
+ Expected = case Expect of
+ {ok, List} -> {ok, format_expected_mdeps(List)};
+ Other -> Other
+ end,
+ mock_warnings(),
+ [{expect, Expected},
+ {warnings, format_expected_mixed_warnings(Warnings)}
+ | setup_project(Case, Config, rebar_test_utils:expand_deps(mixed, Deps))];
+init_per_testcase({DepsType, Case}, Config) ->
{Deps, Warnings, Expect} = deps(Case),
Expected = case Expect of
{ok, List} -> {ok, format_expected_deps(List)};
Other -> Other
end,
- DepsType = ?config(deps_type, Config),
mock_warnings(),
[{expect, Expected},
{warnings, Warnings}
@@ -54,6 +75,32 @@ format_expected_deps(Deps) ->
N -> [{dep, N}, {lock, N}]
end || Dep <- Deps]).
+format_expected_mdeps(Deps) ->
+ %% for mixed deps, lowercase is a package, uppercase is source.
+ %% We can't check which was used from the dep, but the lock contains
+ %% the type and we can use that information.
+ lists:append([
+ case Dep of
+ {N,V} when hd(N) >= $a, hd(N) =< $z ->
+ UN = string:to_upper(N),
+ [{dep, UN, V}, {lock, pkg, UN, V}];
+ {N,V} when hd(N) >= $A, hd(N) =< $Z ->
+ [{dep, N, V}, {lock, src, N, V}];
+ N when hd(N) >= $a, hd(N) =< $z ->
+ UN = string:to_upper(N),
+ [{dep, UN}, {lock, pkg, UN, "0.0.0"}];
+ N when hd(N) >= $A, hd(N) =< $Z ->
+ [{dep, N}, {lock, src, N, "0.0.0"}]
+ end || Dep <- Deps]).
+
+format_expected_mixed_warnings(Warnings) ->
+ [case W of
+ {N, Vsn} when hd(N) >= $a, hd(N) =< $z -> {pkg, string:to_upper(N), Vsn};
+ {N, Vsn} when hd(N) >= $A, hd(N) =< $Z -> {src, N, Vsn};
+ N when hd(N) >= $a, hd(N) =< $z -> {pkg, string:to_upper(N), "0.0.0"};
+ N when hd(N) >= $A, hd(N) =< $Z -> {src, N, "0.0.0"}
+ end || W <- Warnings].
+
%% format:
%% {Spec,
%% [Warning],
@@ -131,6 +178,85 @@ deps(nondefault_pick_highest) ->
%% This is all handled in setup_project
{[],[],{ok,[]}}.
+%% format:
+%% Same as `deps/1' except "A" is a source dep
+%% and "a" is a package dep.
+mdeps(m_flat1) ->
+ {[{"c", []},
+ {"B", []}],
+ [],
+ {ok, ["B","c"]}};
+mdeps(m_flat2) ->
+ {[{"B", []},
+ {"c", []}],
+ [],
+ {ok, ["B","c"]}};
+mdeps(m_circular1) ->
+ {[{"b", [{"a",[]}]}], % "A" is the top app
+ [],
+ {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
+mdeps(m_circular2) ->
+ {[{"B", [{"c", [{"b", []}]}]}],
+ [],
+ {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
+mdeps(m_circular3) ->
+ %% Spot the circular dep due to being to low in the deps tree
+ %% but as a source dep, taking precedence over packages
+ {[{"B", [{"C", "2", [{"B", []}]}]},
+ {"c", "1", [{"d",[]}]}],
+ [],
+ {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
+mdeps(m_pick_source1) ->
+ {[{"B", [{"D", []}]},
+ {"c", [{"d", []}]}],
+ ["d"],
+ {ok, ["B", "c", "D"]}};
+mdeps(m_pick_source2) ->
+ {[{"b", [{"d", []}]},
+ {"C", [{"D", []}]}],
+ ["d"],
+ {ok, ["b", "C", "D"]}};
+mdeps(m_pick_source3) ->
+ %% The order of declaration is important.
+ {[{"b", []},
+ {"B", []}],
+ [],
+ {ok, ["b"]}};
+mdeps(m_pick_source4) ->
+ {[{"B", []},
+ {"b", []}],
+ [],
+ {ok, ["B"]}};
+mdeps(m_pick_source5) ->
+ {[{"B", [{"d", []}]},
+ {"C", [{"D", []}]}],
+ ["d"],
+ {ok, ["B", "C", "D"]}};
+mdeps(m_source_to_pkg) ->
+ {[{"B", [{"c",[{"d", []}]}]}],
+ [],
+ {ok, ["B", "c", "d"]}};
+mdeps(m_pkg_level1) ->
+ {[{"B", [{"D", [{"e", "2", []}]}]},
+ {"C", [{"e", "1", []}]}],
+ [{"e","2"}],
+ {ok, ["B","C","D",{"e","1"}]}};
+mdeps(m_pkg_level2) ->
+ {[{"B", [{"e", "1", []}]},
+ {"C", [{"D", [{"e", "2", []}]}]}],
+ [{"e","2"}],
+ {ok, ["B","C","D",{"e","1"}]}};
+mdeps(m_pkg_level3_alpha_order) ->
+ {[{"B", [{"d", [{"f", "1", []}]}]},
+ {"C", [{"E", [{"f", "2", []}]}]}],
+ [{"f","2"}],
+ {ok, ["B","C","d","E",{"f","1"}]}};
+mdeps(m_pkg_level3) ->
+ {[{"B", [{"d", [{"f", "1", []}]}]},
+ {"C", [{"E", [{"G", [{"f", "2", []}]}]}]}],
+ [{"f","2"}],
+ {ok, ["B","C","d","E","G",{"f","1"}]}}.
+
setup_project(fail_conflict, Config0, Deps) ->
DepsType = ?config(deps_type, Config0),
Config = rebar_test_utils:init_rebar_state(
@@ -142,12 +268,9 @@ setup_project(fail_conflict, Config0, Deps) ->
TopDeps = rebar_test_utils:top_level_deps(Deps),
RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps},
{deps_error_on_conflict, true}]),
- case DepsType of
- git ->
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
- pkg ->
- mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
- end,
+ {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
[{rebarconfig, RebarConf} | Config];
setup_project(nondefault_profile, Config0, Deps) ->
DepsType = ?config(deps_type, Config0),
@@ -161,12 +284,9 @@ setup_project(nondefault_profile, Config0, Deps) ->
RebarConf = rebar_test_utils:create_config(AppDir, [{profiles, [
{nondef, [{deps, TopDeps}]}
]}]),
- case DepsType of
- git ->
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
- pkg ->
- mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
- end,
+ {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
[{rebarconfig, RebarConf} | Config];
setup_project(nondefault_pick_highest, Config0, _) ->
DepsType = ?config(deps_type, Config0),
@@ -187,13 +307,11 @@ setup_project(nondefault_pick_highest, Config0, _) ->
),
case DepsType of
git ->
- mock_git_resource:mock(
- [{deps, rebar_test_utils:flat_deps(DefaultDeps ++ ProfileDeps)}]
- );
+ {SrcDeps, _} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]);
pkg ->
- mock_pkg_resource:mock(
- [{pkgdeps, rebar_test_utils:flat_pkgdeps(DefaultDeps ++ ProfileDeps)}]
- )
+ {_, PkgDeps} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}])
end,
[{rebarconfig, RebarConf} | Config];
setup_project(Case, Config0, Deps) ->
@@ -206,12 +324,9 @@ setup_project(Case, Config0, Deps) ->
rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
TopDeps = rebar_test_utils:top_level_deps(Deps),
RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
- case DepsType of
- git ->
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
- pkg ->
- mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
- end,
+ {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
[{rebarconfig, RebarConf} | Config].
mock_warnings() ->
@@ -241,6 +356,9 @@ default_profile(Config) ->
AppDir = ?config(apps, Config),
{ok, Apps} = Expect = ?config(expect, Config),
rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["lock"], Expect
+ ),
+ rebar_test_utils:run_and_check(
Config, RebarConfig, ["as", "profile", "lock"], Expect
),
check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
@@ -253,6 +371,9 @@ default_profile(Config) ->
|| {dep, App} <- Apps],
%% A second run to another profile also links default to the right spot
rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["lock"], Expect
+ ),
+ rebar_test_utils:run_and_check(
Config, RebarConfig, ["as", "other", "lock"], Expect
),
[?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
@@ -296,10 +417,13 @@ nondefault_profile(Config) ->
nondefault_pick_highest(Config) ->
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
- %AppDir = ?config(apps, Config),
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["lock"],
+ {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "1"}], "default"}
+ ),
rebar_test_utils:run_and_check(
Config, RebarConfig, ["as", "nondef", "lock"],
- {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "2"}], "nondef"}
+ {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
),
rebar_test_utils:run_and_check(
Config, RebarConfig, ["lock"],
@@ -307,9 +431,25 @@ nondefault_pick_highest(Config) ->
),
rebar_test_utils:run_and_check(
Config, RebarConfig, ["as", "nondef", "lock"],
- {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "2"}], "nondef"}
+ {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
).
+m_flat1(Config) -> run(Config).
+m_flat2(Config) -> run(Config).
+m_circular1(Config) -> run(Config).
+m_circular2(Config) -> run(Config).
+m_circular3(Config) -> run(Config).
+m_pick_source1(Config) -> run(Config).
+m_pick_source2(Config) -> run(Config).
+m_pick_source3(Config) -> run(Config).
+m_pick_source4(Config) -> run(Config).
+m_pick_source5(Config) -> run(Config).
+m_source_to_pkg(Config) -> run(Config).
+m_pkg_level1(Config) -> run(Config).
+m_pkg_level2(Config) -> run(Config).
+m_pkg_level3(Config) -> run(Config).
+m_pkg_level3_alpha_order(Config) -> run(Config).
+
run(Config) ->
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
rebar_test_utils:run_and_check(
@@ -327,6 +467,10 @@ error_calls() ->
check_warnings(_, [], _) ->
ok;
+check_warnings(Warns, [{Type, Name, Vsn} | Rest], mixed) ->
+ ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
+ ?assert(in_warnings(Type, Warns, Name, Vsn)),
+ check_warnings(Warns, Rest, mixed);
check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
?assert(in_warnings(Type, Warns, Name, Vsn)),
diff --git a/test/rebar_plugins_SUITE.erl b/test/rebar_plugins_SUITE.erl
index adfeafe..5e2c782 100644
--- a/test/rebar_plugins_SUITE.erl
+++ b/test/rebar_plugins_SUITE.erl
@@ -45,7 +45,8 @@ compile_plugins(Config) ->
PluginName = rebar_test_utils:create_random_name("plugin1_"),
Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Plugins)}]),
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Plugins),
+ mock_git_resource:mock([{deps, SrcDeps}]),
mock_pkg_resource:mock([{pkgdeps, [{{list_to_binary(DepName), list_to_binary(Vsn)}, []}]},
{config, [{plugins, [
@@ -137,7 +138,8 @@ complex_plugins(Config) ->
Deps = rebar_test_utils:expand_deps(git, [{PluginName, Vsn2, [{DepName2, Vsn,
[{DepName3, Vsn, []}]}]}
,{DepName, Vsn, []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]),
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
RConfFile =
rebar_test_utils:create_config(AppDir,
diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl
index b42df39..41bb535 100644
--- a/test/rebar_profiles_SUITE.erl
+++ b/test/rebar_profiles_SUITE.erl
@@ -57,7 +57,8 @@ profile_new_key(Config) ->
AllDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
,{"b", "1.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]),
+ {SrcDeps, []} = rebar_test_utils:flat_deps(AllDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("profile_new_key_"),
Vsn = rebar_test_utils:create_random_vsn(),
@@ -82,7 +83,8 @@ profile_merge_keys(Config) ->
AllDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
,{"b", "1.0.0", []}
,{"b", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]),
+ {SrcDeps, []} = rebar_test_utils:flat_deps(AllDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("profile_new_key_"),
Vsn = rebar_test_utils:create_random_vsn(),
@@ -111,7 +113,8 @@ explicit_profile_deduplicate_deps(Config) ->
,{"a", "2.0.0", []}
,{"b", "1.0.0", []}
,{"b", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]),
+ {SrcDeps, []} = rebar_test_utils:flat_deps(AllDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("explicit_profile_deduplicate_deps_"),
Vsn = rebar_test_utils:create_random_vsn(),
@@ -141,7 +144,8 @@ implicit_profile_deduplicate_deps(Config) ->
,{"a", "2.0.0", []}
,{"b", "1.0.0", []}
,{"b", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]),
+ {SrcDeps, []} = rebar_test_utils:flat_deps(AllDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("implicit_profile_deduplicate_deps_"),
Vsn = rebar_test_utils:create_random_vsn(),
@@ -169,7 +173,8 @@ all_deps_code_paths(Config) ->
AllDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
,{"b", "2.0.0", []}]),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]),
+ {SrcDeps, []} = rebar_test_utils:flat_deps(AllDeps),
+ mock_git_resource:mock([{deps, SrcDeps}]),
Name = rebar_test_utils:create_random_name("all_deps_code_paths"),
Vsn = rebar_test_utils:create_random_vsn(),
diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl
index be52e81..8e00483 100644
--- a/test/rebar_test_utils.erl
+++ b/test/rebar_test_utils.erl
@@ -2,7 +2,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-export([init_rebar_state/1, init_rebar_state/2, run_and_check/4]).
--export([expand_deps/2, flat_deps/1, flat_pkgdeps/1, top_level_deps/1]).
+-export([expand_deps/2, flat_deps/1, top_level_deps/1]).
-export([create_app/4, create_eunit_app/4, create_empty_app/4, create_config/2]).
-export([create_random_name/1, create_random_vsn/0, write_src_file/2]).
@@ -137,24 +137,43 @@ expand_deps(pkg, [{Name, Deps} | Rest]) ->
[{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) ->
Dep = {pkg, Name, Vsn},
- [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)].
-
-flat_deps([]) -> [];
-flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest]) ->
- [{{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}]
- ++
- flat_deps(Deps)
- ++
- flat_deps(Rest).
-
-flat_pkgdeps([]) -> [];
-flat_pkgdeps([{{pkg, Name, Vsn}, Deps} | Rest]) ->
- [{{iolist_to_binary(Name),iolist_to_binary(Vsn)}, top_level_deps(Deps)}]
- ++
- flat_pkgdeps(Deps)
- ++
- flat_pkgdeps(Rest).
-
+ [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
+expand_deps(mixed, [{Name, Deps} | Rest]) ->
+ Dep = if hd(Name) >= $a, hd(Name) =< $z ->
+ {pkg, string:to_upper(Name), "0.0.0"}
+ ; hd(Name) >= $A, hd(Name) =< $Z ->
+ {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}}
+ end,
+ [{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)];
+expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) ->
+ Dep = if hd(Name) >= $a, hd(Name) =< $z ->
+ {pkg, string:to_upper(Name), Vsn}
+ ; hd(Name) >= $A, hd(Name) =< $Z ->
+ {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}}
+ end,
+ [{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)].
+
+%% Source deps can depend on both source and package dependencies;
+%% package deps can only depend on package deps.
+%% For things to work we have to go down the dep tree and find all
+%% lineages of pkg deps and return them, whereas the source deps
+%% can be left as is.
+flat_deps(Deps) -> flat_deps(Deps, [], []).
+
+flat_deps([], Src, Pkg) -> {Src, Pkg};
+flat_deps([{{pkg, Name, Vsn}, PkgDeps} | Rest], Src, Pkg) ->
+ Current = {{iolist_to_binary(Name), iolist_to_binary(Vsn)},
+ top_level_deps(PkgDeps)},
+ {[], FlatPkgDeps} = flat_deps(PkgDeps),
+ flat_deps(Rest,
+ Src,
+ Pkg ++ [Current | FlatPkgDeps]);
+flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest], Src, Pkg) ->
+ Current = {{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)},
+ {FlatDeps, FlatPkgDeps} = flat_deps(Deps),
+ flat_deps(Rest,
+ Src ++ [Current | FlatDeps],
+ Pkg ++ FlatPkgDeps).
vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
vsn_from_ref({git, _, Vsn}) -> Vsn.
@@ -278,6 +297,28 @@ check_results(AppDir, Expected, ProfileRun) ->
?assertEqual(iolist_to_binary(Vsn),
iolist_to_binary(LockVsn))
end
+ ; ({lock, pkg, Name, Vsn}) ->
+ ct:pal("Pkg Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
+ case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
+ false ->
+ error({lock_not_found, Name});
+ {_LockName, {pkg, _, LockVsn}, _} ->
+ ?assertEqual(iolist_to_binary(Vsn),
+ iolist_to_binary(LockVsn));
+ {_LockName, {_, _, {ref, LockVsn}}, _} ->
+ error({source_lock, {Name, LockVsn}})
+ end
+ ; ({lock, src, Name, Vsn}) ->
+ ct:pal("Src Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
+ case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
+ false ->
+ error({lock_not_found, Name});
+ {_LockName, {pkg, _, LockVsn}, _} ->
+ error({pkg_lock, {Name, LockVsn}});
+ {_LockName, {_, _, {ref, LockVsn}}, _} ->
+ ?assertEqual(iolist_to_binary(Vsn),
+ iolist_to_binary(LockVsn))
+ end
; ({release, Name, Vsn, ExpectedDevMode}) ->
ct:pal("Release: ~p-~s", [Name, Vsn]),
{ok, Cwd} = file:get_cwd(),
diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
index 4ab99c7..54f16da 100644
--- a/test/rebar_upgrade_SUITE.erl
+++ b/test/rebar_upgrade_SUITE.erl
@@ -425,17 +425,21 @@ upgrades(compile_upgrade_parity) ->
mock_deps(git, Deps, Upgrades) ->
catch mock_git_resource:unmock(),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}, {upgrade, Upgrades}]);
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
+ mock_git_resource:mock([{deps, SrcDeps}, {upgrade, Upgrades}]);
mock_deps(pkg, Deps, Upgrades) ->
catch mock_pkg_resource:unmock(),
- mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}, {upgrade, Upgrades}]).
+ {_, PkgDeps} = rebar_test_utils:flat_deps(Deps),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}, {upgrade, Upgrades}]).
mock_deps(git, OldDeps, Deps, Upgrades) ->
catch mock_git_resource:unmock(),
- mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps++OldDeps)}, {upgrade, Upgrades}]);
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Deps++OldDeps),
+ mock_git_resource:mock([{deps, SrcDeps}, {upgrade, Upgrades}]);
mock_deps(pkg, OldDeps, Deps, Upgrades) ->
catch mock_pkg_resource:unmock(),
- mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps++OldDeps)}, {upgrade, Upgrades}]).
+ {_, PkgDeps} = rebar_test_utils:flat_deps(Deps++OldDeps),
+ mock_pkg_resource:mock([{pkgdeps, PkgDeps}, {upgrade, Upgrades}]).
normalize_unlocks({App, Locks}) ->
{iolist_to_binary(App),