Skip to content

Commit c938c4a

Browse files
committed
feat: windows support
1 parent 583ee15 commit c938c4a

File tree

3 files changed

+147
-74
lines changed

3 files changed

+147
-74
lines changed

apps/expert/lib/expert/engine_node.ex

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ defmodule Expert.EngineNode do
5252
IO.puts(\"ok\")
5353
"""
5454
| path_append_arguments(paths)
55-
]
55+
] |> dbg()
5656

5757
env =
5858
[
@@ -186,66 +186,86 @@ defmodule Expert.EngineNode do
186186
# Expert release, and we build it on the fly for the project elixir+opt
187187
# versions if it was not built yet.
188188
defp glob_paths(%Project{} = project) do
189-
lsp = Expert.get_lsp()
190-
project_name = Project.name(project)
191-
192189
case Expert.Port.elixir_executable(project) do
193190
{:ok, elixir, env} ->
194-
GenLSP.info(lsp, "Found elixir for #{project_name} at #{elixir}")
195-
196-
expert_priv = :code.priv_dir(:expert)
197-
packaged_engine_source = Path.join([expert_priv, "engine_source", "apps", "engine"])
198-
199-
engine_source =
200-
"EXPERT_ENGINE_PATH"
201-
|> System.get_env(packaged_engine_source)
202-
|> Path.expand()
203-
204-
build_engine_script = Path.join(expert_priv, "build_engine.exs")
205-
206-
opts =
207-
[
208-
:stderr_to_stdout,
209-
args: [
210-
elixir,
211-
build_engine_script,
212-
"--source-path",
213-
engine_source,
214-
"--vsn",
215-
Expert.vsn()
216-
],
217-
env: Expert.Port.ensure_charlists(env),
218-
cd: Project.root_path(project)
219-
]
220-
221-
launcher = Expert.Port.path()
222-
223-
GenLSP.info(lsp, "Finding or building engine for project #{project_name}")
224-
225-
with_progress(project, "Building engine for #{project_name}", fn ->
226-
fn ->
227-
Process.flag(:trap_exit, true)
228-
229-
{:spawn_executable, launcher}
230-
|> Port.open(opts)
231-
|> wait_for_engine()
232-
end
233-
|> Task.async()
234-
|> Task.await(:infinity)
235-
end)
191+
launch_engine_builder(project, elixir, env)
236192

237193
{:error, :no_elixir, message} ->
238194
GenLSP.error(Expert.get_lsp(), message)
239195
Expert.terminate("Failed to find an elixir executable, shutting down", 1)
240196
end
241197
end
242198

199+
defp launch_engine_builder(project, elixir, env) do
200+
lsp = Expert.get_lsp()
201+
202+
project_name = Project.name(project)
203+
Logger.info("Found elixir for #{project_name} at #{elixir}")
204+
GenLSP.info(lsp, "Found elixir for #{project_name} at #{elixir}")
205+
206+
expert_priv = :code.priv_dir(:expert)
207+
packaged_engine_source = Path.join([expert_priv, "engine_source", "apps", "engine"])
208+
209+
engine_source =
210+
"EXPERT_ENGINE_PATH"
211+
|> System.get_env(packaged_engine_source)
212+
|> Path.expand()
213+
214+
build_engine_script = Path.join(expert_priv, "build_engine.exs")
215+
216+
opts =
217+
[
218+
:stderr_to_stdout,
219+
args: [
220+
build_engine_script,
221+
"--source-path",
222+
engine_source,
223+
"--vsn",
224+
Expert.vsn()
225+
],
226+
env: Expert.Port.ensure_charlists(env),
227+
cd: Project.root_path(project)
228+
]
229+
230+
{launcher, opts} =
231+
case :os.type() do
232+
{:win32, _} ->
233+
{elixir, opts}
234+
235+
{:unix, _} ->
236+
launcher = Expert.Port.path()
237+
238+
opts =
239+
Keyword.update(opts, :args, [elixir], fn old_args ->
240+
[elixir | Enum.map(old_args, &to_string/1)]
241+
end)
242+
243+
{launcher, opts}
244+
end
245+
246+
GenLSP.info(lsp, "Finding or building engine for project #{project_name}")
247+
248+
with_progress(project, "Building engine for #{project_name}", fn ->
249+
fn ->
250+
Process.flag(:trap_exit, true)
251+
252+
{:spawn_executable, launcher}
253+
|> Port.open(opts)
254+
|> wait_for_engine()
255+
end
256+
|> Task.async()
257+
|> Task.await(:infinity)
258+
end)
259+
end
260+
243261
defp wait_for_engine(port, last_line \\ "") do
244262
receive do
245263
{^port, {:data, ~c"engine_path:" ++ engine_path}} ->
246264
engine_path = engine_path |> to_string() |> String.trim()
247265
Logger.info("Engine build available at: #{engine_path}")
248266

267+
Logger.info("ebin paths:\n#{inspect(ebin_paths(engine_path), pretty: true)}")
268+
249269
{:ok, ebin_paths(engine_path)}
250270

251271
{^port, {:data, data}} ->
@@ -374,6 +394,7 @@ defmodule Expert.EngineNode do
374394

375395
@impl true
376396
def handle_info({_port, {:data, message}}, %State{} = state) do
397+
dbg(message)
377398
Logger.debug("Node port message: #{to_string(message)}")
378399
{:noreply, state}
379400
end

apps/expert/lib/expert/port.ex

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,50 @@ defmodule Expert.Port do
3434
end
3535

3636
def elixir_executable(%Project{} = project) do
37-
root_path = Project.root_path(project)
38-
39-
shell = System.get_env("SHELL")
40-
path = path_env_at_directory(root_path, shell)
41-
42-
case :os.find_executable(~c"elixir", to_charlist(path)) do
43-
false ->
44-
{:error, :no_elixir,
45-
"Couldn't find an elixir executable for project at #{root_path}. Using shell at #{shell} with PATH=#{path}"}
46-
47-
elixir ->
48-
env =
49-
Enum.map(System.get_env(), fn
50-
{"PATH", _path} -> {"PATH", path}
51-
other -> other
52-
end)
53-
54-
{:ok, elixir, env}
37+
case :os.type() do
38+
{:win32, _} ->
39+
# Remove the burrito binaries from PATH
40+
path =
41+
"PATH"
42+
|> System.get_env()
43+
|> String.split(";", parts: 2)
44+
|> List.last()
45+
46+
case :os.find_executable(~c"elixir", to_charlist(path)) do
47+
nil ->
48+
{:error, :no_elixir, "Couldn't find an elixir executable"}
49+
50+
elixir ->
51+
dbg(elixir)
52+
env =
53+
Enum.map(System.get_env(), fn
54+
{"PATH", _path} -> {"PATH", path}
55+
other -> other
56+
end)
57+
58+
{:ok, elixir, env}
59+
end
60+
61+
_ ->
62+
root_path = Project.root_path(project)
63+
64+
shell = System.get_env("SHELL")
65+
path = path_env_at_directory(root_path, shell)
66+
67+
case :os.find_executable(~c"elixir", to_charlist(path)) do
68+
false ->
69+
{:error, :no_elixir,
70+
"Couldn't find an elixir executable for project at #{root_path}. Using shell at #{shell} with PATH=#{path}"}
71+
72+
elixir ->
73+
env =
74+
Enum.map(System.get_env(), fn
75+
{"PATH", _path} -> {"PATH", path}
76+
other -> other
77+
end)
78+
79+
{:ok, elixir, env}
80+
end
5581
end
5682
end
5783

@@ -96,14 +122,11 @@ defmodule Expert.Port do
96122
Launches an executable in the project context via a port.
97123
"""
98124
def open(%Project{} = project, executable, opts) do
99-
{launcher, opts} = Keyword.pop_lazy(opts, :path, &path/0)
125+
{os_type, _} = :os.type()
100126

101127
opts =
102128
opts
103129
|> Keyword.put_new_lazy(:cd, fn -> Project.root_path(project) end)
104-
|> Keyword.update(:args, [executable], fn old_args ->
105-
[executable | Enum.map(old_args, &to_string/1)]
106-
end)
107130

108131
opts =
109132
if Keyword.has_key?(opts, :env) do
@@ -112,6 +135,24 @@ defmodule Expert.Port do
112135
opts
113136
end
114137

138+
open_port(os_type, executable, opts)
139+
end
140+
141+
defp open_port(:win32, executable, opts) do
142+
dbg("launching for windows")
143+
dbg(opts)
144+
Port.open({:spawn_executable, executable}, [:stderr_to_stdout | opts])
145+
end
146+
147+
defp open_port(:unix, executable, opts) do
148+
dbg(opts)
149+
{launcher, opts} = Keyword.pop_lazy(opts, :path, &path/0)
150+
151+
opts =
152+
Keyword.update(opts, :args, [executable], fn old_args ->
153+
[executable | Enum.map(old_args, &to_string/1)]
154+
end)
155+
115156
Port.open({:spawn_executable, launcher}, [:stderr_to_stdout | opts])
116157
end
117158

justfile

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ os := if os() == "macos" { "darwin" } else { os() }
22
arch := if arch() =~ "(arm|aarch64)" { "arm64" } else { if arch() =~ "(x86|x86_64)" { "amd64" } else { "unsupported" } }
33
local_target := if os =~ "(darwin|linux|windows)" { os + "_" + arch } else { "unsupported" }
44
apps := "expert engine forge expert_credo"
5+
expert_erl_flags := "-start_epmd false -epmd_module Elixir.Forge.EPMD"
6+
engine_erl_flags := "-start_epmd false -epmd_module Elixir.Forge.EPMD"
57

68
[doc('Run mix deps.get for the given project')]
9+
[unix]
710
deps project:
811
#!/usr/bin/env bash
912
cd apps/{{ project }}
1013
mix deps.get
1114

15+
[windows]
16+
deps project:
17+
cd apps/{{ project }} && \
18+
mix deps.get
19+
1220
[doc('Run an arbitrary command inside the given project directory')]
1321
run project +ARGS:
1422
#!/usr/bin/env bash
@@ -34,10 +42,10 @@ mix project="all" *args="":
3442
for proj in {{ apps }}; do
3543
case $proj in
3644
expert)
37-
(cd "apps/$proj" && elixir --erl "-start_epmd false -epmd_module Elixir.Forge.EPMD" -S mix {{args}})
45+
(cd "apps/$proj" && elixir --erl "{{ expert_erl_flags }}" -S mix {{args}})
3846
;;
3947
engine)
40-
(cd "apps/$proj" && elixir --erl "-start_epmd false -epmd_module Elixir.Forge.EPMD" -S mix {{args}})
48+
(cd "apps/$proj" && elixir --erl "{{ engine_erl_flags }}" -S mix {{args}})
4149
;;
4250
*)
4351
(cd "apps/$proj" && mix {{args}})
@@ -46,10 +54,10 @@ mix project="all" *args="":
4654
done
4755
;;
4856
expert)
49-
(cd "apps/expert" && elixir --erl "-start_epmd false -epmd_module Elixir.Forge.EPMD" -S mix {{args}})
57+
(cd "apps/expert" && elixir --erl "{{ expert_erl_flags }}" -S mix {{args}})
5058
;;
5159
engine)
52-
(cd "apps/engine" && elixir --erl "-start_epmd false -epmd_module Elixir.Forge.EPMD" -S mix {{args}})
60+
(cd "apps/engine" && elixir --erl "{{ engine_erl_flags }}" -S mix {{args}})
5361
;;
5462
*)
5563
(cd "apps/{{ project }}" && mix {{args}})
@@ -81,8 +89,11 @@ release-local: (deps "engine") (deps "expert")
8189

8290
[windows]
8391
release-local: (deps "engine") (deps "expert")
84-
# idk actually how to set env vars like this on windows, might crash
85-
EXPERT_RELEASE_MODE=burrito BURRITO_TARGET="windows_amd64" MIX_ENV={{ env('MIX_ENV', 'prod')}} mix release --overwrite
92+
export EXPERT_RELEASE_MODE=burrito && \
93+
export BURRITO_TARGET="windows_amd64" && \
94+
export MIX_ENV={{ env('MIX_ENV', 'prod')}} && \
95+
cd apps/expert && \
96+
mix release --overwrite
8697

8798
[doc('Build releases for all target platforms')]
8899
release-all: (deps "engine") (deps "expert")

0 commit comments

Comments
 (0)