From 066fd0481b1fc0d237eae9dc5c25c71e11701a4d Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 9 Jul 2024 16:00:49 -0300 Subject: [PATCH] feat(mix): compile asn1 files --- .../lib/mix/tasks/compile.asn1.ex | 82 +++++++++++++++++++ apps/emqx_durable_storage/mix.exs | 9 +- 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 apps/emqx_durable_storage/lib/mix/tasks/compile.asn1.ex diff --git a/apps/emqx_durable_storage/lib/mix/tasks/compile.asn1.ex b/apps/emqx_durable_storage/lib/mix/tasks/compile.asn1.ex new file mode 100644 index 000000000..afec3f62c --- /dev/null +++ b/apps/emqx_durable_storage/lib/mix/tasks/compile.asn1.ex @@ -0,0 +1,82 @@ +defmodule Mix.Tasks.Compile.Asn1 do + use Mix.Task.Compiler + + @recursive true + @manifest_vsn 1 + @manifest "compile.asn1" + # TODO: use manifest to track generated files? + + @impl true + def manifests(), do: [manifest()] + defp manifest(), do: Path.join(Mix.Project.manifest_path(), @manifest) + + @impl true + def run(_args) do + add_to_path_and_cache(:asn1) + + Mix.Project.get!() + config = Mix.Project.config() + app_root = File.cwd!() + + asn1_srcs = config[:asn1_srcs] || [] + manifest_data = read_manifest(manifest()) + manifest_modified_time = Mix.Utils.last_modified(manifest()) + Enum.each(asn1_srcs, &compile(&1, app_root, manifest_modified_time)) + write_manifest(manifest(), manifest_data) + + {:noop, []} + end + + defp compile(src, app_root, manifest_modified_time) do + %{ + src: src_path, + compile_opts: compile_opts + } = src + src_path = + app_root + |> Path.join(src_path) + |> Path.expand() + if stale?(src_path, manifest_modified_time) do + Mix.shell().info("compiling asn1 file: #{src_path}") + :ok = :asn1ct.compile(to_charlist(src_path), compile_opts) + else + Mix.shell().info("file is up to date, not compiling: #{src_path}") + end + end + + defp stale?(file, manifest_modified_time) do + with true <- File.exists?(file), + false <- Mix.Utils.stale?([file], [manifest_modified_time]) do + false + else + _ -> true + end + end + + defp read_manifest(file) do + try do + file |> File.read!() |> :erlang.binary_to_term() + rescue + _ -> %{} + else + {@manifest_vsn, data} when is_map(data) -> data + _ -> %{} + end + end + + defp write_manifest(file, data) do + Mix.shell().info("writing manifest #{file}") + File.mkdir_p!(Path.dirname(file)) + File.write!(file, :erlang.term_to_binary({@manifest_vsn, data})) + end + + def add_to_path_and_cache(lib_name) do + :code.lib_dir() + |> Path.join("#{lib_name}-*") + |> Path.wildcard() + |> hd() + |> Path.join("ebin") + |> to_charlist() + |> :code.add_path(:cache) + end +end diff --git a/apps/emqx_durable_storage/mix.exs b/apps/emqx_durable_storage/mix.exs index fd93f3bf5..4cb1a9b35 100644 --- a/apps/emqx_durable_storage/mix.exs +++ b/apps/emqx_durable_storage/mix.exs @@ -7,9 +7,14 @@ defmodule EMQXDurableStorage.MixProject do app: :emqx_durable_storage, version: "0.1.0", build_path: "../../_build", - # config_path: "../../config/config.exs", + compilers: [:yecc, :leex, :elixir, :asn1, :erlang, :app], erlc_options: UMP.erlc_options(), - erlc_paths: UMP.erlc_paths(), + erlc_paths: ["gen_src" | UMP.erlc_paths()], + # used by our `compile.asn1` compiler + asn1_srcs: [ + %{src: "./asn.1/DurableMessage.asn", + compile_opts: [:per, :noobj, outdir: ~c"gen_src"]} + ], deps_path: "../../deps", lockfile: "../../mix.lock", elixir: "~> 1.14",