Packages
nova
0.1.0
0.15.1
0.14.3
0.14.1
0.13.7
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.11
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.4.4
0.4.3
0.4.2
0.4.1
0.2.2
0.1.0
0.0.1
0.0.0
Nova is a web application framework
Current section
Files
Jump to
Current section
Files
src/nova_compiler.erl
%%%-------------------------------------------------------------------
%%% @author Niclas Axelsson <niclas@burbas.se>
%%% @copyright (C) 2018, Niclas Axelsson
%%% @doc
%%%
%%% @end
%%% Created : 28 Jun 2018 by Niclas Axelsson <niclas@burbas.se>
%%%-------------------------------------------------------------------
-module(nova_compiler).
-behaviour(gen_server).
%% API
-export([
start_link/0,
compile_all/0,
compile_all/1,
do_compile/1,
get_views/0,
recompile_view/1
]).
%% gen_server callbacks
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-define(SERVER, ?MODULE).
-record(state, {
compiled_modules
}).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
compile_all(App) ->
gen_server:cast(?SERVER, {compile_app_views, App}).
compile_all() ->
Apps =
case application:get_env(nova_applications) of
undefined -> [];
{ok, AppList} -> AppList
end,
lists:foreach(fun compile_all/1, Apps),
ok.
get_views() ->
gen_server:call(?SERVER, get_views).
recompile_view(Viewname) ->
gen_server:call(?SERVER, {recompile, Viewname}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
compile_all(),
process_flag(trap_exit, true),
{ok, #state{compiled_modules = #{}}}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @spec handle_call(Request, From, State) ->
%% {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(get_views, _From, State = #state{compiled_modules = CM}) ->
{reply, {ok, CM}, State};
handle_call({recompile, Viewname}, _From, State = #state{compiled_modules = CM}) ->
case maps:get(Viewname, CM, undefined) of
undefined ->
logger:warning("Tried to recompile a module (~p) that does not exists?", [Viewname]),
{reply, {error, view_not_found}, State};
File ->
do_compile([File]),
{reply, ok, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast({compile_app_views, App}, State = #state{compiled_modules = CM}) ->
CM2 =
case compile_files(App) of
error ->
CM;
ModMap ->
maps:merge(ModMap, CM)
end,
{noreply, State#state{compiled_modules = CM2}};
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
compile_files(#{name := App}) ->
case code:lib_dir(App, src) of
{error, _} ->
logger:warning("Could not find the src directory of app ~p", [App]),
error;
Filepath ->
{ok, FilesAndDirs} = file:list_dir(Filepath),
Result = [ do_compile(filename:join([Filepath, FileOrDir])) || FileOrDir <- FilesAndDirs ],
maps:from_list(lists:flatten(Result))
end.
do_compile(File) ->
BeamDir =
case application:get_application() of
{ok, App} ->
EbinDir = code:lib_dir(App, ebin),
filelib:ensure_dir(EbinDir),
EbinDir;
_ ->
%% We're running inside an unknown application - just put it in somewhere (This is ugly)
[Dir|_] = code:get_path(),
Dir
end,
case filelib:is_dir(File) of
true ->
{ok, SubFiles} = file:list_dir(File),
[ do_compile(filename:join(File, SubFile)) || SubFile <- SubFiles ];
_ ->
case filename:extension(File) of
".dtl" ->
ModName = list_to_atom(filename:basename(File, ".dtl") ++ "_dtl"),
case erlydtl:compile_file(File, ModName, [{out_dir, BeamDir}]) of
{ok, Module} ->
logger:info("Compiled dtl view: ~p", [Module]);
{ok, Module, Binary} when is_binary(Binary) ->
logger:info("Compiled dtl view: ~p", [Module]);
{ok, Module, Warnings} ->
logger:warning("Compiled dtl view: ~p with warnings: ~p", [Module, Warnings]);
{ok, Module, _Binary, Warnings} ->
logger:warning("Compiled dtl view: ~p with warnings: ~p", [Module, Warnings])
end,
{ModName, File};
".erl" ->
case compile:file(File, [{out_dir, BeamDir}]) of
{error, Errors, Warnings} ->
logger:warning("Got error when compiling ~p. Errors: ~p, Warnings: ~p", [File, Errors, Warnings]),
[];
error ->
logger:warning("Could not compile file ~p", [File]),
[];
{ok, ModuleName, Warnings} ->
logger:info("Compiled erlang module from file: ~p with warnings: ~p", [File, Warnings]),
{ModuleName, File};
{ok, ModuleName} ->
logger:info("Compiled erlang module from file: ~p to dir: ~p", [File, BeamDir]),
{ModuleName, File}
end;
_ ->
%% Not supported file
[]
end
end.