123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- -module(active).
- -behaviour(gen_server).
- -export([
- start_link/0,
- init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3
- ]).
- -record(state, {last, root}).
- start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
- init([]) ->
- case {application:get_env(fs, backwards_compatible, false),
- application:get_env(active, listen_paths, [])} of
- %{true, []} -> fs:subscribe();
- {true, _} ->
- fs:subscribe();
- {false, []} ->
- fs:start_link(default_fs),
- fs:subscribe();
- {false, Paths} ->
- lists:foldl(fun(Path, ok)->
- Name = erlang:list_to_atom(Path),
- fs:start_link(Name, Path),
- fs:subscribe(Name),
- ok
- end, ok, Paths)
- end,
-
- erlang:process_flag(priority, low),
- gen_server:cast(self(), recompile_all),
- {ok, #state{last=fresh, root=fs:path()}}.
- handle_call(_Request, _From, State) ->
- {reply, ok, State}.
- handle_cast(recompile_all, State) ->
- compile(top(), ["all"], []),
- {noreply, State}.
- handle_info({_Pid, {fs, file_event}, {Path, Flags}}, #state{root = Root} = State) ->
- Cur = path_shorten(filename:split(Root)),
- P = filename:split(Path),
- Result = case lists:prefix(Cur, P) of
- true ->
- path_event(P -- Cur, Flags, State, Path);
- false -> ok
- end,
- {noreply, State#state{last = {event, Path, Flags, Result}} };
- handle_info({load_ebin, Atom}, State) ->
- do_load_ebin(Atom),
- {noreply, State#state{last = {do_load_ebin, Atom}}};
- handle_info(Info, State) ->
- {noreply, State#state{last = {unk, Info}}}.
- terminate(_Reason, _State) -> ok.
- code_change(_OldVsn, State, _Extra) -> {ok, State}.
- path_event(C, [E|_], _, Path) when E =:= created; E =:= modified; E =:= renamed ->
- case path_filter(C) of
- true -> maybe_otp(C, Path);
- false -> ignore
- end;
-
- path_event(C, [_E|Events], State, Path) ->
- path_event(C, Events, State, Path);
-
- path_event(_, [], _, _) -> done.
- maybe_otp(C, Path) ->
- case application:get_env(active, handler) of
- {ok, {M, F}} -> M:F(C);
- _ -> otp(C, Path)
- end.
- otp([Some, App|Rest], Path) when Some == "deps" orelse Some == "apps" ->
- maybe_app(App, Rest, Path);
- otp([Some|Rest], Path) ->
- maybe_app(top(), [Some|Rest], Path);
- otp(_, _) ->
- ok.
- maybe_app(App, SplitPath, Path) ->
- EnabledApps = application:get_env(active, apps, []),
- case EnabledApps of
- [] ->
- app(App, SplitPath, Path);
-
- {ok, L} when erlang:is_list(L) ->
- AppAtom = erlang:list_to_atom(App),
- case lists:member(AppAtom, L) of
- true ->
- app(App, SplitPath, Path);
- false -> skip
- end
- end.
- app(App, ["ebin", Module|_], _) -> load_ebin(App, Module);
- app(_App, ["priv", "fdlink" ++ _], _) -> skip;
- app(_App, ["priv", "mac" ++ _], _) -> skip;
- app(_App, ["priv", "windows" ++ _], _) -> skip;
- app(_App, ["priv", "linux" ++ _], _) -> skip;
- app(App, ["priv", "static"|_Rest], Path) ->
- compile_skip(compile_on_static, App, _Rest, Path);
- app(App, ["priv", "templates"|Rest], Path) ->
- compile_skip(compile_on_templates, App, Rest, Path);
- app(App, ["priv"|Rest], Path) ->
- compile_skip(compile_on_priv, App, Rest, Path);
- app(App, ["include"|Rest], Path) ->
- compile(App, Rest, Path);
- app(App, ["src"|Rest], Path) ->
- compile(App, Rest, Path);
- app(App, ["lib"|Rest], Path) ->
- compile(App, Rest, Path);
- app(_, _, _)-> ok.
- compile_skip(Key, App, Rest, Path) ->
- case application:get_env(active, Key, false) of
- false -> skip;
- _ -> compile(App, Rest, Path)
- end.
- top() -> lists:last(filename:split(fs:path())).
- compile(_App, _, []) -> ok;
- compile(_App, [], _Path) -> ok;
- compile(App, Rest, Path) ->
- case lists:last(Rest) of
- ".#" ++ _ -> skip;
- _ ->
- try
- erlang:put(App, updated),
- {M, F} = application:get_env(active, compile, {mad_compile, compile}),
- M:F(App, Path)
- catch E:R ->
- io:format("~p", [erlang:get_stacktrace()]),
- io:format("Catch: ~p:~p", [E, R])
- end
- end.
- load_ebin(_App, EName) ->
- case lists:reverse(EName) of
- "maeb." ++ Tail ->
- Name = lists:reverse(Tail),
- LoadRes = do_load_ebin(erlang:list_to_atom(lists:flatten(Name))),
- io:format("Active: module loaded: ~p~n\n\r", [LoadRes]),
- active_events:notify_reload(LoadRes);
- "#aeb." ++ _ -> ok;
- _ ->
- io:format("Active: unknown BEAM file: ~p", [EName]), ok
- end.
- do_load_ebin(Module) ->
- IsLoaded = case code:is_loaded(Module) of
- {file, _} ->
- true;
- false ->
- false
- end,
- {Module, Binary, Filename} = code:get_object_code(Module),
- case code:load_binary(Module, Filename, Binary) of
- {module, Module} when IsLoaded->
- {reloaded, Module};
- {module, Module} when not IsLoaded ->
- {loaded_new, Module};
- {error, Reason} ->
- {load_error, Module, Reason}
- end.
- %% ["a", "b", ".."] -> ["a"]
- path_shorten(Coms) ->
- path_shorten_r(lists:reverse(Coms), [], 0).
- path_shorten_r([".."|Rest], Acc, Count) ->
- path_shorten_r(Rest, Acc, Count + 1);
-
- path_shorten_r(["."|Rest], Acc, Count) ->
- path_shorten_r(Rest, Acc, Count);
-
- path_shorten_r([_C|Rest], Acc, Count) when Count > 0 ->
- path_shorten_r(Rest, Acc, Count - 1);
-
- path_shorten_r([C|Rest], Acc, 0) ->
- path_shorten_r(Rest, [C|Acc], 0);
-
- path_shorten_r([], Acc, _) ->
- Acc.
- path_filter(L) ->
- not lists:any(fun(E) -> not path_filter_dir(E) end, L)
- andalso path_filter_file( lists:last(L) )
- andalso path_filter_ext( ext(L) ).
- ext(L) -> filename:extension( lists:last(L) ).
- path_filter_dir(".git") -> false;
- path_filter_dir(".hg") -> false;
- path_filter_dir(".svn") -> false;
- path_filter_dir("CVS") -> false;
- %path_filter_dir("ebin") -> false;
- path_filter_dir("build") -> false;
- path_filter_dir("_build") -> false;
- path_filter_dir("log") -> false;
- path_filter_dir("node_modules") -> false;
- path_filter_dir(_) -> true.
- path_filter_file(".rebarinfo") -> false;
- path_filter_file("LICENSE") -> false;
- path_filter_file("4913 (deleted)") -> false; %% vim magical file
- path_filter_file("4913") -> false;
- path_filter_file(_) -> true.
- path_filter_ext(".app") -> false;
- path_filter_ext(".jpg") -> false;
- path_filter_ext(".png") -> false;
- path_filter_ext(".gif") -> false;
- path_filter_ext(_) -> true.
|