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.

493 rindas
16 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
  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.