diff --git a/lib/mix/lib/mix/tasks/compile.app.ex b/lib/mix/lib/mix/tasks/compile.app.ex index 69a250b4857..6f8fd144f43 100644 --- a/lib/mix/lib/mix/tasks/compile.app.ex +++ b/lib/mix/lib/mix/tasks/compile.app.ex @@ -107,45 +107,50 @@ defmodule Mix.Tasks.Compile.App do def run(args) do {opts, _, _} = OptionParser.parse(args, switches: [force: :boolean]) - project = Mix.Project.get! - config = Mix.Project.config + project = Mix.Project.get!() + config = Mix.Project.config() - app = Keyword.get(config, :app) + app = Keyword.get(config, :app) version = Keyword.get(config, :version) validate_app(app) validate_version(version) - path = Mix.Project.compile_path - mods = modules_from(Path.wildcard("#{path}/*.beam")) |> Enum.sort + path = Mix.Project.compile_path() + mods = modules_from(Path.wildcard("#{path}/*.beam")) |> Enum.sort() - target = Path.join(path, "#{app}.app") - sources = Mix.Project.config_files + target = Path.join(path, "#{app}.app") + sources = Mix.Project.config_files() if opts[:force] || Mix.Utils.stale?(sources, [target]) || modules_changed?(mods, target) do best_guess = [ description: to_charlist(config[:description] || app), modules: mods, registered: [], - vsn: to_charlist(version), + vsn: to_charlist(version) ] - properties = if function_exported?(project, :application, 0) do - project_application = project.application() - unless Keyword.keyword?(project_application) do - Mix.raise "Application configuration returned from application/0 should be a keyword list" + properties = + if function_exported?(project, :application, 0) do + project_application = project.application + + unless Keyword.keyword?(project_application) do + Mix.raise( + "Application configuration returned from application/0 should be a keyword list" + ) + end + + Keyword.merge(best_guess, project_application) + else + best_guess end - Keyword.merge(best_guess, project_application) - else - best_guess - end properties = ensure_correct_properties(properties, config) contents = :io_lib.format("~p.~n", [{:application, app, properties}]) Mix.Project.ensure_structure() File.write!(target, IO.chardata_to_string(contents)) - Mix.shell.info "Generated #{app} app" + Mix.shell().info("Generated #{app} app") :ok else :noop @@ -156,12 +161,14 @@ defmodule Mix.Tasks.Compile.App do case :file.consult(target) do {:ok, [{:application, _app, properties}]} -> properties[:modules] != mods + _ -> false end end defp validate_app(app) when is_atom(app), do: :ok + defp validate_app(app) do ensure_present(:app, app) Mix.raise("Expected :app to be an atom, got: #{inspect(app)}") @@ -169,6 +176,7 @@ defmodule Mix.Tasks.Compile.App do defp validate_version(version) do ensure_present(:version, version) + unless is_binary(version) and match?({:ok, _}, Version.parse(version)) do Mix.raise("Expected :version to be a SemVer version, got: #{inspect(version)}") end @@ -177,10 +185,11 @@ defmodule Mix.Tasks.Compile.App do defp ensure_present(name, nil) do Mix.raise("Please ensure mix.exs file has the #{inspect(name)} in the project definition") end + defp ensure_present(_name, _val), do: :ok defp modules_from(beams) do - Enum.map beams, &(&1 |> Path.basename |> Path.rootname(".beam") |> String.to_atom) + Enum.map(beams, &(&1 |> Path.basename() |> Path.rootname(".beam") |> String.to_atom())) end defp language_app(config) do @@ -199,60 +208,103 @@ defmodule Mix.Tasks.Compile.App do end defp validate_properties!(properties) do - Enum.each properties, fn + Enum.each(properties, fn {:description, value} -> unless is_list(value) do - Mix.raise "Application description (:description) is not a character list, got: #{inspect value}" + Mix.raise( + "Application description (:description) is not a character list, got: #{ + inspect(value) + }" + ) end + {:id, value} -> unless is_list(value) do - Mix.raise "Application id (:id) is not a character list, got: #{inspect value}" + Mix.raise("Application id (:id) is not a character list, got: #{inspect(value)}") end + {:vsn, value} -> unless is_list(value) do - Mix.raise "Application vsn (:vsn) is not a character list, got: #{inspect value}" + Mix.raise("Application vsn (:vsn) is not a character list, got: #{inspect(value)}") end + {:maxT, value} -> unless value == :infinity or is_integer(value) do - Mix.raise "Application maximum time (:maxT) is not an integer or :infinity, got: #{inspect value}" + Mix.raise( + "Application maximum time (:maxT) is not an integer or :infinity, got: " <> + inspect(value) + ) end + {:modules, value} -> unless is_list(value) and Enum.all?(value, &is_atom(&1)) do - Mix.raise "Application modules (:modules) should be a list of atoms, got: #{inspect value}" + Mix.raise( + "Application modules (:modules) should be a list of atoms, got: #{inspect(value)}" + ) end + {:registered, value} -> unless is_list(value) and Enum.all?(value, &is_atom(&1)) do - Mix.raise "Application registered processes (:registered) should be a list of atoms, got: #{inspect value}" + Mix.raise( + "Application registered processes (:registered) should be a list of atoms, got: " <> + inspect(value) + ) end + {:included_applications, value} -> unless is_list(value) and Enum.all?(value, &is_atom(&1)) do - Mix.raise "Application included applications (:included_applications) should be a list of atoms, got: #{inspect value}" + Mix.raise( + "Application included applications (:included_applications) should be a list of atoms, got: " <> + inspect(value) + ) end + {:extra_applications, value} -> unless is_list(value) and Enum.all?(value, &is_atom(&1)) do - Mix.raise "Application extra applications (:extra_applications) should be a list of atoms, got: #{inspect value}" + Mix.raise( + "Application extra applications (:extra_applications) should be a list of atoms, got: " <> + inspect(value) + ) end + {:applications, value} -> unless is_list(value) and Enum.all?(value, &is_atom(&1)) do - Mix.raise "Application applications (:applications) should be a list of atoms, got: #{inspect value}" + Mix.raise( + "Application applications (:applications) should be a list of atoms, got: " <> + inspect(value) + ) end + {:env, value} -> unless Keyword.keyword?(value) do - Mix.raise "Application environment (:env) should be a keyword list, got: #{inspect value}" + Mix.raise( + "Application environment (:env) should be a keyword list, got: #{inspect(value)}" + ) end + {:start_phases, value} -> unless Keyword.keyword?(value) do - Mix.raise "Application start phases (:start_phases) should be a keyword list, got: #{inspect value}" + Mix.raise( + "Application start phases (:start_phases) should be a keyword list, got: " <> + inspect(value) + ) end + {:mod, []} -> :ok + {:mod, {module, _args}} when is_atom(module) -> :ok + {:mod, value} -> - Mix.raise "Application callback module (:mod) should be either [] or {module, start_args}, got: #{inspect value}" + Mix.raise( + "Application callback module (:mod) should be either [] or {module, start_args}, got: " <> + inspect(value) + ) + _ -> :ok - end + end) properties end @@ -260,12 +312,11 @@ defmodule Mix.Tasks.Compile.App do defp apps_from_prod_non_optional_deps(properties) do included_applications = Keyword.get(properties, :included_applications, []) - for %{app: app, opts: opts, top_level: true} <- Mix.Dep.cached, + for %{app: app, opts: opts, top_level: true} <- Mix.Dep.cached(), Keyword.get(opts, :app, true), Keyword.get(opts, :runtime, true), not Keyword.get(opts, :optional, false), - app not in included_applications, - do: app + app not in included_applications, do: app end defp normalize_apps(apps, properties, config) do