Oh, I also forgot to reply to this specifically:
Is there any chance you’ve hardcoded the modules that implement the behaviour in the behaviour module itself? I could see different file systems handling the order of compilation differently (though that is a guess—I’m unfamiliar with these types of details in practice).
Hardcoded modules by themselves don’t introduce compile-time dependencies. This can be checked with the following snippet:
iex(1)> defmodule T do
...(1)>
...(1)> def f, do: F.g()
...(1)> end
{:module, T,
<<70, 79, 82, 49, 0, 0, 4, 196, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 140,
0, 0, 0, 16, 8, 69, 108, 105, 120, 105, 114, 46, 84, 8, 95, 95, 105, 110,
102, 111, 95, 95, 10, 97, 116, 116, 114, ...>>, {:f, 0}}
iex(2)> T.f()
** (UndefinedFunctionError) function F.g/0 is undefined (module F is not available)
F.g()
For anyone that might be interested, here’s what I know about compile-time deps:
Compile-time dependencies are almost always added by calling the hardcoded %struct{}
form, by using import
or require
, which are required for using macros. Note that use Module, opts
is just syntactical sugar for require Module; Module.__using__(opts)
, so this also introduces compile-time dependency.
Also, from what I learned today, @behaviour X
also introduces a compile-time dependency. And, finally, @external_resource filename
can be used to introduce a compile-time dependency to a raw file (e.g. a .md
file you use to render the docstring).
What confused me most is that I’m almost sure we had replaced the %Struct{}
calls with Struct.t()
before… We surely missed the one that was the culprit haha