@@ -115,7 +115,7 @@ 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 <- start_node ( project , glob_paths ( project ) ) ,
119119 :ok <- :rpc . call ( node_name , Engine.Bootstrap , :init , bootstrap_args ) ,
120120 :ok <- ensure_apps_started ( node_name ) do
121121 { :ok , node_name , node_pid }
@@ -152,7 +152,7 @@ defmodule Expert.EngineNode do
152152 [ "/**/priv" | app_globs ]
153153 end
154154
155- def glob_paths do
155+ def glob_paths ( _ ) do
156156 for entry <- :code . get_path ( ) ,
157157 entry_string = List . to_string ( entry ) ,
158158 entry_string != "." ,
@@ -165,9 +165,87 @@ defmodule Expert.EngineNode do
165165 # separately and copied to expert's priv directory.
166166 # When Engine is built in CI for a version matrix, we'll need to check if
167167 # we have the right version downloaded, and if not, we should download it.
168- defp glob_paths do
168+ defp glob_paths ( % Project { } = project ) do
169+ { :ok , elixir , _ } = Expert.Port . elixir_executable ( project )
170+
171+ { output , _ } = System . cmd ( elixir , [ "-e" , get_versions_code ( ) ] )
172+
173+ case output |> String . trim ( ) |> String . split ( "-" ) do
174+ [ elixir_vsn , erlang_vsn ] ->
175+ Logger . info (
176+ "Detected project Elixir version: #{ elixir_vsn } , Erlang version: #{ erlang_vsn } "
177+ )
178+
179+ find_engine_build ( elixir_vsn , erlang_vsn )
180+
181+ _ ->
182+ Logger . warning (
183+ "Failed to detect Elixir and Erlang versions, using default Engine build"
184+ )
185+
186+ default_glob_paths ( )
187+ end
188+ end
189+
190+ defp get_versions_code ( ) do
191+ ~S"""
192+ major = :otp_release |> :erlang.system_info() |> List.to_string()
193+ version_file = Path.join([:code.root_dir(), "releases", major, "OTP_VERSION"])
194+
195+ erlang_vsn =
196+ try do
197+ {:ok, contents} = File.read(version_file)
198+ String.split(contents, "\n", trim: true)
199+ else
200+ [full] -> full
201+ _ -> major
202+ catch
203+ :error ->
204+ major
205+ end
206+
207+ IO.puts("#{System.version()}-#{erlang_vsn}")
208+ """
209+ end
210+
211+ defp find_engine_build ( elixir_vsn , erlang_vsn ) do
212+ user_data_path = :filename . basedir ( :user_data , "Expert" , % { version: Expert . vsn ( ) } )
213+ engine_path = Path . join ( [ user_data_path , "engine" , "*-otp-*" ] )
214+
215+ Logger . info ( "Looking for Engine build at: #{ engine_path } " )
216+
217+ candidates =
218+ for entry <- Path . wildcard ( engine_path ) ,
219+ versions = entry |> Path . basename ( entry ) |> String . split ( "-" ) ,
220+ match? ( [ _ , "otp" , _ ] , versions ) ,
221+ [ elixir , "otp" , erlang ] = versions ,
222+ Version . match? ( elixir , "~> #{ elixir_vsn } " ) ,
223+ Version . match? ( erlang , "~> #{ erlang_vsn } " ) do
224+ entry
225+ end
226+
227+ case List . first ( candidates ) do
228+ nil ->
229+ Logger . warning (
230+ "No matching Engine build found for Elixir #{ elixir_vsn } and Erlang #{ erlang_vsn } "
231+ )
232+
233+ default_glob_paths ( )
234+
235+ engine_path ->
236+ Logger . info ( "Using Engine build at: #{ engine_path } " )
237+ ebin_paths ( engine_path )
238+ end
239+ end
240+
241+ defp default_glob_paths do
169242 :expert
170243 |> :code . priv_dir ( )
244+ |> ebin_paths ( )
245+ end
246+
247+ defp ebin_paths ( base_path ) do
248+ base_path
171249 |> Path . join ( "lib/**/ebin" )
172250 |> Path . wildcard ( )
173251 end
0 commit comments