Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

560 rindas
24 KiB

pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
pirms 10 gadiem
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% -------------------------------------------------------------------
  27. -module(rebar_prv_install_deps).
  28. -behaviour(provider).
  29. -export([init/1,
  30. do/1,
  31. format_error/1]).
  32. -include("rebar.hrl").
  33. -include_lib("providers/include/providers.hrl").
  34. -export([handle_deps/3,
  35. handle_deps/4,
  36. handle_deps/5]).
  37. -export_type([dep/0]).
  38. -define(PROVIDER, install_deps).
  39. -define(DEPS, [app_discovery]).
  40. -type src_dep() :: {atom(), {atom(), string(), string()}}
  41. | {atom(), string(), {atom(), string(), string()}}.
  42. -type pkg_dep() :: {atom(), binary()} | atom().
  43. -type dep() :: src_dep() | pkg_dep().
  44. %% ===================================================================
  45. %% Public API
  46. %% ===================================================================
  47. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  48. init(State) ->
  49. State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
  50. {module, ?MODULE},
  51. {bare, true},
  52. {deps, ?DEPS},
  53. {example, undefined},
  54. {short_desc, ""},
  55. {desc, ""},
  56. {opts, []}])),
  57. {ok, State1}.
  58. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  59. do(State) ->
  60. try
  61. ?INFO("Verifying dependencies...", []),
  62. Profiles = rebar_state:current_profiles(State),
  63. ProjectApps = rebar_state:project_apps(State),
  64. {Apps, State1} =
  65. lists:foldl(fun deps_per_profile/2, {[], State}, lists:reverse(Profiles)),
  66. Source = ProjectApps ++ Apps,
  67. case find_cycles(Source) of
  68. {cycles, Cycles} ->
  69. ?PRV_ERROR({cycles, Cycles});
  70. {error, Error} ->
  71. {error, Error};
  72. no_cycle ->
  73. case compile_order(Source, ProjectApps) of
  74. {ok, ToCompile} ->
  75. {ok, rebar_state:deps_to_build(State1, ToCompile)};
  76. {error, Error} ->
  77. {error, Error}
  78. end
  79. end
  80. catch
  81. %% maybe_fetch will maybe_throw an exception to break out of some loops
  82. _:{error, Reason} ->
  83. {error, Reason}
  84. end.
  85. -spec format_error(any()) -> iolist().
  86. format_error({bad_constraint, Name, Constraint}) ->
  87. io_lib:format("Unable to parse version for package ~s: ~s", [Name, Constraint]);
  88. format_error({parse_dep, Dep}) ->
  89. io_lib:format("Failed parsing dep ~p", [Dep]);
  90. format_error({missing_package, Package, Version}) ->
  91. io_lib:format("Package not found in registry: ~s-~s", [Package, Version]);
  92. format_error({cycles, Cycles}) ->
  93. Prints = [["applications: ",
  94. [io_lib:format("~s ", [Dep]) || Dep <- Cycle],
  95. "depend on each other~n"]
  96. || Cycle <- Cycles],
  97. ["Dependency cycle(s) detected:~n", Prints];
  98. format_error(Reason) ->
  99. io_lib:format("~p", [Reason]).
  100. -spec handle_deps(atom(), rebar_state:t(), list()) ->
  101. {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}.
  102. handle_deps(Profile, State, Deps) ->
  103. handle_deps(Profile, State, Deps, false, []).
  104. -spec handle_deps(atom(), rebar_state:t(), list(), list() | boolean()) ->
  105. {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}.
  106. handle_deps(Profile, State, Deps, Upgrade) when is_boolean(Upgrade) ->
  107. handle_deps(Profile, State, Deps, Upgrade, []);
  108. handle_deps(Profile, State, Deps, Locks) when is_list(Locks) ->
  109. Upgrade = rebar_state:get(State, upgrade, false),
  110. handle_deps(Profile, State, Deps, Upgrade, Locks).
  111. -spec handle_deps(atom(), rebar_state:t(), list(), boolean() | {true, binary(), integer()}, list())
  112. -> {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}.
  113. handle_deps(_Profile, State, [], _, _) ->
  114. {ok, [], State};
  115. handle_deps(Profile, State, Deps, Upgrade, Locks) ->
  116. %% Read in package index and dep graph
  117. {Packages, Graph} = rebar_packages:get_packages(State),
  118. %% Split source deps from pkg deps, needed to keep backwards compatibility
  119. DepsDir = rebar_dir:deps_dir(State),
  120. {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, 0),
  121. %% Fetch transitive src deps
  122. {State1, SrcApps, PkgDeps1, Seen} =
  123. update_src_deps(Profile, 0, SrcDeps, PkgDeps, [], State, Upgrade, sets:new(), Locks),
  124. {Solved, State2} =
  125. update_pkg_deps(Profile, Packages, PkgDeps1, Graph, Upgrade, Seen, State1),
  126. AllDeps = lists:ukeymerge(2
  127. ,lists:ukeysort(2, SrcApps)
  128. ,lists:ukeysort(2, Solved)),
  129. %% Sort all apps to build order
  130. State3 = rebar_state:all_deps(State2, AllDeps),
  131. CodePaths = [rebar_app_info:ebin_dir(A) || A <- AllDeps],
  132. ok = code:add_pathsa(CodePaths),
  133. {ok, AllDeps, State3}.
  134. %% ===================================================================
  135. %% Internal functions
  136. %% ===================================================================
  137. deps_per_profile(Profile, {Apps, State}) ->
  138. Locks = rebar_state:get(State, {locks, Profile}, []),
  139. ProfileDeps = rebar_state:get(State, {deps, Profile}, []),
  140. {ok, NewApps, NewState} = handle_deps(Profile, State, ProfileDeps, Locks),
  141. {NewApps++Apps, NewState}.
  142. find_cycles(Apps) ->
  143. case rebar_digraph:compile_order(Apps) of
  144. {error, {cycles, Cycles}} -> {cycles, Cycles};
  145. {error, Error} -> {error, Error};
  146. {ok, _} -> no_cycle
  147. end.
  148. compile_order(Source, ProjectApps) ->
  149. case rebar_digraph:compile_order(Source) of
  150. {ok, Sort} ->
  151. %% Valid apps are compiled and good
  152. {ok, lists:dropwhile(fun not_needs_compile/1, Sort -- ProjectApps)};
  153. {error, Error} ->
  154. {error, Error}
  155. end.
  156. update_pkg_deps(Profile, Packages, PkgDeps, Graph, Upgrade, Seen, State) ->
  157. case PkgDeps of
  158. [] -> %% No pkg deps
  159. {[], State};
  160. PkgDeps ->
  161. %% Find pkg deps needed
  162. S = case rebar_digraph:cull_deps(Graph, PkgDeps) of
  163. {ok, [], _} ->
  164. throw({rebar_digraph, no_solution});
  165. {ok, Solution, []} ->
  166. Solution;
  167. {ok, Solution, Discarded} ->
  168. [warn_skip_pkg(Pkg, State) || Pkg <- Discarded],
  169. Solution
  170. end,
  171. update_pkg_deps(Profile, S, Packages, Upgrade, Seen, State)
  172. end.
  173. update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State) ->
  174. %% Create app_info record for each pkg dep
  175. DepsDir = rebar_dir:deps_dir(State),
  176. {Solved, _, State1}
  177. = lists:foldl(fun(Pkg, {Acc, SeenAcc, StateAcc}) ->
  178. handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, StateAcc)
  179. end, {[], Seen, State}, Pkgs),
  180. {Solved, State1}.
  181. handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) ->
  182. AppInfo = package_to_app(DepsDir, Packages, Pkg),
  183. Level = rebar_app_info:dep_level(AppInfo),
  184. {NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level),
  185. maybe_fetch(AppInfo, Upgrade, Seen, NewState),
  186. {[AppInfo | Fetched], NewSeen, NewState}.
  187. maybe_lock(Profile, AppInfo, Seen, State, Level) ->
  188. case rebar_app_info:is_checkout(AppInfo) of
  189. false ->
  190. case Profile of
  191. default ->
  192. Name = rebar_app_info:name(AppInfo),
  193. case sets:is_element(Name, Seen) of
  194. false ->
  195. Locks = rebar_state:lock(State),
  196. case lists:any(fun(App) -> rebar_app_info:name(App) =:= Name end, Locks) of
  197. true ->
  198. {sets:add_element(Name, Seen), State};
  199. false ->
  200. {sets:add_element(Name, Seen),
  201. rebar_state:lock(State, rebar_app_info:dep_level(AppInfo, Level))}
  202. end;
  203. true ->
  204. {Seen, State}
  205. end;
  206. _ ->
  207. {Seen, State}
  208. end;
  209. true ->
  210. {Seen, State}
  211. end.
  212. package_to_app(DepsDir, Packages, {Name, Vsn, Level}) ->
  213. case dict:find({Name, Vsn}, Packages) of
  214. error ->
  215. throw(?PRV_ERROR({missing_package, Name, Vsn}));
  216. {ok, P} ->
  217. PkgDeps = [{PkgName, PkgVsn}
  218. || {PkgName,PkgVsn} <- proplists:get_value(<<"deps">>, P, [])],
  219. {ok, AppInfo} = rebar_app_info:new(Name, Vsn),
  220. AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
  221. AppInfo2 = rebar_app_info:dir(AppInfo1, rebar_dir:deps_dir(DepsDir, Name)),
  222. AppInfo3 = rebar_app_info:dep_level(AppInfo2, Level),
  223. rebar_app_info:source(AppInfo3, {pkg, Name, Vsn})
  224. end.
  225. -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())}.
  226. update_src_deps(Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) ->
  227. case lists:foldl(
  228. fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) ->
  229. update_src_dep(AppInfo, Profile, Level,
  230. SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc,
  231. Upgrade, SeenAcc, Locks, LocksAcc)
  232. end,
  233. {[], PkgDeps, SrcApps, State, Seen, Locks},
  234. rebar_utils:sort_deps(SrcDeps)) of
  235. {[], NewPkgDeps, NewSrcApps, State1, Seen1, _NewLocks} ->
  236. {State1, NewSrcApps, NewPkgDeps, Seen1};
  237. {NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Seen1, NewLocks} ->
  238. update_src_deps(Profile, Level+1, NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Upgrade, Seen1, NewLocks)
  239. end.
  240. update_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) ->
  241. %% If not seen, add to list of locks to write out
  242. Name = rebar_app_info:name(AppInfo),
  243. case sets:is_element(Name, Seen) of
  244. true ->
  245. update_seen_src_dep(AppInfo, Level,
  246. SrcDeps, PkgDeps, SrcApps,
  247. State, Upgrade, Seen, BaseLocks, Locks);
  248. false ->
  249. update_unseen_src_dep(AppInfo, Profile, Level,
  250. SrcDeps, PkgDeps, SrcApps,
  251. State, Upgrade, Seen, Locks)
  252. end.
  253. update_seen_src_dep(AppInfo, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) ->
  254. Name = rebar_app_info:name(AppInfo),
  255. %% If seen from lock file don't print warning about skipping
  256. case lists:keymember(Name, 1, BaseLocks) of
  257. false ->
  258. warn_skip_deps(AppInfo, State);
  259. true ->
  260. ok
  261. end,
  262. %% scan for app children here if upgrading
  263. case Upgrade of
  264. false ->
  265. {SrcDeps, PkgDeps, SrcApps, State, Seen, Locks};
  266. true ->
  267. {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, NewLocks}
  268. = handle_dep(AppInfo, SrcDeps, PkgDeps, SrcApps,
  269. Level, State, Locks),
  270. {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, Seen, NewLocks}
  271. end.
  272. update_unseen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) ->
  273. {NewSeen, State1} = maybe_lock(Profile, AppInfo, Seen, State, Level),
  274. {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewLocks}
  275. = case Upgrade of
  276. true ->
  277. handle_upgrade(AppInfo, SrcDeps, PkgDeps, SrcApps,
  278. Level, State1, Locks);
  279. _ ->
  280. maybe_fetch(AppInfo, false, Seen, State1),
  281. handle_dep(AppInfo, SrcDeps, PkgDeps, SrcApps,
  282. Level, State1, Locks)
  283. end,
  284. {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewSeen, NewLocks}.
  285. handle_upgrade(AppInfo, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) ->
  286. Name = rebar_app_info:name(AppInfo),
  287. case lists:keyfind(Name, 1, Locks) of
  288. false ->
  289. case maybe_fetch(AppInfo, true, sets:new(), State) of
  290. true ->
  291. handle_dep(AppInfo, SrcDeps, PkgDeps, SrcApps,
  292. Level, State, Locks);
  293. false ->
  294. {[AppInfo|SrcDeps], PkgDeps, SrcApps, State, Locks}
  295. end;
  296. _StillLocked ->
  297. {[AppInfo|SrcDeps], PkgDeps, SrcApps, State, Locks}
  298. end.
  299. handle_dep(AppInfo, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) ->
  300. DepsDir = rebar_dir:deps_dir(State),
  301. {AppInfo1, NewSrcDeps, NewPkgDeps, NewLocks} =
  302. handle_dep(State, DepsDir, AppInfo, Locks, Level),
  303. AppInfo2 = rebar_app_info:dep_level(AppInfo1, Level),
  304. {NewSrcDeps ++ SrcDeps
  305. ,NewPkgDeps++PkgDeps
  306. ,[AppInfo2 | SrcApps]
  307. ,State
  308. ,NewLocks}.
  309. -spec handle_dep(rebar_state:t(), file:filename_all(), rebar_app_info:t(), list(), integer()) ->
  310. {rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()]}.
  311. handle_dep(State, DepsDir, AppInfo, Locks, Level) ->
  312. Profiles = rebar_state:current_profiles(State),
  313. Name = rebar_app_info:name(AppInfo),
  314. C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
  315. S = rebar_app_info:state(AppInfo),
  316. S1 = rebar_state:new(S, C, rebar_app_info:dir(AppInfo)),
  317. S2 = rebar_state:apply_profiles(S1, Profiles),
  318. S3 = rebar_state:apply_overrides(S2, Name),
  319. AppInfo1 = rebar_app_info:state(AppInfo, S3),
  320. Deps = rebar_state:get(S3, deps, []),
  321. %% Upgrade lock level to be the level the dep will have in this dep tree
  322. NewLocks = [{DepName, Source, LockLevel+Level} ||
  323. {DepName, Source, LockLevel} <- rebar_state:get(S3, {locks, default}, [])],
  324. AppInfo2 = rebar_app_info:deps(AppInfo1, rebar_state:deps_names(Deps)),
  325. {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S3, Locks, Level),
  326. {AppInfo2, SrcDeps, PkgDeps, Locks++NewLocks}.
  327. -spec maybe_fetch(rebar_app_info:t(), boolean() | {true, binary(), integer()},
  328. sets:set(binary()), rebar_state:t()) -> boolean().
  329. maybe_fetch(AppInfo, Upgrade, Seen, State) ->
  330. AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
  331. %% Don't fetch dep if it exists in the _checkouts dir
  332. case rebar_app_info:is_checkout(AppInfo) of
  333. true ->
  334. false;
  335. false ->
  336. case rebar_app_discover:find_app(AppDir, all) of
  337. false ->
  338. case in_default(AppInfo, State) of
  339. false ->
  340. fetch_app(AppInfo, AppDir, State);
  341. {true, FoundApp} ->
  342. ?INFO("Linking ~s to ~s", [rebar_app_info:dir(FoundApp), AppDir]),
  343. filelib:ensure_dir(AppDir),
  344. rebar_file_utils:symlink_or_copy(rebar_app_info:dir(FoundApp), AppDir),
  345. true
  346. end;
  347. {true, _} ->
  348. case sets:is_element(rebar_app_info:name(AppInfo), Seen) of
  349. true ->
  350. false;
  351. false ->
  352. maybe_upgrade(AppInfo, AppDir, Upgrade, State)
  353. end
  354. end
  355. end.
  356. in_default(AppInfo, State) ->
  357. Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
  358. DefaultAppDir = filename:join([rebar_state:get(State, base_dir), "default", "lib", Name]),
  359. rebar_app_discover:find_app(DefaultAppDir, all).
  360. -spec parse_deps(binary(), list(), list(), list(), integer()) -> {[rebar_app_info:t()], [pkg_dep()]}.
  361. parse_deps(DepsDir, Deps, State, Locks, Level) ->
  362. lists:foldl(fun(Dep, Acc) ->
  363. Name = case Dep of
  364. Dep when is_tuple(Dep) ->
  365. element(1, Dep);
  366. Dep ->
  367. Dep
  368. end,
  369. case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of
  370. false ->
  371. parse_dep(Dep, Acc, DepsDir, State);
  372. LockedDep ->
  373. LockedLevel = element(3, LockedDep),
  374. case LockedLevel > Level of
  375. true ->
  376. parse_dep(Dep, Acc, DepsDir, State);
  377. false ->
  378. parse_dep(LockedDep, Acc, DepsDir, State)
  379. end
  380. end
  381. end, {[], []}, Deps).
  382. parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_list(Vsn) ->
  383. CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
  384. case rebar_app_info:discover(CheckoutsDir) of
  385. {ok, _App} ->
  386. Dep = new_dep(DepsDir, Name, [], [], State),
  387. {[Dep | SrcDepsAcc], PkgDepsAcc};
  388. not_found ->
  389. {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
  390. ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}
  391. end;
  392. parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_atom(Name) ->
  393. CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
  394. case rebar_app_info:discover(CheckoutsDir) of
  395. {ok, _App} ->
  396. Dep = new_dep(DepsDir, Name, [], [], State),
  397. {[Dep | SrcDepsAcc], PkgDepsAcc};
  398. not_found ->
  399. {SrcDepsAcc, [ec_cnv:to_binary(Name) | PkgDepsAcc]}
  400. end;
  401. parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
  402. Dep = new_dep(DepsDir, Name, [], Source, State),
  403. {[Dep | SrcDepsAcc], PkgDepsAcc};
  404. parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
  405. Dep = new_dep(DepsDir, Name, [], Source, State),
  406. {[Dep | SrcDepsAcc], PkgDepsAcc};
  407. parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
  408. Dep = new_dep(DepsDir, Name, [], Source, State),
  409. {[Dep | SrcDepsAcc], PkgDepsAcc};
  410. parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
  411. ?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
  412. Dep = new_dep(DepsDir, Name, [], Source, State),
  413. {[Dep | SrcDepsAcc], PkgDepsAcc};
  414. parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_integer(Level) ->
  415. CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
  416. case rebar_app_info:discover(CheckoutsDir) of
  417. {ok, _App} ->
  418. Dep = new_dep(DepsDir, Name, [], [], State),
  419. {[Dep | SrcDepsAcc], PkgDepsAcc};
  420. not_found ->
  421. {SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]}
  422. end;
  423. parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source)
  424. , is_integer(Level) ->
  425. Dep = new_dep(DepsDir, Name, [], Source, State),
  426. {[Dep | SrcDepsAcc], PkgDepsAcc};
  427. parse_dep(Dep, _, _, _) ->
  428. throw(?PRV_ERROR({parse_dep, Dep})).
  429. new_dep(DepsDir, Name, Vsn, Source, State) ->
  430. CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
  431. {ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of
  432. {ok, App} ->
  433. {ok, rebar_app_info:is_checkout(App, true)};
  434. not_found ->
  435. Dir = ec_cnv:to_list(filename:join(DepsDir, Name)),
  436. case rebar_app_info:discover(Dir) of
  437. {ok, App} ->
  438. {ok, App};
  439. not_found ->
  440. rebar_app_info:new(Name, Vsn,
  441. ec_cnv:to_list(filename:join(DepsDir, Name)))
  442. end
  443. end,
  444. C = rebar_config:consult(rebar_app_info:dir(Dep)),
  445. S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(Dep)),
  446. Overrides = rebar_state:get(State, overrides, []),
  447. ParentOverrides = rebar_state:overrides(State),
  448. Dep1 = rebar_app_info:state(Dep,
  449. rebar_state:overrides(S, ParentOverrides++Overrides)),
  450. rebar_app_info:source(Dep1, Source).
  451. fetch_app(AppInfo, AppDir, State) ->
  452. ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
  453. Source = rebar_app_info:source(AppInfo),
  454. case rebar_fetch:download_source(AppDir, Source, State) of
  455. {error, Reason} ->
  456. throw(Reason);
  457. Result ->
  458. Result
  459. end.
  460. maybe_upgrade(AppInfo, AppDir, false, _State) ->
  461. Source = rebar_app_info:source(AppInfo),
  462. rebar_fetch:needs_update(AppDir, Source);
  463. maybe_upgrade(AppInfo, AppDir, true, State) ->
  464. Source = rebar_app_info:source(AppInfo),
  465. case rebar_fetch:needs_update(AppDir, Source) of
  466. true ->
  467. ?INFO("Updating ~s", [rebar_app_info:name(AppInfo)]),
  468. case rebar_fetch:download_source(AppDir, Source, State) of
  469. {error, Reason} ->
  470. throw(Reason);
  471. Result ->
  472. Result
  473. end;
  474. false ->
  475. false
  476. end.
  477. -spec parse_goal(binary(), binary()) -> pkg_dep().
  478. parse_goal(Name, Constraint) ->
  479. case re:run(Constraint, "([^\\d]*)(\\d.*)", [{capture, [1,2], binary}]) of
  480. {match, [<<>>, Vsn]} ->
  481. {Name, Vsn};
  482. {match, [Op, Vsn]} ->
  483. {Name, Vsn, binary_to_atom(Op, utf8)};
  484. nomatch ->
  485. throw(?PRV_ERROR({bad_constraint, Name, Constraint}))
  486. end.
  487. warn_skip_deps(AppInfo, State) ->
  488. Msg = "Skipping ~s (from ~p) as an app of the same name "
  489. "has already been fetched~n",
  490. Args = [rebar_app_info:name(AppInfo),
  491. rebar_app_info:source(AppInfo)],
  492. case rebar_state:get(State, deps_error_on_conflict, false) of
  493. false -> ?WARN(Msg, Args);
  494. true -> ?ERROR(Msg, Args), ?FAIL
  495. end.
  496. warn_skip_pkg({Name, Source}, State) ->
  497. Msg = "Skipping ~s (version ~s from package index) as an app of the same "
  498. "name has already been fetched~n",
  499. Args = [Name, Source],
  500. case rebar_state:get(State, deps_error_on_conflict, false) of
  501. false -> ?WARN(Msg, Args);
  502. true -> ?ERROR(Msg, Args), ?FAIL
  503. end.
  504. not_needs_compile(App) ->
  505. not(rebar_app_info:is_checkout(App))
  506. andalso rebar_app_info:valid(App).