lexical-lsp / lexical

Lexical is a next-generation elixir language server
779 stars 77 forks source link

Can not compile dependencies due to File.read! in mix.exs #717

Closed AndrewDryga closed 1 month ago

AndrewDryga commented 2 months ago

Hello guys, I'm trying Lexical out and hitting an issue because we use File.read!/1 inside our mix.exs:

2024-04-25 15:27:50.267 [info]     warning: redefining module Firezone.MixProject (current version defined in memory)
    │
  1 │ defmodule Firezone.MixProject do
    │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    │
    └─ /Users/andrew/Projects/os/firezone/elixir/mix.exs:1: Firezone.MixProject (module)

2024-04-25 15:27:50.948 [info] warning: the log level :warn is deprecated, use :warning instead
  (logger 1.16.2) lib/logger.ex:1165: Logger.elixir_level_to_erlang_level/1
  (logger 1.16.2) lib/logger.ex:627: Logger.compare_levels/2
  (logger_file_backend 0.0.13) lib/logger_file_backend.ex:36: LoggerFileBackend.handle_event/2
  (stdlib 5.2) gen_event.erl:814: :gen_event.server_update/4
  (stdlib 5.2) gen_event.erl:796: :gen_event.server_notify/4
  (stdlib 5.2) gen_event.erl:538: :gen_event.handle_msg/6

2024-04-25 15:27:53.077 [info] 
== Compilation error in file lib/open_telemetry/semantic_conventions/logs.ex ==
** (File.Error) could not read file "VERSION": no such file or directory
    (elixir 1.16.2) lib/file.ex:384: File.read!/1
    /Users/andrew/Projects/os/firezone/elixir/mix.exs:8: Firezone.MixProject.project/0
    (lx_lexical_shared 0.5.0) lib/lexical/project.ex:92: LXical.Project.config/1
    (lx_lexical_shared 0.5.0) lib/lexical/project.ex:105: LXical.Project.display_name/1
    (lx_remote_control 0.5.0) lib/lexical/remote_control/build/state.ex:202: LXical.RemoteControl.Build.State.building_label/1
    (lx_remote_control 0.5.0) lib/lexical/remote_control/compilation/tracer.ex:79: LXical.RemoteControl.Compilation.Tracer.progress_message/1
    (lx_remote_control 0.5.0) lib/lexical/remote_control/compilation/tracer.ex:63: LXical.RemoteControl.Compilation.Tracer.maybe_report_progress/1
    (lx_remote_control 0.5.0) lib/lexical/remote_control/compilation/tracer.ex:11: LXical.RemoteControl.Compilation.Tracer.trace/2

2024-04-25 15:27:53.077 [info] could not compile dependency :opentelemetry_semantic_conventions, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile opentelemetry_semantic_conventions --force", update it with "mix deps.update opentelemetry_semantic_conventions" or clean it with "mix deps.clean opentelemetry_semantic_conventions"

The full source code can be found here: https://github.com/firezone/firezone (in elixir folder).

scohen commented 2 months ago

Which editor are you using, and how are you importing the project?

AndrewDryga commented 2 months ago

@scohen I'm using VSCode, just did code ./firezone (that's the path to the repo I've linked above) and changed the Project Dir to elixir/.

scohen commented 2 months ago

Gotcha, I'll investigate.

bigardone commented 2 months ago

Hi there! Same issue here:

Detected Elixir through asdf: /Users/ricardo/.asdf/installs/elixir/1.15.7-otp-26/bin/elixir
warning: redefining module MyProject.MixProject (current version defined in memory)
  /Users/ricardo/projects/my_project/mix.exs:1: MyProject.MixProject (module)

warning: Logger.warn/1 is deprecated. Use Logger.warning/2 instead
  lib/tailwind.ex:72:14

warning: Logger.warn/1 is deprecated. Use Logger.warning/2 instead
  lib/tailwind.ex:86:16

== Compilation error in file lib/bcrypt/base.ex ==
** (UndefinedFunctionError) function Bcrypt.Base.__info__/1 is undefined (function not available)
    Bcrypt.Base.__info__(:functions)
    (lx_remote_control 0.5.0) lib/lexical/remote_control/compilation/tracer.ex:30: LXical.RemoteControl.Compilation.Tracer.extract_module_updated/3
    (lx_remote_control 0.5.0) lib/lexical/remote_control/compilation/tracer.ex:10: LXical.RemoteControl.Compilation.Tracer.trace/2
    (elixir 1.15.7) src/elixir_env.erl:30: :elixir_env."-trace/2-lc$^0/1-0-"/3
    (elixir 1.15.7) src/elixir_env.erl:30: :elixir_env.trace/2
could not compile dependency :bcrypt_elixir, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile bcrypt_elixir --force", update it with "mix deps.update bcrypt_elixir" or clean it with "mix deps.clean bcrypt_elixir"

Apparently, it does not find Bcrypt.

scohen commented 2 months ago

@bigardone unless you have a non-traditional project structure, or you're reading a file in mix.exs, yours is likely a different issue

scohen commented 2 months ago

@bigardone I tried a simple project with {:bcrypt, "> 0.0.0"} in my mix.exs, and it compiles correctly, can you share the elixir and erlang version as well as your editor?

bigardone commented 2 months ago

Hi @scohen, thanks for the response 🙌🏼 The dependency I'm using is {:bcrypt_elixir, "~> 3.0"}... Here's a test project featuring the issue https://github.com/bigardone/lexical-issue And here are the versions I'm using:

elixir 1.16.2-otp-26   
erlang 26.1.2  

Editor-wise, I'm using Neovim

Should I open a new issue?

scohen commented 2 months ago

@bigardone yes, please open a new issue.

scohen commented 2 months ago

@bigardone with the addition of a .tool-versions file, I was able to get your example project to compile

@AndrewDryga I'm not familiar with vscode and couldn't find anything about how to set the Project Dir. Would you mind popping into our discord or providing a minimal project with vscode config that shows the issue?

bigardone commented 2 months ago

@scohen After reinstalling Erlang, it started working again 🙌🏼

AndrewDryga commented 1 month ago

@scohen I would love to help, which discord server should I join?

Here is how you change this setting:

  1. Open the command palette (CMD + Shift + P on MacOS)
  2. Select "Open Workspace Settings"
  3. Find Lexical: Project Dir settings and change it
Screenshot 2024-05-06 at 15 11 38

We only have to do this because we have multi-language mono repo and all Elixir stuff is not in the git root but in elixir/.

scohen commented 1 month ago

@AndrewDryga this is our server

Thanks for the instructions, I'll investigate now.

scohen commented 1 month ago

@AndrewDryga This is actually a sympathetic bug between your app, the elixir compiler and lexical. When lexical builds your project, it emits a series of messages, and these messages need to have a common prefix so that the editor displays them correctly. The prefix is "Building <your project>". The <your project> part is defined by calling Project.display_name(project) which in turn queries the project config.

Now here's where it gets fun. The message is created in a compile tracer, and the elixir compiler changes directories when compiling dependencies. This is where you get the crash, as the relative path doesn't exist in your dependencies.

The fix is simple enough, make the path absolute, static, and based off your mix.exs's path

defmodule Firezone.MixProject do
@version "VERSION" |> File.read!() |> String.trim()

def project do 
  [name: :firezone, 
   apps_path: "apps",
   version: @version
   ...

After making the above change, I was able to compile your app just fine.

AndrewDryga commented 1 month ago

@scohen hm, this is a tricky one: fixing it for our project is easy but I feel like it can backfire Lexical later once that File.read!/1 is in a dependency developer can't control.

scohen commented 1 month ago

Using relative file paths in your mix.exs is a bad practice if you want things to behave consistently. The mix project config can be loaded whenever, and by whomever, assuming that this loading always happens inside the current directory is an incorrect assumption.

Fixing this in lexical would involve some kind of caching, which would mean that updating your project config would require a restart of the language server.