Can't find the module in the escript made by one `mix.exs` file at runtime

If I could build escript and run it with just one mix.exs file,
I wondered that handling tiny escript would be more easier.

So I made the following code, built and ran it.
Then it can build, but it seems that it have not found the module at runtime.

Is there a way to make it possible to find the module in the escript at runtime?

/Users/niku/sandbox% elixir -v
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.4.0-rc.1
/Users/niku/sandbox% ls
mix.exs
/Users/niku/sandbox% cat mix.exs
defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [app: :my_app,
     version: "0.0.1",
     escript: escript()]
  end

  def escript do
    [main_module: MyApp.CLI]
  end
end

defmodule MyApp.CLI do
  def main(_args) do
    IO.puts("Hello from MyApp!")
  end
end
/Users/niku/sandbox% mix escript.build
Generated my_app app
Generated escript my_app with MIX_ENV=dev
/Users/niku/sandbox% ls
_build  mix.exs  my_app
/Users/niku/sandbox% ./my_app
** (UndefinedFunctionError) function MyApp.CLI.main/1 is undefined (module MyApp.CLI is not available)
    MyApp.CLI.main([])
    (elixir) lib/kernel/cli.ex:76: anonymous fn/3 in Kernel.CLI.exec_fun/2

You should create a file:
lib/my_app/cli.ex
in your project folder and move MyApp.CLI module definition to this file.

1 Like

Everything in an exs is considered auxiliary and as such only compiled in memory but not to a beam file. That’s why your module isn’t found.

3 Likes

@eiji
Thank you for your ansower. You tought me a correct way.
By the way, could you let me know if you know how to include module files on the outside of lib into the escript?

@nobbz
Thank you for your ansower. I didn’t realize the extname affects to the escript.

Here is the codes for verify it.
When the extname had been renamed from .exs to .ex, mix escript.build was from failure to success.

/Users/niku/sandbox% find . -type f
./lib/my_app/cli.exs
./mix.exs
/Users/niku/sandbox% cat ./mix.exs
defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [app: :my_app,
     version: "0.0.1",
     escript: escript()]
  end

  def escript do
    [main_module: MyApp.CLI]
  end
end
/Users/niku/sandbox% cat ./lib/my_app/cli.exs
defmodule MyApp.CLI do
  def main(_args) do
    IO.puts("Hello from MyApp!")
  end
end
/Users/niku/sandbox% mix escript.build
Generated my_app app
** (Mix) Could not generate escript, module Elixir.MyApp.CLI defined as :main_module could not be loaded
/Users/niku/sandbox% mv ./lib/my_app/cli.exs ./lib/my_app/cli.ex
/Users/niku/sandbox% mix escript.build
Compiling 1 file (.ex)
Generated my_app app
Generated escript my_app with MIX_ENV=dev
/Users/niku/sandbox% ./my_app
Hello from MyApp!

You would configure :elixirc_paths in mix.exs, if you really wanted to do that… so eg. to compile files in same directory as the mix.exs file instead of in lib/, you could have:

~/src/my_app$ tree
.
|-- cli.ex
`-- mix.exs

0 directories, 2 files
~/src/my_app$ cat mix.exs
defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [app: :my_app,
     version: "0.0.1",
     elixirc_paths: ["."],
     escript: escript()]
  end

  def escript do
    [main_module: MyApp.CLI]
  end
end
~/src/my_app$ cat cli.ex
defmodule MyApp.CLI do
  def main(_args) do
    IO.puts("Hello from MyApp!")
  end
end
3 Likes

Thank you for your great answer.

Finally, I got to install an tiny escript via gist. yay!

/Users/niku% mix escript.install git https://gist.github.com/niku/9be74fbcdb5d2b9e36b4259fe85e08a8
* Getting new package (https://gist.github.com/niku/9be74fbcdb5d2b9e36b4259fe85e08a8)
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Compiling 1 file (.ex)
Generated my_app app
Generated escript my_app with MIX_ENV=prod
Are you sure you want to install escript "my_app"? [Yn]
* creating /Users/niku/.mix/escripts/my_app
/Users/niku% my_app
Hello from MyApp!

Ahh yeah… nice :slight_smile:

Nice! What about to set it to as wiki (I mean escript from gists) @AstonJ?

same err. for me,
thanx for explain;

change my lib/nnera/cli.exs -> cli.ex

$ mix escript.build

is working

What I still fail to understand is why it is documented this way ( module inside mix.exs ) even in the Elixir documentation ?

Can you please point out where exactly you mean? The examples talking about *.exs scripts I am aware of, run them through elixir directly, and not build them into an escript through a mix project.

1 Like

Thanks for taking the time.

It is in the following link, under the example section:
https://hexdocs.pm/mix/Mix.Tasks.Escript.Build.html

Am I missing something ?

Oh, indeed, I think it should be made more clear here, that those need to be individual files. Thank you for pointing this out.


edit `mix`: Example for `escript.build` is misleading · Issue #11770 · elixir-lang/elixir · GitHub

1 Like