@@ -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,80 @@ 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+ :stderr_to_stdout ,
187+ args: [
188+ elixir ,
189+ build_engine_script ,
190+ "--source-path" ,
191+ engine_source ,
192+ "--vsn" ,
193+ Expert . vsn ( )
194+ ] ,
195+ cd: engine_source
196+ ]
197+
198+ launcher = Expert.Port . path ( )
199+
200+ Logger . info ( "Finding or building engine for project #{ Project . name ( project ) } " )
201+
202+ port =
203+ Port . open (
204+ { :spawn_executable , launcher } ,
205+ opts
206+ )
207+
208+ wait_for_engine ( port )
209+ end
210+
211+ defp wait_for_engine ( port ) do
212+ receive do
213+ { ^ port , { :data , ~c" engine_path:" ++ engine_path } } ->
214+ engine_path = engine_path |> to_string ( ) |> String . trim ( )
215+ Logger . info ( "Engine build available at: #{ engine_path } " )
216+
217+ { :ok , ebin_paths ( engine_path ) }
218+
219+ { ^ port , _data } ->
220+ wait_for_engine ( port )
221+
222+ { :EXIT , ^ port , reason } ->
223+ Logger . error ( "Engine build script exited with reason: #{ inspect ( reason ) } " )
224+ { :error , reason }
225+ end
226+ end
227+
228+ defp ebin_paths ( base_path ) do
229+ base_path
171230 |> Path . join ( "lib/**/ebin" )
172231 |> Path . wildcard ( )
173232 end
0 commit comments