Elixir: Mix.Project.deps_paths/0 returns invalid paths if called from a custom compiler compiling a dependency

Created on 17 Apr 2018  路  4Comments  路  Source: elixir-lang/elixir

Environment

  • Elixir & Erlang versions (elixir --version):
    Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
    Elixir 1.6.4 (compiled with OTP 20)
  • Operating system: Mac OS Sierra

Current behavior

I have:

  • compiler MyCompiler, compiling some native code
  • projects A, B and C
  • A has dependencies: B (added by path - /custom/path/to/b) and C (added anyhow)
  • project C has MyCompiler in project compilers and dependency B

Then, if MyCompiler compile task looks like

defmodule Mix.Tasks.Compile.MyCompiler do
  use Mix.Task
  def run(_args) do
    Mix.Project.deps_paths() |> inspect() |> Mix.shell().info()
  end
end

then when compiling project A, I get printed:

%{
  b: "/path/to/a/deps/b"
}

despite that path to B is /custom/path/to/b.

Expected behavior

Mix.Project.deps_paths/0 should always return proper paths, in this case

%{
  b: "/custom/path/to/b"
}

as it does in runtime.

Mix Bug

All 4 comments

Thank you @mat-hek! Can you please push a sample application to GitHub that reproduces the issue? That will make it much easier for us to reproduce and address this.

When reproducing this from scratch, I found out that this only happens if a dependency (B in this case) is overriden in the main (A) project. Here is an example: https://github.com/mat-hek/elixir_deps_paths_bug

Until this is fixed, we're using the following workaround. It seems to work ;)

defmodule MixProjectHelper do
  @path_store_name :deps_paths_store

  def store_project_path() do
    Agent.start(fn -> %{} end, name: @path_store_name)
    app = Mix.Project.get!().project |> Keyword.fetch!(:app)
    dir = Mix.ProjectStack.peek().file |> Path.dirname()
    Agent.update(@path_store_name, &Map.put(&1, app, dir))
  end

  def deps_paths() do
    Agent.start(fn -> %{} end, name: @path_store_name)
    app = Mix.Project.get!().project |> Keyword.fetch!(:app)
    Mix.Project.deps_paths()
    |> Map.merge(Agent.get(@path_store_name, & &1))
    |> Map.delete(app)
  end
end

This is utilizing the fact that deps added by path are always recompiled, so if store_project_path/0 is called, the proper path is stored. Fallback to Mix.Project.deps_paths/0 makes it work in other cases. Of course works only for deps that have the compiler in project compilers.

Closing in favor of #7834.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

josevalim picture josevalim  路  33Comments

josevalim picture josevalim  路  30Comments

devonestes picture devonestes  路  33Comments

josevalim picture josevalim  路  31Comments

conradwt picture conradwt  路  34Comments