Skip to content

Commit

Permalink
make directory structure VOXOZ like
Browse files Browse the repository at this point in the history
  • Loading branch information
5HT committed May 10, 2014
1 parent 8e98c27 commit 4b1fcd0
Show file tree
Hide file tree
Showing 21 changed files with 746 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/mad/rebar.config
@@ -0,0 +1 @@
{deps_dir, ["../../deps"]}.
7 changes: 7 additions & 0 deletions apps/mad/src/mad.app.src
@@ -0,0 +1,7 @@
{application, mad,
[{description, "MAD VXZ Manage Deps"},
{vsn, ""},
{registered, []},
{applications, [kernel,stdlib]},
{mod, { mad_app, []}},
{env, []}]}.
113 changes: 113 additions & 0 deletions apps/mad/src/mad.erl
@@ -0,0 +1,113 @@
-module(mad).
-copyright('Sina Samavati').
-compile(export_all).
-export([main/1]).

main([]) -> help();
main(Args) ->

{Opts, Params} = case getopt:parse(option_spec_list(), Args) of
{ok, {Opts1, Params1}} ->
{Opts1, [list_to_atom(E) || E <- Params1]};
{error, {Reason, Data}} ->
help(Reason, Data)
end,
maybe_invalid(Params),
maybe_help(Opts, Params),

Cwd = mad_utils:cwd(),
ConfigFile = get_value(config_file, Opts, "rebar.config"),
ConfigFileAbs = filename:join(Cwd, ConfigFile),
Conf = mad_utils:consult(ConfigFileAbs),
Conf1 = mad_utils:script(ConfigFileAbs, Conf),

%% rebar should create deps dir in deps_dir only, this is not a list
DepsDir = filename:join([mad_utils:get_value(deps_dir, Conf1, ["deps"]),"*","ebin"]),
Paths = ["ebin"|filelib:wildcard(DepsDir)],
code:add_paths(Paths),

%% add lib_dirs to path
LibDirs = mad_utils:lib_dirs(Cwd, Conf),
code:add_paths(LibDirs),

Fun = fun(F) -> ?MODULE:F(Cwd, ConfigFile, Conf1) end,
lists:foreach(Fun, Params).

%% fetch dependencies
'get-deps'(Cwd, ConfigFile, Conf) ->
case get_value(deps, Conf, []) of
[] -> ok;
Deps ->
Cache = mad_utils:get_value(deps_dir, Conf, deps_fetch),
case Cache of
deps_fetch -> skip;
Dir -> file:make_dir(Dir) end,
FetchDir = mad_utils:get_value(deps_dir, Conf, ["deps"]),
file:make_dir(FetchDir),
mad_deps:fetch(Cwd, Conf, ConfigFile, Deps)
end.

%% compile dependencies and the app
compile(Cwd, ConfigFile, Conf) ->
'compile-deps'(Cwd, ConfigFile, Conf),
'compile-apps'(Cwd, ConfigFile, Conf).

'compile-apps'(Cwd, ConfigFile, Conf) ->
Dirs = mad_utils:sub_dirs(Cwd, ConfigFile, Conf),
mad_compile:deps(Cwd, Conf, ConfigFile, Dirs).

'compile-deps'(Cwd, ConfigFile, Conf) ->
mad_compile:deps(Cwd, Conf, ConfigFile, get_value(deps, Conf, [])).

get_value(Key, Opts, Default) ->
case lists:keyfind(Key, 1, Opts) of
{Key, Value} ->
Value;
_ -> Default end.

option_spec_list() ->
[
{help, $h, "help", undefined, "Displays this message"},
{config_file, $C, "config", {string, "rebar.config"}, "Rebar config file to use"}
].

maybe_help(Opts, Params) ->
Fun = fun(L) ->
case lists:member(help, L) of
true ->
help();
false ->
ok
end
end,
Fun(Opts),
Fun(Params).

maybe_invalid(Params) ->
lists:foreach(fun(E) ->
case erlang:function_exported(?MODULE, E, 3) of
true -> ok;
false -> help("invalid_parameter", E)
end
end, Params).

help("invalid_parameter", Data) ->
help(io_lib:format("invalid_parameter \"~s\"", [Data]));
help(Reason, Data) ->
help(io_lib:format("~s ~p", [Reason, Data])).

help(Msg) ->
io:format("Error: ~s~n~n", [Msg]),
help().

help() ->
io:format("Manage Deps~n"),
Params = [
{"", ""},
{"get-deps", "Fetch dependencies"},
{"compile-deps", "Compile dependencies"},
{"compile-apps", "Compile applications"},
{"compile", "Compile dependencies and applications"}
],
getopt:usage(option_spec_list(), escript:script_name(), "", Params),
halt().
176 changes: 176 additions & 0 deletions apps/mad/src/mad_compile.erl
@@ -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).
83 changes: 83 additions & 0 deletions apps/mad/src/mad_deps.erl
@@ -0,0 +1,83 @@
-module(mad_deps).
-copyright('Sina Samavati').
-compile(export_all).

-type directory() :: string().
-type filename() :: string().
-type name() :: atom().
-type uri() :: string().
-type version_control() :: git | hg.
-type repo() :: {version_control(), uri(), {branch | tag, string()} | string()}.
-type dependency() :: {name(), string(), repo()}.
-export_type([dependency/0]).

-spec fetch(directory(), any(), filename(), [dependency()]) -> ok.
fetch(_, _Config, _, []) -> ok;
fetch(Cwd, Config, ConfigFile, [H|T]) when is_tuple(H) =:= false -> fetch(Cwd, Config, ConfigFile, T);
fetch(Cwd, Config, ConfigFile, [H|T]) ->
{Name, Repo} = name_and_repo(H),
{Cmd, Uri, Co} = case Repo of
V={_, _, _} ->
V;
{_Cmd, _Url, _Co, _} ->
{_Cmd, _Url, _Co}
end,
Cmd1 = atom_to_list(Cmd),
Cache = mad_utils:get_value(cache, Config, deps_fetch),
case get(Name) of
fetched -> ok;
_ -> fetch_dep(Cwd, Config, ConfigFile, Name, Cmd1, Uri, Co, Cache)
end,
fetch(Cwd, Config, ConfigFile, T).

-spec fetch_dep(directory(), any(), filename(), string(), string(), uri(), any(), atom()) -> ok.
fetch_dep(Cwd, Config, ConfigFile, Name, Cmd, Uri, Co, Cache) ->

TrunkPath = case Cache of
deps_fetch -> filename:join([mad_utils:get_value(deps_dir,Config,"deps"),Name]);
Dir -> filename:join([Dir,get_publisher(Uri),Name]) end,

io:format("==> dependency: ~p tag: ~p~n", [Uri,Co]),

{R,Co1} = case Co of
{_,Rev} ->
{["git clone ",Uri," ",TrunkPath," && cd ",TrunkPath,
" && git checkout \"",Rev,"\"" ],Rev};
Master -> {["git clone ", Uri," ", TrunkPath ],lists:concat([Master])} end,

os:cmd(R),

put(Name, fetched),

%% check dependencies of the dependency
TrunkConfigFile = filename:join(TrunkPath, ConfigFile),
Conf = mad_utils:consult(TrunkConfigFile),
Conf1 = mad_utils:script(TrunkConfigFile, Conf),
fetch(Cwd, Config, ConfigFile, mad_utils:get_value(deps, Conf1, [])),
case Cache of
deps_fetch -> skip;
CacheDir -> build_dep(Cwd, Config, ConfigFile, get_publisher(Uri), Name, Cmd, Co1, CacheDir) end.

%% build dependency based on branch/tag/commit
-spec build_dep(directory(), any(), string(), string(), string(), string(), string(), string()) -> ok.
build_dep(Cwd, Conf, _ConfFile, Publisher, Name, _Cmd, _Co, Dir) ->
TrunkPath = filename:join([Dir, Publisher, Name]),
DepsDir = filename:join([mad_utils:get_value(deps_dir, Conf, ["deps"]),Name]),
os:cmd(["cp -r ", TrunkPath, " ", DepsDir]),
ok = file:set_cwd(DepsDir),
ok = file:set_cwd(Cwd).

%% internal
-spec name_and_repo(dependency()) -> {string(), repo()}.
name_and_repo({Name, _, Repo}) -> {atom_to_list(Name), Repo};
name_and_repo({Name, _, Repo, _}) -> {atom_to_list(Name), Repo};
name_and_repo(Name) -> {Name,Name}.

-spec get_publisher(uri()) -> string().
get_publisher(Uri) ->
case http_uri:parse(Uri, [{scheme_defaults,
[{git, 9418}|http_uri:scheme_defaults()]}]) of
{ok, {_, _, _, _, Path, _}} -> hd(string:tokens(Path,"/"));
_ -> case string:tokens(Uri,":/") of
[_Server,Publisher,_Repo] -> Publisher;
_ -> exit(error) end end.

0 comments on commit 4b1fcd0

Please sign in to comment.