|
@@ -0,0 +1,176 @@
|
|
|
+-module(mad_compile).
|
|
|
+-copyright('Sina Samavati').
|
|
|
+-compile(export_all).
|
|
|
+-define(COMPILE_OPTS(Inc, Ebin, Opts), [report, {i, Inc}, {outdir, Ebin}] ++ Opts).
|
|
|
+
|
|
|
+-type directory() :: string().
|
|
|
+-type filename() :: string().
|
|
|
+
|
|
|
+%% compile dependencies
|
|
|
+-spec deps(directory(), any(), filename(), [mad_deps:dependency()]) -> ok.
|
|
|
+deps(_, _, _, []) -> ok;
|
|
|
+deps(Cwd, Conf, ConfigFile, [H|T]) ->
|
|
|
+ {Name, _} = mad_deps:name_and_repo(H),
|
|
|
+ case get(mad_utils:to_atom(Name)) of
|
|
|
+ compiled -> ok;
|
|
|
+ _ -> dep(Cwd, Conf, ConfigFile, Name) end,
|
|
|
+ deps(Cwd, Conf, ConfigFile, T).
|
|
|
+
|
|
|
+%% compile a dependency
|
|
|
+-spec dep(directory(), any(), filename(), string()) -> ok.
|
|
|
+dep(Cwd, _Conf, ConfigFile, Name) ->
|
|
|
+ io:format("==> ~p~n",[Name]),
|
|
|
+ %% check dependencies of the dependency
|
|
|
+ DepsDir = filename:join([mad_utils:get_value(deps_dir, _Conf, ["deps"])]),
|
|
|
+ DepPath = filename:join([Cwd, DepsDir, Name]),
|
|
|
+ DepConfigFile = filename:join(DepPath, ConfigFile),
|
|
|
+ Conf = mad_utils:consult(DepConfigFile),
|
|
|
+ Conf1 = mad_utils:script(DepConfigFile, Conf),
|
|
|
+ deps(Cwd, Conf, ConfigFile, mad_utils:get_value(deps, Conf1, [])),
|
|
|
+
|
|
|
+ %% add lib_dirs to path
|
|
|
+ LibDirs = mad_utils:lib_dirs(DepPath, Conf1),
|
|
|
+ code:add_paths(LibDirs),
|
|
|
+
|
|
|
+ SrcDir = filename:join([mad_utils:src(DepPath)]),
|
|
|
+ Files = yrl_files(SrcDir) ++ erl_files(SrcDir) ++ app_src_files(SrcDir),
|
|
|
+
|
|
|
+ case Files of
|
|
|
+ [] -> ok;
|
|
|
+ Files ->
|
|
|
+ IncDir = mad_utils:include(DepPath),
|
|
|
+ EbinDir = mad_utils:ebin(DepPath),
|
|
|
+
|
|
|
+ %% create EbinDir and add it to code path
|
|
|
+ file:make_dir(EbinDir),
|
|
|
+ code:add_path(EbinDir),
|
|
|
+
|
|
|
+ Opts = mad_utils:get_value(erl_opts, Conf1, []),
|
|
|
+ lists:foreach(compile_fun(IncDir, EbinDir, Opts), Files),
|
|
|
+
|
|
|
+ dtl(DepPath,Conf1),
|
|
|
+
|
|
|
+ put(mad_utils:to_atom(Name), compiled),
|
|
|
+ ok
|
|
|
+ end.
|
|
|
+
|
|
|
+
|
|
|
+dtl(Dir,Config) ->
|
|
|
+ case mad_utils:get_value(erlydtl_opts, Config, []) of
|
|
|
+ [] -> skip;
|
|
|
+ X -> compile_erlydtl_files(validate_erlydtl_opts(Dir,X)) end.
|
|
|
+
|
|
|
+
|
|
|
+-spec validate_property({atom(), term()}, term()) -> {atom(), term()}.
|
|
|
+validate_property({modules, _}, Modules) -> {modules, Modules};
|
|
|
+validate_property(Else, _) -> Else.
|
|
|
+
|
|
|
+-spec compile_fun(directory(), directory(), [compile:option()]) ->
|
|
|
+ fun((file:name(),string(),string(),list(tuple(any(),any())),string()) -> ok).
|
|
|
+compile_fun(Inc,Bin,Opt) -> fun(File) -> compile(File,Inc,Bin,Opt,filetype(File)) end.
|
|
|
+
|
|
|
+filetype(File) -> L=length(hd(string:tokens(File,"."))), string:substr(File,L+1,length(File)).
|
|
|
+
|
|
|
+compile(File,Inc,Bin,Opt,".yrl") ->
|
|
|
+ ErlFile = yrl_to_erl(File),
|
|
|
+ Compiled = is_compiled(ErlFile,File),
|
|
|
+ if Compiled == false ->
|
|
|
+ yecc:file(File),
|
|
|
+ compile(ErlFile,Inc,Bin,Opt,".erl"); true -> ok end;
|
|
|
+compile(File,Inc,Bin,Opt,".erl") ->
|
|
|
+ BeamFile = erl_to_beam(Bin, File),
|
|
|
+ Compiled = is_compiled(BeamFile, File),
|
|
|
+ if Compiled =:= false ->
|
|
|
+ io:format("Compiling ~s~n", [File]),
|
|
|
+ Opts1 = ?COMPILE_OPTS(Inc, Bin, Opt),
|
|
|
+ compile:file(File, Opts1),
|
|
|
+ ok;
|
|
|
+ true -> ok end;
|
|
|
+compile(File,_Inc,Bin,_Opt,".app.src") ->
|
|
|
+ AppFile = filename:join(Bin, app_src_to_app(File)),
|
|
|
+ Compiled = is_compiled(AppFile, File),
|
|
|
+ if Compiled =:= false ->
|
|
|
+ io:format("Writing ~s~n", [AppFile]),
|
|
|
+ BeamFiles = filelib:wildcard("*.beam", Bin),
|
|
|
+ Modules = [list_to_atom(filename:basename(X, ".beam")) || X <- BeamFiles],
|
|
|
+ [Struct|_] = mad_utils:consult(File),
|
|
|
+ {application, AppName, Props} = Struct,
|
|
|
+ Props1 = add_modules_property(Props),
|
|
|
+ Props2 = [validate_property(X, Modules) || X <- Props1],
|
|
|
+ Struct1 = {application, AppName, Props2},
|
|
|
+ file:write_file(AppFile, io_lib:format("~p.~n", [Struct1])),
|
|
|
+ ok;
|
|
|
+ true -> ok end;
|
|
|
+compile(File,_Inc,_Bin,_Opt,_) ->
|
|
|
+ io:format("Unknown file type: ~p~n",[File]).
|
|
|
+
|
|
|
+-spec erl_files(directory()) -> [file:name()].
|
|
|
+-spec app_src_files(directory()) -> [file:name()].
|
|
|
+-spec app_src_to_app(file:name()) -> file:name().
|
|
|
+-spec erl_to_beam(directory(), file:name()) -> file:name().
|
|
|
+-spec is_compiled(directory(), file:name()) -> boolean().
|
|
|
+-spec add_modules_property([{atom(), term()}]) -> [{atom(), term()}].
|
|
|
+
|
|
|
+erl_files(Dir) -> filelib:fold_files(Dir, ".erl", true, fun(F, Acc) -> [F|Acc] end, []).
|
|
|
+yrl_files(Dir) -> filelib:fold_files(Dir, ".yrl", true, fun(F, Acc) -> [F|Acc] end, []).
|
|
|
+app_src_files(Dir) -> filelib:fold_files(Dir, ".app.src", false, fun(F, Acc) -> [F|Acc] end, []).
|
|
|
+
|
|
|
+app_src_to_app(Filename) -> filename:basename(Filename, ".app.src") ++ ".app".
|
|
|
+yrl_to_erl(Filename) -> filename:join(filename:dirname(Filename),filename:basename(Filename, ".yrl")) ++ ".erl".
|
|
|
+erl_to_beam(Bin, Filename) -> filename:join(Bin, filename:basename(Filename, ".erl") ++ ".beam").
|
|
|
+is_compiled(BeamFile, File) -> mad_utils:last_modified(BeamFile) > mad_utils:last_modified(File).
|
|
|
+add_modules_property(Properties) ->
|
|
|
+ case lists:keyfind(modules, 1, Properties) of
|
|
|
+ {modules, _} -> Properties;
|
|
|
+ _ -> Properties ++ [{modules, []}] end.
|
|
|
+
|
|
|
+-spec foreach(fun((directory(), filename()) -> ok), [filename()], any(), filename()) -> ok.
|
|
|
+foreach(_, [], _, _) -> ok;
|
|
|
+foreach(Fun, [Dir|T], Config, ConfigFile) ->
|
|
|
+ Fun(Dir, Config, ConfigFile),
|
|
|
+ foreach(Fun, T, Config, ConfigFile).
|
|
|
+
|
|
|
+get_kv(K, Opts, Default) ->
|
|
|
+ V = mad_utils:get_value(K, Opts, Default),
|
|
|
+ KV = {K, V},
|
|
|
+ {KV, Opts -- [KV]}.
|
|
|
+
|
|
|
+validate_erlydtl_opts(Cwd, Opts) ->
|
|
|
+ DefaultDocRoot = filename:join("priv", "templates"),
|
|
|
+ {DocRoot, Opts1} = get_kv(doc_root, Opts, DefaultDocRoot),
|
|
|
+ {OutDir, Opts2} = get_kv(out_dir, Opts1, "ebin"),
|
|
|
+ {CompilerOpts, Opts3} = get_kv(compiler_options, Opts2, []),
|
|
|
+ {SourceExt, Opts4} = get_kv(source_ext, Opts3, ".dtl"),
|
|
|
+ {ModuleExt, Opts5} = get_kv(module_ext, Opts4, ""),
|
|
|
+
|
|
|
+ {_, DocRootDir} = DocRoot,
|
|
|
+ DocRoot1 = {doc_root, filename:join(Cwd, DocRootDir)},
|
|
|
+ {_, OutDir1} = OutDir,
|
|
|
+ OutDir2 = {out_dir, filename:join(Cwd, OutDir1)},
|
|
|
+
|
|
|
+ [DocRoot1, OutDir2, CompilerOpts, SourceExt, ModuleExt|Opts5].
|
|
|
+
|
|
|
+module_name(File, Ext, NewExt) ->
|
|
|
+ list_to_atom(filename:basename(File, Ext) ++ NewExt).
|
|
|
+
|
|
|
+compile_erlydtl_files(Opts) ->
|
|
|
+
|
|
|
+ {{_, DocRoot}, Opts1} = get_kv(doc_root, Opts, ""),
|
|
|
+ {{_, SourceExt}, Opts2} = get_kv(source_ext, Opts1, ""),
|
|
|
+ {{_, ModuleExt}, Opts3} = get_kv(module_ext, Opts2, ""),
|
|
|
+ {{_, OutDir}, _} = get_kv(out_dir, Opts3, ""),
|
|
|
+
|
|
|
+ Files = filelib:fold_files(DocRoot, SourceExt, true,
|
|
|
+ fun(F, Acc) -> [F|Acc] end, []),
|
|
|
+
|
|
|
+ Compile = fun(F) ->
|
|
|
+ ModuleName = module_name(F, SourceExt, ModuleExt),
|
|
|
+ BeamFile = erl_to_beam(OutDir, atom_to_list(ModuleName)),
|
|
|
+ Compiled = is_compiled(BeamFile, F),
|
|
|
+ if Compiled =:= false ->
|
|
|
+ io:format("DTL Compiling ~s~n", [F]),
|
|
|
+ erlydtl:compile(F, ModuleName, Opts3);
|
|
|
+ true -> ok end
|
|
|
+ end,
|
|
|
+
|
|
|
+ lists:foreach(Compile, Files).
|