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