From ef435541ba32437828baf615cfef32bf17f932c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Tue, 8 Oct 2024 15:40:43 +0800 Subject: [PATCH 1/4] Wrap deps tasks in a lock --- lib/mix/lib/mix/project.ex | 14 ++++++++++++++ lib/mix/lib/mix/tasks/deps.clean.ex | 8 ++++++++ lib/mix/lib/mix/tasks/deps.get.ex | 8 ++++++++ lib/mix/lib/mix/tasks/deps.unlock.ex | 8 ++++++++ lib/mix/lib/mix/tasks/deps.update.ex | 8 ++++++++ 5 files changed, 46 insertions(+) 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/deps.clean.ex b/lib/mix/lib/mix/tasks/deps.clean.ex index ef40731d330..dd7ea584027 100644 --- a/lib/mix/lib/mix/tasks/deps.clean.ex +++ b/lib/mix/lib/mix/tasks/deps.clean.ex @@ -29,6 +29,14 @@ defmodule Mix.Tasks.Deps.Clean do Mix.Project.get!() {opts, apps} = OptionParser.parse!(args, strict: @switches) + config = Mix.Project.config() + + Mix.Project.with_deps_lock(config, fn -> + do_run(args, opts, apps) + end) + end + + defp do_run(args, opts, apps) do build_path = Mix.Project.build_path() |> Path.dirname() diff --git a/lib/mix/lib/mix/tasks/deps.get.ex b/lib/mix/lib/mix/tasks/deps.get.ex index 4e63c218bea..29101dc6de5 100644 --- a/lib/mix/lib/mix/tasks/deps.get.ex +++ b/lib/mix/lib/mix/tasks/deps.get.ex @@ -26,6 +26,14 @@ defmodule Mix.Tasks.Deps.Get do {opts, _, _} = OptionParser.parse(args, switches: [only: :string, target: :string, check_locked: :boolean]) + config = Mix.Project.config() + + Mix.Project.with_deps_lock(config, 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..0ca8ad384eb 100644 --- a/lib/mix/lib/mix/tasks/deps.unlock.ex +++ b/lib/mix/lib/mix/tasks/deps.unlock.ex @@ -27,6 +27,14 @@ defmodule Mix.Tasks.Deps.Unlock do Mix.Project.get!() {opts, apps, _} = OptionParser.parse(args, switches: @switches) + config = Mix.Project.config() + + Mix.Project.with_deps_lock(config, 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..44538d9cb68 100644 --- a/lib/mix/lib/mix/tasks/deps.update.ex +++ b/lib/mix/lib/mix/tasks/deps.update.ex @@ -45,6 +45,14 @@ defmodule Mix.Tasks.Deps.Update do {opts, rest, _} = OptionParser.parse(args, switches: [all: :boolean, only: :string, target: :string]) + config = Mix.Project.config() + + Mix.Project.with_deps_lock(config, 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], From 848d50b5791874cc0b05735a04b91fa83808e4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Tue, 8 Oct 2024 16:21:40 +0800 Subject: [PATCH 2/4] Up --- lib/mix/lib/mix/tasks/clean.ex | 6 ++++++ lib/mix/lib/mix/tasks/deps.clean.ex | 8 ++++---- lib/mix/lib/mix/tasks/deps.get.ex | 4 +--- lib/mix/lib/mix/tasks/deps.unlock.ex | 4 +--- lib/mix/lib/mix/tasks/deps.update.ex | 4 +--- 5 files changed, 13 insertions(+), 13 deletions(-) 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 dd7ea584027..8fdb5726b7d 100644 --- a/lib/mix/lib/mix/tasks/deps.clean.ex +++ b/lib/mix/lib/mix/tasks/deps.clean.ex @@ -29,10 +29,10 @@ defmodule Mix.Tasks.Deps.Clean do Mix.Project.get!() {opts, apps} = OptionParser.parse!(args, strict: @switches) - config = Mix.Project.config() - - Mix.Project.with_deps_lock(config, fn -> - do_run(args, opts, apps) + Mix.Project.with_build_lock(fn -> + Mix.Project.with_deps_lock(fn -> + do_run(args, opts, apps) + end) end) end diff --git a/lib/mix/lib/mix/tasks/deps.get.ex b/lib/mix/lib/mix/tasks/deps.get.ex index 29101dc6de5..12fae37aec2 100644 --- a/lib/mix/lib/mix/tasks/deps.get.ex +++ b/lib/mix/lib/mix/tasks/deps.get.ex @@ -26,9 +26,7 @@ defmodule Mix.Tasks.Deps.Get do {opts, _, _} = OptionParser.parse(args, switches: [only: :string, target: :string, check_locked: :boolean]) - config = Mix.Project.config() - - Mix.Project.with_deps_lock(config, fn -> + Mix.Project.with_deps_lock(fn -> do_run(opts) end) end diff --git a/lib/mix/lib/mix/tasks/deps.unlock.ex b/lib/mix/lib/mix/tasks/deps.unlock.ex index 0ca8ad384eb..27c5d8d6059 100644 --- a/lib/mix/lib/mix/tasks/deps.unlock.ex +++ b/lib/mix/lib/mix/tasks/deps.unlock.ex @@ -27,9 +27,7 @@ defmodule Mix.Tasks.Deps.Unlock do Mix.Project.get!() {opts, apps, _} = OptionParser.parse(args, switches: @switches) - config = Mix.Project.config() - - Mix.Project.with_deps_lock(config, fn -> + Mix.Project.with_deps_lock(fn -> do_run(opts, apps) end) end diff --git a/lib/mix/lib/mix/tasks/deps.update.ex b/lib/mix/lib/mix/tasks/deps.update.ex index 44538d9cb68..529dad817f3 100644 --- a/lib/mix/lib/mix/tasks/deps.update.ex +++ b/lib/mix/lib/mix/tasks/deps.update.ex @@ -45,9 +45,7 @@ defmodule Mix.Tasks.Deps.Update do {opts, rest, _} = OptionParser.parse(args, switches: [all: :boolean, only: :string, target: :string]) - config = Mix.Project.config() - - Mix.Project.with_deps_lock(config, fn -> + Mix.Project.with_deps_lock(fn -> do_run(opts, rest) end) end From e77307ec84945b7143e46eea972acaf9fc52d79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Tue, 8 Oct 2024 18:30:03 +0800 Subject: [PATCH 3/4] Up --- lib/mix/lib/mix/tasks/deps.clean.ex | 38 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/mix/lib/mix/tasks/deps.clean.ex b/lib/mix/lib/mix/tasks/deps.clean.ex index 8fdb5726b7d..66600d2d8cb 100644 --- a/lib/mix/lib/mix/tasks/deps.clean.ex +++ b/lib/mix/lib/mix/tasks/deps.clean.ex @@ -29,14 +29,6 @@ defmodule Mix.Tasks.Deps.Clean do Mix.Project.get!() {opts, apps} = OptionParser.parse!(args, strict: @switches) - Mix.Project.with_build_lock(fn -> - Mix.Project.with_deps_lock(fn -> - do_run(args, opts, apps) - end) - end) - end - - defp do_run(args, opts, apps) do build_path = Mix.Project.build_path() |> Path.dirname() @@ -71,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) + + 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 + if opts[:unlock] do + Mix.Task.run("deps.unlock", args) + else + :ok + end + end) end defp checked_deps(build_path, deps_path) do @@ -116,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}") @@ -130,7 +126,15 @@ 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 + shell = Mix.shell() + + 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. From fc8e5d296d1f2c495942cb71d3943012d7da9031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Tue, 8 Oct 2024 20:34:19 +0800 Subject: [PATCH 4/4] Up --- lib/mix/lib/mix/tasks/deps.clean.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/mix/lib/mix/tasks/deps.clean.ex b/lib/mix/lib/mix/tasks/deps.clean.ex index 66600d2d8cb..93279343a79 100644 --- a/lib/mix/lib/mix/tasks/deps.clean.ex +++ b/lib/mix/lib/mix/tasks/deps.clean.ex @@ -130,8 +130,6 @@ defmodule Mix.Tasks.Deps.Clean do end defp clean_source(apps, deps, deps_path, build_only?) do - shell = Mix.shell() - local = for %{scm: scm, app: app} <- deps, not scm.fetchable?(), do: Atom.to_string(app) Enum.each(apps, fn app ->