Skip to content

Commit

Permalink
Load mix deps only when there is a need to use them
Browse files Browse the repository at this point in the history
This helps speed up peformance to many tasks. Tasks like
clean, help and compile --list must now explicitly call
loadpaths with checks disabled so they can still show
data coming from dependencies but without causing crashes.
  • Loading branch information
José Valim committed Aug 4, 2014
1 parent c8a3a61 commit f0f2530
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 30 deletions.
6 changes: 1 addition & 5 deletions lib/mix/lib/mix/cli.ex
Expand Up @@ -51,11 +51,7 @@ defmodule Mix.CLI do

defp run_task(name, args) do
try do
if Mix.Project.get do
Mix.Task.run "loadconfig"
Mix.Task.run "loadpaths", ["--no-elixir-version-check", "--no-deps-check"]
Mix.Task.reenable "loadpaths"
end
Mix.Task.run "loadconfig"

# If the task is not available, let's try to
# compile the repository and then run it again.
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/dep/lock.ex
Expand Up @@ -33,7 +33,7 @@ defmodule Mix.Dep.Lock do
Returns the elixir version in the lock manifest unless is an umbrella app.
"""
def elixir_vsn() do
unless Mix.Project.umbrella?, do: elixir_vsn(Mix.Project.manifest_path)
elixir_vsn(Mix.Project.manifest_path)
end

@doc """
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/project.ex
Expand Up @@ -18,7 +18,7 @@ defmodule Mix.Project do
After being defined, the configuration for this project can be read
as `Mix.Project.config/0`. Notice that `config/0` won't fail if a
project is not defined; this allows many mix tasks to work
even without a project.
without a project.
In case the developer needs a project or wants to access a special
function in the project, the developer can call `Mix.Project.get!/0`
Expand Down
1 change: 1 addition & 0 deletions lib/mix/lib/mix/tasks/app.start.ex
Expand Up @@ -18,6 +18,7 @@ defmodule Mix.Tasks.App.Start do
"""
def run(args) do
Mix.Project.get!
Mix.Task.run "loadpaths", ["--no-readd"|args]

unless "--no-compile" in args do
Expand Down
19 changes: 13 additions & 6 deletions lib/mix/lib/mix/tasks/clean.ex
Expand Up @@ -18,14 +18,15 @@ defmodule Mix.Tasks.Clean do
"""

def run(args) do
Mix.Project.get!
loadpaths!

{opts, _, _} = OptionParser.parse(args)

_ = for compiler <- Mix.Tasks.Compile.compilers() do
module = Mix.Task.get!("compile.#{compiler}")
if function_exported?(module, :clean, 0) do
module.clean
end
end
_ = for compiler <- Mix.Tasks.Compile.compilers(),
module = Mix.Task.get("compile.#{compiler}"),
function_exported?(module, :clean, 0),
do: module.clean

if opts[:all] do
Mix.Task.run("deps.clean", args)
Expand All @@ -41,4 +42,10 @@ defmodule Mix.Tasks.Clean do
|> Enum.each(&File.rm_rf/1)
end
end

# Loadpaths without checks because compilers may be defined in deps.
defp loadpaths! do
Mix.Task.run "loadpaths", ["--no-elixir-version-check", "--no-deps-check"]
Mix.Task.reenable "loadpaths"
end
end
8 changes: 8 additions & 0 deletions lib/mix/lib/mix/tasks/compile.ex
Expand Up @@ -23,6 +23,7 @@ defmodule Mix.Tasks.Compile do
"""
def run(["--list"]) do
loadpaths!
Mix.Task.load_all

shell = Mix.shell
Expand All @@ -49,6 +50,7 @@ defmodule Mix.Tasks.Compile do
end

def run(args) do
Mix.Project.get!
Mix.Task.run "loadpaths", ["--no-readd"|args]

res =
Expand All @@ -61,6 +63,12 @@ defmodule Mix.Tasks.Compile do
if Enum.any?(res, &(:ok in &1)), do: :ok, else: :noop
end

# Loadpaths without checks because compilers may be defined in deps.
defp loadpaths! do
Mix.Task.run "loadpaths", ["--no-elixir-version-check", "--no-deps-check"]
Mix.Task.reenable "loadpaths"
end

@doc """
Returns all compilers.
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.check.ex
Expand Up @@ -78,7 +78,7 @@ defmodule Mix.Tasks.Deps.Check do
paths = Mix.Project.build_path(config)
|> Path.join("lib/*/ebin")
|> Path.wildcard
|> List.delete(not Mix.Project.umbrella? && Mix.Project.compile_path(config))
|> List.delete(config[:app] && Mix.Project.compile_path(config))

to_prune = Enum.reduce(all, paths, &(&2 -- Mix.Dep.load_paths(&1)))

Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.clean.ex
Expand Up @@ -15,7 +15,7 @@ defmodule Mix.Tasks.Deps.Clean do
@switches [unlock: :boolean, all: :boolean, only: :string]

def run(args) do
Mix.Project.get! # Require the project to be available
Mix.Project.get!
{opts, args, _} = OptionParser.parse(args, switches: @switches)

cond do
Expand Down
3 changes: 1 addition & 2 deletions lib/mix/lib/mix/tasks/deps.compile.ex
Expand Up @@ -28,8 +28,7 @@ defmodule Mix.Tasks.Deps.Compile do
format_dep: 1, make?: 1, mix?: 1, rebar?: 1]

def run(args) do
Mix.Project.get! # Require the project to be available

Mix.Project.get!
case OptionParser.parse(args) do
{_, [], _} ->
compile(Enum.filter(loaded(env: Mix.env), &compilable?/1))
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.ex
Expand Up @@ -96,7 +96,7 @@ defmodule Mix.Tasks.Deps do
"""
def run(args) do
Mix.Project.get! # Require the project to be available
Mix.Project.get!
{opts, _, _} = OptionParser.parse(args)

if opts[:all] do
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.get.ex
Expand Up @@ -13,7 +13,7 @@ defmodule Mix.Tasks.Deps.Get do
* `--only` - only fetch dependencies for given environment
"""
def run(args) do
Mix.Project.get! # Require the project to be available
Mix.Project.get!
{opts, _, _} = OptionParser.parse(args, switches: [quiet: :boolean, only: :string])

# Fetch all deps by default unless --only is given
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.unlock.ex
Expand Up @@ -11,7 +11,7 @@ defmodule Mix.Tasks.Deps.Unlock do
"""

def run(args) do
Mix.Project.get! # Require the project to be available
Mix.Project.get!
{opts, args, _} = OptionParser.parse(args, switches: [all: :boolean])

cond do
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.update.ex
Expand Up @@ -17,7 +17,7 @@ defmodule Mix.Tasks.Deps.Update do
* `--only` - only fetch dependencies for given environment
"""
def run(args) do
Mix.Project.get! # Require the project to be available
Mix.Project.get!
{opts, rest, _} = OptionParser.parse(args, switches: [all: :boolean, only: :string])

# Fetch all deps by default unless --only is given
Expand Down
4 changes: 1 addition & 3 deletions lib/mix/lib/mix/tasks/escript.build.ex
Expand Up @@ -72,10 +72,8 @@ defmodule Mix.Tasks.Escript.Build do
"""
def run(args) do
{opts, _, _} = OptionParser.parse(args, switches: [force: :boolean, compile: :boolean])

# Require the project to be available
Mix.Project.get!
{opts, _, _} = OptionParser.parse(args, switches: [force: :boolean, compile: :boolean])

if Keyword.get(opts, :compile, true) do
Mix.Task.run :compile, args
Expand Down
12 changes: 12 additions & 0 deletions lib/mix/lib/mix/tasks/help.ex
Expand Up @@ -37,6 +37,8 @@ defmodule Mix.Tasks.Help do
"""

def run([]) do
loadpaths!

shell = Mix.shell
modules = Mix.Task.load_all()

Expand All @@ -59,6 +61,8 @@ defmodule Mix.Tasks.Help do
end

def run(["--names"]) do
loadpaths!

tasks =
Mix.Task.load_all()
|> Enum.map(&Mix.Task.task_name/1)
Expand All @@ -74,6 +78,8 @@ defmodule Mix.Tasks.Help do
end

def run([task]) do
loadpaths!

module = Mix.Task.get!(task)
doc = Mix.Task.moduledoc(module) || "There is no documentation for this task"
opts = Application.get_env(:mix, :colors)
Expand All @@ -94,6 +100,12 @@ defmodule Mix.Tasks.Help do
Mix.raise "Unexpected arguments, expected `mix help` or `mix help TASK`"
end

# Loadpaths without checks because tasks may be defined in deps.
defp loadpaths! do
Mix.Task.run "loadpaths", ["--no-elixir-version-check", "--no-deps-check"]
Mix.Task.reenable "loadpaths"
end

defp ansi_docs?(opts) do
if Keyword.has_key?(opts, :enabled) do
opts[:enabled]
Expand Down
8 changes: 5 additions & 3 deletions lib/mix/lib/mix/tasks/loadpaths.ex
Expand Up @@ -23,7 +23,9 @@ defmodule Mix.Tasks.Loadpaths do
load_deps(config, args)
end

load_project(config, args)
if config[:app] do
load_project(config, args)
end

unless "--no-readd" in args do
Code.readd_paths()
Expand Down Expand Up @@ -53,12 +55,12 @@ defmodule Mix.Tasks.Loadpaths do
Mix.Project.build_path(config)
|> Path.join("lib/*/ebin")
|> Path.wildcard
|> List.delete(not Mix.Project.umbrella? && Mix.Project.compile_path(config))
|> List.delete(config[:app] && Mix.Project.compile_path(config))
|> Enum.each(&Code.prepend_path/1)
end

defp load_project(config, _args) do
# Force recompile if we have a version mismatch
# Force recompile if we have an app and a version mismatch
old_vsn = Mix.Dep.Lock.elixir_vsn
if old_vsn && old_vsn != System.version, do: Mix.Dep.Lock.touch

Expand Down
3 changes: 0 additions & 3 deletions lib/mix/lib/mix/tasks/run.ex
Expand Up @@ -39,9 +39,6 @@ defmodule Mix.Tasks.Run do
switches: [parallel_require: :keep, require: :keep, eval: :keep, config: :keep,
halt: :boolean, compile: :boolean, deps_check: :boolean, start: :boolean])

# Require the project to be available
Mix.Project.get!

{file, argv} =
case {Keyword.has_key?(opts, :eval), head} do
{true, _} -> {nil, head}
Expand Down

0 comments on commit f0f2530

Please sign in to comment.