defmodule Mix.Tasks.Arch.Apps.Xref do @moduledoc """ Mix Task to generate a dependency graph for the applications within an umbrella. Usage: mix arch.apps.xref [options] The following options are accepted: * `--format` - Can be one of `dot`, `png` or `svg` * `--out` - Output filename * `--include-dep` to include extra applications in the generated graph. The application must be a dependency to the main application. """ @shortdoc "Generates a dependency graph of applications" require Logger require EEx alias Archeometer.Explore.Project use Mix.Task @supported_formats ["png", "dot", "mermaid", "svg"] @impl Mix.Task def run(argv) do case parse_args(argv) do {:ok, [format: format, out: out_file, extra_deps: extra_deps]} -> if 0 < length(extra_deps) and Mix.Project.umbrella?() do Logger.warn( "You are analysing multirepo dependencies on an umbrella project. This is probably not what you want." ) end Archeometer.Analysis.Apps.Xref.gen_graph(format, extra_deps) |> write(out_file) Mix.shell().info("Graph ready at: '#{out_file}'") {:error, error} -> Mix.shell().error("Error: #{error}") print_help() end end defp parse_args(argv) do {opts, _, invalid_switches} = OptionParser.parse( argv, # --db is currently supported, but NOT USED strict: [format: :string, out: :string, db: :string, include_deps: :keep] ) case invalid_switches do [] -> format = Keyword.get(opts, :format, "dot") out_fname = Keyword.get(opts, :out, "console") db_name = Keyword.get(opts, :db, Archeometer.Repo.default_db_name()) deps = Keyword.get_values(opts, :include_deps) |> get_matching_deps() case validate_options(%{format: format, out: out_fname, db: db_name}) do :ok -> {:ok, [ format: format, out: out_fname, extra_deps: deps ]} {:error, error} -> {:error, error} end _ -> {:error, :wrong_arguments} end end defp get_matching_deps(globs) do case Project.filter_deps(globs) do false -> [] other -> other end |> Keyword.keys() end defp validate_options(%{format: format, out: out_fname, db: db_name}) do cond do not Archeometer.Repo.db_ready?(:full, db_name) -> {:error, "Database is not existent or doesn't have the required tables."} not (format in @supported_formats) -> {:error, :unsupported_output_format} format == "png" and out_fname == "console" -> {:error, :png_not_writable_to_console} true -> :ok end end defp print_help() do Mix.shell().info(""" Usage: mix arch.apps.xref [opts] opts: --format (dot, png), --out out_fname --include-dep dep """) end defp write(content, "console") do Mix.shell().info(content) end defp write(content, out_fname) do File.write!(out_fname, content) end end