defmodule Blueprint do @moduledoc """ A blueprint represents a collection of applications that are used to understand how they work together. """ defstruct [:xref] @type t :: %Blueprint{ xref: pid } defp add_app(_, []), do: :ok defp add_app(xref, lib) when is_atom(lib), do: { :ok, _ } = :xref.add_application(xref, :code.lib_dir(lib)) defp add_app(xref, path) when is_binary(path) do if File.exists?(Path.join(path, "ebin")) do { :ok, _ } = :xref.add_application(xref, to_charlist(path)) else Path.wildcard(Path.join(path, "*/ebin")) |> Enum.each(fn ebin -> length = round((bit_size(ebin) / 8) - 4) <> = ebin if File.dir?(lib) do { :ok, _ } = :xref.add_application(xref, to_charlist(lib)) end end) end end defp add_app(xref, [h|t]) do add_app(xref, h) add_app(xref, t) end @doc """ Create a new blueprint. Blueprints will represent any applications that are added to them. Atoms are interpreted as library names, while strings are expected to be valid paths to either a library or a collection of libraries. """ @spec new(atom | String.t | [atom | String.t]) :: Blueprint.t def new(path) do { :ok, xref } = :xref.start([]) add_app(xref, path) %Blueprint{ xref: xref } end @doc """ Close an active blueprint. """ @spec close(Blueprint.t) :: :ok def close(%Blueprint{ xref: xref }) do :xref.stop(xref) :ok end end