Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/engine/.formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ current_directory = Path.dirname(__ENV__.file)

import_deps = [:forge]

locals_without_parens = [with_progress: 2, with_progress: 3, defkey: 2, defkey: 3, with_wal: 2]
locals_without_parens = [defkey: 2, defkey: 3, with_wal: 2]

[
locals_without_parens: locals_without_parens,
Expand Down
13 changes: 12 additions & 1 deletion apps/engine/lib/engine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Engine do
alias Engine.Api.Proxy
alias Engine.CodeAction
alias Engine.CodeIntelligence
alias Engine.Progress
alias Forge.Project

require Logger
Expand Down Expand Up @@ -68,10 +69,12 @@ defmodule Engine do
do: app
end

def ensure_apps_started do
def ensure_apps_started(token \\ Progress.noop_token()) do
apps_to_start = [:elixir, :runtime_tools | @allowed_apps]

Enum.reduce_while(apps_to_start, :ok, fn app_name, _ ->
Progress.report(token, message: "Starting #{app_name}...")

case :application.ensure_all_started(app_name) do
{:ok, _} -> {:cont, :ok}
error -> {:halt, error}
Expand Down Expand Up @@ -111,4 +114,12 @@ defmodule Engine do
def set_project(%Project{} = project) do
:persistent_term.put({__MODULE__, :project}, project)
end

def get_manager_node do
:persistent_term.get({__MODULE__, :manager_node}, nil)
end

def set_manager_node(node) when is_atom(node) do
:persistent_term.put({__MODULE__, :manager_node}, node)
end
end
9 changes: 1 addition & 8 deletions apps/engine/lib/engine/api/proxy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ defmodule Engine.Api.Proxy do

The logic follows below
`broadcast` - Buffered - Though, those related to other events, like compilation are subject to
the rules that govern their source events. Progress messages are sent regardless of
buffering.
the rules that govern their source events.
`schedule_compile` - Buffered - Only one call is kept
`compile_document` - Buffered, though only one call per URI is kept, and if a `schedule_compile` call
was buffered, all `compile_document` calls are dropped
Expand All @@ -39,9 +38,7 @@ defmodule Engine.Api.Proxy do
alias Engine.Api.Proxy.Records
alias Engine.CodeMod
alias Engine.Commands
alias Forge.EngineApi.Messages

import Messages
import Record
import Records, only: :macros

Expand All @@ -62,10 +59,6 @@ defmodule Engine.Api.Proxy do

# proxied functions

def broadcast(percent_progress() = message) do
Engine.Dispatch.broadcast(message)
end

def broadcast(message) do
mfa = to_mfa(Engine.Dispatch.broadcast(message))
:gen_statem.call(__MODULE__, buffer(contents: mfa))
Expand Down
3 changes: 1 addition & 2 deletions apps/engine/lib/engine/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defmodule Engine.Application do
@moduledoc false

use Application
require Logger

@impl true
def start(_type, _args) do
Expand All @@ -12,7 +11,7 @@ defmodule Engine.Application do
Engine.Api.Proxy,
Engine.Commands.Reindex,
Engine.Module.Loader,
{Engine.Dispatch, progress: true},
Engine.Dispatch,
Engine.ModuleMappings,
Engine.Build,
Engine.Build.CaptureServer,
Expand Down
3 changes: 2 additions & 1 deletion apps/engine/lib/engine/bootstrap.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Engine.Bootstrap do

require Logger

def init(%Project{} = project, document_store_entropy, app_configs) do
def init(%Project{} = project, document_store_entropy, app_configs, manager_node) do
Forge.Document.Store.set_entropy(document_store_entropy)

Application.put_all_env(app_configs)
Expand All @@ -26,6 +26,7 @@ defmodule Engine.Bootstrap do
{:ok, _} <- Application.ensure_all_started(:logger) do
project = maybe_load_mix_exs(project)
Engine.set_project(project)
Engine.set_manager_node(manager_node)
Mix.env(:test)
ExUnit.start()
start_logger(project)
Expand Down
16 changes: 10 additions & 6 deletions apps/engine/lib/engine/build.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
defmodule Engine.Build do
alias Forge.Document
alias Forge.Project

alias Engine.Build.Document.Compilers.HEEx
alias Engine.Build.State
alias Forge.{Document, Project}

require Logger
use GenServer
Expand Down Expand Up @@ -35,9 +33,15 @@ defmodule Engine.Build do
:ok
end

def with_lock(func) do
Engine.with_lock(__MODULE__, func)
end
def with_lock(func), do: Engine.with_lock(__MODULE__, func)

# can't pass work token to Tracer module, so store it in persistent term.

def set_progress_token(token), do: :persistent_term.put({__MODULE__, :progress_token}, token)

def get_progress_token, do: :persistent_term.get({__MODULE__, :progress_token}, nil)

def clear_progress_token, do: :persistent_term.erase({__MODULE__, :progress_token})

# GenServer Callbacks

Expand Down
117 changes: 56 additions & 61 deletions apps/engine/lib/engine/build/project.ex
Original file line number Diff line number Diff line change
@@ -1,51 +1,60 @@
defmodule Engine.Build.Project do
alias Forge.Project

alias Engine.Build
alias Engine.{Build, Plugin, Progress}
alias Engine.Build.Isolation
alias Engine.Plugin
alias Mix.Task.Compiler.Diagnostic

use Engine.Progress
require Logger

def compile(%Project{} = project, initial?) do
Engine.Mix.in_project(fn _ ->
Mix.Task.clear()

prepare_for_project_build(initial?)
Logger.info("Building #{Project.display_name(project)}")

compile_fun = fn ->
Mix.Task.clear()
Progress.with_progress("Building #{Project.display_name(project)}", fn token ->
Build.set_progress_token(token)

with_progress building_label(project), fn ->
result = compile_in_isolation()
Mix.Task.run(:loadpaths)
result
try do
{:done, do_compile(project, initial?, token)}
after
Build.clear_progress_token()
end
end

case compile_fun.() do
{:error, diagnostics} ->
diagnostics =
diagnostics
|> List.wrap()
|> Build.Error.refine_diagnostics()

{:error, diagnostics}

{status, diagnostics} when status in [:ok, :noop] ->
Logger.info(
"Compile completed with status #{status} " <>
"Produced #{length(diagnostics)} diagnostics " <>
inspect(diagnostics)
)

Build.Error.refine_diagnostics(diagnostics)
end
end)
end)
end

defp do_compile(project, initial?, token) do
Mix.Task.clear()

if initial?, do: prepare_for_project_build(token)

compile_fun = fn ->
Mix.Task.clear()
Progress.report(token, message: "Compiling #{Project.display_name(project)}")
result = compile_in_isolation()
Mix.Task.run(:loadpaths)
result
end

case compile_fun.() do
{:error, diagnostics} ->
diagnostics =
diagnostics
|> List.wrap()
|> Build.Error.refine_diagnostics()

{:error, diagnostics}

{status, diagnostics} when status in [:ok, :noop] ->
Logger.info(
"Compile completed with status #{status} " <>
"Produced #{length(diagnostics)} diagnostics " <>
inspect(diagnostics)
)

Build.Error.refine_diagnostics(diagnostics)
end
end

defp compile_in_isolation do
compile_fun = fn -> Mix.Task.run(:compile, mix_compile_opts()) end

Expand All @@ -66,40 +75,30 @@ defmodule Engine.Build.Project do
end
end

defp prepare_for_project_build(false = _initial?) do
:ok
end

defp prepare_for_project_build(true = _initial?) do
defp prepare_for_project_build(token) do
if connected_to_internet?() do
with_progress "mix local.hex", fn ->
Mix.Task.run("local.hex", ~w(--force))
end
Progress.report(token, message: "mix local.hex")
Mix.Task.run("local.hex", ~w(--force))

with_progress "mix local.rebar", fn ->
Mix.Task.run("local.rebar", ~w(--force))
end
Progress.report(token, message: "mix local.rebar")
Mix.Task.run("local.rebar", ~w(--force))

with_progress "mix deps.get", fn ->
Mix.Task.run("deps.get")
end
Progress.report(token, message: "mix deps.get")
Mix.Task.run("deps.get")
else
Logger.warning("Could not connect to hex.pm, dependencies will not be fetched")
end

with_progress "mix loadconfig", fn ->
Mix.Task.run(:loadconfig)
end
Progress.report(token, message: "mix loadconfig")
Mix.Task.run(:loadconfig)

unless Elixir.Features.compile_keeps_current_directory?() do
with_progress "mix deps.compile", fn ->
Mix.Task.run("deps.safe_compile", ~w(--skip-umbrella-children))
end
if not Elixir.Features.compile_keeps_current_directory?() do
Progress.report(token, message: "mix deps.compile")
Mix.Task.run("deps.safe_compile", ~w(--skip-umbrella-children))
end

with_progress "loading plugins", fn ->
Plugin.Discovery.run()
end
Progress.report(token, message: "Loading plugins")
Plugin.Discovery.run()
end

defp connected_to_internet? do
Expand All @@ -113,10 +112,6 @@ defmodule Engine.Build.Project do
end
end

def building_label(%Project{} = project) do
"Building #{Project.display_name(project)}"
end

defp mix_compile_opts do
~w(
--return-errors
Expand Down
6 changes: 0 additions & 6 deletions apps/engine/lib/engine/build/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ defmodule Engine.Build.State do

import Messages

use Engine.Progress

defstruct project: nil,
build_number: 0,
uri_to_document: %{},
Expand Down Expand Up @@ -207,10 +205,6 @@ defmodule Engine.Build.State do
end
end

def building_label(%Project{} = project) do
"Building #{Project.display_name(project)}"
end

defp to_ms(microseconds) do
microseconds / 1000
end
Expand Down
13 changes: 5 additions & 8 deletions apps/engine/lib/engine/compilation/tracer.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Engine.Compilation.Tracer do
alias Engine.Build
alias Engine.Module.Loader
alias Engine.Progress

import Forge.EngineApi.Messages

Expand Down Expand Up @@ -56,10 +57,9 @@ defmodule Engine.Compilation.Tracer do
end

defp maybe_report_progress(file) do
if Path.extname(file) == ".ex" do
file
|> progress_message()
|> Engine.broadcast()
with ".ex" <- Path.extname(file),
token when not is_nil(token) <- Build.get_progress_token() do
Progress.report(token, message: progress_message(file))
end
end

Expand All @@ -72,9 +72,6 @@ defmodule Engine.Compilation.Tracer do
base_dir = List.first(relative_path_elements)
file_name = List.last(relative_path_elements)

message = "compiling: " <> Path.join([base_dir, "...", file_name])

label = Build.State.building_label(Engine.get_project())
project_progress(label: label, message: message)
"compiling: " <> Path.join([base_dir, "...", file_name])
end
end
Loading