Просмотр исходного кода

compiling without mad, with Emakefile

221V 3 лет назад
Родитель
Сommit
05d9dc7690
5 измененных файлов с 347 добавлено и 15 удалено
  1. 1 1
      ebin/active.app
  2. 24 13
      src/active.erl
  3. 252 0
      src/active_compile_files.erl
  4. 68 0
      src/active_emakefile.erl
  5. 2 1
      src/active_sup.erl

+ 1 - 1
ebin/active.app

@@ -3,7 +3,7 @@
   {vsn, "6.1.1"},
   {registered, [active_sup]},
   {applications, [kernel, stdlib, fs]},
-  {modules, [active_app, active_events, active_sh, active_sup, active]},
+  {modules, [active_app, active_compile_files, active_emakefile, active_events, active_sh, active_sup, active]},
   {mod, { active_app, []}},
   {env, []}
  ]}.

+ 24 - 13
src/active.erl

@@ -11,7 +11,7 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 init([]) ->
   fs:subscribe(),
   erlang:process_flag(priority, low),
-  gen_server:cast(self(), recompile_all),
+  gen_server:cast(erlang:self(), recompile_all),
   {ok, #state{last=fresh, root=fs:path()}}.
 
 handle_call(_Request, _From, State) -> {reply, ok, State}.
@@ -68,8 +68,8 @@ maybe_app(App, SplitPath, Path) ->
     [] ->
       app(App, SplitPath, Path);
     
-    {ok, L} when is_list(L) ->
-      AppAtom = list_to_atom(App),
+    {ok, L} when erlang:is_list(L) ->
+      AppAtom = erlang:list_to_atom(App),
       case lists:member(AppAtom, L) of
         true ->
           app(App, SplitPath, Path);
@@ -82,8 +82,10 @@ 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", "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);
@@ -95,7 +97,7 @@ compile_skip(Key, App, Rest, Path) ->
   case application:get_env(active, Key, false) of
     false -> skip;
     _ -> compile(App, Rest, Path)
-  end;
+  end.
 
 top() -> lists:last(filename:split(fs:path())).
 
@@ -105,14 +107,23 @@ compile(_App, [], _Path) -> ok;
 compile(App, Rest, Path) ->
   case lists:last(Rest) of
     ".#" ++ _ -> skip;
+    "." ++ _ -> skip;
     _ ->
       try
-        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])
+        %erlang:put(App, updated),
+        %{M, F} = application:get_env(active, compile, {mad_compile, compile}), % no mad usage
+        %M:F(App, Path)
+        
+        %io:format("active:compile:::App, Rest, Path ~n ~p~n ~p~n ~p~n", [App, Rest, Path]),
+        RZ = gen_server:call(active_emakefile, {get_data, App}),
+        %io:format("active:compile:::gen_server call ~n ~p~n", [RZ]),
+        case RZ of
+          undefined -> skip;
+          {H, S} -> active_compile_files:do_compile(Rest, Path, H, S)
+        end
+        
+      catch C:E:Stk ->
+        io:format("active:compile Catch: ~p:~p~n ~p~n", [C, E, Stk])
       end
   end.
 
@@ -120,7 +131,7 @@ load_ebin(_App, EName) ->
   case lists:reverse(EName) of
     "maeb." ++ Tail ->
       Name = lists:reverse(Tail),
-      LoadRes = do_load_ebin(list_to_atom(lists:flatten(Name))),
+      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;

+ 252 - 0
src/active_compile_files.erl

@@ -0,0 +1,252 @@
+-module(active_compile_files).
+
+-export([do_compile/4, compile_one_erl/3, compile_all_dtl/2, compile_one_dtl/4]).
+
+
+%do_compile(Rest, Path, Emakefile_Paths, Emakefile_Settings) ->
+do_compile([File], Path, Emakefile_Paths, Emakefile_Settings) ->
+  case lists:reverse(File) of
+    "lre." ++ _ ->
+      % .erl
+      
+      % get path like in Emakefile and compare
+      RP = filename:absname(""),
+      % erlang:tl(lists:flatten(string:replace("/home/user/my_app/deps/active/src/active.erl", "/home/user/my_app", ""))) =
+      %   "deps/active/src/active.erl"
+      % lists:flatten(string:replace("deps/active/src/active.erl", "active.erl", "")) = 
+      %   "deps/active/src/"
+      Path1 = erlang:tl(lists:flatten(string:replace(Path, RP, ""))),
+      Path2 = lists:flatten(string:replace(Path1, File, "")),
+      
+      Is_In = is_in_emakefile_paths(Emakefile_Paths, Path1, lists:reverse(Path2)),
+      
+      case Is_In of
+        true -> compile_one_erl(File, Path2, Emakefile_Settings);
+        _ -> skip
+      end;
+      
+    "ltd." ++ _ ->
+      % .dtl
+      
+      RP = filename:absname(""),
+      File_Path = erlang:tl(lists:flatten(string:replace(Path, RP, ""))),
+      Path2 = lists:flatten(string:replace(File_Path, File, "")),
+      Ebin_Dir = get_emakefile_param(Emakefile_Settings, outdir),
+      
+      compile_one_dtl(File_Path, Path2, Ebin_Dir, ".dtl");
+      
+    "lmth." ++ _ ->
+      % .html = .dtl
+      
+      RP = filename:absname(""),
+      File_Path = erlang:tl(lists:flatten(string:replace(Path, RP, ""))),
+      Path2 = lists:flatten(string:replace(File_Path, File, "")),
+      Ebin_Dir = get_emakefile_param(Emakefile_Settings, outdir),
+      
+      compile_one_dtl(File_Path, Path2, Ebin_Dir, ".html");
+      
+    "lrx." ++ _ ->
+      % .xrl
+      
+      RP = filename:absname(""),
+      File_Path = erlang:tl(lists:flatten(string:replace(Path, RP, ""))),
+      
+      io:format("~nCompiling ~p~n", [File_Path]),
+      leex:file(File_Path),
+      ok;
+      
+    "lry." ++ _ ->
+      % .yrl
+      
+      RP = filename:absname(""),
+      File_Path = erlang:tl(lists:flatten(string:replace(Path, RP, ""))),
+      
+      io:format("~nCompiling ~p~n", [File_Path]),
+      yecc:file(File_Path),
+      ok;
+      
+    _ -> skip
+  end.
+
+is_in_emakefile_paths([], _, _) -> false;
+%is_in_emakefile_paths(Emakefile_Paths, Path1, Reversed_Path2) ->
+is_in_emakefile_paths([H|T], Path1, Reversed_Path2) ->
+  V = lists:reverse(H) =:= ("*" ++ Reversed_Path2),
+  case V of
+    true -> true;
+    _ ->
+      V2 = Path1 =:= H,
+      case V2 of
+        true -> true;
+        _ -> is_in_emakefile_paths(T, Path1, Reversed_Path2)
+      end
+  end.
+
+
+compile_one_erl(File, Path2, Emakefile_Settings) ->
+  Inc_Dir = get_emakefile_param(Emakefile_Settings, i),
+  Ebin_Dir = get_emakefile_param(Emakefile_Settings, outdir),
+  
+  File_Path = filename:join(Path2, File),
+  BeamName = beam_path_name2(Ebin_Dir, File),
+  
+  case is_compiled(BeamName, File_Path) of
+    false ->
+      % todo : Options debug_info etc
+      Options = if Inc_Dir =:= undefined ->
+          [report, return, {outdir, Ebin_Dir}];
+        true ->
+          [report, return, {i, [Inc_Dir]}, {outdir, Ebin_Dir}]
+      end,
+      
+      io:format("~nCompiling ~p: ", [File_Path]),
+      
+      case compile:file(File_Path, Options) of
+        error ->
+          io:format("~nError!~n", []);
+        {error, [], Warning} ->
+          io:format("~nWarning: ~p~n", [Warning]);
+        {error, Error, Warning} ->
+          io:format("~nErrors and warnings:~n ~p~n ~p~n", [Error, Warning]);
+        _ ->
+          io:format("ok~n", []),
+          Module = erlang:list_to_atom(lists:flatten(string:replace(File, ".erl", ""))),
+          code:purge(Module),
+          code:load_file(Module)
+      end,
+      ok;
+    _ ->
+      ok
+  end.
+
+%get_emakefile_param(Emakefile_Settings, Param) ->
+get_emakefile_param([], _) -> undefined;
+get_emakefile_param([{K, V}|T], Param) ->
+  case K =:= Param of
+    true -> V;
+    _ -> get_emakefile_param(T, Param)
+  end;
+get_emakefile_param([H|T], Param) ->
+  case H =:= Param of
+    true -> H;
+    _ -> get_emakefile_param(T, Param)
+  end.
+
+
+%{erlydtl_opts, [
+%  {doc_root,   "priv/templates"},
+%  {out_dir,    "ebin"},
+%  {compiler_options, [report, return, debug_info]},
+%  {auto_escape, false},
+%  {source_ext, ".html"},
+%  {module_ext, "_view"}
+%]}
+
+% [ {doc_root, "priv/templates"}, {out_dir, "ebin"}, {compiler_options, [report, return, debug_info]}, {auto_escape, false}, {source_ext, ".html"}, {module_ext, "_view"} ]
+
+dtl_opts(DocRoot, OutDir, SourceExt, ModuleExt) ->
+  [ {doc_root, DocRoot}, {out_dir, OutDir}, {source_ext, SourceExt}, {module_ext, ModuleExt}, {auto_escape, false},
+    {compiler_options, [report, return]} ].
+    %{compiler_options, [report, return, debug_info]} ].
+
+
+compile_all_dtl(DTL_Dir, Ebin_Dir) ->
+  %DocRoot = "priv/templates",
+  DocRoot = DTL_Dir,
+  %OutDir = "ebin",
+  OutDir = Ebin_Dir,
+  SourceExt = ".html",
+  ModuleExt = "_view",
+  
+  
+  %https://erlang.org/doc/man/filelib.html#fold_files-5
+  %fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut
+  
+  Files = filelib:fold_files(DocRoot, SourceExt, true,
+    fun(F, Acc) ->
+      [F|Acc]
+    end, []),
+  
+  Opts = dtl_opts(DocRoot, OutDir, SourceExt, ModuleExt),
+  
+  io:format("DTL compiling:~n", []),
+  
+  lists:foldl(
+    fun(F, _Acc) ->
+      ModuleName = module_name(F, SourceExt, ModuleExt),
+      BeamName = beam_path_name(OutDir, erlang:atom_to_list(ModuleName)),
+      case is_compiled(BeamName, F) of
+        false ->
+          case erlydtl:compile(F, ModuleName, Opts) of
+            {error, Error} ->
+              io:format("Error: ~p~n", [Error]);
+            _ ->
+              file:change_time(BeamName, calendar:local_time()),
+              io:format("~p: ok~n", [F])
+          end;
+        _ ->
+          ok
+      end,
+      
+      []
+    end, [], Files),
+  [].
+
+
+compile_one_dtl(File_Path, Path, Ebin_Dir, SourceExt) ->
+  %DocRoot = "priv/templates",
+  DocRoot = Path,
+  %OutDir = "ebin",
+  OutDir = Ebin_Dir,
+  %SourceExt = ".html", % ".html" | ".dtl"
+  ModuleExt = "_view",
+  
+  Opts = dtl_opts(DocRoot, OutDir, SourceExt, ModuleExt),
+  
+  ModuleName = module_name(File_Path, SourceExt, ModuleExt),
+  BeamName = beam_path_name(OutDir, erlang:atom_to_list(ModuleName)),
+  
+  case is_compiled(BeamName, File_Path) of
+    false ->
+      io:format("~nCompiling ~p: ", [File_Path]),
+      
+      case erlydtl:compile(File_Path, ModuleName, Opts) of
+        {error, Error} ->
+          io:format("~nError: ~p~n", [Error]);
+        _ ->
+          file:change_time(BeamName, calendar:local_time()),
+          io:format("ok~n", [])
+      end;
+    _ ->
+      ok
+  end,
+  ok.
+
+
+% https://erlang.org/doc/man/filename.html#basename-2
+% > filename:basename("src/test.txt", ".txt").
+% "test"
+module_name(File, SourceExt, ModuleExt) ->
+  erlang:list_to_atom(filename:basename(File, SourceExt) ++ ModuleExt).
+
+
+beam_path_name(OutDir, ModuleName) ->
+  filename:join(OutDir, filename:basename(ModuleName) ++ ".beam").
+
+
+beam_path_name2(OutDir, File) ->
+  filename:join(OutDir, filename:basename(File, ".erl") ++ ".beam").
+
+
+is_compiled(BeamFile, File) ->
+  last_modified(BeamFile) >= last_modified(File).
+
+
+last_modified(File) ->
+  case filelib:last_modified(File) of
+    0 -> 0;
+    N ->
+      calendar:datetime_to_gregorian_seconds(N)
+  end.
+
+

+ 68 - 0
src/active_emakefile.erl

@@ -0,0 +1,68 @@
+-module(active_emakefile).
+-behaviour(gen_server).
+
+-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
+
+% read and save Emakefile data
+
+
+start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+
+init([]) ->
+  erlang:self() ! init,
+  State = [],
+  {ok, State}.
+
+
+%handle_call(_Request, _From, State) -> {reply, ok, State}.
+handle_call({get_data, App_Name}, _From, State) ->
+  App_Data = get_app_data(State, App_Name),
+  {reply, App_Data, State}.
+
+handle_cast(_Request, State) -> {reply, ok, State}.
+
+handle_info(init, _State0) ->
+  State = emakefile_to_data(),
+  {noreply, State};
+handle_info(_Request, State) -> {noreply, State}.
+
+terminate(_Reason, _State) -> ok.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+
+
+emakefile_to_data() ->
+  Emakefile = filename:absname("") ++ "/Emakefile",
+  {ok, Data} = file:consult(Emakefile),
+  % add app name to Data structure, wrap to list if path is string --
+  %            [{"deps/ranch/src/*", [{outdir,"deps/ebin"}]}, ..] ->
+  % [{"ranch", ["deps/ranch/src/*"], [{outdir,"deps/ebin"}]}, ..]
+  add_app_names(Data, []).
+
+add_app_names([], Acc) -> lists:reverse(Acc);
+add_app_names([{H, S}|T], Acc) ->
+  %Is_String = erlang:is_list(H) andalso H /= [] andalso erlang:is_integer(erlang:hd(H)),
+  Is_String = erlang:is_integer(erlang:hd(H)),
+  case Is_String of
+    true ->
+      App_Name = lists:nth(2, string:split(H, "/", all)),
+      H2 = [H];
+    _ ->
+      App_Name = lists:nth(2, string:split(erlang:hd(H), "/", all)),
+      H2 = H
+  end,
+  add_app_names(T, [{App_Name, H2, S}|Acc]).
+
+
+get_app_data([], _) -> undefined; % not found
+%get_app_data(State, App_Name) ->
+get_app_data([{App, H, S}|T], App_Name) ->
+  case App =:= App_Name of
+    true ->
+      {H, S};
+    _ ->
+      get_app_data(T, App_Name)
+  end.
+

+ 2 - 1
src/active_sup.erl

@@ -8,7 +8,8 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
 init([]) ->
   {ok, { {one_for_one, 5, 10}, 
          [ {active, {active, start_link, []}, permanent, 5000, worker, [active]},
-           {active_events, {active_events, start_link, []}, permanent, 5000, worker, [active_events]}, 
+           {active_events, {active_events, start_link, []}, permanent, 5000, worker, [active_events]},
+           {active_emakefile, {active_emakefile, start_link, []}, permanent, 5000, worker, [active_emakefile]}
          ]
   }}.