/modules/mod_development/z_development_server.erl

https://code.google.com/p/zotonic/ · Erlang · 188 lines · 82 code · 35 blank · 71 comment · 3 complexity · 0536a4ca94b2211255cf56788169ccc0 MD5 · raw file

  1. %% @author Marc Worrell <marc@worrell.nl>
  2. %% @copyright 2009 Marc Worrell
  3. %% Date: 2009-11-01
  4. %%
  5. %% @doc Development server. Periodically loads modules whose beam file have been updated.
  6. %% This server is started by the mod_development.
  7. %% Copyright 2009 Marc Worrell
  8. %%
  9. %% Licensed under the Apache License, Version 2.0 (the "License");
  10. %% you may not use this file except in compliance with the License.
  11. %% You may obtain a copy of the License at
  12. %%
  13. %% http://www.apache.org/licenses/LICENSE-2.0
  14. %%
  15. %% Unless required by applicable law or agreed to in writing, software
  16. %% distributed under the License is distributed on an "AS IS" BASIS,
  17. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. %% See the License for the specific language governing permissions and
  19. %% limitations under the License.
  20. -module(z_development_server).
  21. -author("Marc Worrell <marc@worrell.nl>").
  22. -behaviour(gen_server).
  23. %% gen_server exports
  24. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
  25. -export([start_link/0, start_link/1]).
  26. %% interface functions
  27. -export([
  28. reload/0,
  29. make/0
  30. ]).
  31. -include_lib("zotonic.hrl").
  32. % The state record for this server
  33. -record(state, {}).
  34. % Interval for checking for new and/or changed files.
  35. -define(DEV_POLL_INTERVAL, 10000).
  36. %%====================================================================
  37. %% API
  38. %%====================================================================
  39. %% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
  40. %% @doc Starts the server
  41. start_link() ->
  42. start_link([]).
  43. start_link(Args) when is_list(Args) ->
  44. gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
  45. %% @doc Check if beam files are changed, load the changed ones.
  46. reload() ->
  47. gen_server:cast(?MODULE, reload).
  48. %% @doc Perform a make:all() and reload the changed beam files.
  49. make() ->
  50. gen_server:cast(?MODULE, make).
  51. %%====================================================================
  52. %% gen_server callbacks
  53. %%====================================================================
  54. %% @spec init(Args) -> {ok, State} |
  55. %% {ok, State, Timeout} |
  56. %% ignore |
  57. %% {stop, Reason}
  58. %% @doc Initiates the server.
  59. init(_Args) ->
  60. timer:send_interval(?DEV_POLL_INTERVAL, reload_beams),
  61. {ok, #state{}}.
  62. %% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
  63. %% {reply, Reply, State, Timeout} |
  64. %% {noreply, State} |
  65. %% {noreply, State, Timeout} |
  66. %% {stop, Reason, Reply, State} |
  67. %% {stop, Reason, State}
  68. %% @doc Trap unknown calls
  69. handle_call(Message, _From, State) ->
  70. {stop, {unknown_call, Message}, State}.
  71. %% @spec handle_cast(Msg, State) -> {noreply, State} |
  72. %% {noreply, State, Timeout} |
  73. %% {stop, Reason, State}
  74. handle_cast(reload, State) ->
  75. reload_all(),
  76. {noreply, State};
  77. handle_cast(make, State) ->
  78. make_all(),
  79. reload_all(),
  80. {noreply, State};
  81. %% @doc Trap unknown casts
  82. handle_cast(Message, State) ->
  83. {stop, {unknown_cast, Message}, State}.
  84. %% @spec handle_info(Info, State) -> {noreply, State} |
  85. %% {noreply, State, Timeout} |
  86. %% {stop, Reason, State}
  87. %% @doc Periodic check for changed beam files.
  88. handle_info(reload_beams, State) ->
  89. reload_all(),
  90. z_utils:flush_message(reload_beams),
  91. {noreply, State};
  92. %% @doc Handling all non call/cast messages
  93. handle_info(_Info, State) ->
  94. {noreply, State}.
  95. %% @spec terminate(Reason, State) -> void()
  96. %% @doc This function is called by a gen_server when it is about to
  97. %% terminate. It should be the opposite of Module:init/1 and do any necessary
  98. %% cleaning up. When it returns, the gen_server terminates with Reason.
  99. %% The return value is ignored.
  100. terminate(_Reason, _State) ->
  101. ok.
  102. %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
  103. %% @doc Convert process state when code is changed
  104. code_change(_OldVsn, State, _Extra) ->
  105. {ok, State}.
  106. %%====================================================================
  107. %% support functions
  108. %%====================================================================
  109. %% @doc Check if any of the loaded modules has been changed. If so reload the module's beam file. Also rescan for templates and files.
  110. reload_all() ->
  111. case reload_loaded_modules() of
  112. [] ->
  113. z_sites_manager:update_dispatchinfo(),
  114. [ z_module_indexer:reindex(C) || C <- z_sites_manager:get_site_contexts() ],
  115. ok;
  116. _ ->
  117. z:flush()
  118. end.
  119. %% @doc Remake beam files from source. Do not load the new files (yet).
  120. make_all() ->
  121. make:all([]),
  122. ok.
  123. %% @doc Reload a module, purge the old code.
  124. reload_module(M) ->
  125. code:purge(M),
  126. code:soft_purge(M),
  127. {module, M} = code:load_file(M),
  128. {ok, M}.
  129. %% @doc Reload all modules from the zotonic directory or subdirectories. Return a list of modules reloaded. Empty list when nothing changed.
  130. %% @spec reload_loaded_modules() -> [Result]
  131. reload_loaded_modules() ->
  132. Dir = z_utils:lib_dir(),
  133. Modules = [{M,P} || {M, P} <- code:all_loaded(), is_list(P) andalso string:str(P, Dir) > 0],
  134. [reload_module(M) || {M,Path} <- Modules, module_changed(M,Path)].
  135. %% @doc Check if the version number of the module has been changed. Skip template modules.
  136. %% @spec module_changed(atom(), filename()) -> bool()
  137. module_changed(Module, BeamFile) ->
  138. case z_template:is_template_module(Module) of
  139. true ->
  140. false;
  141. false ->
  142. Props = Module:module_info(attributes),
  143. case proplists:get_value(vsn, Props) of
  144. undefined ->
  145. false;
  146. Version ->
  147. case beam_lib:version(BeamFile) of
  148. {ok, {_Module, Version}} -> false;
  149. {ok, {_Module, _OtherVersion}} -> true;
  150. {error, _, _} -> false
  151. end
  152. end
  153. end.