Is it possible to include resource files when packaging my project using Mix/escript?

Hi everyone,

I was thinking of resources like images, XML files or documents in other text formats. Is there something similar to the resources folder in Java (“src/main/resources”)? The goal would be to add files to my package that can be accessed from code on runtime.

Or is this somehow a very un-Elixir-y way of thinking? Elixir is my first functional language - so what would be the “right” way to do it?

Cheers,
smon

4 Likes

Hey!

That is precisely what the /priv folder is for in your project root. It’ll get packaged up along with your application when you build a release. If you have a data.xml file in your /priv directory you can get to it in your code by doing

Path.join(:code.priv_dir(:my_app), "data.xml")
12 Likes

Awesome!

Thank you very much. priv is an abbreviation of what exactly? “private”? Or am I missing some more obvious term it stands for?

Cheers,
smon

1 Like

Erlang OTP Design Principles User’s Guide at Directory Structure

priv - Used for application specific files. For example, C executables are placed here. The function code:priv_dir/1 should be used to access this directory.

Per Jose:

“priv” is like “OTP” where its name made sense at the beginning but today it has grown beyond that. All it matters now is that we put in the “priv” directory any artifact that you need in production alongside your code.

Elixir Lang Talk - What is Priv?

7 Likes

I am unable to open a File.stream! for the file in my /priv folder when running my escript-build. Do I have to fallback to Erlang? Using a path generated the way you described only works when starting the application in iex.

Edit

From the Mix.Tasks.Escript.Build:

Note: escripts do not support projects and dependencies
that need to store or read artifacts from the priv directory.

Damn…

I have it on good authority that http://elixir-lang.org/docs/master/elixir/Application.html#app_dir/1 is an option here.

Edited my previous reply in light of new information.

I am not quite sure if I understood correctly.

Path.join(:code.priv_dir(:my_app), "data.xml")

produced the path /path/to/my_app/my_app/priv/data.xml, where the first my_app corresponds the escript build file.

By replacing :code.priv_dir(:my_app) with app_dir(:my_app), I can get rid off the priv sub-folder, but that still does not allow me to open a valid file stream.

Hi @smon and @benwilson512, I’d also like to know if there are options to access the /priv folder for a command line utility built with escript. Please update if you’ve found anything. Thank you in advance!

Hey!

Sorry I did not check the forum for quite some time. The answer seems to be the thing I already mentioned:

I basically gave up on the idea of adding the file to the build file.

To confirm: although priv is the appropriate place, we can’t load files from priv inside escripts. What I usually do is to load the file at compilation time and store it as a module attribute:

@external_resource "priv/foo/bar"
@bar_contents File.read! "priv/foo/bar"
def bar_contents, do: @bar_contents
18 Likes

Thanks for the replies @smon and @josevalim! :grinning:

1 Like

Just out of curiosity, if there are huge static files in the priv folder, will this way make the compiled binary huge? as well as the module loading time…

It will only affect release size. The Priv folder is only included there, not in individual modules.

Also it won’t increase module load time as it’s not included in modules as I said.

1 Like

Thank you @NobbZ. Please pardon me(a newbie) for digging a bit more:

  1. Where are the artifects stored after being loaded as a module attribute at compile time?
  2. using the example Jose mentioned above @bar_contents File.read! "priv/foo/bar" , if priv/foo/bar is a huge vidoe file, what is the @bar_contents after the program being compiled(in the BEAM)? Is it something a pointer to the bar file or a thing holding all the data of that file.

Copying files into an attribute indeed increases module size. Though that is not related to the Priv folder.

Due to how module attributes in elixir work, you can assume that each usage of the attribute causes a literal copy of the file.

For some data types there might be optimisations but assume there are not.

1 Like

Thanks a lot @NobbZ . Sorry for my question is not related to this topic. Anyway, just to confirm what I’ve understood so far:

By “copying files into an attribute”, are you saying @bar_contents File.read! "priv/foo/bar" is actually copying the bar into attribute bar_contents? (hence it increase the module size)

Your question is indeed related, though I misunderstood your first phrasing of the question, as you asked solely about the Priv folder not about attributes. I should have scrolled back to the post you have quoted, then I might have seen it in the context…

Yes. File.read! returns the file content as a binary, so the attribute will contain the binary representing the files content. Though it won’t be saved as part of the module until the attribute is actually used in a way that would “generate appropriate code” or persisted into the module by using Module.register_attribute (I might misremember the function name).

Thank you very much @NobbZ. I’m clear now. I’ve compiled a simple code reading a big file into an attribute. The compiled beam file is big, so the loaded binary is indeed stored in the beam file( whereelse could it be anyway :wink:

I actually love this way to handle runtime resourcse that have to be deliveried along with the release. It make the release structure simple and neat. No matter they’re in the /priv folder or in beam files, you need pack them into somewhere in your release anyway.

Thanks again.