rustyio / sync

On-the-fly recompiling and reloading in Erlang. Code without friction.
MIT License
749 stars 163 forks source link

Elixir infinite loop #56

Closed deadtrickster closed 7 years ago

deadtrickster commented 9 years ago

I'm using sync and elixir in an erlang.mk based project. Elixir declared as dependency and lives in ./deps. Shortly after start Sync enters infinite loop constantly recompiling elixir sources. Execution flow in maybe_recompile_src_file goes straight to _ -> case branch because Module in case of Elixir files is always 'filename.ex' while by Elixir convention modules named as Elixir.<module name from defmodule>. If I understand correrctly the idea behind sync is that you can infer module name from erl file because files usually named after modules. Maybe in Elixir case things are different.

Or maybe I'm just missing something :-)

homeway commented 9 years ago

+1

iex(9)> :sync.go
Starting Sync (Automatic Code Compiler / Reloader)
Scanning source files...
:ok
iex(10)> Scanning source files...

16:15:54.913 [error] GenServer :sync_scanner terminating
Last message: {:"$gen_cast", :discover_src_dirs}
State: {:state, [Logger.Utils, MatchError, ErlangError, String.Chars.List, Exception, Inspect.Reference, Inspect.Integer, Inspect.List, Inspect.Tuple, Logger.Translator, :sync_notify, :sync_utils, :sync_scanner, :sync_options, :sync, IEx.History.State, Inspect.Atom, Node, IEx.History, IEx.Helpers, IEx.Evaluator, List.Chars.Atom, Mix.Tasks.Compile.App, Atom, Project1, UUID, Kernel.ErrorHandler, Kernel.ParallelCompiler, Mix.Compilers.Elixir, Mix.Tasks.Compile.Elixir, List.Chars.List, Mix.Tasks.Compile.Erlang, Mix.Tasks.Compile.Leex, Mix.Compilers.Erlang, Mix.Tasks.Compile.Yecc, Mix.Tasks.Compile.All, Mix.Tasks.Compile, Mix.Shell, Mix.Shell.IO, Inspect.BitString, Inspect, Inspect.Algebra, Inspect.Opts, Mix.Tasks.Deps.Loadpaths, Mix.Tasks.Deps.Compile, String.Unicode, Access.Map, Hex.Registry, ...], [], [], [], [], :undefined, [], [], [discover_modules: {1430640984666206, #Reference<0.0.0.22299>}], false, false}
** (MatchError) no match of right hand side value: :undefined
    (sync) src/sync_scanner.erl:729: anonymous fn/2 in :sync_scanner.discover_source_dirs/2
    (stdlib) lists.erl:1261: :lists.foldl/3
    (sync) src/sync_scanner.erl:739: :sync_scanner.discover_source_dirs/2
    (stdlib) gen_server.erl:593: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:659: :gen_server.handle_msg/5
    (stdlib) proc_lib.erl:237: :proc_lib.init_p_do_apply/3
choptastic commented 9 years ago

Thanks for posting and confirming (sorry for the delayed response).

Since it seems you have a decent grasp of the possible problems, any chance you'd like to try your hand at posting a fix? I've not used Elixir yet, so my including elixir support is currently based 100% on trusting those who submit the relevant pull requests.

deadtrickster commented 9 years ago

@choptastic Question: how sync handles src file modifications? does it have something like a map <SrcFile -> LastMod>?

mattiasw2 commented 8 years ago

I just tested, and to me it works very well. Thank you!

http://stackoverflow.com/questions/32540703/make-elixir-app-recompile-and-reload-on-source-code-change/36019510#36019510

formido commented 8 years ago

On my machine it loops. I can't see how it could be working. On first run, maybe_recompile_src_file/3 is called on each found source file:

https://github.com/rustyio/sync/blob/master/src/sync_scanner.erl#L461

This code is:

maybe_recompile_src_file(File, LastMod, EnablePatching) ->
    Module = list_to_atom(filename:basename(File, ".erl")),
    case code:which(Module) of
        BeamFile when is_list(BeamFile) ->
            %% check with beam file
            case filelib:last_modified(BeamFile) of
                BeamLastMod when LastMod > BeamLastMod ->
                    recompile_src_file(File, EnablePatching);
                _ ->
                    ok
            end;
        _ ->
            %% File is new, recompile...
            recompile_src_file(File, EnablePatching)
    end.

It tries to find the source's beam file, but it's only matching .erl files. A second problem is that it locates a beam file of the same name as the source, but the beam files Elixir compiles do not match the names of the source files. Because of both of these things, the first clause always fails and recompile_src_file is called every time a rescan has been scheduled by sync.

deadtrickster commented 7 years ago

Not following for a while, was this fixed? :confused:

SkipMike commented 7 years ago

@deadtrickster I just tried this today and had an infinite loop.

maximvl commented 5 years ago

@deadtrickster I can reproduce loop as well having an Elixir librabry in my Erlang project.

maximvl commented 5 years ago

There is a workaround - to exclude all Elixir modules if they are not changed often, until it's fixed:

{sync, [{excluded_modules, ["^Elixir."]}]}