How to get application name at compile time?

I need to access the application name under which a module was defined, in other words, I need the name defined in the module’s parent mix.exs.

  def __on_definition__(env, _kind, func_name, args, _guards, _body) do
    app_name = Application.get_application(env.module)

Application.get_application/1 doesn’t always work, as the application may or may not be available when recompiling.

Is there a non-hacky way of getting this info? I’m tempted to look at the file directory looking for a mix.exs and parsing it.

1 Like

I don’t have an answer, but I’d like to double-down on your question about looking at files during compilation as I am currently tempted to do the same. Is this a truly terrible idea? And if so, it is wrong because it’s too hacky/surprising/confusing, or is it wrong because there are gotchas?

You could put it in a file called APPLICATION and read that into the mix file as the application name and wherever else. This is sometimes done for defining the version. Could be considered hacky tho :melting_face:

Yup, I’m gonna just do this, and change it later if a better solution arises.

The problem is that this is library code, that is supposed to look into your project, but also, other libraries, so it’s a no-no, to need to adhere to that standard.

For now goes like this:

app_name = find_app_name(env.file)

...

  def find_app_name(module_file) when is_binary(module_file) do
    module_file |> Path.dirname() |> find_app_name_in_dir()
  end

  defp find_app_name_in_dir(dir) do
    {:ok, files} = File.ls(dir)

    if "mix.exs" in files do
      mix_file = Path.join(dir, "mix.exs")
      {:ok, content} = File.read(mix_file)

      case Regex.named_captures(~r/app: :(?<app_name>[a-z_]+),*\n/, content) do
        %{"app_name" => app_name} -> String.to_existing_atom(app_name)
        _ -> raise("Failed to read an app name from #{mix_file}")
      end
    else
      dir |> Path.dirname() |> find_app_name_in_dir()
    end
  end


1 Like

Mix.Project.config(), but only invoke it at compile time, as Mix isn’t available inside releases. Or ask the user to pass it as parameter (as Phoenix/Ecto do).

4 Likes

I just have to ask, but wouldnt this enable most macros to avoid requiring the app option? Is it «safe» to fallback to the app property defined in mix config inside a library macro? Which obviously messes with our heads because its hard to understand without knowing how it works, why it works… its a «compile runtime» call actually. In the application using the macro.?

No. Because those macros are not necessarily used just by the top level mix application (if there even is one).

2 Likes

So there was a waaaay less hacky way of doing it :smile:

Thanks!