選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

493 行
16 KiB

  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.