@@ -45,7 +45,7 @@ defmodule Mix.Tasks.Escript.Build do
45
45
* `:embed_elixir` - if `true` embed elixir and its children apps
46
46
(`ex_unit`, `mix`, etc.) mentioned in the `:applications` list inside the
47
47
`application` function in `mix.exs`.
48
- Defaults to `true`.
48
+ Defaults to `true` for Elixir projects .
49
49
50
50
* `:shebang` - shebang interpreter directive used to execute the escript.
51
51
Defaults to `"#! /usr/bin/env escript\n"`.
@@ -56,6 +56,13 @@ defmodule Mix.Tasks.Escript.Build do
56
56
* `:emu_args` - emulator arguments to embed in the escript file.
57
57
Defaults to `""`.
58
58
59
+ There is one project-level option that affects how the escript is generated:
60
+
61
+ * `language: :elixir | :erlang` - set it to `:erlang` for Erlang projects
62
+ managed by mix. Doing so will ensure Elixir is not embedded by default.
63
+ Your app will still be started as part of escript loading, with the
64
+ config used during build.
65
+
59
66
## Example
60
67
61
68
defmodule MyApp.Mixfile do
@@ -90,6 +97,7 @@ defmodule Mix.Tasks.Escript.Build do
90
97
main = escript_opts [ :main_module ]
91
98
app = Keyword . get ( escript_opts , :app , project [ :app ] )
92
99
files = project_files ( )
100
+ language = Keyword . get ( project , :language , :elixir )
93
101
94
102
escript_mod = String . to_atom ( Atom . to_string ( app ) <> "_escript" )
95
103
@@ -103,8 +111,9 @@ defmodule Mix.Tasks.Escript.Build do
103
111
"in your project configuration (under `:escript` option) to a module that implements main/1"
104
112
105
113
force || Mix.Utils . stale? ( files , [ filename ] ) ->
106
- tuples = gen_main ( escript_mod , main , app ) ++
107
- to_tuples ( files ) ++ deps_tuples ( ) ++ embed_tuples ( escript_opts )
114
+ tuples = gen_main ( escript_mod , main , app , language ) ++
115
+ to_tuples ( files ) ++ deps_tuples ( ) ++
116
+ embed_tuples ( escript_opts , language )
108
117
109
118
case :zip . create 'mem' , tuples , [ :memory ] do
110
119
{ :ok , { 'mem' , zip } } ->
@@ -156,8 +165,8 @@ defmodule Mix.Tasks.Escript.Build do
156
165
Enum . flat_map ( deps , fn dep -> get_tuples ( dep . opts [ :build ] ) end )
157
166
end
158
167
159
- defp embed_tuples ( escript_opts ) do
160
- if Keyword . get ( escript_opts , :embed_elixir , true ) do
168
+ defp embed_tuples ( escript_opts , language ) do
169
+ if Keyword . get ( escript_opts , :embed_elixir , language == :elixir ) do
161
170
Enum . flat_map [ :elixir | extra_apps ( ) ] , & app_tuples ( & 1 )
162
171
else
163
172
[ ]
@@ -190,61 +199,80 @@ defmodule Mix.Tasks.Escript.Build do
190
199
"%%! -escript main #{ escript_mod } #{ user_args } \n "
191
200
end
192
201
193
- defp gen_main ( name , module , app ) do
202
+ defp gen_main ( name , module , app , language ) do
194
203
config =
195
204
if File . regular? ( "config/config.exs" ) do
196
205
Mix.Config . read! ( "config/config.exs" )
197
206
else
198
207
[ ]
199
208
end
200
209
201
- { :module , ^ name , binary , _ } =
202
- defmodule name do
203
- @ module module
204
- @ config config
205
- @ app app
206
-
207
- # We need to use Erlang modules at this point
208
- # because we are not sure Elixir is available.
209
- def main ( args ) do
210
- case :application . ensure_all_started ( :elixir ) do
211
- { :ok , _ } ->
212
- load_config ( @ config )
213
- start_app ( @ app )
214
- args = Enum . map ( args , & List . to_string ( & 1 ) )
215
- Kernel.CLI . run fn _ -> @ module . main ( args ) end , true
216
- _ ->
217
- :io . put_chars :standard_error , "Elixir is not available, aborting.\n "
218
- :erlang . halt ( 1 )
219
- end
220
- end
210
+ module_body = quote do
211
+ @ module unquote ( module )
212
+ @ config unquote ( config )
213
+ @ app unquote ( app )
221
214
222
- defp load_config ( config ) do
223
- for { app , kw } <- config , { k , v } <- kw do
215
+ def main ( args ) do
216
+ unquote ( main_body_for ( language ) )
217
+ end
218
+
219
+ defp load_config ( config ) do
220
+ :lists . foreach ( fn { app , kw } ->
221
+ :lists . foreach ( fn { k , v } ->
224
222
:application . set_env ( app , k , v , persistent: true )
225
- end
226
- :ok
227
- end
223
+ end , kw )
224
+ end , config )
225
+ :ok
226
+ end
228
227
229
- defp start_app ( nil ) do
230
- :ok
231
- end
228
+ defp start_app ( nil ) do
229
+ :ok
230
+ end
232
231
233
- defp start_app ( app ) do
234
- case :application . ensure_all_started ( app ) do
235
- { :ok , _ } -> :ok
236
- { :error , { app , reason } } ->
237
- io_error "Could not start application #{ app } : " <>
238
- Application . format_error ( reason )
239
- System . halt ( 1 )
240
- end
232
+ defp start_app ( app ) do
233
+ case :application . ensure_all_started ( app ) do
234
+ { :ok , _ } -> :ok
235
+ { :error , { app , reason } } ->
236
+ formatted_error = case :code . ensure_loaded ( Application ) do
237
+ { :module , Application } -> Application . format_error ( reason )
238
+ { :error , _ } -> :io_lib . format ( '~p' , [ reason ] )
239
+ end
240
+ io_error [ "Could not start application " ,
241
+ :erlang . atom_to_binary ( app , :utf8 ) ,
242
+ ": " , formatted_error , ?\n ]
243
+ :erlang . halt ( 1 )
241
244
end
245
+ end
242
246
243
- defp io_error ( message ) do
244
- IO . puts :stderr , IO.ANSI . format ( [ :red , :bright , message ] )
245
- end
247
+ defp io_error ( message ) do
248
+ :io . put_chars ( :standard_error , message )
246
249
end
250
+ end
247
251
252
+ { :module , ^ name , binary , _ } = Module . create ( name , module_body , Macro.Env . location ( __ENV__ ) )
248
253
[ { '#{ name } .beam' , binary } ]
249
254
end
255
+
256
+ defp main_body_for ( :elixir ) do
257
+ quote do
258
+ case :application . ensure_all_started ( :elixir ) do
259
+ { :ok , _ } ->
260
+ load_config ( @ config )
261
+ start_app ( @ app )
262
+ args = Enum . map ( args , & List . to_string ( & 1 ) )
263
+ Kernel.CLI . run fn _ -> @ module . main ( args ) end , true
264
+ _ ->
265
+ :io . put_chars :standard_error , "Elixir is not available, aborting.\n "
266
+ :erlang . halt ( 1 )
267
+ end
268
+ end
269
+ end
270
+
271
+ defp main_body_for ( :erlang ) do
272
+ quote do
273
+ load_config ( @ config )
274
+ start_app ( @ app )
275
+ @ module . main ( args )
276
+ end
277
+ end
250
278
end
0 commit comments