Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
746 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{deps_dir, ["../../deps"]}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{application, mad, | ||
[{description, "MAD VXZ Manage Deps"}, | ||
{vsn, ""}, | ||
{registered, []}, | ||
{applications, [kernel,stdlib]}, | ||
{mod, { mad_app, []}}, | ||
{env, []}]}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
Oops, something went wrong.