Recompile project is compiletime ENVVAR changed?

Does anyone know how to force certain-files/project to recompile if compiletime envvars changed?

Example I have a config.exs like so

region = System.get_env("REGION", "us") |> String.to_atom()
config :app, :region, region

and code that uses it

defmodule MyModule do
    if Application.compile_env!(:app, :region) == :us do
      def lol() do "United" end
    else
      def lol() do "United in another language" end
    end
end

I would like MyModule to recompile (outside a release setting) if REGION envvar changed.

Example doing

REGION=us iex -S mix
compile
REGION=us iex -S mix
nothing
REGION=ca iex -S mix
compile
REGION=us iex -S mix
compile

I can change compile_env to get_env, then the app just runs without recompiling anything. I rather not crash, and not false negative.

The simple solution is write a compiler task to edit the code file each time to force recompile.

defmodule Mix.Tasks.Compile.ForceRc do
  use Mix.Task

  def run(_args) do
    File.write("[path of your module]", " ", [:append])
    IO.puts "edited"
  end
end

run mix compile to load this module. Then add it before other compilers in mix.exs

compilers: [:force_rc, :phoenix, :gettext] ++ Mix.compilers(),

Then every time you run iex -S mix, the file you specified will be re-compile.

It is such a stupid solution :sweat_smile: Could someone tell me how to force elixir recompile a file, without changing the file content.

You should be able to use mix compile --force to force recompilation of the project.

This is pretty creative, I think it might be possible to work something along these lines, like reading back the AST of the module that used Application.compile_env!/2 and if the compile_env func cached the wrong envvar, force a recompile by doing mix compile --force.

Yea but I would like it to autodetect when to execute a force recompile, so only if certain ENVVARS changed from the last time it got compiled.

Note Elixir source, Mix.Tasks.Compile.App:

  defp app_changed?(properties, mods, compile_env) do
    Keyword.get(properties, :modules, []) != mods or
      Keyword.get(properties, :compile_env, []) != compile_env
  end

You can probably use this in conjunction with runtime configuration like in config/runtime.exs introduced as in Elixir 1.11, see Elixir v1.11 released - The Elixir programming language so stuff System.get_env(name) in config/runtime.exs and use Application.compile_env/3 to get it.

However IMO this is a smell and is best avoided

2 Likes