|
|
@ -57,37 +57,37 @@ preprocess(Config, _) -> |
|
|
|
%% Get the list of deps for the current working directory and identify those |
|
|
|
%% deps that are available/present. |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
{AvailableDeps, MissingDeps} = find_deps(find, Deps), |
|
|
|
{Config1, {AvailableDeps, MissingDeps}} = find_deps(Config, find, Deps), |
|
|
|
|
|
|
|
?DEBUG("Available deps: ~p\n", [AvailableDeps]), |
|
|
|
?DEBUG("Missing deps : ~p\n", [MissingDeps]), |
|
|
|
|
|
|
|
%% Add available deps to code path |
|
|
|
update_deps_code_path(AvailableDeps), |
|
|
|
Config2 = update_deps_code_path(Config1, AvailableDeps), |
|
|
|
|
|
|
|
%% If skip_deps=true, mark each dep dir as a skip_dir w/ the core so that |
|
|
|
%% the current command doesn't run on the dep dir. However, pre/postprocess |
|
|
|
%% WILL run (and we want it to) for transitivity purposes. |
|
|
|
case rebar_config:get_global(skip_deps, false) of |
|
|
|
"true" -> |
|
|
|
lists:foreach(fun (#dep{dir = Dir}) -> |
|
|
|
rebar_core:skip_dir(Dir) |
|
|
|
end, AvailableDeps); |
|
|
|
_ -> |
|
|
|
ok |
|
|
|
end, |
|
|
|
NewConfig = case rebar_config:get_global(skip_deps, false) of |
|
|
|
"true" -> |
|
|
|
lists:foldl( |
|
|
|
fun(#dep{dir = Dir}, C) -> |
|
|
|
rebar_config:set_skip_dir(C, Dir) |
|
|
|
end, Config2, AvailableDeps); |
|
|
|
_ -> |
|
|
|
Config2 |
|
|
|
end, |
|
|
|
|
|
|
|
%% Return all the available dep directories for process |
|
|
|
{ok, [D#dep.dir || D <- AvailableDeps]}. |
|
|
|
|
|
|
|
{ok, NewConfig, dep_dirs(AvailableDeps)}. |
|
|
|
|
|
|
|
postprocess(_Config, _) -> |
|
|
|
case erlang:get(?MODULE) of |
|
|
|
undefined -> |
|
|
|
postprocess(Config, _) -> |
|
|
|
case rebar_config:get_xconf(Config, ?MODULE) of |
|
|
|
error -> |
|
|
|
{ok, []}; |
|
|
|
Dirs -> |
|
|
|
erlang:erase(?MODULE), |
|
|
|
{ok, Dirs} |
|
|
|
{ok, Dirs} -> |
|
|
|
NewConfig = rebar_config:erase_xconf(Config, ?MODULE), |
|
|
|
{ok, NewConfig, Dirs} |
|
|
|
end. |
|
|
|
|
|
|
|
compile(Config, AppFile) -> |
|
|
@ -114,11 +114,11 @@ setup_env(_Config) -> |
|
|
|
'check-deps'(Config, _) -> |
|
|
|
%% Get the list of immediate (i.e. non-transitive) deps that are missing |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
case find_deps(find, Deps) of |
|
|
|
{_, []} -> |
|
|
|
case find_deps(Config, find, Deps) of |
|
|
|
{Config1, {_, []}} -> |
|
|
|
%% No missing deps |
|
|
|
ok; |
|
|
|
{_, MissingDeps} -> |
|
|
|
{ok, Config1}; |
|
|
|
{_Config1, {_, MissingDeps}} -> |
|
|
|
lists:foreach(fun (#dep{app=App, vsn_regex=Vsn, source=Src}) -> |
|
|
|
?CONSOLE("Dependency not available: " |
|
|
|
"~p-~s (~p)\n", [App, Vsn, Src]) |
|
|
@ -129,47 +129,52 @@ setup_env(_Config) -> |
|
|
|
'get-deps'(Config, _) -> |
|
|
|
%% Determine what deps are available and missing |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
{_AvailableDeps, MissingDeps} = find_deps(find, Deps), |
|
|
|
{Config1, {_AvailableDeps, MissingDeps}} = find_deps(Config, find, Deps), |
|
|
|
MissingDeps1 = [D || D <- MissingDeps, D#dep.source =/= undefined], |
|
|
|
|
|
|
|
%% For each missing dep with a specified source, try to pull it. |
|
|
|
PulledDeps = [use_source(D) || D <- MissingDeps, D#dep.source /= undefined], |
|
|
|
{Config2, PulledDeps} = |
|
|
|
lists:foldl(fun(D, {C, PulledDeps0}) -> |
|
|
|
{C1, D1} = use_source(C, D), |
|
|
|
{C1, [D1 | PulledDeps0]} |
|
|
|
end, {Config1, []}, MissingDeps1), |
|
|
|
|
|
|
|
%% Add each pulled dep to our list of dirs for post-processing. This yields |
|
|
|
%% the necessary transitivity of the deps |
|
|
|
erlang:put(?MODULE, [D#dep.dir || D <- PulledDeps]), |
|
|
|
ok. |
|
|
|
{ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}. |
|
|
|
|
|
|
|
'update-deps'(Config, _) -> |
|
|
|
%% Determine what deps are available and missing |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
UpdatedDeps = [update_source(D) || D <- find_deps(read, Deps), |
|
|
|
D#dep.source /= undefined], |
|
|
|
%% Determine what deps are required |
|
|
|
RawDeps = rebar_config:get_local(Config, deps, []), |
|
|
|
{Config1, Deps} = find_deps(Config, read, RawDeps), |
|
|
|
|
|
|
|
%% Update each dep |
|
|
|
UpdatedDeps = [update_source(D) || D <- Deps, D#dep.source =/= undefined], |
|
|
|
|
|
|
|
%% Add each updated dep to our list of dirs for post-processing. This yields |
|
|
|
%% the necessary transitivity of the deps |
|
|
|
erlang:put(?MODULE, [D#dep.dir || D <- UpdatedDeps]), |
|
|
|
ok. |
|
|
|
{ok, save_dep_dirs(Config1, UpdatedDeps)}. |
|
|
|
|
|
|
|
'delete-deps'(Config, _) -> |
|
|
|
%% Delete all the available deps in our deps/ directory, if any |
|
|
|
{true, DepsDir} = get_deps_dir(), |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
{AvailableDeps, _} = find_deps(find, Deps), |
|
|
|
{Config1, {AvailableDeps, _}} = find_deps(Config, find, Deps), |
|
|
|
_ = [delete_dep(D) |
|
|
|
|| D <- AvailableDeps, |
|
|
|
lists:prefix(DepsDir, D#dep.dir)], |
|
|
|
ok. |
|
|
|
{ok, Config1}. |
|
|
|
|
|
|
|
'list-deps'(Config, _) -> |
|
|
|
Deps = rebar_config:get_local(Config, deps, []), |
|
|
|
case find_deps(find, Deps) of |
|
|
|
{AvailDeps, []} -> |
|
|
|
case find_deps(Config, find, Deps) of |
|
|
|
{Config1, {AvailDeps, []}} -> |
|
|
|
lists:foreach(fun(Dep) -> print_source(Dep) end, AvailDeps), |
|
|
|
ok; |
|
|
|
{ok, Config1}; |
|
|
|
{_, MissingDeps} -> |
|
|
|
?ABORT("Missing dependencies: ~p\n", [MissingDeps]) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
%% =================================================================== |
|
|
|
%% Internal functions |
|
|
|
%% =================================================================== |
|
|
@ -191,6 +196,12 @@ get_deps_dir(App) -> |
|
|
|
DepsDir = rebar_config:get_global(deps_dir, "deps"), |
|
|
|
{true, filename:join([BaseDir, DepsDir, App])}. |
|
|
|
|
|
|
|
dep_dirs(Deps) -> |
|
|
|
[D#dep.dir || D <- Deps]. |
|
|
|
|
|
|
|
save_dep_dirs(Config, Deps) -> |
|
|
|
rebar_config:set_xconf(Config, ?MODULE, dep_dirs(Deps)). |
|
|
|
|
|
|
|
get_lib_dir(App) -> |
|
|
|
%% Find App amongst the reachable lib directories |
|
|
|
%% Returns either the found path or a tagged tuple with a boolean |
|
|
@ -200,72 +211,77 @@ get_lib_dir(App) -> |
|
|
|
Path -> {true, Path} |
|
|
|
end. |
|
|
|
|
|
|
|
update_deps_code_path([]) -> |
|
|
|
ok; |
|
|
|
update_deps_code_path([Dep | Rest]) -> |
|
|
|
case is_app_available(Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir) of |
|
|
|
{true, _} -> |
|
|
|
Dir = filename:join(Dep#dep.dir, "ebin"), |
|
|
|
ok = filelib:ensure_dir(filename:join(Dir, "dummy")), |
|
|
|
?DEBUG("Adding ~s to code path~n", [Dir]), |
|
|
|
true = code:add_patha(Dir); |
|
|
|
{false, _} -> |
|
|
|
true |
|
|
|
end, |
|
|
|
update_deps_code_path(Rest). |
|
|
|
|
|
|
|
|
|
|
|
find_deps(find=Mode, Deps) -> |
|
|
|
find_deps(Mode, Deps, {[], []}); |
|
|
|
find_deps(read=Mode, Deps) -> |
|
|
|
find_deps(Mode, Deps, []). |
|
|
|
|
|
|
|
find_deps(find, [], {Avail, Missing}) -> |
|
|
|
{lists:reverse(Avail), lists:reverse(Missing)}; |
|
|
|
find_deps(read, [], Deps) -> |
|
|
|
lists:reverse(Deps); |
|
|
|
find_deps(Mode, [App | Rest], Acc) when is_atom(App) -> |
|
|
|
find_deps(Mode, [{App, ".*", undefined} | Rest], Acc); |
|
|
|
find_deps(Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) -> |
|
|
|
find_deps(Mode, [{App, VsnRegex, undefined} | Rest], Acc); |
|
|
|
find_deps(Mode, [{App, VsnRegex, Source} | Rest], Acc) -> |
|
|
|
update_deps_code_path(Config, []) -> |
|
|
|
Config; |
|
|
|
update_deps_code_path(Config, [Dep | Rest]) -> |
|
|
|
Config2 = |
|
|
|
case is_app_available(Config, Dep#dep.app, |
|
|
|
Dep#dep.vsn_regex, Dep#dep.dir) of |
|
|
|
{Config1, {true, _}} -> |
|
|
|
Dir = filename:join(Dep#dep.dir, "ebin"), |
|
|
|
ok = filelib:ensure_dir(filename:join(Dir, "dummy")), |
|
|
|
?DEBUG("Adding ~s to code path~n", [Dir]), |
|
|
|
true = code:add_patha(Dir), |
|
|
|
Config1; |
|
|
|
{Config1, {false, _}} -> |
|
|
|
Config1 |
|
|
|
end, |
|
|
|
update_deps_code_path(Config2, Rest). |
|
|
|
|
|
|
|
find_deps(Config, find=Mode, Deps) -> |
|
|
|
find_deps(Config, Mode, Deps, {[], []}); |
|
|
|
find_deps(Config, read=Mode, Deps) -> |
|
|
|
find_deps(Config, Mode, Deps, []). |
|
|
|
|
|
|
|
find_deps(Config, find, [], {Avail, Missing}) -> |
|
|
|
{Config, {lists:reverse(Avail), lists:reverse(Missing)}}; |
|
|
|
find_deps(Config, read, [], Deps) -> |
|
|
|
{Config, lists:reverse(Deps)}; |
|
|
|
find_deps(Config, Mode, [App | Rest], Acc) when is_atom(App) -> |
|
|
|
find_deps(Config, Mode, [{App, ".*", undefined} | Rest], Acc); |
|
|
|
find_deps(Config, Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) -> |
|
|
|
find_deps(Config, Mode, [{App, VsnRegex, undefined} | Rest], Acc); |
|
|
|
find_deps(Config, Mode, [{App, VsnRegex, Source} | Rest], Acc) -> |
|
|
|
Dep = #dep { app = App, |
|
|
|
vsn_regex = VsnRegex, |
|
|
|
source = Source }, |
|
|
|
{Availability, FoundDir} = find_dep(Dep), |
|
|
|
find_deps(Mode, Rest, acc_deps(Mode, Availability, Dep, FoundDir, Acc)); |
|
|
|
find_deps(_Mode, [Other | _Rest], _Acc) -> |
|
|
|
{Config1, {Availability, FoundDir}} = find_dep(Config, Dep), |
|
|
|
find_deps(Config1, Mode, Rest, |
|
|
|
acc_deps(Mode, Availability, Dep, FoundDir, Acc)); |
|
|
|
find_deps(_Config, _Mode, [Other | _Rest], _Acc) -> |
|
|
|
?ABORT("Invalid dependency specification ~p in ~s\n", |
|
|
|
[Other, rebar_utils:get_cwd()]). |
|
|
|
|
|
|
|
find_dep(Dep) -> |
|
|
|
find_dep(Config, Dep) -> |
|
|
|
%% Find a dep based on its source, |
|
|
|
%% e.g. {git, "https://github.com/mochi/mochiweb.git", "HEAD"} |
|
|
|
%% Deps with a source must be found (or fetched) locally. |
|
|
|
%% Those without a source may be satisfied from lib dir (get_lib_dir). |
|
|
|
find_dep(Dep, Dep#dep.source). |
|
|
|
find_dep(Config, Dep, Dep#dep.source). |
|
|
|
|
|
|
|
find_dep(Dep, undefined) -> |
|
|
|
find_dep(Config, Dep, undefined) -> |
|
|
|
%% 'source' is undefined. If Dep is not satisfied locally, |
|
|
|
%% go ahead and find it amongst the lib_dir's. |
|
|
|
case find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)) of |
|
|
|
{avail, _Dir} = Avail -> Avail; |
|
|
|
{missing, _} -> find_dep_in_dir(Dep, get_lib_dir(Dep#dep.app)) |
|
|
|
case find_dep_in_dir(Config, Dep, get_deps_dir(Dep#dep.app)) of |
|
|
|
{_Config1, {avail, _Dir}} = Avail -> |
|
|
|
Avail; |
|
|
|
{Config1, {missing, _}} -> |
|
|
|
find_dep_in_dir(Config1, Dep, get_lib_dir(Dep#dep.app)) |
|
|
|
end; |
|
|
|
find_dep(Dep, _Source) -> |
|
|
|
find_dep(Config, Dep, _Source) -> |
|
|
|
%% _Source is defined. Regardless of what it is, we must find it |
|
|
|
%% locally satisfied or fetch it from the original source |
|
|
|
%% into the project's deps |
|
|
|
find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)). |
|
|
|
find_dep_in_dir(Config, Dep, get_deps_dir(Dep#dep.app)). |
|
|
|
|
|
|
|
find_dep_in_dir(_Dep, {false, Dir}) -> |
|
|
|
{missing, Dir}; |
|
|
|
find_dep_in_dir(Dep, {true, Dir}) -> |
|
|
|
find_dep_in_dir(Config, _Dep, {false, Dir}) -> |
|
|
|
{Config, {missing, Dir}}; |
|
|
|
find_dep_in_dir(Config, Dep, {true, Dir}) -> |
|
|
|
App = Dep#dep.app, |
|
|
|
VsnRegex = Dep#dep.vsn_regex, |
|
|
|
case is_app_available(App, VsnRegex, Dir) of |
|
|
|
{true, _AppFile} -> {avail, Dir}; |
|
|
|
{false, _} -> {missing, Dir} |
|
|
|
case is_app_available(Config, App, VsnRegex, Dir) of |
|
|
|
{Config1, {true, _AppFile}} -> {Config1, {avail, Dir}}; |
|
|
|
{Config1, {false, _}} -> {Config1, {missing, Dir}} |
|
|
|
end. |
|
|
|
|
|
|
|
acc_deps(find, avail, Dep, AppDir, {Avail, Missing}) -> |
|
|
@ -288,57 +304,59 @@ require_source_engine(Source) -> |
|
|
|
true = source_engine_avail(Source), |
|
|
|
ok. |
|
|
|
|
|
|
|
is_app_available(App, VsnRegex, Path) -> |
|
|
|
is_app_available(Config, App, VsnRegex, Path) -> |
|
|
|
?DEBUG("is_app_available, looking for App ~p with Path ~p~n", [App, Path]), |
|
|
|
case rebar_app_utils:is_app_dir(Path) of |
|
|
|
{true, AppFile} -> |
|
|
|
case rebar_app_utils:app_name(AppFile) of |
|
|
|
App -> |
|
|
|
Vsn = rebar_app_utils:app_vsn(AppFile), |
|
|
|
case rebar_app_utils:app_name(Config, AppFile) of |
|
|
|
{Config1, App} -> |
|
|
|
{Config2, Vsn} = rebar_app_utils:app_vsn(Config1, AppFile), |
|
|
|
?INFO("Looking for ~s-~s ; found ~s-~s at ~s\n", |
|
|
|
[App, VsnRegex, App, Vsn, Path]), |
|
|
|
case re:run(Vsn, VsnRegex, [{capture, none}]) of |
|
|
|
match -> |
|
|
|
{true, Path}; |
|
|
|
{Config2, {true, Path}}; |
|
|
|
nomatch -> |
|
|
|
?WARN("~s has version ~p; requested regex was ~s\n", |
|
|
|
[AppFile, Vsn, VsnRegex]), |
|
|
|
{false, {version_mismatch, |
|
|
|
{AppFile, |
|
|
|
{expected, VsnRegex}, {has, Vsn}}}} |
|
|
|
{Config2, |
|
|
|
{false, {version_mismatch, |
|
|
|
{AppFile, |
|
|
|
{expected, VsnRegex}, {has, Vsn}}}}} |
|
|
|
end; |
|
|
|
OtherApp -> |
|
|
|
{Config1, OtherApp} -> |
|
|
|
?WARN("~s has application id ~p; expected ~p\n", |
|
|
|
[AppFile, OtherApp, App]), |
|
|
|
{false, {name_mismatch, |
|
|
|
{AppFile, {expected, App}, {has, OtherApp}}}} |
|
|
|
{Config1, |
|
|
|
{false, {name_mismatch, |
|
|
|
{AppFile, {expected, App}, {has, OtherApp}}}}} |
|
|
|
end; |
|
|
|
false -> |
|
|
|
?WARN("Expected ~s to be an app dir (containing ebin/*.app), " |
|
|
|
"but no .app found.\n", [Path]), |
|
|
|
{false, {missing_app_file, Path}} |
|
|
|
{Config, {false, {missing_app_file, Path}}} |
|
|
|
end. |
|
|
|
|
|
|
|
use_source(Dep) -> |
|
|
|
use_source(Dep, 3). |
|
|
|
use_source(Config, Dep) -> |
|
|
|
use_source(Config, Dep, 3). |
|
|
|
|
|
|
|
use_source(Dep, 0) -> |
|
|
|
use_source(_Config, Dep, 0) -> |
|
|
|
?ABORT("Failed to acquire source from ~p after 3 tries.\n", |
|
|
|
[Dep#dep.source]); |
|
|
|
use_source(Dep, Count) -> |
|
|
|
use_source(Config, Dep, Count) -> |
|
|
|
case filelib:is_dir(Dep#dep.dir) of |
|
|
|
true -> |
|
|
|
%% Already downloaded -- verify the versioning matches the regex |
|
|
|
case is_app_available(Dep#dep.app, |
|
|
|
case is_app_available(Config, Dep#dep.app, |
|
|
|
Dep#dep.vsn_regex, Dep#dep.dir) of |
|
|
|
{true, _} -> |
|
|
|
{Config1, {true, _}} -> |
|
|
|
Dir = filename:join(Dep#dep.dir, "ebin"), |
|
|
|
ok = filelib:ensure_dir(filename:join(Dir, "dummy")), |
|
|
|
%% Available version matches up -- we're good to go; |
|
|
|
%% add the app dir to our code path |
|
|
|
true = code:add_patha(Dir), |
|
|
|
Dep; |
|
|
|
{false, Reason} -> |
|
|
|
{Config1, Dep}; |
|
|
|
{_Config1, {false, Reason}} -> |
|
|
|
%% The app that was downloaded doesn't match up (or had |
|
|
|
%% errors or something). For the time being, abort. |
|
|
|
?ABORT("Dependency dir ~s failed application validation " |
|
|
@ -349,7 +367,7 @@ use_source(Dep, Count) -> |
|
|
|
require_source_engine(Dep#dep.source), |
|
|
|
{true, TargetDir} = get_deps_dir(Dep#dep.app), |
|
|
|
download_source(TargetDir, Dep#dep.source), |
|
|
|
use_source(Dep#dep { dir = TargetDir }, Count-1) |
|
|
|
use_source(Config, Dep#dep { dir = TargetDir }, Count-1) |
|
|
|
end. |
|
|
|
|
|
|
|
download_source(AppDir, {hg, Url, Rev}) -> |
|
|
@ -433,9 +451,6 @@ update_source(AppDir, {bzr, _Url, Rev}) -> |
|
|
|
update_source(AppDir, {rsync, Url}) -> |
|
|
|
rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%% =================================================================== |
|
|
|
%% Source helper functions |
|
|
|
%% =================================================================== |
|
|
|