diff --git a/lib/elixir/src/elixir.erl b/lib/elixir/src/elixir.erl index 352c529dc61..169cf7c7134 100644 --- a/lib/elixir/src/elixir.erl +++ b/lib/elixir/src/elixir.erl @@ -64,6 +64,11 @@ start(_Type, _Args) -> application:set_env(elixir, ansi_enabled, prim_tty:isatty(stdout) == true) end, + %% Store the initial dbg_callback value before any runtime modifications. + %% This allows Mix compiler to detect config changes vs runtime changes + %% (e.g., Kino wrapping dbg_callback at runtime should not trigger recompilation). + {ok, InitialDbgCallback} = application:get_env(elixir, dbg_callback), + Tokenizer = case code:ensure_loaded('Elixir.String.Tokenizer') of {module, Mod} -> Mod; _ -> elixir_tokenizer @@ -90,6 +95,7 @@ start(_Type, _Args) -> {docs, true}, {ignore_already_consolidated, false}, {ignore_module_conflict, false}, + {initial_dbg_callback, InitialDbgCallback}, {infer_signatures, [elixir]}, {on_undefined_variable, raise}, {parser_options, [{columns, true}]}, diff --git a/lib/mix/lib/mix/compilers/elixir.ex b/lib/mix/lib/mix/compilers/elixir.ex index 8a25b7aa647..74e405ee27c 100644 --- a/lib/mix/lib/mix/compilers/elixir.ex +++ b/lib/mix/lib/mix/compilers/elixir.ex @@ -287,7 +287,13 @@ defmodule Mix.Compilers.Elixir do end defp deps_config_compile_env_apps(deps_config) do - if deps_config[:dbg] != Application.fetch_env!(:elixir, :dbg_callback) do + # Use initial_dbg_callback instead of dbg_callback to ignore runtime modifications. + # Tools like Kino modify dbg_callback at runtime to customize dbg/2 behavior, + # but this should not trigger recompilation since the config hasn't actually changed. + # initial_dbg_callback is set when :elixir app starts, before any runtime modifications. + initial_dbg = :elixir_config.get(:initial_dbg_callback) + + if deps_config[:dbg] != initial_dbg do [:elixir] else [] diff --git a/lib/mix/test/mix/tasks/compile.elixir_test.exs b/lib/mix/test/mix/tasks/compile.elixir_test.exs index fdfccdef4b6..2a36f35d394 100644 --- a/lib/mix/test/mix/tasks/compile.elixir_test.exs +++ b/lib/mix/test/mix/tasks/compile.elixir_test.exs @@ -278,9 +278,11 @@ defmodule Mix.Tasks.Compile.ElixirTest do assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]} assert_received {:mix_shell, :info, ["Compiled lib/b.ex"]} - # Change the dbg_callback at runtime + # Simulate a config change by updating both dbg_callback and initial_dbg_callback. + # This represents the case where the user actually changed the config file. File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time) Application.put_env(:elixir, :dbg_callback, {__MODULE__, :dbg, []}) + :elixir_config.put(:initial_dbg_callback, {__MODULE__, :dbg, []}) assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []} assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]} @@ -289,6 +291,35 @@ defmodule Mix.Tasks.Compile.ElixirTest do end) after Application.put_env(:elixir, :dbg_callback, {Macro, :dbg, []}) + :elixir_config.put(:initial_dbg_callback, {Macro, :dbg, []}) + end + + test "does not recompile when dbg_callback is modified at runtime but initial is unchanged" do + in_fixture("no_mixfile", fn -> + Mix.Project.push(MixTest.Case.Sample) + + File.write!("lib/a.ex", """ + defmodule A do + def a, do: dbg(:ok) + end + """) + + assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []} + assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]} + + # Simulate a tool like Kino modifying dbg_callback at runtime. + # Since initial_dbg_callback remains unchanged, this should NOT trigger recompilation. + original_dbg = Application.fetch_env!(:elixir, :dbg_callback) + :elixir_config.put(:initial_dbg_callback, original_dbg) + Application.put_env(:elixir, :dbg_callback, {SomeDebugTool, :dbg, [original_dbg]}) + + # Should NOT trigger recompilation since initial_dbg_callback is unchanged + assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:noop, []} + refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]} + end) + after + Application.put_env(:elixir, :dbg_callback, {Macro, :dbg, []}) + :elixir_config.put(:initial_dbg_callback, {Macro, :dbg, []}) end test "recompiles files when config changes export dependencies" do