Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

264 Zeilen
11 KiB

  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(rebar_provider).
  29. -export([init/1,
  30. do/1]).
  31. -include("rebar.hrl").
  32. -export([setup_env/1]).
  33. %% for internal use only
  34. -export([get_deps_dir/1]).
  35. -export([get_deps_dir/2]).
  36. -define(PROVIDER, install_deps).
  37. -define(DEPS, [app_discovery]).
  38. -type src_dep() :: {atom(), string(), {atom(), string(), string()}}.
  39. -type binary_dep() :: {atom(), binary()} | atom().
  40. -type dep() :: src_dep() | binary_dep().
  41. %% ===================================================================
  42. %% Public API
  43. %% ===================================================================
  44. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  45. init(State) ->
  46. State1 = rebar_state:add_provider(State, #provider{name = ?PROVIDER,
  47. provider_impl = ?MODULE,
  48. bare = false,
  49. deps = ?DEPS,
  50. example = "",
  51. short_desc = "Install dependencies",
  52. desc = info("Install dependencies"),
  53. opts = []}),
  54. {ok, State1}.
  55. -spec do(rebar_state:t()) -> {ok, rebar_state:t()}.
  56. do(State) ->
  57. case rebar_state:get(State, locks, []) of
  58. [] ->
  59. handle_deps(State, ordsets:from_list(rebar_state:get(State, deps, [])));
  60. _Locks ->
  61. {ok, State}
  62. end.
  63. %% set REBAR_DEPS_DIR and ERL_LIBS environment variables
  64. setup_env(State) ->
  65. DepsDir = get_deps_dir(State),
  66. %% include rebar's DepsDir in ERL_LIBS
  67. Separator = case os:type() of
  68. {win32, nt} ->
  69. ";";
  70. _ ->
  71. ":"
  72. end,
  73. ERL_LIBS = case os:getenv("ERL_LIBS") of
  74. false ->
  75. {"ERL_LIBS", DepsDir};
  76. PrevValue ->
  77. {"ERL_LIBS", DepsDir ++ Separator ++ PrevValue}
  78. end,
  79. [{"REBAR_DEPS_DIR", DepsDir}, ERL_LIBS].
  80. -spec get_deps_dir(rebar_state:t()) -> file:filename_all().
  81. get_deps_dir(State) ->
  82. BaseDir = rebar_state:get(State, base_dir, ""),
  83. get_deps_dir(BaseDir, "deps").
  84. -spec get_deps_dir(file:filename_all(), rebar_state:t()) -> file:filename_all().
  85. get_deps_dir(DepsDir, App) ->
  86. filename:join(DepsDir, App).
  87. %% ===================================================================
  88. %% Internal functions
  89. %% ===================================================================
  90. -spec handle_deps(rebar_state:t(), [dep()]) -> {ok, rebar_state:t()}.
  91. handle_deps(State, []) ->
  92. {ok, State};
  93. handle_deps(State, Deps) ->
  94. %% Read in package index and dep graph
  95. {Packages, Graph} = rebar_packages:get_packages(State),
  96. ProjectApps = rebar_state:project_apps(State),
  97. %% Split source deps form binary deps, needed to keep backwards compatibility
  98. DepsDir = get_deps_dir(State),
  99. {SrcDeps, BinaryDeps} = parse_deps(DepsDir, Deps),
  100. State1 = rebar_state:src_deps(rebar_state:binary_deps(State, BinaryDeps),
  101. SrcDeps),
  102. %% Fetch transitive src deps
  103. State2 = update_src_deps(State1),
  104. Solved = case rebar_state:binary_deps(State2) of
  105. [] -> %% No binary deps
  106. [];
  107. BinaryDeps1 ->
  108. %% Find binary deps needed
  109. {ok, S} = rlx_depsolver:solve(Graph, BinaryDeps1),
  110. %% Create app_info record for each binary dep
  111. lists:map(fun({Name, Vsn}) ->
  112. AppInfo = package_to_app(DepsDir
  113. ,Packages
  114. ,Name
  115. ,Vsn),
  116. ok = maybe_fetch(AppInfo),
  117. AppInfo
  118. end, S)
  119. end,
  120. Source = ProjectApps ++ ordsets:to_list(rebar_state:src_deps(State2)),
  121. AllDeps = ordsets:union([ordsets:from_list(ProjectApps)
  122. ,ordsets:to_list(rebar_state:src_deps(State2))
  123. ,ordsets:from_list(Solved)]),
  124. %% Sort all apps to build order
  125. State3 = rebar_state:set(State2, all_deps, AllDeps),
  126. {ok, Sort} = rebar_topo:sort_apps(ordsets:to_list(Source)),
  127. {ok, rebar_state:set(State3, deps_to_build, lists:dropwhile(fun is_valid/1, Sort) -- ProjectApps)}.
  128. -spec is_valid(rebar_app_info:t()) -> boolean().
  129. is_valid(App) ->
  130. rebar_app_info:valid(App).
  131. -spec package_to_app(file:name(), dict:dict(), binary(), binary()) -> rebar_app_info:t().
  132. package_to_app(DepsDir, Packages, Name, Vsn) ->
  133. FmtVsn = ec_cnv:to_binary(rlx_depsolver:format_version(Vsn)),
  134. {ok, P} = dict:find({Name, FmtVsn}, Packages),
  135. PkgDeps = proplists:get_value(<<"deps">>, P),
  136. Link = proplists:get_value(<<"link">>, P),
  137. {ok, AppInfo} = rebar_app_info:new(Name, FmtVsn),
  138. AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
  139. AppInfo2 =
  140. rebar_app_info:dir(AppInfo1, get_deps_dir(DepsDir, <<Name/binary, "-", FmtVsn/binary>>)),
  141. rebar_app_info:source(AppInfo2, {Name, FmtVsn, Link}).
  142. -spec update_src_deps(rebar_state:t()) -> rebat_state:t().
  143. update_src_deps(State) ->
  144. SrcDeps = rebar_state:src_deps(State),
  145. DepsDir = get_deps_dir(State),
  146. case lists:foldl(fun(AppInfo, {SrcDepsAcc, BinaryDepsAcc}) ->
  147. ok = maybe_fetch(AppInfo),
  148. {AppInfo1, NewSrcDeps, NewBinaryDeps} = handle_dep(DepsDir, AppInfo),
  149. {ordsets:union(ordsets:add_element(AppInfo1, SrcDepsAcc), NewSrcDeps)
  150. ,NewBinaryDeps++BinaryDepsAcc}
  151. end, {ordsets:new(), rebar_state:binary_deps(State)}, SrcDeps) of
  152. {NewSrcDeps, NewBinaryDeps} when length(SrcDeps) =:= length(NewSrcDeps) ->
  153. rebar_state:src_deps(rebar_state:binary_deps(State, NewBinaryDeps), NewSrcDeps);
  154. {NewSrcDeps, NewBinaryDeps} ->
  155. State1 = rebar_state:src_deps(rebar_state:binary_deps(State, NewBinaryDeps), NewSrcDeps),
  156. update_src_deps(State1)
  157. end.
  158. -spec handle_dep(binary(), rebar_state:t()) -> {[rebar_app_info:t()], [binary_dep()]}.
  159. handle_dep(DepsDir, AppInfo) ->
  160. C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
  161. S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(AppInfo)),
  162. Deps = rebar_state:get(S, deps, []),
  163. AppInfo1 = rebar_app_info:deps(AppInfo, rebar_state:deps_names(S)),
  164. {SrcDeps, BinaryDeps} = parse_deps(DepsDir, Deps),
  165. {AppInfo1, SrcDeps, BinaryDeps}.
  166. -spec maybe_fetch(rebar_app_info:t()) -> ok.
  167. maybe_fetch(AppInfo) ->
  168. AppDir = rebar_app_info:dir(AppInfo),
  169. case filelib:is_dir(AppDir) of
  170. false ->
  171. ?INFO("Fetching ~s~n", [rebar_app_info:name(AppInfo)]),
  172. Source = rebar_app_info:source(AppInfo),
  173. rebar_fetch:download_source(AppDir, Source);
  174. true ->
  175. ok
  176. end.
  177. -spec parse_deps(binary(), [dep()]) -> {ordsets:ordset(rebar_app_info:t()), [binary_dep()]}.
  178. parse_deps(DepsDir, Deps) ->
  179. lists:foldl(fun({Name, Vsn}, {SrcDepsAcc, BinaryDepsAcc}) ->
  180. {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
  181. ,ec_cnv:to_binary(Vsn)) | BinaryDepsAcc]};
  182. (Name, {SrcDepsAcc, BinaryDepsAcc}) when is_atom(Name) ->
  183. {SrcDepsAcc, [ec_cnv:to_binary(Name) | BinaryDepsAcc]};
  184. ({Name, _, Source}, {SrcDepsAcc, BinaryDepsAcc}) ->
  185. {ok, Dep} = rebar_app_info:discover(get_deps_dir(DepsDir, Name)),
  186. Dep1 = rebar_app_info:source(Dep, Source),
  187. {ordsets:add_element(Dep1, SrcDepsAcc), BinaryDepsAcc}
  188. end, {ordsets:new(), []}, Deps).
  189. -spec parse_goal(binary(), binary()) -> binary_dep().
  190. parse_goal(Name, Constraint) ->
  191. case re:run(Constraint, "([^\\d]*)(\\d.*)", [{capture, [1,2], binary}]) of
  192. {match, [<<>>, Vsn]} ->
  193. {Name, Vsn};
  194. {match, [Op, Vsn]} ->
  195. {Name, Vsn, binary_to_atom(Op, utf8)};
  196. nomatch ->
  197. fail
  198. end.
  199. info(Description) ->
  200. io_lib:format("~s.~n"
  201. "~n"
  202. "Valid rebar.config options:~n"
  203. " ~p~n"
  204. " ~p~n"
  205. "Valid command line options:~n"
  206. " deps_dir=\"deps\" (override default or rebar.config deps_dir)~n",
  207. [
  208. Description,
  209. {deps_dir, "deps"},
  210. {deps,
  211. [app_name,
  212. {rebar, "1.0.*"},
  213. {rebar, ".*",
  214. {git, "git://github.com/rebar/rebar.git"}},
  215. {rebar, ".*",
  216. {git, "git://github.com/rebar/rebar.git", "Rev"}},
  217. {rebar, "1.0.*",
  218. {git, "git://github.com/rebar/rebar.git", {branch, "master"}}},
  219. {rebar, "1.0.0",
  220. {git, "git://github.com/rebar/rebar.git", {tag, "1.0.0"}}},
  221. {rebar, "",
  222. {git, "git://github.com/rebar/rebar.git", {branch, "master"}},
  223. [raw]},
  224. {app_name, ".*", {hg, "https://www.example.org/url"}},
  225. {app_name, ".*", {rsync, "Url"}},
  226. {app_name, ".*", {svn, "https://www.example.org/url"}},
  227. {app_name, ".*", {svn, "svn://svn.example.org/url"}},
  228. {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}},
  229. {app_name, ".*", {fossil, "https://www.example.org/url"}},
  230. {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}},
  231. {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}
  232. ]).