@@ -36,11 +36,6 @@ defmodule Mix.Tasks.Escript.Build do
36
36
the compiled `.beam` files to reduce the size of the escript.
37
37
If this is not desired, check the `:strip_beams` option.
38
38
39
- > #### `priv` directory support {: .warning}
40
- >
41
- > escripts do not support projects and dependencies
42
- > that need to store or read artifacts from the priv directory.
43
-
44
39
## Command line options
45
40
46
41
Expects the same command line options as `mix compile`.
@@ -94,6 +89,11 @@ defmodule Mix.Tasks.Escript.Build do
94
89
* `:emu_args` - emulator arguments to embed in the escript file.
95
90
Defaults to `""`.
96
91
92
+ * `:include_priv_for` - a list of application names (atoms) specifying
93
+ applications which priv directory should be included in the resulting
94
+ escript archive. Currently the expected way of accessing priv files
95
+ in an escript is via `:escript.extract/2`. Defaults to `[]`.
96
+
97
97
There is one project-level option that affects how the escript is generated:
98
98
99
99
* `language: :elixir | :erlang` - set it to `:erlang` for Erlang projects
@@ -185,11 +185,16 @@ defmodule Mix.Tasks.Escript.Build do
185
185
186
186
escript_mod = String . to_atom ( Atom . to_string ( app ) <> "_escript" )
187
187
188
+ include_priv_for = MapSet . new ( escript_opts [ :include_priv_for ] || [ ] )
189
+
188
190
beam_paths =
189
- [ project_files ( ) , deps_files ( ) , core_files ( escript_opts , language ) ]
191
+ [
192
+ project_files ( project , include_priv_for ) ,
193
+ deps_files ( include_priv_for ) ,
194
+ core_files ( escript_opts , language , include_priv_for )
195
+ ]
190
196
|> Stream . concat ( )
191
- |> prepare_beam_paths ( )
192
- |> Map . merge ( consolidated_paths ( project ) )
197
+ |> replace_consolidated_paths ( project )
193
198
194
199
tuples = gen_main ( project , escript_mod , main , app , language ) ++ read_beams ( beam_paths )
195
200
tuples = if strip_options , do: strip_beams ( tuples , strip_options ) , else: tuples
@@ -213,28 +218,40 @@ defmodule Mix.Tasks.Escript.Build do
213
218
:ok
214
219
end
215
220
216
- defp project_files ( ) do
217
- get_files ( Mix.Project . app_path ( ) )
221
+ defp project_files ( project , include_priv_for ) do
222
+ get_files ( Mix.Project . app_path ( ) , project [ :app ] in include_priv_for )
218
223
end
219
224
220
- defp get_files ( app ) do
221
- Path . wildcard ( "#{ app } /ebin/*.{app,beam}" ) ++
222
- ( Path . wildcard ( "#{ app } /priv/**/*" ) |> Enum . filter ( & File . regular? / 1 ) )
225
+ defp get_files ( app_path , include_priv? ) do
226
+ paths = Path . wildcard ( "#{ app_path } /ebin/*.{app,beam}" )
227
+
228
+ paths =
229
+ if include_priv? do
230
+ paths ++ ( Path . wildcard ( "#{ app_path } /priv/**/*" ) |> Enum . filter ( & File . regular? / 1 ) )
231
+ else
232
+ paths
233
+ end
234
+
235
+ apps_dir = Path . dirname ( app_path )
236
+
237
+ for path <- paths do
238
+ { Path . relative_to ( path , apps_dir ) , path }
239
+ end
223
240
end
224
241
225
242
defp set_perms ( filename ) do
226
243
stat = File . stat! ( filename )
227
244
:ok = File . chmod ( filename , stat . mode ||| 0o111 )
228
245
end
229
246
230
- defp deps_files ( ) do
247
+ defp deps_files ( include_priv_for ) do
231
248
deps = Mix.Dep . cached ( )
232
- Enum . flat_map ( deps , fn dep -> get_files ( dep . opts [ :build ] ) end )
249
+ Enum . flat_map ( deps , fn dep -> get_files ( dep . opts [ :build ] , dep . app in include_priv_for ) end )
233
250
end
234
251
235
- defp core_files ( escript_opts , language ) do
252
+ defp core_files ( escript_opts , language , include_priv_for ) do
236
253
if Keyword . get ( escript_opts , :embed_elixir , language == :elixir ) do
237
- Enum . flat_map ( [ :elixir | extra_apps ( ) ] , & app_files / 1 )
254
+ Enum . flat_map ( [ :elixir | extra_apps ( ) ] , & app_files ( & 1 , include_priv_for ) )
238
255
else
239
256
[ ]
240
257
end
@@ -269,17 +286,13 @@ defmodule Mix.Tasks.Escript.Build do
269
286
end
270
287
end
271
288
272
- defp app_files ( app ) do
289
+ defp app_files ( app , include_priv_for ) do
273
290
case :code . where_is_file ( ~c" #{ app } .app" ) do
274
291
:non_existing -> Mix . raise ( "Could not find application #{ app } " )
275
- file -> get_files ( Path . dirname ( Path . dirname ( file ) ) )
292
+ file -> get_files ( Path . dirname ( Path . dirname ( file ) ) , app in include_priv_for )
276
293
end
277
294
end
278
295
279
- defp prepare_beam_paths ( paths ) do
280
- for path <- paths , into: % { } , do: { Path . basename ( path ) , path }
281
- end
282
-
283
296
defp read_beams ( items ) do
284
297
Enum . map ( items , fn { basename , beam_path } ->
285
298
{ String . to_charlist ( basename ) , File . read! ( beam_path ) }
@@ -305,14 +318,31 @@ defmodule Mix.Tasks.Escript.Build do
305
318
end
306
319
end
307
320
308
- defp consolidated_paths ( config ) do
321
+ defp replace_consolidated_paths ( files , config ) do
322
+ # We could write modules to a consolidated/ directory and prepend
323
+ # it to code path using VM args. However, when Erlang Escript
324
+ # boots, it prepends all second-level ebin/ directories to the
325
+ # path, so the unconsolidated modules would take precedence.
326
+ #
327
+ # Instead of writing consolidated/ into the archive, we replace
328
+ # the protocol modules with their consolidated version in their
329
+ # usual location. As a side benefit, this reduces the Escript
330
+ # file size, since we do not include the unconsolidated modules.
331
+
309
332
if config [ :consolidate_protocols ] do
310
- Mix.Project . consolidation_path ( config )
311
- |> Path . join ( "*" )
312
- |> Path . wildcard ( )
313
- |> prepare_beam_paths ( )
333
+ consolidation_path = Mix.Project . consolidation_path ( config )
334
+
335
+ consolidated =
336
+ consolidation_path
337
+ |> Path . join ( "*" )
338
+ |> Path . wildcard ( )
339
+ |> Map . new ( fn path -> { Path . basename ( path ) , path } end )
340
+
341
+ for { zip_path , path } <- files do
342
+ { zip_path , consolidated [ Path . basename ( path ) ] || path }
343
+ end
314
344
else
315
- % { }
345
+ [ ]
316
346
end
317
347
end
318
348
0 commit comments