Browse Source

make directory structure VOXOZ like

Maxim Sokhatsky 11 years ago
parent
commit
4b1fcd0f44

+ 1 - 0
apps/mad/rebar.config

@@ -0,0 +1 @@
+{deps_dir, ["../../deps"]}.

+ 7 - 0
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 - 0
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 - 0
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 - 0
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.

+ 87 - 0
apps/mad/src/mad_utils.erl

@@ -0,0 +1,87 @@
+-module(mad_utils).
+-copyright('Sina Samavati').
+-compile(export_all).
+
+-type directory() :: string().
+
+-spec cwd() -> directory().
+cwd() -> {ok, Cwd} = file:get_cwd(), Cwd.
+exec(Cmd, Opts) -> os:cmd([Cmd," ",string:join(Opts," ")]).
+
+-spec home() -> directory().
+home() -> {ok, [[H|_]]} = init:get_argument(home), H.
+
+-spec consult(file:name_all()) -> [term()].
+consult(File) ->
+    AbsFile = filename:absname(File),
+    case file:consult(AbsFile) of
+        {ok, V} ->
+            V;
+        _ ->
+            []
+    end.
+
+-spec src(directory()) -> directory().
+src(Dir) -> filename:join(Dir, "src").
+
+-spec include(directory()) -> directory().
+include(Dir) -> filename:join(Dir, "include").
+
+-spec ebin(directory()) -> directory().
+ebin(Dir) -> filename:join(Dir, "ebin").
+
+-spec deps(file:name_all()) -> [term()].
+deps(File) -> get_value(deps, consult(File), []).
+
+-spec get_value(term(), [{term(), term()}], Default) -> term() | Default.
+get_value(Key, Opts, Default) ->
+    case lists:keyfind(Key, 1, Opts) of
+        {Key, Value} ->
+            Value;
+        _ -> Default end.
+
+-spec script(file:name(), [term()]) -> [term()].
+script(ConfigFile, Conf) ->
+    File = ConfigFile ++ ".script",
+    Filename = filename:basename(File),
+    case file:script(File, [{'CONFIG', Conf}, {'SCRIPT', Filename}]) of
+        {ok, {error,_}} -> Conf;
+        {ok, Out} -> Out;
+        {error, _} -> Conf
+    end.
+
+-spec sub_dirs(directory(), file:filename(), [term()]) -> [directory()].
+sub_dirs(Cwd, ConfigFile, Conf) ->
+    sub_dirs(Cwd, ConfigFile, get_value(sub_dirs, Conf, []), []).
+
+-spec sub_dirs(directory(), file:filename(), [term()], [term()]) -> [directory()].
+sub_dirs(_, _, [], Acc) -> Acc;
+sub_dirs(Cwd, ConfigFile, [Dir|T], Acc) ->
+    SubDir = filename:join(Cwd, Dir),
+    ConfigFile1 = filename:join(SubDir, ConfigFile),
+    Conf = consult(ConfigFile1),
+    Conf1 = script(ConfigFile1, Conf),
+    Acc1 = sub_dirs(SubDir, ConfigFile, get_value(sub_dirs, Conf1, []),
+                    Acc ++ [SubDir]),
+    sub_dirs(Cwd, ConfigFile, T, Acc1).
+
+-spec lib_dirs(directory(), [term()]) -> [directory()].
+lib_dirs(Cwd, Conf) -> lib_dirs(Cwd, get_value(lib_dirs, Conf, []), []).
+
+-spec lib_dirs(directory(), [term()], [term()]) -> [directory()].
+lib_dirs(_, [], Acc) -> Acc;
+lib_dirs(Cwd, [H|T], Acc) ->
+    Dirs = filelib:wildcard(filename:join([Cwd, H, "*", "ebin"])),
+    lib_dirs(Cwd, T, Acc ++ Dirs).
+
+-spec last_modified(file:name_all()) -> Seconds :: non_neg_integer().
+last_modified(File) ->
+    case filelib:last_modified(File) of
+        0 -> 0;
+        Else -> calendar:datetime_to_gregorian_seconds(Else) end.
+
+to_atom(X) when is_atom(X) -> X;
+to_atom(X) when is_list(X) -> list_to_atom(X);
+to_atom(X) when is_binary(X) -> to_atom(binary_to_list(X));
+to_atom(X) -> X.
+

+ 10 - 0
apps/mad/test/helper.erl

@@ -0,0 +1,10 @@
+-module(helper).
+-export([get_value/2]).
+
+get_value(Key, Conf) ->
+    case lists:keyfind(Key, 1, Conf) of
+        {Key, Value} ->
+            Value;
+        _ ->
+            undefined
+    end.

+ 77 - 0
apps/mad/test/mad_compile_SUITE.erl

@@ -0,0 +1,77 @@
+-module(mad_compile_SUITE).
+
+-export([all/0]).
+-export([erl_files/1]).
+-export([app_src_files/1]).
+-export([is_app_src/1]).
+-export([app_src_to_app/1]).
+-export([erl_to_beam/1]).
+-export([deps/1]).
+-export([app/1]).
+-export([is_compiled/1]).
+
+-import(helper, [get_value/2]).
+
+
+all() ->
+    [
+     erl_files, app_src_files, is_app_src, app_src_to_app, erl_to_beam, deps,
+     app, is_compiled
+    ].
+
+erl_files(Config) ->
+    DataDir = get_value(data_dir, Config),
+    SrcDir = filename:join([DataDir, "deps", "one", "src"]),
+    ErlFile = filename:join(SrcDir, "one.erl"),
+    [ErlFile] = mad_compile:erl_files(SrcDir).
+
+app_src_files(Config) ->
+    DataDir = get_value(data_dir, Config),
+    SrcDir = filename:join([DataDir, "deps", "one", "src"]),
+    AppSrcFile = filename:join(SrcDir, "one.app.src"),
+    [AppSrcFile] = mad_compile:app_src_files(SrcDir).
+
+is_app_src(_) ->
+    false = mad_compile:is_app_src("/path/to/file.erl"),
+    true = mad_compile:is_app_src("/path/to/file.app.src").
+
+app_src_to_app(_) ->
+    "file.app" = mad_compile:app_src_to_app("/path/to/file.app.src").
+
+erl_to_beam(_) ->
+    "/path/to/ebin/file.beam" = mad_compile:erl_to_beam("/path/to/ebin",
+                                                        "/path/to/file.erl").
+
+deps(Config) ->
+    DataDir = get_value(data_dir, Config),
+    Deps = [{one, "", {}}, {two, "", {}}],
+    ok = mad_compile:deps(DataDir, Config, "rebar.config", Deps),
+    pong = one:ping(),
+    pong = two:ping(),
+    ok = application:load(one),
+    ok = application:load(two),
+    {ok, [one]} = application:get_key(one, modules),
+    {ok, [two]} = application:get_key(two, modules),
+
+    ok = one:test_inc_hrl(),
+    ok = one:test_src_hrl(),
+    ok = two:test_inc_hrl(),
+    ok = two:test_src_hrl().
+
+app(Config) ->
+    DataDir = get_value(data_dir, Config),
+    ok = mad_compile:app(DataDir, Config, "rebar.config"),
+    pong = three:ping(),
+    ok = application:load(three),
+    {ok, [three]} = application:get_key(three, modules),
+    ok = three:test_inc_hrl(),
+    ok = three:test_src_hrl().
+
+is_compiled(Config) ->
+    DataDir = get_value(data_dir, Config),
+    SrcDir = filename:join([DataDir, "deps", "one", "src"]),
+    EbinDir = filename:join([SrcDir, "..", "ebin"]),
+    BeamFile1 = filename:join(EbinDir, "x.beam"),
+    BeamFile2 = filename:join(EbinDir, "one.beam"),
+    false = mad_compile:is_compiled(BeamFile1, filename:join(SrcDir, "x.erl")),
+    true = mad_compile:is_compiled(BeamFile2, filename:join(SrcDir, "one.erl")).

+ 1 - 0
apps/mad/test/mad_compile_SUITE_data/include/three_inc.hrl

@@ -0,0 +1 @@
+test_inc_hrl() -> ok.

+ 11 - 0
apps/mad/test/mad_compile_SUITE_data/src/three.app.src

@@ -0,0 +1,11 @@
+{application, three,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {env, []}
+ ]}.

+ 10 - 0
apps/mad/test/mad_compile_SUITE_data/src/three.erl

@@ -0,0 +1,10 @@
+-module(three).
+
+-export([test_inc_hrl/0]).
+-export([test_src_hrl/0]).
+-export([ping/0]).
+
+ping() -> pong.
+
+-include_lib("three_inc.hrl").
+-include("three_src.hrl").

+ 1 - 0
apps/mad/test/mad_compile_SUITE_data/src/three_src.hrl

@@ -0,0 +1 @@
+test_src_hrl() -> ok.

+ 51 - 0
apps/mad/test/mad_deps_SUITE.erl

@@ -0,0 +1,51 @@
+-module(mad_deps_SUITE).
+
+-export([all/0]).
+-export([repos_path/1]).
+-export([path/1]).
+-export([name_and_repo/1]).
+-export([checkout_to/1]).
+-export([get_publisher/1]).
+-export([fetch/1]).
+
+-import(helper, [get_value/2]).
+
+
+all() ->
+    [repos_path, path, name_and_repo, checkout_to, get_publisher, fetch].
+
+repos_path(_) ->
+    Path = filename:join([os:cmd("echo -n $HOME"), ".mad", "repos"]),
+    Path = mad_deps:repos_path().
+
+path(_) ->
+    Path = filename:join([os:cmd("echo -n $HOME"), ".mad", "repos",
+                          "publisher", "repo"]),
+    Path = mad_deps:path("publisher", "repo").
+
+name_and_repo(_) ->
+    Dep = {x,".*",{git,"blah, blah..",{tag,"v0.8.10"}}},
+    {"x",{git,"blah, blah..", {tag,"v0.8.10"}}} = mad_deps:name_and_repo(Dep).
+
+checkout_to(_) ->
+    "v0.8.10" = mad_deps:checkout_to({tag, "v0.8.10"}),
+    "develop" = mad_deps:checkout_to({branch, "develop"}).
+
+get_publisher(_) ->
+    "erlang" = mad_deps:get_publisher("git://github.com/erlang/otp.git"),
+    "s1n4" = mad_deps:get_publisher("https://github.com/s1n4/mad"),
+    "xyz" = mad_deps:get_publisher("https://bitbucket.org/xyz/repo").
+
+fetch(Config) ->
+    DataDir = get_value(data_dir, Config),
+    DepsDir = filename:join(DataDir, "deps"),
+    os:cmd("rm -rf " ++ DepsDir),
+    %% make repos and deps directories
+    os:cmd("mkdir -p " ++ mad_deps:repos_path()),
+    os:cmd("mkdir -p " ++ DepsDir),
+    Deps = [{mad, ".*",
+             {git, "git://github.com/s1n4/mad.git", {branch, "master"}}
+            }],
+    mad_deps:fetch(DataDir, Config, "rebar.config", Deps),
+    {ok, _} = file:list_dir(filename:join(DepsDir, "mad")),
+    os:cmd("rm -rf " ++ DepsDir).

+ 108 - 0
apps/mad/test/mad_utils_SUITE.erl

@@ -0,0 +1,108 @@
+-module(mad_utils_SUITE).
+
+-export([all/0]).
+-export([cwd/1]).
+-export([exec/1]).
+-export([home/1]).
+-export([consult/1]).
+-export([src/1]).
+-export([include/1]).
+-export([ebin/1]).
+-export([deps/1]).
+-export([get_value/1]).
+-export([script/1]).
+-export([lib_dirs/1]).
+-export([sub_dirs/1]).
+-export([https_to_git/1]).
+-export([git_to_https/1]).
+-export([last_modified/1]).
+
+-import(helper, [get_value/2]).
+
+
+all() ->
+    [
+     cwd, exec, home, consult, src, include, ebin, deps, get_value, script,
+     lib_dirs, sub_dirs, https_to_git, git_to_https, last_modified
+    ].
+
+cwd(_) ->
+    Cwd = os:cmd("pwd") -- "\n",
+    Cwd = mad_utils:cwd().
+
+exec(_) ->
+    "xyz" = mad_utils:exec("echo", ["-n", "xyz"]).
+
+home(_) ->
+    Home = os:cmd("echo $HOME") -- "\n",
+    Home = mad_utils:home().
+
+consult(Config) ->
+    File = filename:join(get_value(data_dir, Config), "rebar"),
+    [] = mad_utils:consult(File),
+    [{deps, [
+             {mad, ".*", {git, "git://github.com/s1n4/mad.git",
+                          {branch, "master"}}}
+            ]},
+     {erl_opts, [d, 'X']}] = mad_utils:consult(File ++ ".config").
+
+src(_) ->
+    "/path/to/app/src" = mad_utils:src("/path/to/app").
+
+include(_) ->
+    "/path/to/app/include" = mad_utils:include("/path/to/app").
+
+ebin(_) ->
+    "/path/to/app/ebin" = mad_utils:ebin("/path/to/app").
+
+deps(Config) ->
+    File = filename:join(get_value(data_dir, Config), "rebar"),
+    [] = mad_utils:deps(File),
+    [{mad, ".*",
+      {git, "git://github.com/s1n4/mad.git", {branch, "master"}
+      }}] = mad_utils:deps(File ++ ".config").
+
+get_value(_) ->
+    Opts = [{numbers, [0,1,2,"and so on"]}],
+    patience_dude = mad_utils:get_value(gimme_wat_I_want, Opts, patience_dude),
+    [0,1,2,"and so on"] = mad_utils:get_value(numbers, Opts, undefined).
+
+script(Config) ->
+    [a, b, c] = mad_utils:script("rebar.config", [a, b, c]),
+    File = filename:join(get_value(data_dir, Config), "rebar.config"),
+    [{sub_dirs, ["sub_dir1", "sub_dir2"]},
+     a, b, c] = mad_utils:script(File, [a, b, c]).
+
+sub_dirs(Config) ->
+    ["/sub_dir0"] = mad_utils:sub_dirs("/", "rebar.config",
+                                       [{sub_dirs, ["sub_dir0"]}]),
+    DataDir = get_value(data_dir, Config),
+    SD1 = filename:absname(filename:join(DataDir, "sub_dir1")),
+    SD2 = filename:join(SD1, "trap"),
+    SD3 = filename:absname(filename:join(DataDir, "sub_dir2")),
+    SD4 = filename:join(SD3, "time-machine"),
+    [
+     SD1, SD2, SD3, SD4
+    ] =  mad_utils:sub_dirs(DataDir, "rebar.config",
+                            [{sub_dirs, ["sub_dir1", "sub_dir2"]}]).
+
+lib_dirs(Config) ->
+    [] = mad_utils:lib_dirs("/", [{lib_dirs, ["lib_dir0"]}]),
+    DataDir = get_value(data_dir, Config),
+    LD1 = filename:absname(filename:join([DataDir, "lib_dir1", "app1", "ebin"])),
+    LD2 = filename:absname(filename:join([DataDir, "lib_dir2", "app2", "ebin"])),
+    [LD1, LD2] = mad_utils:lib_dirs(DataDir,
+                                    [{lib_dirs, ["lib_dir1", "lib_dir2"]}]).
+
+https_to_git(_) ->
+    Repo = "https://github.com/erlang/otp.git",
+    "git://github.com/erlang/otp.git" = mad_utils:https_to_git(Repo).
+
+git_to_https(_) ->
+    Repo = "git://github.com/s1n4/some_secret.git",
+    "https://github.com/s1n4/some_secret.git" = mad_utils:git_to_https(Repo).
+
+last_modified(Config) ->
+    0 = mad_utils:last_modified("you_mad_bro"),
+    DataDir = get_value(data_dir, Config),
+    true = (mad_utils:last_modified(DataDir ++ "rebar.config") > 0).

+ 4 - 0
apps/mad/test/mad_utils_SUITE_data/rebar.config

@@ -0,0 +1,4 @@
+{deps, [
+        {mad, ".*", {git, "git://github.com/s1n4/mad.git", {branch, "master"}}}
+       ]}.
+{erl_opts, [d, 'X']}.

+ 1 - 0
apps/mad/test/mad_utils_SUITE_data/rebar.config.script

@@ -0,0 +1 @@
+[{sub_dirs, ["sub_dir1", "sub_dir2"]}|CONFIG].

+ 1 - 0
apps/mad/test/mad_utils_SUITE_data/sub_dir1/rebar.config

@@ -0,0 +1 @@
+{sub_dirs, ["trap"]}.

+ 0 - 0
apps/mad/test/mad_utils_SUITE_data/sub_dir1/trap/.gitkeep


+ 1 - 0
apps/mad/test/mad_utils_SUITE_data/sub_dir2/rebar.config

@@ -0,0 +1 @@
+{sub_dirs, ["time-machine"]}.

+ 0 - 0
apps/mad/test/mad_utils_SUITE_data/sub_dir2/time-machine/.gitkeep


+ 3 - 0
apps/rebar.config

@@ -0,0 +1,3 @@
+{sub_dirs, [ "mad" ]}.
+{lib_dirs, ["../apps"]}.
+{deps_dir, ["../deps"]}.