Load all modules implementing a behaviour in escript

Update, a slight modification, without using Mix is marginally better:

  defp available_modules_with_behaviour(behaviour) do
    # compiled from `Mix.Task.run("loadpaths", [])`
    build_path = Path.expand("_build/prod")
    app_path = Path.join([build_path, "lib", Atom.to_string(:external_data_sync)])
    Path.join(app_path, "ebin")

    # Fetch all .beam files
    Path.wildcard(Path.join([Path.expand("_build/prod"), "**/ebin/**/*.beam"]))
    # the rest is the same
    ...
  end

This works, but in reality it’s only because lib is right where the code expects it. So it looks like the related lib module isn’t included in the escript.

I tried several ways to include all code from lib in that escript related module, no luck. Not even :ok = Application.ensure_started(:app) seems to be working. Even just aliasing those modules doesn’t work, they need to be explicitly used. This seems to work after all:

  @scenarios [
    SyncData1
    SyncData2
    SyncData3
  ]

  # and before requesting modules that implement `Scenario` behaviour
  Enum.each(@scenarios, &Code.ensure_loaded?(&1))

  # now finding those modules works
  all_scenarios = available_modules_with_behaviour(Scenario)

So I guess this is all about how to actually include the main app into escript. If it’s even possible…