@@ -115,7 +115,8 @@ defmodule Expert.EngineNode do
115115 bootstrap_args = [ project , Document.Store . entropy ( ) , all_app_configs ( ) ]
116116
117117 with { :ok , node_pid } <- EngineSupervisor . start_project_node ( project ) ,
118- :ok <- start_node ( project , glob_paths ( ) ) ,
118+ { :ok , glob_paths } <- glob_paths ( project ) ,
119+ :ok <- start_node ( project , glob_paths ) ,
119120 :ok <- :rpc . call ( node_name , Engine.Bootstrap , :init , bootstrap_args ) ,
120121 :ok <- ensure_apps_started ( node_name ) do
121122 { :ok , node_name , node_pid }
@@ -152,22 +153,79 @@ defmodule Expert.EngineNode do
152153 [ "/**/priv" | app_globs ]
153154 end
154155
155- def glob_paths do
156- for entry <- :code . get_path ( ) ,
157- entry_string = List . to_string ( entry ) ,
158- entry_string != "." ,
159- Enum . any? ( app_globs ( ) , & PathGlob . match? ( entry_string , & 1 , match_dot: true ) ) do
160- entry
161- end
156+ def glob_paths ( _ ) do
157+ entries =
158+ for entry <- :code . get_path ( ) ,
159+ entry_string = List . to_string ( entry ) ,
160+ entry_string != "." ,
161+ Enum . any? ( app_globs ( ) , & PathGlob . match? ( entry_string , & 1 , match_dot: true ) ) do
162+ entry
163+ end
164+
165+ { :ok , entries }
162166 end
163167 else
164- # In dev and prod environments, a default build of Engine is built
165- # separately and copied to expert's priv directory.
166- # When Engine is built in CI for a version matrix, we'll need to check if
167- # we have the right version downloaded, and if not, we should download it.
168- defp glob_paths do
169- :expert
170- |> :code . priv_dir ( )
168+ # In dev and prod environments, the engine source code is included in the
169+ # Expert release, and we build it on the fly for the project elixir+opt
170+ # versions if it was not built yet.
171+ defp glob_paths ( % Project { } = project ) do
172+ { :ok , elixir , _ } = Expert.Port . elixir_executable ( project )
173+
174+ expert_priv = :code . priv_dir ( :expert )
175+ packaged_engine_source = Path . join ( [ expert_priv , "engine_source" , "apps" , "engine" ] )
176+
177+ engine_source =
178+ "EXPERT_ENGINE_PATH"
179+ |> System . get_env ( packaged_engine_source )
180+ |> Path . expand ( )
181+
182+ build_engine_script = Path . join ( expert_priv , "build_engine.exs" )
183+
184+ opts =
185+ [
186+ args: [
187+ elixir ,
188+ build_engine_script ,
189+ "--source-path" ,
190+ engine_source ,
191+ "--vsn" ,
192+ Expert . vsn ( )
193+ ] ,
194+ cd: engine_source
195+ ]
196+
197+ launcher = Expert.Port . path ( )
198+
199+ Logger . info ( "Finding or building engine for project #{ Project . name ( project ) } " )
200+
201+ port =
202+ Port . open (
203+ { :spawn_executable , launcher } ,
204+ opts
205+ )
206+
207+ wait_for_engine ( port )
208+ end
209+
210+ defp wait_for_engine ( port ) do
211+ receive do
212+ { ^ port , { :data , ~c" engine_path:" ++ engine_path } } ->
213+ engine_path = engine_path |> to_string ( ) |> String . trim ( )
214+ Logger . info ( "Engine build available at: #{ engine_path } " )
215+
216+ { :ok , ebin_paths ( engine_path ) }
217+
218+ { ^ port , _data } ->
219+ wait_for_engine ( port )
220+
221+ { :EXIT , ^ port , reason } ->
222+ Logger . error ( "Engine build script exited with reason: #{ inspect ( reason ) } " )
223+ { :error , reason }
224+ end
225+ end
226+
227+ defp ebin_paths ( base_path ) do
228+ base_path
171229 |> Path . join ( "lib/**/ebin" )
172230 |> Path . wildcard ( )
173231 end
0 commit comments