You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 rivejä
16 KiB

10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
9 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
9 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
9 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
  1. -module(rebar_app_info).
  2. -export([new/0,
  3. new/1,
  4. new/2,
  5. new/3,
  6. new/4,
  7. new/5,
  8. update_opts/3,
  9. discover/1,
  10. name/1,
  11. name/2,
  12. app_file_src/1,
  13. app_file_src/2,
  14. app_file_src_script/1,
  15. app_file_src_script/2,
  16. app_file/1,
  17. app_file/2,
  18. app_details/1,
  19. app_details/2,
  20. parent/1,
  21. parent/2,
  22. original_vsn/1,
  23. original_vsn/2,
  24. ebin_dir/1,
  25. priv_dir/1,
  26. applications/1,
  27. applications/2,
  28. profiles/1,
  29. profiles/2,
  30. deps/1,
  31. deps/2,
  32. dep_level/1,
  33. dep_level/2,
  34. dir/1,
  35. dir/2,
  36. out_dir/1,
  37. out_dir/2,
  38. default/1,
  39. default/2,
  40. opts/1,
  41. opts/2,
  42. get/2,
  43. get/3,
  44. set/3,
  45. resource_type/1,
  46. resource_type/2,
  47. source/1,
  48. source/2,
  49. is_lock/1,
  50. is_lock/2,
  51. is_checkout/1,
  52. is_checkout/2,
  53. valid/1,
  54. valid/2,
  55. verify_otp_vsn/1,
  56. has_all_artifacts/1,
  57. apply_overrides/2,
  58. add_to_profile/3,
  59. apply_profiles/2,
  60. deduplicate/1,
  61. do_deduplicate/2]).
  62. -include("rebar.hrl").
  63. -include_lib("providers/include/providers.hrl").
  64. -export_type([t/0]).
  65. -record(app_info_t, {name :: binary(),
  66. app_file_src :: file:filename_all() | undefined,
  67. app_file_src_script:: file:filename_all() | undefined,
  68. app_file :: file:filename_all() | undefined,
  69. original_vsn :: binary() | string() | undefined,
  70. parent=root :: binary() | root,
  71. app_details=[] :: list(),
  72. applications=[] :: list(),
  73. deps=[] :: list(),
  74. profiles=[default] :: [atom()],
  75. default=dict:new() :: rebar_dict(),
  76. opts=dict:new() :: rebar_dict(),
  77. dep_level=0 :: integer(),
  78. dir :: file:name(),
  79. out_dir :: file:name(),
  80. resource_type :: pkg | src,
  81. source :: string() | tuple() | checkout | undefined,
  82. is_lock=false :: boolean(),
  83. is_checkout=false :: boolean(),
  84. valid :: boolean()}).
  85. %%============================================================================
  86. %% types
  87. %%============================================================================
  88. -type t() :: #app_info_t{}.
  89. %%============================================================================
  90. %% API
  91. %% ============================================================================
  92. %% @doc Build a new, empty, app info value. This is not of a lot of use and you
  93. %% probably wont be doing this much.
  94. -spec new() -> t().
  95. new() ->
  96. #app_info_t{}.
  97. -spec new(atom() | binary() | string()) ->
  98. {ok, t()}.
  99. new(AppName) ->
  100. {ok, #app_info_t{name=ec_cnv:to_binary(AppName)}}.
  101. -spec new(atom() | binary() | string(), binary() | string()) ->
  102. {ok, t()}.
  103. new(AppName, Vsn) ->
  104. {ok, #app_info_t{name=ec_cnv:to_binary(AppName),
  105. original_vsn=Vsn}}.
  106. %% @doc build a complete version of the app info with all fields set.
  107. -spec new(atom() | binary() | string(), binary() | string(), file:name()) ->
  108. {ok, t()}.
  109. new(AppName, Vsn, Dir) ->
  110. {ok, #app_info_t{name=ec_cnv:to_binary(AppName),
  111. original_vsn=Vsn,
  112. dir=ec_cnv:to_list(Dir),
  113. out_dir=ec_cnv:to_list(Dir)}}.
  114. %% @doc build a complete version of the app info with all fields set.
  115. -spec new(atom() | binary() | string(), binary() | string(), file:name(), list()) ->
  116. {ok, t()}.
  117. new(AppName, Vsn, Dir, Deps) ->
  118. {ok, #app_info_t{name=ec_cnv:to_binary(AppName),
  119. original_vsn=Vsn,
  120. dir=ec_cnv:to_list(Dir),
  121. out_dir=ec_cnv:to_list(Dir),
  122. deps=Deps}}.
  123. %% @doc build a complete version of the app info with all fields set.
  124. -spec new(atom() | binary(), atom() | binary() | string(), binary() | string(), file:name(), list()) ->
  125. {ok, t()}.
  126. new(Parent, AppName, Vsn, Dir, Deps) ->
  127. {ok, #app_info_t{name=ec_cnv:to_binary(AppName),
  128. parent=Parent,
  129. original_vsn=Vsn,
  130. dir=ec_cnv:to_list(Dir),
  131. out_dir=ec_cnv:to_list(Dir),
  132. deps=Deps}}.
  133. update_opts(AppInfo, Opts, Config) ->
  134. LockDeps = case resource_type(AppInfo) of
  135. pkg ->
  136. Deps = deps(AppInfo),
  137. [{{locks, default}, Deps}, {{deps, default}, Deps}];
  138. _ ->
  139. deps_from_config(dir(AppInfo), Config)
  140. end,
  141. Plugins = proplists:get_value(plugins, Config, []),
  142. Terms = LockDeps++[{{plugins, default}, Plugins} | Config],
  143. true = rebar_config:verify_config_format(Terms),
  144. LocalOpts = dict:from_list(Terms),
  145. NewOpts = rebar_opts:merge_opts(LocalOpts, Opts),
  146. AppInfo#app_info_t{opts=NewOpts
  147. ,default=NewOpts}.
  148. deps_from_config(Dir, Config) ->
  149. case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
  150. [D] ->
  151. %% We want the top level deps only from the lock file.
  152. %% This ensures deterministic overrides for configs.
  153. Deps = [X || X <- D, element(3, X) =:= 0],
  154. [{{locks, default}, D}, {{deps, default}, Deps}];
  155. _ ->
  156. [{{deps, default}, proplists:get_value(deps, Config, [])}]
  157. end.
  158. %% @doc discover a complete version of the app info with all fields set.
  159. -spec discover(file:filename_all()) -> {ok, t()} | not_found.
  160. discover(Dir) ->
  161. case rebar_app_discover:find_app(Dir, all) of
  162. {true, AppInfo} ->
  163. {ok, AppInfo};
  164. false ->
  165. not_found
  166. end.
  167. -spec name(t()) -> binary().
  168. name(#app_info_t{name=Name}) ->
  169. Name.
  170. -spec name(t(), atom() | binary() | string()) -> t().
  171. name(AppInfo=#app_info_t{}, AppName) ->
  172. AppInfo#app_info_t{name=ec_cnv:to_binary(AppName)}.
  173. opts(#app_info_t{opts=Opts}) ->
  174. Opts.
  175. opts(AppInfo, Opts) ->
  176. AppInfo#app_info_t{opts=Opts}.
  177. default(#app_info_t{default=Default}) ->
  178. Default.
  179. default(AppInfo, Default) ->
  180. AppInfo#app_info_t{default=Default}.
  181. get(AppInfo, Key) ->
  182. {ok, Value} = dict:find(Key, AppInfo#app_info_t.opts),
  183. Value.
  184. get(AppInfo, Key, Default) ->
  185. case dict:find(Key, AppInfo#app_info_t.opts) of
  186. {ok, Value} ->
  187. Value;
  188. error ->
  189. Default
  190. end.
  191. -spec set(t(), any(), any()) -> t().
  192. set(AppInfo=#app_info_t{opts=Opts}, Key, Value) ->
  193. AppInfo#app_info_t{opts = dict:store(Key, Value, Opts)}.
  194. -spec app_file_src(t()) -> file:filename_all() | undefined.
  195. app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name}) ->
  196. AppFileSrc = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src"]),
  197. case filelib:is_file(AppFileSrc) of
  198. true ->
  199. AppFileSrc;
  200. false ->
  201. undefined
  202. end;
  203. app_file_src(#app_info_t{app_file_src=AppFileSrc}) ->
  204. ec_cnv:to_list(AppFileSrc).
  205. -spec app_file_src(t(), file:filename_all() | undefined) -> t().
  206. app_file_src(AppInfo=#app_info_t{}, undefined) ->
  207. AppInfo#app_info_t{app_file_src=undefined};
  208. app_file_src(AppInfo=#app_info_t{}, AppFileSrc) ->
  209. AppInfo#app_info_t{app_file_src=ec_cnv:to_list(AppFileSrc)}.
  210. -spec app_file_src_script(t()) -> file:filename_all() | undefined.
  211. app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Name}) ->
  212. AppFileSrcScript = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src.script"]),
  213. case filelib:is_file(AppFileSrcScript) of
  214. true ->
  215. AppFileSrcScript;
  216. false ->
  217. undefined
  218. end;
  219. app_file_src_script(#app_info_t{app_file_src_script=AppFileSrcScript}) ->
  220. ec_cnv:to_list(AppFileSrcScript).
  221. -spec app_file_src_script(t(), file:filename_all()) -> t().
  222. app_file_src_script(AppInfo=#app_info_t{}, undefined) ->
  223. AppInfo#app_info_t{app_file_src_script=undefined};
  224. app_file_src_script(AppInfo=#app_info_t{}, AppFileSrcScript) ->
  225. AppInfo#app_info_t{app_file_src_script=ec_cnv:to_list(AppFileSrcScript)}.
  226. -spec app_file(t()) -> file:filename_all() | undefined.
  227. app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) ->
  228. AppFile = filename:join([ec_cnv:to_list(Dir), "ebin", ec_cnv:to_list(Name)++".app"]),
  229. case filelib:is_file(AppFile) of
  230. true ->
  231. AppFile;
  232. false ->
  233. undefined
  234. end;
  235. app_file(#app_info_t{app_file=AppFile}) ->
  236. AppFile.
  237. -spec app_file(t(), file:filename_all()) -> t().
  238. app_file(AppInfo=#app_info_t{}, AppFile) ->
  239. AppInfo#app_info_t{app_file=AppFile}.
  240. -spec app_details(t()) -> list().
  241. app_details(AppInfo=#app_info_t{app_details=[]}) ->
  242. case app_file(AppInfo) of
  243. undefined ->
  244. rebar_file_utils:try_consult(app_file_src(AppInfo));
  245. AppFile ->
  246. try
  247. rebar_file_utils:try_consult(AppFile)
  248. catch
  249. throw:{error, {Module, Reason}} ->
  250. ?DEBUG("Warning, falling back to .app.src because of: ~s",
  251. [Module:format_error(Reason)]),
  252. rebar_file_utils:try_consult(app_file_src(AppInfo))
  253. end
  254. end;
  255. app_details(#app_info_t{app_details=AppDetails}) ->
  256. AppDetails.
  257. -spec app_details(t(), list()) -> t().
  258. app_details(AppInfo=#app_info_t{}, AppDetails) ->
  259. AppInfo#app_info_t{app_details=AppDetails}.
  260. parent(#app_info_t{parent=Parent}) ->
  261. Parent.
  262. -spec parent(t(), binary() | root) -> t().
  263. parent(AppInfo=#app_info_t{}, Parent) ->
  264. AppInfo#app_info_t{parent=Parent}.
  265. -spec original_vsn(t()) -> string().
  266. original_vsn(#app_info_t{original_vsn=Vsn}) ->
  267. Vsn.
  268. -spec original_vsn(t(), string()) -> t().
  269. original_vsn(AppInfo=#app_info_t{}, Vsn) ->
  270. AppInfo#app_info_t{original_vsn=Vsn}.
  271. -spec applications(t()) -> list().
  272. applications(#app_info_t{applications=Applications}) ->
  273. Applications.
  274. -spec applications(t(), list()) -> t().
  275. applications(AppInfo=#app_info_t{}, Applications) ->
  276. AppInfo#app_info_t{applications=Applications}.
  277. -spec profiles(t()) -> list().
  278. profiles(#app_info_t{profiles=Profiles}) ->
  279. Profiles.
  280. -spec profiles(t(), list()) -> t().
  281. profiles(AppInfo=#app_info_t{}, Profiles) ->
  282. AppInfo#app_info_t{profiles=Profiles}.
  283. -spec deps(t()) -> list().
  284. deps(#app_info_t{deps=Deps}) ->
  285. Deps.
  286. -spec deps(t(), list()) -> t().
  287. deps(AppInfo=#app_info_t{}, Deps) ->
  288. AppInfo#app_info_t{deps=Deps}.
  289. dep_level(AppInfo=#app_info_t{}, Level) ->
  290. AppInfo#app_info_t{dep_level=Level}.
  291. dep_level(#app_info_t{dep_level=Level}) ->
  292. Level.
  293. -spec dir(t()) -> file:name().
  294. dir(#app_info_t{dir=Dir}) ->
  295. Dir.
  296. -spec dir(t(), file:name()) -> t().
  297. dir(AppInfo=#app_info_t{out_dir=undefined}, Dir) ->
  298. AppInfo#app_info_t{dir=ec_cnv:to_list(Dir),
  299. out_dir=ec_cnv:to_list(Dir)};
  300. dir(AppInfo=#app_info_t{}, Dir) ->
  301. AppInfo#app_info_t{dir=ec_cnv:to_list(Dir)}.
  302. -spec out_dir(t()) -> file:name().
  303. out_dir(#app_info_t{out_dir=OutDir}) ->
  304. OutDir.
  305. -spec out_dir(t(), file:name()) -> t().
  306. out_dir(AppInfo=#app_info_t{}, OutDir) ->
  307. AppInfo#app_info_t{out_dir=ec_cnv:to_list(OutDir)}.
  308. -spec ebin_dir(t()) -> file:name().
  309. ebin_dir(#app_info_t{out_dir=OutDir}) ->
  310. ec_cnv:to_list(filename:join(OutDir, "ebin")).
  311. -spec priv_dir(t()) -> file:name().
  312. priv_dir(#app_info_t{out_dir=OutDir}) ->
  313. ec_cnv:to_list(filename:join(OutDir, "priv")).
  314. -spec resource_type(t(), pkg | src) -> t().
  315. resource_type(AppInfo=#app_info_t{}, Type) ->
  316. AppInfo#app_info_t{resource_type=Type}.
  317. -spec resource_type(t()) -> pkg | src.
  318. resource_type(#app_info_t{resource_type=ResourceType}) ->
  319. ResourceType.
  320. -spec source(t(), string() | tuple() | checkout) -> t().
  321. source(AppInfo=#app_info_t{}, Source) ->
  322. AppInfo#app_info_t{source=Source}.
  323. -spec source(t()) -> string() | tuple().
  324. source(#app_info_t{source=Source}) ->
  325. Source.
  326. -spec is_lock(t(), boolean()) -> t().
  327. is_lock(AppInfo=#app_info_t{}, IsLock) ->
  328. AppInfo#app_info_t{is_lock=IsLock}.
  329. -spec is_lock(t()) -> boolean().
  330. is_lock(#app_info_t{is_lock=IsLock}) ->
  331. IsLock.
  332. -spec is_checkout(t(), boolean()) -> t().
  333. is_checkout(AppInfo=#app_info_t{}, IsCheckout) ->
  334. AppInfo#app_info_t{is_checkout=IsCheckout}.
  335. -spec is_checkout(t()) -> boolean().
  336. is_checkout(#app_info_t{is_checkout=IsCheckout}) ->
  337. IsCheckout.
  338. -spec valid(t()) -> boolean().
  339. valid(AppInfo=#app_info_t{valid=undefined}) ->
  340. case rebar_app_utils:validate_application_info(AppInfo) =:= true
  341. andalso has_all_artifacts(AppInfo) =:= true of
  342. true ->
  343. true;
  344. _ ->
  345. false
  346. end;
  347. valid(#app_info_t{valid=Valid}) ->
  348. Valid.
  349. -spec valid(t(), boolean()) -> t().
  350. valid(AppInfo=#app_info_t{}, Valid) ->
  351. AppInfo#app_info_t{valid=Valid}.
  352. verify_otp_vsn(AppInfo) ->
  353. rebar_utils:check_min_otp_version(rebar_app_info:get(AppInfo, minimum_otp_vsn, undefined)),
  354. rebar_utils:check_blacklisted_otp_versions(rebar_app_info:get(AppInfo, blacklisted_otp_vsns, [])).
  355. -spec has_all_artifacts(#app_info_t{}) -> true | {false, file:filename()}.
  356. has_all_artifacts(AppInfo) ->
  357. Artifacts = rebar_app_info:get(AppInfo, artifacts, []),
  358. OutDir = out_dir(AppInfo),
  359. Context = [{base_dir, rebar_app_info:get(AppInfo, base_dir, ?DEFAULT_BASE_DIR)}
  360. ,{profile_dir, rebar_dir:profile_dir(opts(AppInfo), profiles(AppInfo))}
  361. ,{out_dir, OutDir}],
  362. all(OutDir, Context, Artifacts).
  363. all(_, _, []) ->
  364. true;
  365. all(Dir, Context, [File|Artifacts]) ->
  366. FilePath = filename:join(Dir, rebar_templater:render(File, Context)),
  367. case filelib:is_regular(FilePath) of
  368. false ->
  369. ?DEBUG("Missing artifact ~s", [FilePath]),
  370. {false, File};
  371. true ->
  372. all(Dir, Context, Artifacts)
  373. end.
  374. %%%%%
  375. apply_overrides(Overrides, AppInfo) ->
  376. Name = binary_to_atom(rebar_app_info:name(AppInfo), utf8),
  377. Opts = rebar_opts:apply_overrides(opts(AppInfo), Name, Overrides),
  378. AppInfo#app_info_t{default=Opts, opts=Opts}.
  379. add_to_profile(AppInfo, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
  380. Opts = rebar_opts:add_to_profile(opts(AppInfo), Profile, KVs),
  381. AppInfo#app_info_t{opts=Opts}.
  382. apply_profiles(AppInfo, Profile) when not is_list(Profile) ->
  383. apply_profiles(AppInfo, [Profile]);
  384. apply_profiles(AppInfo, [default]) ->
  385. AppInfo;
  386. apply_profiles(AppInfo=#app_info_t{default = Defaults, profiles=CurrentProfiles}, Profiles) ->
  387. AppliedProfiles = case Profiles of
  388. %% Head of list global profile is special, only for use by rebar3
  389. %% It does not clash if a user does `rebar3 as global...` but when
  390. %% it is the head we must make sure not to prepend `default`
  391. [global | _] ->
  392. Profiles;
  393. _ ->
  394. deduplicate(CurrentProfiles ++ Profiles)
  395. end,
  396. ConfigProfiles = rebar_app_info:get(AppInfo, profiles, []),
  397. NewOpts =
  398. lists:foldl(fun(default, OptsAcc) ->
  399. OptsAcc;
  400. (Profile, OptsAcc) ->
  401. case proplists:get_value(Profile, ConfigProfiles, []) of
  402. OptsList when is_list(OptsList) ->
  403. ProfileOpts = dict:from_list(OptsList),
  404. rebar_opts:merge_opts(Profile, ProfileOpts, OptsAcc);
  405. Other ->
  406. throw(?PRV_ERROR({profile_not_list, Profile, Other}))
  407. end
  408. end, Defaults, AppliedProfiles),
  409. AppInfo#app_info_t{profiles = AppliedProfiles, opts=NewOpts}.
  410. deduplicate(Profiles) ->
  411. do_deduplicate(lists:reverse(Profiles), []).
  412. do_deduplicate([], Acc) ->
  413. Acc;
  414. do_deduplicate([Head | Rest], Acc) ->
  415. case lists:member(Head, Acc) of
  416. true -> do_deduplicate(Rest, Acc);
  417. false -> do_deduplicate(Rest, [Head | Acc])
  418. end.