mix.exs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. defmodule EMQXUmbrella.MixProject do
  2. use Mix.Project
  3. @moduledoc """
  4. The purpose of this file is to configure the release of EMQX under
  5. Mix. Since EMQX uses its own configuration conventions and startup
  6. procedures, one cannot simply use `iex -S mix`. Instead, it's
  7. recommendd to build and use the release.
  8. ## Profiles
  9. To control the profile and edition to build, we case split on the
  10. MIX_ENV value.
  11. The following profiles are valid:
  12. * `emqx`
  13. * `emqx-enterprise`
  14. * `emqx-pkg`
  15. * `emqx-enterprise-pkg`
  16. * `dev` -> same as `emqx`, for convenience
  17. ## Release Environment Variables
  18. The release build is controlled by a few environment variables.
  19. * `ELIXIR_MAKE_TAR` - If set to `yes`, will produce a `.tar.gz`
  20. tarball along with the release.
  21. """
  22. def project() do
  23. check_profile!()
  24. [
  25. app: :emqx_mix,
  26. version: pkg_vsn(),
  27. deps: deps(),
  28. releases: releases()
  29. ]
  30. end
  31. defp deps() do
  32. # we need several overrides here because dependencies specify
  33. # other exact versions, and not ranges.
  34. [
  35. {:lc, github: "emqx/lc", tag: "0.3.1"},
  36. {:redbug, "2.0.7"},
  37. {:typerefl, github: "ieQu1/typerefl", tag: "0.9.1", override: true},
  38. {:ehttpc, github: "emqx/ehttpc", tag: "0.2.1"},
  39. {:gproc, github: "uwiger/gproc", tag: "0.8.0", override: true},
  40. {:jiffy, github: "emqx/jiffy", tag: "1.0.5", override: true},
  41. {:cowboy, github: "emqx/cowboy", tag: "2.9.0", override: true},
  42. {:esockd, github: "emqx/esockd", tag: "5.9.3", override: true},
  43. {:ekka, github: "emqx/ekka", tag: "0.13.2", override: true},
  44. {:gen_rpc, github: "emqx/gen_rpc", tag: "2.8.1", override: true},
  45. {:grpc, github: "emqx/grpc-erl", tag: "0.6.6", override: true},
  46. {:minirest, github: "emqx/minirest", tag: "1.3.5", override: true},
  47. {:ecpool, github: "emqx/ecpool", tag: "0.5.2"},
  48. {:replayq, "0.3.4", override: true},
  49. {:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4", override: true},
  50. {:emqtt, github: "emqx/emqtt", tag: "1.6.0", override: true},
  51. {:rulesql, github: "emqx/rulesql", tag: "0.1.4"},
  52. {:observer_cli, "1.7.1"},
  53. {:system_monitor, github: "ieQu1/system_monitor", tag: "3.0.3"},
  54. # in conflict by emqtt and hocon
  55. {:getopt, "1.0.2", override: true},
  56. {:snabbkaffe, github: "kafka4beam/snabbkaffe", tag: "1.0.0", override: true},
  57. {:hocon, github: "emqx/hocon", tag: "0.29.0", override: true},
  58. {:emqx_http_lib, github: "emqx/emqx_http_lib", tag: "0.5.1", override: true},
  59. {:esasl, github: "emqx/esasl", tag: "0.2.0"},
  60. {:jose, github: "potatosalad/erlang-jose", tag: "1.11.2"},
  61. # in conflict by ehttpc and emqtt
  62. {:gun, github: "emqx/gun", tag: "1.3.7", override: true},
  63. # in conflict by emqx_connectior and system_monitor
  64. {:epgsql, github: "emqx/epgsql", tag: "4.7-emqx.2", override: true},
  65. # in conflict by mongodb and eredis_cluster
  66. {:poolboy, github: "emqx/poolboy", tag: "1.5.2", override: true},
  67. # in conflict by emqx and observer_cli
  68. {:recon, github: "ferd/recon", tag: "2.5.1", override: true},
  69. {:jsx, github: "talentdeficit/jsx", tag: "v3.1.0", override: true},
  70. # dependencies of dependencies; we choose specific refs to match
  71. # what rebar3 chooses.
  72. # in conflict by gun and emqtt
  73. {:cowlib,
  74. github: "ninenines/cowlib", ref: "c6553f8308a2ca5dcd69d845f0a7d098c40c3363", override: true},
  75. # in conflict by cowboy_swagger and cowboy
  76. {:ranch,
  77. github: "ninenines/ranch", ref: "a692f44567034dacf5efcaa24a24183788594eb7", override: true},
  78. # in conflict by grpc and eetcd
  79. {:gpb, "4.11.2", override: true, runtime: false}
  80. ] ++ umbrella_apps() ++ bcrypt_dep() ++ jq_dep() ++ quicer_dep()
  81. end
  82. defp umbrella_apps() do
  83. "apps/*"
  84. |> Path.wildcard()
  85. |> Enum.map(fn path ->
  86. app =
  87. path
  88. |> String.trim_leading("apps/")
  89. |> String.to_atom()
  90. {app, path: path, manager: :rebar3, override: true}
  91. end)
  92. end
  93. defp releases() do
  94. [
  95. emqx: fn ->
  96. %{
  97. release_type: release_type,
  98. package_type: package_type,
  99. edition_type: edition_type
  100. } = check_profile!()
  101. base_steps = [
  102. &make_docs(&1),
  103. :assemble,
  104. &create_RELEASES/1,
  105. &copy_files(&1, release_type, package_type, edition_type),
  106. &copy_escript(&1, "nodetool"),
  107. &copy_escript(&1, "install_upgrade.escript")
  108. ]
  109. steps =
  110. if System.get_env("ELIXIR_MAKE_TAR") == "yes" do
  111. base_steps ++ [&prepare_tar_overlays/1, :tar]
  112. else
  113. base_steps
  114. end
  115. [
  116. applications: applications(edition_type),
  117. skip_mode_validation_for: [
  118. :emqx_gateway,
  119. :emqx_dashboard,
  120. :emqx_resource,
  121. :emqx_connector,
  122. :emqx_exhook,
  123. :emqx_bridge,
  124. :emqx_modules,
  125. :emqx_management,
  126. :emqx_statsd,
  127. :emqx_retainer,
  128. :emqx_prometheus,
  129. :emqx_auto_subscribe,
  130. :emqx_slow_subs,
  131. :emqx_plugins
  132. ],
  133. steps: steps,
  134. strip_beams: false
  135. ]
  136. end
  137. ]
  138. end
  139. def applications(edition_type) do
  140. [
  141. crypto: :permanent,
  142. public_key: :permanent,
  143. asn1: :permanent,
  144. syntax_tools: :permanent,
  145. ssl: :permanent,
  146. os_mon: :permanent,
  147. inets: :permanent,
  148. compiler: :permanent,
  149. runtime_tools: :permanent,
  150. redbug: :permanent,
  151. xmerl: :permanent,
  152. hocon: :load,
  153. emqx: :load,
  154. emqx_conf: :load,
  155. emqx_machine: :permanent
  156. ] ++
  157. if(enable_rocksdb?(),
  158. do: [mnesia_rocksdb: :load],
  159. else: []
  160. ) ++
  161. [
  162. mnesia: :load,
  163. ekka: :load,
  164. emqx_plugin_libs: :load,
  165. esasl: :load,
  166. observer_cli: :permanent,
  167. system_monitor: :load,
  168. emqx_http_lib: :permanent,
  169. emqx_resource: :permanent,
  170. emqx_connector: :permanent,
  171. emqx_authn: :permanent,
  172. emqx_authz: :permanent,
  173. emqx_auto_subscribe: :permanent,
  174. emqx_gateway: :permanent,
  175. emqx_exhook: :permanent,
  176. emqx_bridge: :permanent,
  177. emqx_rule_engine: :permanent,
  178. emqx_modules: :permanent,
  179. emqx_management: :permanent,
  180. emqx_dashboard: :permanent,
  181. emqx_retainer: :permanent,
  182. emqx_statsd: :permanent,
  183. emqx_prometheus: :permanent,
  184. emqx_psk: :permanent,
  185. emqx_slow_subs: :permanent,
  186. emqx_plugins: :permanent,
  187. emqx_mix: :none
  188. ] ++
  189. if(enable_quicer?(), do: [quicer: :permanent], else: []) ++
  190. if(enable_bcrypt?(), do: [bcrypt: :permanent], else: []) ++
  191. if(enable_jq?(), do: [jq: :permanent], else: []) ++
  192. if(is_app(:observer),
  193. do: [observer: :load],
  194. else: []
  195. ) ++
  196. if(edition_type == :enterprise,
  197. do: [
  198. emqx_license: :permanent,
  199. emqx_enterprise_conf: :load
  200. ],
  201. else: []
  202. )
  203. end
  204. defp is_app(name) do
  205. case Application.load(name) do
  206. :ok ->
  207. true
  208. {:error, {:already_loaded, _}} ->
  209. true
  210. _ ->
  211. false
  212. end
  213. end
  214. def check_profile!() do
  215. valid_envs = [
  216. :dev,
  217. :emqx,
  218. :"emqx-pkg",
  219. :"emqx-enterprise",
  220. :"emqx-enterprise-pkg"
  221. ]
  222. if Mix.env() not in valid_envs do
  223. formatted_envs =
  224. valid_envs
  225. |> Enum.map(&" * #{&1}")
  226. |> Enum.join("\n")
  227. Mix.raise("""
  228. Invalid env #{Mix.env()}. Valid options are:
  229. #{formatted_envs}
  230. """)
  231. end
  232. {
  233. release_type,
  234. package_type,
  235. edition_type
  236. } =
  237. case Mix.env() do
  238. :dev ->
  239. {:cloud, :bin, :community}
  240. :emqx ->
  241. {:cloud, :bin, :community}
  242. :"emqx-enterprise" ->
  243. {:cloud, :bin, :enterprise}
  244. :"emqx-pkg" ->
  245. {:cloud, :pkg, :community}
  246. :"emqx-enterprise-pkg" ->
  247. {:cloud, :pkg, :enterprise}
  248. end
  249. normalize_env!()
  250. %{
  251. release_type: release_type,
  252. package_type: package_type,
  253. edition_type: edition_type
  254. }
  255. end
  256. #############################################################################
  257. # Custom Steps
  258. #############################################################################
  259. defp make_docs(release) do
  260. profile = System.get_env("MIX_ENV")
  261. os_cmd("build", [profile, "docs"])
  262. release
  263. end
  264. defp copy_files(release, release_type, package_type, edition_type) do
  265. overwrite? = Keyword.get(release.options, :overwrite, false)
  266. bin = Path.join(release.path, "bin")
  267. etc = Path.join(release.path, "etc")
  268. log = Path.join(release.path, "log")
  269. Mix.Generator.create_directory(bin)
  270. Mix.Generator.create_directory(etc)
  271. Mix.Generator.create_directory(log)
  272. Mix.Generator.create_directory(Path.join(etc, "certs"))
  273. Enum.each(
  274. ["mnesia", "configs", "patches", "scripts"],
  275. fn dir ->
  276. path = Path.join([release.path, "data", dir])
  277. Mix.Generator.create_directory(path)
  278. end
  279. )
  280. Mix.Generator.copy_file(
  281. "apps/emqx_authz/etc/acl.conf",
  282. Path.join(etc, "acl.conf"),
  283. force: overwrite?
  284. )
  285. # required by emqx_authz
  286. File.cp_r!(
  287. "apps/emqx/etc/certs",
  288. Path.join(etc, "certs")
  289. )
  290. Mix.Generator.copy_file(
  291. "apps/emqx_dashboard/etc/emqx.conf.en.example",
  292. Path.join(etc, "emqx-example.conf"),
  293. force: overwrite?
  294. )
  295. # this is required by the produced escript / nodetool
  296. Mix.Generator.copy_file(
  297. Path.join(release.version_path, "start_clean.boot"),
  298. Path.join(bin, "no_dot_erlang.boot"),
  299. force: overwrite?
  300. )
  301. assigns = template_vars(release, release_type, package_type, edition_type)
  302. # This is generated by `scripts/merge-config.escript` or `make
  303. # conf-segs`. So, this should be run before the release.
  304. # TODO: run as a "compiler" step???
  305. render_template(
  306. "apps/emqx_conf/etc/emqx.conf.all",
  307. assigns,
  308. Path.join(etc, "emqx.conf")
  309. )
  310. if edition_type == :enterprise do
  311. render_template(
  312. "apps/emqx_conf/etc/emqx_enterprise.conf.all",
  313. assigns,
  314. Path.join(etc, "emqx_enterprise.conf")
  315. )
  316. end
  317. render_template(
  318. "rel/emqx_vars",
  319. assigns,
  320. Path.join([release.path, "releases", "emqx_vars"])
  321. )
  322. vm_args_template_path =
  323. case release_type do
  324. :cloud ->
  325. "apps/emqx/etc/vm.args.cloud"
  326. end
  327. render_template(
  328. vm_args_template_path,
  329. assigns,
  330. [
  331. Path.join(etc, "vm.args"),
  332. Path.join(release.version_path, "vm.args")
  333. ]
  334. )
  335. for name <- [
  336. "emqx",
  337. "emqx_ctl"
  338. ] do
  339. Mix.Generator.copy_file(
  340. "bin/#{name}",
  341. Path.join(bin, name),
  342. force: overwrite?
  343. )
  344. # Files with the version appended are expected by the release
  345. # upgrade script `install_upgrade.escript`
  346. Mix.Generator.copy_file(
  347. Path.join(bin, name),
  348. Path.join(bin, name <> "-#{release.version}"),
  349. force: overwrite?
  350. )
  351. end
  352. for base_name <- ["emqx", "emqx_ctl"],
  353. suffix <- ["", "-#{release.version}"] do
  354. name = base_name <> suffix
  355. File.chmod!(Path.join(bin, name), 0o755)
  356. end
  357. Mix.Generator.copy_file(
  358. "bin/node_dump",
  359. Path.join(bin, "node_dump"),
  360. force: overwrite?
  361. )
  362. File.chmod!(Path.join(bin, "node_dump"), 0o755)
  363. Mix.Generator.copy_file(
  364. "bin/emqx_cluster_rescue",
  365. Path.join(bin, "emqx_cluster_rescue"),
  366. force: overwrite?
  367. )
  368. File.chmod!(Path.join(bin, "emqx_cluster_rescue"), 0o755)
  369. render_template(
  370. "rel/BUILD_INFO",
  371. assigns,
  372. Path.join(release.version_path, "BUILD_INFO")
  373. )
  374. release
  375. end
  376. defp render_template(template, assigns, target) when is_binary(target) do
  377. render_template(template, assigns, [target])
  378. end
  379. defp render_template(template, assigns, tartgets) when is_list(tartgets) do
  380. rendered =
  381. File.read!(template)
  382. |> from_rebar_to_eex_template()
  383. |> EEx.eval_string(assigns)
  384. for target <- tartgets do
  385. File.write!(target, rendered)
  386. end
  387. end
  388. # needed by nodetool and by release_handler
  389. defp create_RELEASES(release) do
  390. apps =
  391. Enum.map(release.applications, fn {app_name, app_props} ->
  392. app_vsn = Keyword.fetch!(app_props, :vsn)
  393. app_path =
  394. "./lib"
  395. |> Path.join("#{app_name}-#{app_vsn}")
  396. |> to_charlist()
  397. {app_name, app_vsn, app_path}
  398. end)
  399. release_entry = [
  400. {
  401. :release,
  402. to_charlist(release.name),
  403. to_charlist(release.version),
  404. release.erts_version,
  405. apps,
  406. :permanent
  407. }
  408. ]
  409. release.path
  410. |> Path.join("releases")
  411. |> Path.join("RELEASES")
  412. |> File.open!([:write, :utf8], fn handle ->
  413. IO.puts(handle, "%% coding: utf-8")
  414. :io.format(handle, '~tp.~n', [release_entry])
  415. end)
  416. release
  417. end
  418. defp copy_escript(release, escript_name) do
  419. [shebang, rest] =
  420. "bin/#{escript_name}"
  421. |> File.read!()
  422. |> String.split("\n", parts: 2)
  423. # the elixir version of escript + start.boot required the boot_var
  424. # RELEASE_LIB to be defined.
  425. boot_var = "%%!-boot_var RELEASE_LIB $RUNNER_ROOT_DIR/lib"
  426. # Files with the version appended are expected by the release
  427. # upgrade script `install_upgrade.escript`
  428. Enum.each(
  429. [escript_name, escript_name <> "-" <> release.version],
  430. fn name ->
  431. path = Path.join([release.path, "bin", name])
  432. File.write!(path, [shebang, "\n", boot_var, "\n", rest])
  433. end
  434. )
  435. release
  436. end
  437. # The `:tar` built-in step in Mix Release does not currently add the
  438. # `etc` directory into the resulting tarball. The workaround is to
  439. # add those to the `:overlays` key before running `:tar`.
  440. # See: https://hexdocs.pm/mix/1.13.4/Mix.Release.html#__struct__/0
  441. defp prepare_tar_overlays(release) do
  442. Map.update!(
  443. release,
  444. :overlays,
  445. &[
  446. "etc",
  447. "data",
  448. "bin/node_dump"
  449. | &1
  450. ]
  451. )
  452. end
  453. #############################################################################
  454. # Helper functions
  455. #############################################################################
  456. defp template_vars(release, release_type, :bin = _package_type, edition_type) do
  457. [
  458. platform_data_dir: "data",
  459. platform_etc_dir: "etc",
  460. platform_log_dir: "log",
  461. platform_plugins_dir: "plugins",
  462. runner_bin_dir: "$RUNNER_ROOT_DIR/bin",
  463. emqx_etc_dir: "$RUNNER_ROOT_DIR/etc",
  464. runner_lib_dir: "$RUNNER_ROOT_DIR/lib",
  465. runner_log_dir: "$RUNNER_ROOT_DIR/log",
  466. runner_user: "",
  467. release_version: release.version,
  468. erts_vsn: release.erts_version,
  469. # FIXME: this is empty in `make emqx` ???
  470. erl_opts: "",
  471. emqx_description: emqx_description(release_type, edition_type),
  472. emqx_schema_mod: emqx_schema_mod(edition_type),
  473. is_elixir: "yes",
  474. is_enterprise: if(edition_type == :enterprise, do: "yes", else: "no")
  475. ] ++ build_info()
  476. end
  477. defp template_vars(release, release_type, :pkg = _package_type, edition_type) do
  478. [
  479. platform_data_dir: "/var/lib/emqx",
  480. platform_etc_dir: "/etc/emqx",
  481. platform_log_dir: "/var/log/emqx",
  482. platform_plugins_dir: "/var/lib/emqx/plugins",
  483. runner_bin_dir: "/usr/bin",
  484. emqx_etc_dir: "/etc/emqx",
  485. runner_lib_dir: "$RUNNER_ROOT_DIR/lib",
  486. runner_log_dir: "/var/log/emqx",
  487. runner_user: "emqx",
  488. release_version: release.version,
  489. erts_vsn: release.erts_version,
  490. # FIXME: this is empty in `make emqx` ???
  491. erl_opts: "",
  492. emqx_description: emqx_description(release_type, edition_type),
  493. emqx_schema_mod: emqx_schema_mod(edition_type),
  494. is_elixir: "yes",
  495. is_enterprise: if(edition_type == :enterprise, do: "yes", else: "no")
  496. ] ++ build_info()
  497. end
  498. defp emqx_description(release_type, edition_type) do
  499. case {release_type, edition_type} do
  500. {:cloud, :enterprise} ->
  501. "EMQX Enterprise"
  502. {:cloud, :community} ->
  503. "EMQX"
  504. end
  505. end
  506. defp emqx_schema_mod(:enterprise), do: :emqx_enterprise_conf_schema
  507. defp emqx_schema_mod(:community), do: :emqx_conf_schema
  508. defp bcrypt_dep() do
  509. if enable_bcrypt?(),
  510. do: [{:bcrypt, github: "emqx/erlang-bcrypt", tag: "0.6.0", override: true}],
  511. else: []
  512. end
  513. defp jq_dep() do
  514. if enable_jq?(),
  515. do: [{:jq, github: "emqx/jq", tag: "v0.3.5", override: true}],
  516. else: []
  517. end
  518. defp quicer_dep() do
  519. if enable_quicer?(),
  520. # in conflict with emqx and emqtt
  521. do: [{:quicer, github: "emqx/quic", tag: "0.0.16", override: true}],
  522. else: []
  523. end
  524. defp enable_bcrypt?() do
  525. not win32?()
  526. end
  527. defp enable_jq?() do
  528. not Enum.any?([
  529. build_without_jq?(),
  530. win32?()
  531. ]) or "1" == System.get_env("BUILD_WITH_JQ")
  532. end
  533. defp enable_quicer?() do
  534. not Enum.any?([
  535. build_without_quic?(),
  536. win32?(),
  537. centos6?(),
  538. macos?()
  539. ]) or "1" == System.get_env("BUILD_WITH_QUIC")
  540. end
  541. defp enable_rocksdb?() do
  542. not Enum.any?([
  543. build_without_rocksdb?(),
  544. raspbian?()
  545. ]) or "1" == System.get_env("BUILD_WITH_ROCKSDB")
  546. end
  547. defp pkg_vsn() do
  548. %{edition_type: edition_type} = check_profile!()
  549. basedir = Path.dirname(__ENV__.file)
  550. script = Path.join(basedir, "pkg-vsn.sh")
  551. os_cmd(script, [Atom.to_string(edition_type)])
  552. end
  553. defp os_cmd(script, args) do
  554. {str, 0} = System.cmd("bash", [script | args])
  555. String.trim(str)
  556. end
  557. defp win32?(),
  558. do: match?({:win_32, _}, :os.type())
  559. defp centos6?() do
  560. case File.read("/etc/centos-release") do
  561. {:ok, "CentOS release 6" <> _} ->
  562. true
  563. _ ->
  564. false
  565. end
  566. end
  567. defp macos?() do
  568. {:unix, :darwin} == :os.type()
  569. end
  570. defp raspbian?() do
  571. os_cmd("./scripts/get-distro.sh", []) =~ "raspbian"
  572. end
  573. defp build_without_jq?() do
  574. opt = System.get_env("BUILD_WITHOUT_JQ", "false")
  575. String.downcase(opt) != "false"
  576. end
  577. defp build_without_quic?() do
  578. opt = System.get_env("BUILD_WITHOUT_QUIC", "false")
  579. String.downcase(opt) != "false"
  580. end
  581. defp build_without_rocksdb?() do
  582. opt = System.get_env("BUILD_WITHOUT_ROCKSDB", "false")
  583. String.downcase(opt) != "false"
  584. end
  585. defp from_rebar_to_eex_template(str) do
  586. # we must not consider surrounding space in the template var name
  587. # because some help strings contain informative variables that
  588. # should not be interpolated, and those have no spaces.
  589. Regex.replace(
  590. ~r/\{\{ ([a-zA-Z0-9_]+) \}\}/,
  591. str,
  592. "<%= \\g{1} %>"
  593. )
  594. end
  595. defp build_info() do
  596. [
  597. build_info_arch: to_string(:erlang.system_info(:system_architecture)),
  598. build_info_wordsize: wordsize(),
  599. build_info_os: os_cmd("./scripts/get-distro.sh", []),
  600. build_info_erlang: otp_release(),
  601. build_info_elixir: System.version(),
  602. build_info_relform: System.get_env("EMQX_REL_FORM", "tgz")
  603. ]
  604. end
  605. # https://github.com/erlang/rebar3/blob/e3108ac187b88fff01eca6001a856283a3e0ec87/src/rebar_utils.erl#L142
  606. defp wordsize() do
  607. size =
  608. try do
  609. :erlang.system_info({:wordsize, :external})
  610. rescue
  611. ErlangError ->
  612. :erlang.system_info(:wordsize)
  613. end
  614. to_string(8 * size)
  615. end
  616. defp normalize_env!() do
  617. env =
  618. case Mix.env() do
  619. :dev ->
  620. :emqx
  621. env ->
  622. env
  623. end
  624. Mix.env(env)
  625. end
  626. # As from Erlang/OTP 17, the OTP release number corresponds to the
  627. # major OTP version number. No erlang:system_info() argument gives
  628. # the exact OTP version.
  629. # https://www.erlang.org/doc/man/erlang.html#system_info_otp_release
  630. # https://github.com/erlang/rebar3/blob/e3108ac187b88fff01eca6001a856283a3e0ec87/src/rebar_utils.erl#L572-L577
  631. defp otp_release() do
  632. major_version = System.otp_release()
  633. root_dir = to_string(:code.root_dir())
  634. [root_dir, "releases", major_version, "OTP_VERSION"]
  635. |> Path.join()
  636. |> File.read()
  637. |> case do
  638. {:error, _} ->
  639. major_version
  640. {:ok, version} ->
  641. version
  642. |> String.trim()
  643. |> String.split("**")
  644. |> List.first()
  645. end
  646. end
  647. end