diff --git a/lib/mix/lib/mix/project.ex b/lib/mix/lib/mix/project.ex index d7ae332754d..ec10da1d000 100644 --- a/lib/mix/lib/mix/project.ex +++ b/lib/mix/lib/mix/project.ex @@ -899,6 +899,20 @@ defmodule Mix.Project do Mix.Lock.with_lock(build_path, fun, on_taken: on_taken) end + @doc false + def with_deps_lock(config \\ config(), fun) do + # We wrap operations on the deps directory and on mix.lock to + # avoid write conflicts. + + deps_path = deps_path(config) + + on_taken = fn os_pid -> + Mix.shell().info("Waiting for lock on the deps directory (held by process #{os_pid})") + end + + Mix.Lock.with_lock(deps_path, fun, on_taken: on_taken) + end + # Loads mix.exs in the current directory or loads the project from the # mixfile cache and pushes the project onto the project stack. defp load_project(app, post_config) do diff --git a/lib/mix/lib/mix/tasks/clean.ex b/lib/mix/lib/mix/tasks/clean.ex index a51b235a343..18c01dd1685 100644 --- a/lib/mix/lib/mix/tasks/clean.ex +++ b/lib/mix/lib/mix/tasks/clean.ex @@ -30,6 +30,12 @@ defmodule Mix.Tasks.Clean do {opts, _, _} = OptionParser.parse(args, switches: @switches) + Mix.Project.with_build_lock(fn -> + do_run(opts) + end) + end + + defp do_run(opts) do # First, we get the tasks. After that, we clean them. # This is to avoid a task cleaning a compiler module. tasks = diff --git a/lib/mix/lib/mix/tasks/deps.clean.ex b/lib/mix/lib/mix/tasks/deps.clean.ex index ef40731d330..93279343a79 100644 --- a/lib/mix/lib/mix/tasks/deps.clean.ex +++ b/lib/mix/lib/mix/tasks/deps.clean.ex @@ -63,13 +63,19 @@ defmodule Mix.Tasks.Deps.Clean do ) end - do_clean(apps_to_clean, loaded_deps, build_path, deps_path, opts[:build]) + Mix.Project.with_build_lock(fn -> + clean_build(apps_to_clean, build_path) + end) - if opts[:unlock] do - Mix.Task.run("deps.unlock", args) - else - :ok - end + Mix.Project.with_deps_lock(fn -> + clean_source(apps_to_clean, loaded_deps, deps_path, opts[:build]) + + if opts[:unlock] do + Mix.Task.run("deps.unlock", args) + else + :ok + end + end) end defp checked_deps(build_path, deps_path) do @@ -108,11 +114,9 @@ defmodule Mix.Tasks.Deps.Clean do end end - defp do_clean(apps, deps, build_path, deps_path, build_only?) do + defp clean_build(apps, build_path) do shell = Mix.shell() - local = for %{scm: scm, app: app} <- deps, not scm.fetchable?(), do: Atom.to_string(app) - Enum.each(apps, fn app -> shell.info("* Cleaning #{app}") @@ -122,7 +126,13 @@ defmodule Mix.Tasks.Deps.Clean do |> Path.wildcard() |> maybe_warn_for_invalid_path(app) |> Enum.map(&(&1 |> File.rm_rf() |> maybe_warn_failed_file_deletion())) + end) + end + defp clean_source(apps, deps, deps_path, build_only?) do + local = for %{scm: scm, app: app} <- deps, not scm.fetchable?(), do: Atom.to_string(app) + + Enum.each(apps, fn app -> # Remove everything from the source directory of dependencies. # Skip this step if --build option is specified or if # the dependency is local, i.e., referenced using :path. diff --git a/lib/mix/lib/mix/tasks/deps.get.ex b/lib/mix/lib/mix/tasks/deps.get.ex index 4e63c218bea..12fae37aec2 100644 --- a/lib/mix/lib/mix/tasks/deps.get.ex +++ b/lib/mix/lib/mix/tasks/deps.get.ex @@ -26,6 +26,12 @@ defmodule Mix.Tasks.Deps.Get do {opts, _, _} = OptionParser.parse(args, switches: [only: :string, target: :string, check_locked: :boolean]) + Mix.Project.with_deps_lock(fn -> + do_run(opts) + end) + end + + defp do_run(opts) do fetch_opts = for {switch, key} <- [only: :env, target: :target, check_locked: :check_locked], value = opts[switch], diff --git a/lib/mix/lib/mix/tasks/deps.unlock.ex b/lib/mix/lib/mix/tasks/deps.unlock.ex index eb72786254c..27c5d8d6059 100644 --- a/lib/mix/lib/mix/tasks/deps.unlock.ex +++ b/lib/mix/lib/mix/tasks/deps.unlock.ex @@ -27,6 +27,12 @@ defmodule Mix.Tasks.Deps.Unlock do Mix.Project.get!() {opts, apps, _} = OptionParser.parse(args, switches: @switches) + Mix.Project.with_deps_lock(fn -> + do_run(opts, apps) + end) + end + + defp do_run(opts, apps) do cond do opts[:all] -> Mix.Dep.Lock.write(%{}) diff --git a/lib/mix/lib/mix/tasks/deps.update.ex b/lib/mix/lib/mix/tasks/deps.update.ex index d74a0b105c3..529dad817f3 100644 --- a/lib/mix/lib/mix/tasks/deps.update.ex +++ b/lib/mix/lib/mix/tasks/deps.update.ex @@ -45,6 +45,12 @@ defmodule Mix.Tasks.Deps.Update do {opts, rest, _} = OptionParser.parse(args, switches: [all: :boolean, only: :string, target: :string]) + Mix.Project.with_deps_lock(fn -> + do_run(opts, rest) + end) + end + + defp do_run(opts, rest) do fetch_opts = for {switch, key} <- [only: :env, target: :target], value = opts[switch],