Specifying paths for static library assets used in macro (compile vs runtime && `/app/_build/prod/lib/` vs `/app/lib`)

TL;DR: How to ensure that file paths specified in a library when compiled in a macro reflect correct paths at release. /app/_build/prod/lib/library_name/priv/ when compiled vs /app/lib/library_name-version/priv/ when released.

Hi there,
Working on an avatar generation library:
Avatarex - Generate unique, reproducible avatars from hashed strings (*hat tip to @Eiji for help in the rewrite)

I’m a bit confused as to how to ensure that the image asset paths which are being generated at compile time, as part of a using macro, reflect where they’ll actually be at runtime.
So paths that are generated at compile time:
:avatarex |> :code.priv_dir() |> Path.join("sets") |> ...
corresponds to locations that exist during compilation:
/app/_build/prod/lib/avatarex/priv/sets/birdy/body/body_1.png
but are different from the paths that are used at runtime:
/app/lib/avatarex-0.2.0/priv/sets/birdy/body/body_1.png

I do need to be able to File.ls the /app/_build/prod/lib/avatarex/priv/sets/birdy/body/ path at compile time to then generate the list of available images in that directory, but then again at runtime they need to look in /app/lib/....

I thought this may also be related to me not understanding how /app/_build/prod/lib becomes /app/lib upon release.

Thanks,
David

Resources I’ve Reviewed:

Oh, I see the point. Since paths are not guaranteed to be the same across different environments and releases we were using :code.priv_dir/1 to get a proper path. However we are doing it at compile time in prod environment and paths were changed in release:thinking:

You should do 2 things:

  1. Rewrite the current compile-time paths to be relative to priv directory. Since contents of priv directory does not changes we can safely call File.ls!/1 on them.
  2. In runtime we need to rewrite relative urls to absolute urls
prod_priv_path = :code.priv_dir(:avatarex)
dir_path = Path.join(~w[sets body])
prod_full_path = Path.join(prod_priv_path, dir_path)
# in struct we store `dir_path`
# for File.ls!/1 we use prod_full_path

def construct(…) do
  release_priv_path = :code.priv_dir(:avatarex)
  images = Enum.map(images, fn {layer, path} -> {layer, Path.join(release_priv_dir, path)} end)
  # …
end

The problem is that images is not the only variable which stores path. You would need to remember when you would update the code.

Let me know if you have some questions.

2 Likes