|
@@ -0,0 +1,205 @@
|
|
|
+-module(mad).
|
|
|
+
|
|
|
+-export([deps/1]).
|
|
|
+-export([clone_deps/1]).
|
|
|
+-export([compile/1]).
|
|
|
+-export([compile_deps/1]).
|
|
|
+-export([update_path/1]).
|
|
|
+
|
|
|
+
|
|
|
+%% read rebar.config file and return the {deps, V}
|
|
|
+deps(RebarFile) ->
|
|
|
+ case file:consult(RebarFile) of
|
|
|
+ {ok, Conf} ->
|
|
|
+ case lists:keyfind(deps, 1, Conf) of
|
|
|
+ {deps, Deps} ->
|
|
|
+ {ok, Deps};
|
|
|
+ _ ->
|
|
|
+ {ok, []}
|
|
|
+ end;
|
|
|
+ Else ->
|
|
|
+ Else
|
|
|
+ end.
|
|
|
+
|
|
|
+clone_deps(Deps) ->
|
|
|
+ exec("mkdir", ["-p", deps_path()]),
|
|
|
+ do_clone_deps(Deps).
|
|
|
+
|
|
|
+do_clone_deps([]) ->
|
|
|
+ ok;
|
|
|
+do_clone_deps([{Name, _, Repo}|T]) ->
|
|
|
+ Name1 = atom_to_list(Name),
|
|
|
+ {Cmd, Url, Co} = Repo,
|
|
|
+
|
|
|
+ %% branch/tag it should checkout to
|
|
|
+ Co1 = case Co of
|
|
|
+ {_, V} -> V;
|
|
|
+ Else -> Else
|
|
|
+ end,
|
|
|
+ Name2 = make_dep_name(Name1, Co1),
|
|
|
+
|
|
|
+ %% command options: clone url path/to/dep -b Branch/Tag
|
|
|
+ Opts = ["clone", Url, get_path(Name2), "-b", Co1],
|
|
|
+ io:format("dependency: ~s~n", [Name1]),
|
|
|
+
|
|
|
+ %% run the command
|
|
|
+ exec(Cmd, Opts),
|
|
|
+
|
|
|
+ %% check dependencies of the dependency
|
|
|
+ case deps(rebar_config_file(Name2)) of
|
|
|
+ {ok, Deps} ->
|
|
|
+ do_clone_deps(Deps);
|
|
|
+ {error, _} ->
|
|
|
+ ok
|
|
|
+ end,
|
|
|
+ do_clone_deps(T).
|
|
|
+
|
|
|
+%% compile dependencies and the app
|
|
|
+compile(Dir) ->
|
|
|
+ RebarFile = filename:join([Dir, "rebar.config"]),
|
|
|
+ case deps(RebarFile) of
|
|
|
+ {ok, Deps} ->
|
|
|
+ compile_deps(Deps);
|
|
|
+ {error, _} ->
|
|
|
+ ok
|
|
|
+ end,
|
|
|
+ compile_app(Dir).
|
|
|
+
|
|
|
+compile_deps([]) ->
|
|
|
+ ok;
|
|
|
+compile_deps([{Name, _, Repo}|T]) ->
|
|
|
+ Name1 = atom_to_list(Name),
|
|
|
+ {_, _, Co} = Repo,
|
|
|
+ %% branch/tag it should checkout to
|
|
|
+ Co1 = case Co of
|
|
|
+ {_, V} -> V;
|
|
|
+ Else -> Else
|
|
|
+ end,
|
|
|
+
|
|
|
+ Name2 = make_dep_name(Name1, Co1),
|
|
|
+ SrcDir = src(Name2),
|
|
|
+ EbinDir = ebin(Name2),
|
|
|
+ case file:list_dir(SrcDir) of
|
|
|
+ {ok, Files} ->
|
|
|
+ exec("mkdir", ["-p", EbinDir]),
|
|
|
+ lists:foreach(compile_fun(SrcDir, EbinDir), Files);
|
|
|
+ {error, _} -> ok
|
|
|
+ end,
|
|
|
+
|
|
|
+ %% check dependencies of the dependency
|
|
|
+ case deps(rebar_config_file(Name2)) of
|
|
|
+ {ok, Deps} ->
|
|
|
+ compile_deps(Deps);
|
|
|
+ {error, _} ->
|
|
|
+ ok
|
|
|
+ end,
|
|
|
+ compile_deps(T).
|
|
|
+
|
|
|
+compile_app(Dir) ->
|
|
|
+ SrcDir = filename:join([Dir, "src"]),
|
|
|
+ case file:list_dir(SrcDir) of
|
|
|
+ {ok, Files} ->
|
|
|
+ EbinDir = filename:join([Dir, "ebin"]),
|
|
|
+ lists:foreach(compile_fun(SrcDir, EbinDir), Files);
|
|
|
+ {error, _} ->
|
|
|
+ ok
|
|
|
+ end.
|
|
|
+
|
|
|
+exec(Cmd, Opts) ->
|
|
|
+ Opts1 = [concat([" ", X]) || X <- Opts],
|
|
|
+ os:cmd(concat([Cmd, concat(Opts1)])).
|
|
|
+
|
|
|
+concat(L) ->
|
|
|
+ lists:concat(L).
|
|
|
+
|
|
|
+make_dep_name(Name, Suffix) ->
|
|
|
+ %% Name-Suffix
|
|
|
+ concat([Name, "-", Suffix]).
|
|
|
+
|
|
|
+home() ->
|
|
|
+ %% ~/
|
|
|
+ {ok, [[H|_]]} = init:get_argument(home),
|
|
|
+ H.
|
|
|
+
|
|
|
+deps_path() ->
|
|
|
+ %% ~/.otp/deps
|
|
|
+ filename:join([home(), ".otp", "deps"]).
|
|
|
+
|
|
|
+get_path(X) ->
|
|
|
+ %% ~/.otp/deps/X
|
|
|
+ filename:join([deps_path(), X]).
|
|
|
+
|
|
|
+rebar_config_file(X) ->
|
|
|
+ %% ~/.otp/deps/X/rebar.config
|
|
|
+ filename:join([get_path(X), "rebar.config"]).
|
|
|
+
|
|
|
+ebin(X) ->
|
|
|
+ %% ~/.otp/deps/X/ebin
|
|
|
+ filename:join([get_path(X), "ebin"]).
|
|
|
+
|
|
|
+src(X) ->
|
|
|
+ %% ~/.otp/deps/X/src
|
|
|
+ filename:join([get_path(X), "src"]).
|
|
|
+
|
|
|
+deps_path(Deps) ->
|
|
|
+ deps_path(Deps, []).
|
|
|
+
|
|
|
+deps_path([], Acc) ->
|
|
|
+ Acc;
|
|
|
+deps_path([{Name, _, Repo}|T], Acc) ->
|
|
|
+ Name1 = atom_to_list(Name),
|
|
|
+ {_, _, Co} = Repo,
|
|
|
+ %% branch/tag it should checkout to
|
|
|
+ Co1 = case Co of
|
|
|
+ {_, V} -> V;
|
|
|
+ Else -> Else
|
|
|
+ end,
|
|
|
+ Name2 = make_dep_name(Name1, Co1),
|
|
|
+ Acc1 = case deps(rebar_config_file(Name2)) of
|
|
|
+ {ok, Deps} ->
|
|
|
+ deps_path(Deps, []);
|
|
|
+ {error, _} ->
|
|
|
+ []
|
|
|
+ end,
|
|
|
+ deps_path(T, [get_path(Name2)|Acc ++ Acc1]).
|
|
|
+
|
|
|
+deps_ebin(Deps) ->
|
|
|
+ deps_ebin(deps_path(Deps), []).
|
|
|
+
|
|
|
+deps_ebin([], Acc) ->
|
|
|
+ Acc;
|
|
|
+deps_ebin([H|T], Acc) ->
|
|
|
+ deps_ebin(T, [filename:join([H, "ebin"])|Acc]).
|
|
|
+
|
|
|
+%% add application directory (its ebin) and its dependencies to the code path
|
|
|
+update_path(Dir) ->
|
|
|
+ Ebin = filename:join([Dir, "ebin"]),
|
|
|
+ code:add_path(Ebin),
|
|
|
+
|
|
|
+ RebarFile = filename:join([Dir, "rebar.config"]),
|
|
|
+ case deps(RebarFile) of
|
|
|
+ {ok, Deps} ->
|
|
|
+ code:add_paths(deps_ebin(Deps));
|
|
|
+ {error, _} ->
|
|
|
+ ok
|
|
|
+ end.
|
|
|
+
|
|
|
+is_app_src(Filename) ->
|
|
|
+ Filename =/= filename:rootname(Filename, ".app.src").
|
|
|
+
|
|
|
+app_src_to_app(Filename) ->
|
|
|
+ filename:join([filename:basename(Filename, ".app.src") ++ ".app"]).
|
|
|
+
|
|
|
+compile_fun(SrcDir, EbinDir) ->
|
|
|
+ fun(F) ->
|
|
|
+ F1 = filename:join([SrcDir, F]),
|
|
|
+ case is_app_src(F1) of
|
|
|
+ false ->
|
|
|
+ io:format("Compiling ~s~n", [F]),
|
|
|
+ compile:file(F1, [{outdir, EbinDir}]);
|
|
|
+ true ->
|
|
|
+ AppF = app_src_to_app(F1),
|
|
|
+ io:format("Writing ebin/~s~n", [AppF]),
|
|
|
+ exec("cp", [F1, filename:join([EbinDir, AppF])])
|
|
|
+ end
|
|
|
+ end.
|