rebar.config.erl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. -module('rebar.config').
  2. -export([do/2]).
  3. do(Dir, CONFIG) ->
  4. ok = assert_otp(),
  5. ok = warn_profile_env(),
  6. case iolist_to_binary(Dir) of
  7. <<".">> ->
  8. C1 = deps(CONFIG),
  9. Config = dialyzer(C1),
  10. maybe_dump(Config ++ [{overrides, overrides()}] ++ coveralls() ++ config());
  11. _ ->
  12. CONFIG
  13. end.
  14. assert_otp() ->
  15. Oldest = 23,
  16. Latest = 24,
  17. OtpRelease = list_to_integer(erlang:system_info(otp_release)),
  18. case OtpRelease < Oldest orelse OtpRelease > Latest of
  19. true ->
  20. io:format(
  21. standard_error,
  22. "ERROR: Erlang/OTP version ~p found. min=~p, recommended=~p~n",
  23. [OtpRelease, Oldest, Latest]
  24. ),
  25. halt(1);
  26. false when OtpRelease =/= Latest ->
  27. io:format(
  28. "WARNING: Erlang/OTP version ~p found, recommended==~p~n",
  29. [OtpRelease, Latest]
  30. );
  31. false ->
  32. ok
  33. end.
  34. bcrypt() ->
  35. {bcrypt, {git, "https://github.com/emqx/erlang-bcrypt.git", {tag, "0.6.0"}}}.
  36. quicer() ->
  37. {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.16"}}}.
  38. jq() ->
  39. {jq, {git, "https://github.com/emqx/jq", {tag, "v0.3.5"}}}.
  40. deps(Config) ->
  41. {deps, OldDeps} = lists:keyfind(deps, 1, Config),
  42. MoreDeps =
  43. [bcrypt() || provide_bcrypt_dep()] ++
  44. [jq() || is_jq_supported()] ++
  45. [quicer() || is_quicer_supported()],
  46. lists:keystore(deps, 1, Config, {deps, OldDeps ++ MoreDeps}).
  47. overrides() ->
  48. [{add, [{extra_src_dirs, [{"etc", [{recursive, true}]}]}]}] ++ snabbkaffe_overrides().
  49. %% Temporary workaround for a rebar3 erl_opts duplication
  50. %% bug. Ideally, we want to set this define globally
  51. snabbkaffe_overrides() ->
  52. Apps = [snabbkaffe, ekka, mria, gen_rpc],
  53. [{add, App, [{erl_opts, [{d, snk_kind, msg}]}]} || App <- Apps].
  54. config() ->
  55. [
  56. {cover_enabled, is_cover_enabled()},
  57. {profiles, profiles()},
  58. {plugins, plugins()}
  59. ].
  60. is_cover_enabled() ->
  61. case os:getenv("ENABLE_COVER_COMPILE") of
  62. "1" -> true;
  63. "true" -> true;
  64. _ -> false
  65. end.
  66. is_enterprise(ce) -> false;
  67. is_enterprise(ee) -> true.
  68. is_jq_supported() ->
  69. not (false =/= os:getenv("BUILD_WITHOUT_JQ") orelse
  70. is_win32()) orelse
  71. "1" == os:getenv("BUILD_WITH_JQ").
  72. is_quicer_supported() ->
  73. not (false =/= os:getenv("BUILD_WITHOUT_QUIC") orelse
  74. is_macos() orelse
  75. is_win32() orelse is_centos_6()) orelse
  76. "1" == os:getenv("BUILD_WITH_QUIC").
  77. is_rocksdb_supported() ->
  78. not (false =/= os:getenv("BUILD_WITHOUT_ROCKSDB") orelse
  79. is_raspbian()) orelse
  80. "1" == os:getenv("BUILD_WITH_ROCKSDB").
  81. is_macos() ->
  82. {unix, darwin} =:= os:type().
  83. is_centos_6() ->
  84. %% reason:
  85. %% glibc is too old
  86. case file:read_file("/etc/centos-release") of
  87. {ok, <<"CentOS release 6", _/binary>>} ->
  88. true;
  89. _ ->
  90. false
  91. end.
  92. is_raspbian() ->
  93. case os_cmd("./scripts/get-distro.sh") of
  94. "raspbian" ++ _ ->
  95. true;
  96. _ ->
  97. false
  98. end.
  99. is_win32() ->
  100. win32 =:= element(1, os:type()).
  101. project_app_dirs() ->
  102. project_app_dirs(get_edition_from_profille_env()).
  103. project_app_dirs(Edition) ->
  104. ["apps/*"] ++
  105. case is_enterprise(Edition) of
  106. true -> ["lib-ee/*"];
  107. false -> []
  108. end.
  109. plugins() ->
  110. [
  111. {relup_helper, {git, "https://github.com/emqx/relup_helper", {tag, "2.1.0"}}},
  112. %% emqx main project does not require port-compiler
  113. %% pin at root level for deterministic
  114. {pc, "v1.14.0"}
  115. ] ++
  116. %% test plugins are concatenated to default profile plugins
  117. %% otherwise rebar3 test profile runs are super slow
  118. test_plugins().
  119. test_plugins() ->
  120. [
  121. {rebar3_proper, "0.12.1"},
  122. {coveralls, {git, "https://github.com/emqx/coveralls-erl", {tag, "v2.2.0-emqx-1"}}}
  123. ].
  124. test_deps() ->
  125. [
  126. {bbmustache, "1.10.0"},
  127. {meck, "0.9.2"},
  128. {proper, "1.4.0"},
  129. {er_coap_client, {git, "https://github.com/emqx/er_coap_client", {tag, "v1.0.5"}}}
  130. ].
  131. common_compile_opts(Vsn) ->
  132. common_compile_opts(get_edition_from_profille_env(), Vsn).
  133. common_compile_opts(Edition, Vsn) ->
  134. % always include debug_info
  135. [
  136. debug_info,
  137. {compile_info, [{emqx_vsn, Vsn}]},
  138. {d, 'EMQX_RELEASE_EDITION', Edition}
  139. ] ++
  140. [{d, 'EMQX_BENCHMARK'} || os:getenv("EMQX_BENCHMARK") =:= "1"] ++
  141. [{d, 'BUILD_WITHOUT_QUIC'} || not is_quicer_supported()].
  142. warn_profile_env() ->
  143. case os:getenv("PROFILE") of
  144. false ->
  145. io:format(
  146. standard_error,
  147. "WARN: environment variable PROFILE is not set, using 'emqx-enterprise'~n",
  148. []
  149. );
  150. _ ->
  151. ok
  152. end.
  153. %% this function is only used for test/check profiles
  154. get_edition_from_profille_env() ->
  155. case os:getenv("PROFILE") of
  156. "emqx-enterprise" ++ _ ->
  157. ee;
  158. "emqx" ++ _ ->
  159. ce;
  160. false ->
  161. ee;
  162. V ->
  163. io:format(standard_error, "ERROR: bad_PROFILE ~p~n", [V]),
  164. exit(bad_PROFILE)
  165. end.
  166. prod_compile_opts(Edition, Vsn) ->
  167. [
  168. compressed,
  169. deterministic,
  170. warnings_as_errors
  171. | common_compile_opts(Edition, Vsn)
  172. ].
  173. prod_overrides() ->
  174. [{add, [{erl_opts, [deterministic]}]}].
  175. profiles() ->
  176. profiles_ce() ++ profiles_ee() ++ profiles_dev().
  177. profiles_ce() ->
  178. Vsn = get_vsn(emqx),
  179. [
  180. {'emqx', [
  181. {erl_opts, prod_compile_opts(ce, Vsn)},
  182. {relx, relx(Vsn, cloud, bin, ce)},
  183. {overrides, prod_overrides()},
  184. {project_app_dirs, project_app_dirs(ce)}
  185. ]},
  186. {'emqx-pkg', [
  187. {erl_opts, prod_compile_opts(ce, Vsn)},
  188. {relx, relx(Vsn, cloud, pkg, ce)},
  189. {overrides, prod_overrides()},
  190. {project_app_dirs, project_app_dirs(ce)}
  191. ]}
  192. ].
  193. profiles_ee() ->
  194. Vsn = get_vsn('emqx-enterprise'),
  195. [
  196. {'emqx-enterprise', [
  197. {erl_opts, prod_compile_opts(ee, Vsn)},
  198. {relx, relx(Vsn, cloud, bin, ee)},
  199. {overrides, prod_overrides()},
  200. {project_app_dirs, project_app_dirs(ee)}
  201. ]},
  202. {'emqx-enterprise-pkg', [
  203. {erl_opts, prod_compile_opts(ee, Vsn)},
  204. {relx, relx(Vsn, cloud, pkg, ee)},
  205. {overrides, prod_overrides()},
  206. {project_app_dirs, project_app_dirs(ee)}
  207. ]}
  208. ].
  209. %% EE has more files than CE, always test/check with EE options.
  210. profiles_dev() ->
  211. Vsn = get_vsn('emqx-enterprise'),
  212. [
  213. {check, [
  214. {erl_opts, common_compile_opts(Vsn)},
  215. {project_app_dirs, project_app_dirs()}
  216. ]},
  217. {test, [
  218. {deps, test_deps()},
  219. {erl_opts, common_compile_opts(Vsn) ++ erl_opts_i()},
  220. {extra_src_dirs, [{"test", [{recursive, true}]}]},
  221. {project_app_dirs, project_app_dirs()}
  222. ]}
  223. ].
  224. %% RelType: cloud (full size)
  225. %% PkgType: bin | pkg
  226. %% Edition: ce (opensource) | ee (enterprise)
  227. relx(Vsn, RelType, PkgType, Edition) ->
  228. [
  229. {include_src, false},
  230. {include_erts, true},
  231. {extended_start_script, false},
  232. {generate_start_script, false},
  233. {sys_config, false},
  234. {vm_args, false},
  235. {release, {emqx, Vsn}, relx_apps(RelType, Edition)},
  236. {overlay, relx_overlay(RelType, Edition)},
  237. {overlay_vars,
  238. build_info() ++
  239. [
  240. {emqx_description, emqx_description(RelType, Edition)}
  241. | overlay_vars(RelType, PkgType, Edition)
  242. ]}
  243. ].
  244. %% Make a HOCON compatible format
  245. build_info() ->
  246. Os = os_cmd("./scripts/get-distro.sh"),
  247. [
  248. {build_info_arch, erlang:system_info(system_architecture)},
  249. {build_info_wordsize, rebar_utils:wordsize()},
  250. {build_info_os, Os},
  251. {build_info_erlang, rebar_utils:otp_release()},
  252. {build_info_elixir, none},
  253. {build_info_relform, relform()}
  254. ].
  255. relform() ->
  256. case os:getenv("EMQX_REL_FORM") of
  257. false -> "tgz";
  258. Other -> Other
  259. end.
  260. emqx_description(cloud, ee) -> "EMQX Enterprise";
  261. emqx_description(cloud, ce) -> "EMQX".
  262. overlay_vars(RelType, PkgType, Edition) ->
  263. overlay_vars_rel(RelType) ++
  264. overlay_vars_pkg(PkgType) ++
  265. overlay_vars_edition(Edition).
  266. overlay_vars_rel(cloud) ->
  267. [{vm_args_file, "vm.args"}].
  268. overlay_vars_edition(ce) ->
  269. [
  270. {emqx_schema_mod, emqx_conf_schema},
  271. {is_enterprise, "no"}
  272. ];
  273. overlay_vars_edition(ee) ->
  274. [
  275. {emqx_schema_mod, emqx_enterprise_conf_schema},
  276. {is_enterprise, "yes"}
  277. ].
  278. %% vars per packaging type, bin(zip/tar.gz/docker) or pkg(rpm/deb)
  279. overlay_vars_pkg(bin) ->
  280. [
  281. {platform_data_dir, "data"},
  282. {platform_etc_dir, "etc"},
  283. {platform_log_dir, "log"},
  284. {platform_plugins_dir, "plugins"},
  285. {runner_bin_dir, "$RUNNER_ROOT_DIR/bin"},
  286. {emqx_etc_dir, "$RUNNER_ROOT_DIR/etc"},
  287. {runner_lib_dir, "$RUNNER_ROOT_DIR/lib"},
  288. {runner_log_dir, "$RUNNER_ROOT_DIR/log"},
  289. {runner_user, ""},
  290. {is_elixir, "no"}
  291. ];
  292. overlay_vars_pkg(pkg) ->
  293. [
  294. {platform_data_dir, "/var/lib/emqx"},
  295. {platform_etc_dir, "/etc/emqx"},
  296. {platform_log_dir, "/var/log/emqx"},
  297. {platform_plugins_dir, "/var/lib/emqx/plugins"},
  298. {runner_bin_dir, "/usr/bin"},
  299. {emqx_etc_dir, "/etc/emqx"},
  300. {runner_lib_dir, "$RUNNER_ROOT_DIR/lib"},
  301. {runner_log_dir, "/var/log/emqx"},
  302. {runner_user, "emqx"},
  303. {is_elixir, "no"}
  304. ].
  305. relx_apps(ReleaseType, Edition) ->
  306. [
  307. kernel,
  308. sasl,
  309. crypto,
  310. public_key,
  311. asn1,
  312. syntax_tools,
  313. ssl,
  314. os_mon,
  315. inets,
  316. compiler,
  317. runtime_tools,
  318. redbug,
  319. xmerl,
  320. {hocon, load},
  321. % started by emqx_machine
  322. {emqx, load},
  323. {emqx_conf, load},
  324. emqx_machine
  325. ] ++
  326. [{mnesia_rocksdb, load} || is_rocksdb_supported()] ++
  327. [
  328. {mnesia, load},
  329. {ekka, load},
  330. {emqx_plugin_libs, load},
  331. {esasl, load},
  332. observer_cli,
  333. % started by emqx_machine
  334. {system_monitor, load},
  335. emqx_http_lib,
  336. emqx_resource,
  337. emqx_connector,
  338. emqx_authn,
  339. emqx_authz,
  340. emqx_auto_subscribe,
  341. emqx_gateway,
  342. emqx_exhook,
  343. emqx_bridge,
  344. emqx_rule_engine,
  345. emqx_modules,
  346. emqx_management,
  347. emqx_dashboard,
  348. emqx_retainer,
  349. emqx_statsd,
  350. emqx_prometheus,
  351. emqx_psk,
  352. emqx_slow_subs,
  353. emqx_plugins
  354. ] ++
  355. [quicer || is_quicer_supported()] ++
  356. [bcrypt || provide_bcrypt_release(ReleaseType)] ++
  357. [jq || is_jq_supported()] ++
  358. [{observer, load} || is_app(observer)] ++
  359. relx_apps_per_edition(Edition).
  360. is_app(Name) ->
  361. case application:load(Name) of
  362. ok -> true;
  363. {error, {already_loaded, _}} -> true;
  364. _ -> false
  365. end.
  366. relx_apps_per_edition(ee) ->
  367. [
  368. emqx_license,
  369. {emqx_enterprise_conf, load}
  370. ];
  371. relx_apps_per_edition(ce) ->
  372. [].
  373. relx_overlay(ReleaseType, Edition) ->
  374. [
  375. {mkdir, "log/"},
  376. {mkdir, "data/"},
  377. {mkdir, "plugins"},
  378. {mkdir, "data/mnesia"},
  379. {mkdir, "data/configs"},
  380. {mkdir, "data/patches"},
  381. {mkdir, "data/scripts"},
  382. {template, "rel/emqx_vars", "releases/emqx_vars"},
  383. {template, "rel/BUILD_INFO", "releases/{{release_version}}/BUILD_INFO"},
  384. {copy, "bin/emqx", "bin/emqx"},
  385. {copy, "bin/emqx_ctl", "bin/emqx_ctl"},
  386. {copy, "bin/emqx_cluster_rescue", "bin/emqx_cluster_rescue"},
  387. {copy, "bin/node_dump", "bin/node_dump"},
  388. {copy, "bin/install_upgrade.escript", "bin/install_upgrade.escript"},
  389. %% for relup
  390. {copy, "bin/emqx", "bin/emqx-{{release_version}}"},
  391. %% for relup
  392. {copy, "bin/emqx_ctl", "bin/emqx_ctl-{{release_version}}"},
  393. %% for relup
  394. {copy, "bin/install_upgrade.escript", "bin/install_upgrade.escript-{{release_version}}"},
  395. {copy, "apps/emqx_gateway/src/lwm2m/lwm2m_xml", "etc/lwm2m_xml"},
  396. {copy, "apps/emqx_authz/etc/acl.conf", "etc/acl.conf"},
  397. {template, "bin/emqx.cmd", "bin/emqx.cmd"},
  398. {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"},
  399. {copy, "bin/nodetool", "bin/nodetool"},
  400. {copy, "bin/nodetool", "bin/nodetool-{{release_version}}"}
  401. ] ++ etc_overlay(ReleaseType, Edition).
  402. etc_overlay(ReleaseType, Edition) ->
  403. Templates = emqx_etc_overlay(ReleaseType, Edition),
  404. [
  405. {mkdir, "etc/"},
  406. {copy, "{{base_dir}}/lib/emqx/etc/certs", "etc/"},
  407. {copy, "apps/emqx_dashboard/etc/emqx.conf.en.example", "etc/emqx-example.conf"}
  408. ] ++
  409. lists:map(
  410. fun
  411. ({From, To}) -> {template, From, To};
  412. (FromTo) -> {template, FromTo, FromTo}
  413. end,
  414. Templates
  415. ).
  416. emqx_etc_overlay(ReleaseType, Edition) ->
  417. emqx_etc_overlay_per_rel(ReleaseType) ++
  418. emqx_etc_overlay_per_edition(Edition) ++
  419. emqx_etc_overlay_common().
  420. emqx_etc_overlay_per_rel(cloud) ->
  421. [{"{{base_dir}}/lib/emqx/etc/vm.args.cloud", "etc/vm.args"}].
  422. emqx_etc_overlay_common() ->
  423. [{"{{base_dir}}/lib/emqx/etc/ssl_dist.conf", "etc/ssl_dist.conf"}].
  424. emqx_etc_overlay_per_edition(ce) ->
  425. [
  426. {"{{base_dir}}/lib/emqx_conf/etc/emqx.conf.all", "etc/emqx.conf"}
  427. ];
  428. emqx_etc_overlay_per_edition(ee) ->
  429. [
  430. {"{{base_dir}}/lib/emqx_conf/etc/emqx_enterprise.conf.all", "etc/emqx_enterprise.conf"},
  431. {"{{base_dir}}/lib/emqx_conf/etc/emqx.conf.all", "etc/emqx.conf"}
  432. ].
  433. get_vsn(Profile) ->
  434. %% to make it compatible to Linux and Windows,
  435. %% we must use bash to execute the bash file
  436. %% because "./" will not be recognized as an internal or external command
  437. os_cmd("pkg-vsn.sh " ++ atom_to_list(Profile)).
  438. os_cmd(Cmd) ->
  439. Output = os:cmd("bash " ++ Cmd),
  440. re:replace(Output, "\n", "", [{return, list}]).
  441. maybe_dump(Config) ->
  442. is_debug() andalso
  443. file:write_file("rebar.config.rendered", [io_lib:format("~p.\n", [I]) || I <- Config]),
  444. Config.
  445. is_debug() -> is_debug("DEBUG") orelse is_debug("DIAGNOSTIC").
  446. is_debug(VarName) ->
  447. case os:getenv(VarName) of
  448. false -> false;
  449. "" -> false;
  450. _ -> true
  451. end.
  452. provide_bcrypt_dep() ->
  453. not is_win32().
  454. provide_bcrypt_release(ReleaseType) ->
  455. provide_bcrypt_dep() andalso ReleaseType =:= cloud.
  456. erl_opts_i() ->
  457. [{i, "apps"}] ++
  458. [{i, Dir} || Dir <- filelib:wildcard(filename:join(["apps", "*", "include"]))] ++
  459. [{i, Dir} || Dir <- filelib:wildcard(filename:join(["lib-ee", "*", "include"]))].
  460. dialyzer(Config) ->
  461. {dialyzer, OldDialyzerConfig} = lists:keyfind(dialyzer, 1, Config),
  462. AppsToAnalyse =
  463. case os:getenv("DIALYZER_ANALYSE_APP") of
  464. false ->
  465. [];
  466. Value ->
  467. [list_to_atom(App) || App <- string:tokens(Value, ",")]
  468. end,
  469. AppNames = app_names(),
  470. KnownApps = [Name || Name <- AppsToAnalyse, lists:member(Name, AppNames)],
  471. AppsToExclude = AppNames -- KnownApps,
  472. case length(AppsToAnalyse) > 0 of
  473. true ->
  474. lists:keystore(
  475. dialyzer,
  476. 1,
  477. Config,
  478. {dialyzer, OldDialyzerConfig ++ [{exclude_apps, AppsToExclude}]}
  479. );
  480. false ->
  481. Config
  482. end.
  483. coveralls() ->
  484. case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of
  485. {"true", Token} when is_list(Token) ->
  486. Cfgs = [
  487. {coveralls_repo_token, Token},
  488. {coveralls_service_job_id, os:getenv("GITHUB_RUN_ID")},
  489. {coveralls_commit_sha, os:getenv("GITHUB_SHA")},
  490. {coveralls_coverdata, "_build/test/cover/*.coverdata"},
  491. {coveralls_service_name, "github"}
  492. ],
  493. case
  494. os:getenv("GITHUB_EVENT_NAME") =:= "pull_request" andalso
  495. string:tokens(os:getenv("GITHUB_REF"), "/")
  496. of
  497. [_, "pull", PRNO, _] ->
  498. [{coveralls_service_pull_request, PRNO} | Cfgs];
  499. _ ->
  500. Cfgs
  501. end;
  502. _ ->
  503. []
  504. end.
  505. app_names() -> list_dir("apps") ++ list_dir("lib-ee").
  506. list_dir(Dir) ->
  507. case filelib:is_dir(Dir) of
  508. true ->
  509. {ok, Names} = file:list_dir(Dir),
  510. [list_to_atom(Name) || Name <- Names, filelib:is_dir(filename:join([Dir, Name]))];
  511. false ->
  512. []
  513. end.