-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Automatically inflect the list of applications, closes #5249 #5473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,13 +18,16 @@ defmodule Mix.Tasks.Compile.App do | |
| function in your `mix.exs` with the following options: | ||
|
|
||
| * `:applications` - all applications your application depends | ||
| on at runtime. For example, if your application depends on | ||
| Erlang's `:crypto`, it needs to be added to this list. Most | ||
| of your dependencies must be added as well (unless they're | ||
| a development or test dependency). Mix and other tools use this | ||
| list in order to properly boot your application dependencies | ||
| on at runtime. By default, this list is automatically inflected | ||
| from your dependencies. Any extra Erlang/Elixir dependency | ||
| must be specified in `:extra_applications`. Mix and other tools | ||
| use the application list in order to start your dependencies | ||
| before starting the application itself. | ||
|
|
||
| * `:extra_applications` - a list of Erlang/Elixir applications | ||
| that you want started before your application. For example, | ||
| Elixir's `:logger` or Erlang's `:crypto`. | ||
|
||
|
|
||
| * `:registered` - the name of all registered processes in the | ||
| application. If your application defines a local GenServer | ||
| with name `MyServer`, it is recommended to add `MyServer` | ||
|
|
@@ -79,10 +82,10 @@ defmodule Mix.Tasks.Compile.App do | |
|
|
||
| if opts[:force] || Mix.Utils.stale?(sources, [target]) || modules_changed?(mods, target) do | ||
| best_guess = [ | ||
| vsn: to_charlist(version), | ||
| description: to_charlist(config[:description] || app), | ||
| modules: mods, | ||
| applications: [], | ||
| registered: [], | ||
| vsn: to_charlist(version), | ||
| ] | ||
|
|
||
| properties = if function_exported?(project, :application, 0) do | ||
|
|
@@ -95,13 +98,7 @@ defmodule Mix.Tasks.Compile.App do | |
| best_guess | ||
| end | ||
|
|
||
| properties = ensure_correct_properties(app, config, properties) | ||
|
|
||
| # Ensure we always prepend the standard application dependencies | ||
| properties = Keyword.update!(properties, :applications, fn apps -> | ||
| [:kernel, :stdlib] ++ language_app(config) ++ apps | ||
| end) | ||
|
|
||
| properties = ensure_correct_properties(properties, config) | ||
| contents = :io_lib.format("~p.~n", [{:application, app, properties}]) | ||
|
|
||
| Mix.Project.ensure_structure() | ||
|
|
@@ -152,13 +149,14 @@ defmodule Mix.Tasks.Compile.App do | |
| end | ||
| end | ||
|
|
||
| defp ensure_correct_properties(app, config, properties) do | ||
| defp ensure_correct_properties(properties, config) do | ||
| properties | ||
| |> Keyword.put_new(:description, to_charlist(config[:description] || app)) | ||
| |> validate_properties | ||
| |> validate_properties! | ||
| |> Keyword.put_new_lazy(:applications, fn -> apps_from_prod_non_optional_deps(properties) end) | ||
| |> Keyword.update!(:applications, fn apps -> normalize_apps(apps, properties, config) end) | ||
| end | ||
|
|
||
| defp validate_properties(properties) do | ||
| defp validate_properties!(properties) do | ||
| Enum.each properties, fn | ||
| {:description, value} -> | ||
| unless is_list(value) do | ||
|
|
@@ -188,13 +186,17 @@ defmodule Mix.Tasks.Compile.App do | |
| 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}" | ||
| 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}" | ||
| end | ||
| {:applications, value} -> | ||
| unless is_list(value) and Enum.all?(value, &is_atom(&1)) do | ||
| Mix.raise "Application dependencies (:applications) should be a list of atoms, got: #{inspect value}" | ||
| Mix.raise "Application applications (:applications) should be a list of atoms, got: #{inspect value}" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Application applications"?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And above "Application extra applications". |
||
| end | ||
| {:env, value} -> | ||
| unless Keyword.keyword?(value) do | ||
| Mix.raise "Application dependencies (: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 | ||
|
|
@@ -212,4 +214,20 @@ defmodule Mix.Tasks.Compile.App do | |
|
|
||
| properties | ||
| end | ||
|
|
||
| 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, | ||
| Keyword.get(opts, :app, true), | ||
| Keyword.get(opts, :runtime, true), | ||
| not Keyword.get(opts, :optional, false), | ||
| not app in included_applications, | ||
| do: app | ||
| end | ||
|
|
||
| defp normalize_apps(apps, properties, config) do | ||
| extra = Keyword.get(properties, :extra_applications, []) | ||
| Enum.uniq([:kernel, :stdlib] ++ language_app(config) ++ extra ++ apps) | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,30 @@ defmodule Mix.Tasks.Compile.AppTest do | |
|
|
||
| def application do | ||
| [maxT: :infinity, | ||
| applications: [:example_app]] | ||
| applications: [:example_app], | ||
| extra_applications: [:logger]] | ||
| end | ||
| end | ||
|
|
||
| defmodule CustomDeps do | ||
| def project do | ||
| [app: :custom_deps, version: "0.2.0", deps: deps()] | ||
| end | ||
|
|
||
| def application do | ||
| [extra_applications: [:logger], included_applications: [:ok9]] | ||
| end | ||
|
|
||
| def deps do | ||
| [{:ok1, path: "../ok"}, | ||
| {:ok2, path: "../ok", only: :prod}, | ||
| {:ok3, path: "../ok", only: :dev}, | ||
| {:ok4, path: "../ok", runtime: true}, | ||
| {:ok5, path: "../ok", runtime: false}, | ||
| {:ok6, path: "../ok", optional: true}, | ||
| {:ok7, path: "../ok", optional: false}, | ||
| {:ok8, path: "../ok", app: false}, | ||
| {:ok9, path: "../ok"}] | ||
| end | ||
| end | ||
|
|
||
|
|
@@ -48,7 +71,7 @@ defmodule Mix.Tasks.Compile.AppTest do | |
| end | ||
| end | ||
|
|
||
| test "use custom application settings" do | ||
| test "uses custom application settings" do | ||
| Mix.Project.push CustomProject | ||
|
|
||
| in_fixture "no_mixfile", fn -> | ||
|
|
@@ -57,11 +80,22 @@ defmodule Mix.Tasks.Compile.AppTest do | |
| contents = File.read!("_build/dev/lib/custom_project/ebin/custom_project.app") | ||
| assert contents =~ "0.2.0" | ||
| assert contents =~ "{maxT,infinity}" | ||
| assert contents =~ "{applications,[kernel,stdlib,elixir,example_app]}" | ||
| assert contents =~ "{applications,[kernel,stdlib,elixir,logger,example_app]}" | ||
| assert contents =~ "Some UTF-8 description (uma descrição em UTF-8)" | ||
| end | ||
| end | ||
|
|
||
| test "automatically inflects applications" do | ||
| Mix.Project.push CustomDeps | ||
|
|
||
| in_fixture "no_mixfile", fn -> | ||
| Mix.Tasks.Compile.Elixir.run([]) | ||
| Mix.Tasks.Compile.App.run([]) | ||
| contents = File.read!("_build/dev/lib/custom_deps/ebin/custom_deps.app") | ||
| assert contents =~ "{applications,[kernel,stdlib,elixir,logger,ok1,ok3,ok4,ok7]}" | ||
| end | ||
| end | ||
|
|
||
| test "application properties validation" do | ||
| Mix.Project.push InvalidProject | ||
|
|
||
|
|
@@ -90,26 +124,32 @@ defmodule Mix.Tasks.Compile.AppTest do | |
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
|
||
| Process.put(:application, [extra_applications: ["invalid"]]) | ||
| message = "Application extra applications (:extra_applications) should be a list of atoms, got: [\"invalid\"]" | ||
| assert_raise Mix.Error, message, fn -> | ||
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
|
||
| Process.put(:application, [included_applications: ["invalid"]]) | ||
| message = "Application included applications (:included_applications) should be a list of atoms, got: [\"invalid\"]" | ||
| assert_raise Mix.Error, message, fn -> | ||
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
|
||
| Process.put(:application, [applications: ["invalid"]]) | ||
| message = "Application dependencies (:applications) should be a list of atoms, got: [\"invalid\"]" | ||
| message = "Application applications (:applications) should be a list of atoms, got: [\"invalid\"]" | ||
| assert_raise Mix.Error, message, fn -> | ||
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
|
||
| Process.put(:application, [applications: nil]) | ||
| message = "Application dependencies (:applications) should be a list of atoms, got: nil" | ||
| message = "Application applications (:applications) should be a list of atoms, got: nil" | ||
| assert_raise Mix.Error, message, fn -> | ||
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
|
||
| Process.put(:application, [env: [:invalid]]) | ||
| message = "Application dependencies (:env) should be a keyword list, got: [:invalid]" | ||
| message = "Application environment (:env) should be a keyword list, got: [:invalid]" | ||
| assert_raise Mix.Error, message, fn -> | ||
| Mix.Tasks.Compile.App.run([]) | ||
| end | ||
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing final period.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other items on the list do not have a final period.