11defmodule Popcorn.BeamTools.Packager do
22 @ static_nif_beams MapSet . new ( [ "wasm.beam" ] )
33
4+ # To run, Beam needs following apps:
5+ # - kernel
6+ # - stdlib (kernel dep)
7+ #
8+ # We also add elixir support out of the box and need:
9+ # - compiler (elixir dep)
10+ # - elixir
11+ #
12+ # All of them should be in elixir's transitive dependency closure
13+ @ base_apps [ "elixir" ]
14+
415 @ type options :: % {
516 root_dir: Path . t ( ) ,
617 entrypoint_app: String . t ( ) | nil ,
718 out_dir: Path . t ( ) ,
819 manifest_path: Path . t ( ) ,
9- strip: boolean ( ) ,
10- tar_paths: [ Path . t ( ) ]
20+ strip: boolean ( )
1121 }
1222
1323 defp strip_tarball ( path , out_dir ) do
@@ -43,8 +53,7 @@ defmodule Popcorn.BeamTools.Packager do
4353 entrypoint_app: entrypoint_app ,
4454 out_dir: out_dir ,
4555 manifest_path: manifest_path ,
46- strip: strip ,
47- tar_paths: input_tar_paths
56+ strip: strip
4857 } = args
4958
5059 with { :ok , manifest } <- read_manifest ( manifest_path ) ,
@@ -57,20 +66,19 @@ defmodule Popcorn.BeamTools.Packager do
5766
5867 File . mkdir_p! ( out_dir )
5968
60- # TODO: We still use builtin apps, needs to change
6169 packed_apps =
6270 apps_info
63- |> Task . async_stream ( fn { app , info } ->
71+ |> async_stream ( fn { app , info } ->
6472 version = Keyword . get ( info . props , :vsn , ~c" " ) |> to_string ( )
6573 tar_path = create_tarball ( out_dir , app , info . ebin_dir )
6674
6775 { app , % { tar: tar_path , version: version } }
6876 end )
69- |> Map . new ( fn { :ok , app } -> app end )
77+ |> Map . new ( )
7078
7179 diagnostics =
7280 apps_info
73- |> Task . async_stream ( fn { app , info } ->
81+ |> async_stream ( fn { app , info } ->
7482 case loaded_dynamic_nifs ( app , info . ebin_dir ) do
7583 [ ] ->
7684 [ ]
@@ -80,21 +88,20 @@ defmodule Popcorn.BeamTools.Packager do
8088 [ context ]
8189 end
8290 end )
83- |> Enum . flat_map ( fn { :ok , diagnostics } -> diagnostics end )
91+ |> Enum . concat ( )
8492
8593 manifest_path = Path . join ( out_dir , "manifest.json" )
86- manifest_apps = Map . merge ( packed_apps , manifest . apps )
8794
8895 packed_tar_paths =
8996 packed_apps
9097 |> Map . values ( )
9198 |> Enum . map ( & Path . expand ( Path . join ( out_dir , & 1 . tar ) ) )
9299
93- tar_paths = maybe_strip_tarballs ( packed_tar_paths ++ input_tar_paths , strip , out_dir )
100+ tar_paths = maybe_strip_tarballs ( packed_tar_paths , strip , out_dir )
94101
95102 manifest = % {
96103 entrypoint: entrypoint_app ,
97- apps: manifest_apps ,
104+ apps: packed_apps ,
98105 notes: diagnostics ,
99106 toolchain: toolchain ,
100107 vm: % { boot: "bin/vm.boot" , version: vm_version }
@@ -107,7 +114,7 @@ defmodule Popcorn.BeamTools.Packager do
107114 entrypoint: entrypoint_app ,
108115 manifestPath: Path . expand ( manifest_path ) ,
109116 tarPaths: tar_paths ,
110- apps: manifest_apps ,
117+ apps: packed_apps ,
111118 notes: diagnostics ,
112119 toolchain: toolchain
113120 }
@@ -116,6 +123,12 @@ defmodule Popcorn.BeamTools.Packager do
116123 end
117124 end
118125
126+ defp async_stream ( enumerable , fun ) do
127+ enumerable
128+ |> Task . async_stream ( fun , timeout: :infinity )
129+ |> Enum . map ( fn { :ok , result } -> result end )
130+ end
131+
119132 defp maybe_strip_tarballs ( paths , false , _out_dir ) , do: paths
120133
121134 defp maybe_strip_tarballs ( paths , true , out_dir ) do
@@ -155,34 +168,37 @@ defmodule Popcorn.BeamTools.Packager do
155168
156169 defp read_manifest ( manifest_path ) do
157170 with { :ok , json } <- File . read ( manifest_path ) ,
158- { :ok , % { "apps" => apps , " vm" => % { "version" => version } } } <- decode_json ( json ) do
159- { :ok , % { version: version , apps: apps } }
171+ { :ok , % { "vm" => % { "version" => version } } } <- decode_json ( json ) do
172+ { :ok , % { version: version } }
160173 else
161174 _ -> err ( :bad_manifest , manifest_path )
162175 end
163176 end
164177
165- defp apps_to_pack ( project_apps , _builtin_apps , nil ) do
166- { :ok , Enum . sort_by ( project_apps , fn { app , _info } -> app end ) }
167- end
168-
169- defp apps_to_pack ( project_apps , builtin_apps , entrypoint )
170- when is_map_key ( project_apps , entrypoint ) do
178+ defp apps_to_pack ( project_apps , builtin_apps , entrypoint ) do
171179 all_apps_info = Map . merge ( builtin_apps , project_apps )
172180
173- with { :ok , selected_apps } <-
174- gather_required_apps ( all_apps_info , project_apps , entrypoint , MapSet . new ( ) ) do
175- project_apps
181+ gather_from_root = fn app , selected ->
182+ gather_required_apps ( all_apps_info , project_apps , app , selected )
183+ end
184+
185+ with { :ok , roots } <- root_apps ( project_apps , entrypoint ) ,
186+ { :ok , selected_apps } <- reduce_while_ok ( roots , MapSet . new ( ) , gather_from_root ) do
187+ all_apps_info
176188 |> Map . filter ( fn { app , _info } -> MapSet . member? ( selected_apps , app ) end )
177189 |> Enum . sort ( )
178190 |> then ( & { :ok , & 1 } )
179191 end
180192 end
181193
182- defp apps_to_pack ( _project_apps , _builtin_apps , entrypoint ) do
183- err ( :missing_entrypoint , entrypoint )
194+ defp root_apps ( _project_apps , nil ) , do: { :ok , @ base_apps }
195+
196+ defp root_apps ( project_apps , entrypoint ) when is_map_key ( project_apps , entrypoint ) do
197+ { :ok , [ entrypoint | @ base_apps ] }
184198 end
185199
200+ defp root_apps ( _project_apps , entrypoint ) , do: err ( :missing_entrypoint , entrypoint )
201+
186202 defp gather_required_apps ( all_apps_info , project_apps , app , selected ) do
187203 if MapSet . member? ( selected , app ) do
188204 { :ok , selected }
0 commit comments