Просмотр исходного кода

Merge branch 'escript'

Get clone-deps to work

Evaluate script file

Update 'compile' and related things

Fix erl_files/1 & app_src_files/1

Fix sub_dirs/3

Fix compile-app/1

Add 'build' escript for building 'mad' escript

Remove update-path/1 & shell/1
Sina Samavati 11 лет назад
Родитель
Сommit
4995cd9885
7 измененных файлов с 396 добавлено и 292 удалено
  1. 5 2
      Makefile
  2. 25 0
      build
  3. 38 290
      src/mad.erl
  4. 2 0
      src/mad.hrl
  5. 99 0
      src/mad_compile.erl
  6. 119 0
      src/mad_deps.erl
  7. 108 0
      src/mad_utils.erl

+ 5 - 2
Makefile

@@ -1,6 +1,6 @@
-.PHONY: all compile clean
+.PHONY: all compile clean build
 
-all: clean compile
+all: clean compile build
 
 clean:
 	rm -rf ebin
@@ -16,3 +16,6 @@ compile: $(shell find src -type f -name \*.erl)
 	@mkdir -p ebin/
 	$(if $(strip $(filter %.erl ,$?)), \
 		$(call compile_erl,$(filter %.erl %.core,$?)))
+
+build:
+	@./build

+ 25 - 0
build

@@ -0,0 +1,25 @@
+#!/usr/bin/env escript
+
+main(_) ->
+    Files = lists:ukeysort(1, lists:flatten(files())),
+    {ok, {"mad", ZipBin}} = zip:create("mad", Files, [memory]),
+    Script = iolist_to_binary([
+                               "#!/usr/bin/env escript\n",
+                               "%% " ++ comment(),
+                               "%%! -pa mad/mad/ebin\n",
+                               ZipBin]),
+    file:write_file("mad", Script),
+    os:cmd("chmod +x mad").
+
+files() ->
+    [read_file(F) || F <- filelib:wildcard("*", "ebin")].
+
+read_file(F) ->
+    [[{"ebin/", <<>>}], {F, file_contents(F)}].
+
+file_contents(F) ->
+    {ok, Bin} = file:read_file(filename:join("ebin", F)),
+    Bin.
+
+comment() ->
+    "Dependency manager for Erlang\n".

+ 38 - 290
src/mad.erl

@@ -1,291 +1,60 @@
 -module(mad).
 
--export([clone_deps/1]).
+-export([main/1]).
+-export(['clone-deps'/1]).
 -export([compile/1]).
--export([compile_app/1]).
--export([compile_deps/1]).
--export([update_path/1]).
--export([init/0]).
+-export(['compile-app'/1]).
+-export(['compile-deps'/1]).
 
--define(DEPS_PATH, filename:join([home(), ".otp", "deps"])).
--define(COMPILE_OPTS(Inc, Ebin), [report, {i, Inc}, {outdir, Ebin}]).
+-include("mad.hrl").
 
 
+main([]) ->
+    io:format("no args~n");
+main(Args) ->
+    Conf = mad_utils:consult("rebar.config"),
+    Conf1 = mad_utils:script(".", Conf),
+    Fun = fun(F) -> F1 = list_to_atom(F), ?MODULE:F1(Conf1) end,
+    lists:foreach(Fun, Args).
+
 %% clone dependencies
-clone_deps(RebarFile) ->
-    Conf = consult(RebarFile),
+'clone-deps'(Conf) ->
     case get_value(deps, Conf, []) of
         [] ->
             ok;
         Deps ->
-            exec("mkdir", ["-p", ?DEPS_PATH]),
-            do_clone_deps(Deps)
+            mad_utils:exec("mkdir", ["-p", mad_deps:path()]),
+            mad_deps:clone(Deps)
     end.
 
 %% compile dependencies and the app
-compile(Dir) ->
-    Dir1 = filename:absname(Dir),
-    RebarFile = rebar_conf_file(Dir1),
-    Conf = consult(RebarFile),
+compile(Conf) ->
+    Cwd = mad_utils:cwd(),
+    %% add lib_dirs to path
+    LibDirs = mad_utils:lib_dirs(Cwd, Conf),
+    code:add_paths(LibDirs),
+
+    %% compile dependencies
+    'compile-deps'(Conf),
     %% check sub_dirs if they have something to be compiled
-    compile_deps(get_value(deps, Conf, [])),
-    subdirs(Dir1, get_value(sub_dirs, Conf, []), fun compile_app/1),
-    compile_app(Dir1).
-
-%% compile a project according to the conventions
-compile_app(Dir) ->
-    AbsDir = filename:absname(Dir),
-    SrcDir = src(AbsDir),
-    Files = erl_files(SrcDir) ++ app_src_files(SrcDir),
-    case Files of
-        [] ->
-            ok;
-        Files ->
-            IncDir = include(AbsDir),
-            EbinDir = ebin(AbsDir),
-            exec("mkdir", ["-p", EbinDir]),
-            lists:foreach(compile_fun(SrcDir, EbinDir, IncDir), Files)
-    end.
-
-%% compile dependencies
-compile_deps([]) ->
-    ok;
-compile_deps([H|T]) ->
-    {Name, Repo} = name_and_repo(H),
-    Co = case Repo of
-             {_, _, V} ->
-                 V;
-             {_, _, V, _} ->
-                 V
-         end,
-    Co1 = checkout_to(Co),
-    DepName = make_dep_name(Name, Co1),
-    case get(DepName) of
-        compiled ->
-            ok;
-        _ ->
-            compile_dep(H)
-    end,
-    compile_deps(T).
-
-compile_dep(Dep) ->
-    {Name, Repo} = name_and_repo(Dep),
-    Co = case Repo of
-             {_, _, V} ->
-                 V;
-             {_, _, V, _} ->
-                 V
-         end,
-
-    %% branch/tag it should checkout to
-    Co1 = checkout_to(Co),
-    DepName = make_dep_name(Name, Co1),
-
-    %% check dependencies of the dependency
-    RebarFile = rebar_conf_file(dep_path(DepName)),
-    compile_deps(deps(RebarFile)),
-
-    SrcDir = src(dep_path(DepName)),
-    Files = erl_files(SrcDir) ++ app_src_files(SrcDir),
-    case Files of
-        [] ->
-            ok;
-        Files ->
-            EbinDir = ebin(dep_path(DepName)),
-            IncDir = include(dep_path(DepName)),
-            exec("mkdir", ["-p", EbinDir]),
-            lists:foreach(compile_fun(SrcDir, EbinDir, IncDir), Files),
-            put(DepName, compiled)
-    end.
-
-%% add application directory (its ebin) and its dependencies to the code path
-update_path(Dir) ->
-    AbsDir = filename:absname(Dir),
-    Ebin = ebin(AbsDir),
-    code:add_path(Ebin),
-
-    Conf = consult(rebar_conf_file(AbsDir)),
-    case get_value(deps, Conf, []) of
-        [] ->
-            ok;
-        Deps ->
-            code:add_paths(deps_ebin(Deps))
-    end,
-    LibDirs = get_value(lib_dirs, Conf, []),
-    code:add_paths(libdirs(Dir, LibDirs, [])).
-
-init() ->
-    {ok, Cwd} = file:get_cwd(),
-    update_path(Cwd).
-
-
-%% internal
-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.
-
-dep_path(X) ->
-    %% ~/.otp/deps/X
-    filename:join([?DEPS_PATH, X]).
+    SubDirs = mad_utils:sub_dirs(Cwd, Conf),
+    lists:foreach(fun mad_compile:app/1, SubDirs),
 
-rebar_conf_file(X) ->
-    filename:join([filename:absname(X), "rebar.config"]).
+    %% compile the app
+    'compile-app'(Conf).
 
-ebin(X) ->
-    %% X/ebin
-    filename:join([X, "ebin"]).
-
-src(X) ->
-    %% X/src
-    filename:join([X, "src"]).
-
-include(X) ->
-    %% X/include
-    filename:join([X, "include"]).
-
-deps_path(Deps) ->
-    deps_path(Deps, []).
-
-deps_path([], Acc) ->
-    Acc;
-deps_path([H|T], Acc) when is_tuple(H) =:= false ->
-    deps_path(T, Acc);
-deps_path([H|T], Acc) ->
-    {Name, Repo} = name_and_repo(H),
-    Co = case Repo of
-             {_, _, V} ->
-                 V;
-             {_, _, V, _} ->
-                 V
-         end,
-    %% branch/tag it should checkout to
-    Co1 = checkout_to(Co),
-    Name1 = make_dep_name(Name, Co1),
-    RebarFile = rebar_conf_file(dep_path(Name1)),
-    Conf = consult(RebarFile),
-    Deps = get_value(deps, Conf, []),
-    Acc1 = deps_path(Deps, []),
-    deps_path(T, [dep_path(Name1)|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]).
-
-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, IncDir) ->
-    fun(F) ->
-            F1 = filename:join([SrcDir, F]),
-            case is_app_src(F1) of
-                false ->
-                    io:format("Compiling ~s~n", [F1]),
-                    compile:file(F1, ?COMPILE_OPTS(IncDir, EbinDir));
-                true ->
-                    AppFile = filename:join([EbinDir, app_src_to_app(F1)]),
-                    io:format("Writing ~s~n", [AppFile]),
-                    exec("cp", [F1, AppFile])
-            end,
-            code:add_path(EbinDir)
-    end.
-
-%% read rebar.config file and return the {deps, V}
-deps(RebarFile) ->
-    get_value(deps, consult(RebarFile), []).
-
-do_clone_deps([]) ->
-    ok;
-do_clone_deps([H|T]) when is_tuple(H) =:= false ->
-    do_clone_deps(T);
-do_clone_deps([H|T]) ->
-    {Name, Repo} = name_and_repo(H),
-    Co = case Repo of
-             {_, _, V} ->
-                 V;
-             {_, _, V, _} ->
-                 V
-         end,
-    Co1 = checkout_to(Co),
-    DepName = make_dep_name(Name, Co1),
-    case get(DepName) of
-        cloned ->
-            ok;
-        _ ->
-            clone_dep(H)
-    end,
-    do_clone_deps(T).
-
-clone_dep(Dep) ->
-    {Name, Repo} = name_and_repo(Dep),
-    {Cmd, Url, Co} = case Repo of
-                         V={_, _, _} ->
-                             V;
-                         {_Cmd, _Url, _Co, _} ->
-                             {_Cmd, _Url, _Co}
-                     end,
-    Co1 = checkout_to(Co),
-    DepName = make_dep_name(Name, Co1),
-
-    %% command options: clone url path/to/dep -b Branch/Tag
-    DepPath = dep_path(DepName),
-    Opts = ["clone", Url, DepPath],
-    io:format("dependency: ~s~n", [Name]),
-    %% clone
-    exec(Cmd, Opts),
-
-    %% checkout to Co1
-    {ok, Cwd} = file:get_cwd(),
-    ok = file:set_cwd(DepPath),
-    exec(Cmd, ["checkout", Co1]),
-    ok = file:set_cwd(Cwd),
-
-    put(DepName, cloned),
-
-    %% check dependencies of the dependency
-    RebarFile = rebar_conf_file(dep_path(DepName)),
-    do_clone_deps(deps(RebarFile)).
-
-erl_files(Dir) ->
-    filelib:wildcard(filename:join([Dir, "**", "*.erl"])).
-
-app_src_files(Dir) ->
-    filelib:wildcard(filename:join([Dir, "**", "*.app.src"])).
-
-checkout_to({_, V}) -> V;
-checkout_to(Else) -> Else.
+%% compile a project according to the conventions
+'compile-app'(Conf) ->
+    Cwd = mad_utils:cwd(),
+    %% add lib_dirs to path
+    LibDirs = mad_utils:lib_dirs(Cwd, Conf),
+    code:add_paths(LibDirs),
 
-name_and_repo({Name, _, Repo}) ->
-    {atom_to_list(Name), Repo};
-name_and_repo({Name, _, Repo, _}) ->
-    {atom_to_list(Name), Repo}.
+    Dirs = [Cwd|mad_utils:sub_dirs(Cwd, Conf)],
+    lists:foreach(fun mad_compile:app/1, Dirs).
 
-consult(File) ->
-    AbsFile = filename:absname(File),
-    case file:consult(AbsFile) of
-        {ok, V} ->
-            V;
-        _ ->
-            []
-    end.
+'compile-deps'(Conf) ->
+    mad_compile:deps(get_value(deps, Conf, [])).
 
 get_value(Key, Opts, Default) ->
     case lists:keyfind(Key, 1, Opts) of
@@ -293,24 +62,3 @@ get_value(Key, Opts, Default) ->
             Value;
         _ -> Default
     end.
-
-subdirs(_, [], _) ->
-    ok;
-subdirs(Cwd, [H|T], Fun) ->
-    Dir = filename:join([Cwd, H]),
-    Conf = consult(rebar_conf_file(Dir)),
-    subdirs(Dir, get_value(sub_dirs, Conf, []), Fun),
-    Fun(Dir),
-    subdirs(Cwd, T, Fun).
-
-libdirs(_, [], Acc) ->
-    Acc;
-libdirs(Cwd, [H|T], Acc) ->
-    Dirs = filelib:wildcard(filename:join([Cwd, H, "*", "ebin"])),
-    libdirs(Cwd, T, Acc ++ Dirs).
-
-%% https_to_git(X) ->
-%%     re:replace(X, "https://", "git://", [{return, list}]).
-
-%% git_to_https(X) ->
-%%     re:replace(X, "git://", "https://", [{return, list}]).

+ 2 - 0
src/mad.hrl

@@ -0,0 +1,2 @@
+-define(DEPS_PATH, filename:join([mad_utils:home(), ".otp", "deps"])).
+-define(COMPILE_OPTS(Inc, Ebin), [report, {i, Inc}, {outdir, Ebin}]).

+ 99 - 0
src/mad_compile.erl

@@ -0,0 +1,99 @@
+-module(mad_compile).
+
+-export([deps/1]).
+-export([app/1]).
+-compile(export_all).
+-include("mad.hrl").
+
+
+%% compile dependencies
+deps([]) ->
+    ok;
+deps([H|T]) ->
+    {Name, Repo} = mad_deps:name_and_repo(H),
+    Co = case Repo of
+             {_, _, V} ->
+                 V;
+             {_, _, V, _} ->
+                 V
+         end,
+    Co1 = mad_deps:checkout_to(Co),
+    DepName = mad_deps:make_dep_name(Name, Co1),
+    case get(DepName) of
+        compiled ->
+            ok;
+        _ ->
+            dep(DepName)
+    end,
+    deps(T).
+
+%% compile a dependency
+dep(DepName) ->
+    %% check dependencies of the dependency
+    DepPath = mad_deps:path(DepName),
+    RebarFile = mad_utils:rebar_conf_file(DepPath),
+    Conf = mad_utils:consult(RebarFile),
+    Conf1 = mad_utils:script(DepPath, Conf),
+    deps(mad_utils:get_value(deps, Conf1, [])),
+
+    %% add lib_dirs to path
+    LibDirs = mad_utils:lib_dirs(DepPath, Conf1),
+    code:add_paths(LibDirs),
+
+    %% compile sub_dirs and add them to path
+    SubDirs = mad_utils:sub_dirs(DepPath, Conf),
+    lists:foreach(fun app/1, SubDirs),
+
+    SrcDir = mad_utils:src(DepPath),
+    Files = erl_files(SrcDir) ++ app_src_files(SrcDir),
+    case Files of
+        [] ->
+            ok;
+        Files ->
+            EbinDir = mad_utils:ebin(DepPath),
+            IncDir = mad_utils:include(DepPath),
+            mad_utils:exec("mkdir", ["-p", EbinDir]),
+            lists:foreach(compile_fun(SrcDir, EbinDir, IncDir), Files),
+            put(DepName, compiled)
+    end.
+
+app(Dir) ->
+    SrcDir = mad_utils:src(Dir),
+    Files = erl_files(SrcDir) ++ app_src_files(SrcDir),
+    case Files of
+        [] ->
+            ok;
+        Files ->
+            IncDir = mad_utils:include(Dir),
+            EbinDir = mad_utils:ebin(Dir),
+            mad_utils:exec("mkdir", ["-p", EbinDir]),
+            lists:foreach(compile_fun(SrcDir, EbinDir, IncDir), Files),
+            code:add_path(EbinDir)
+    end.
+
+compile_fun(SrcDir, EbinDir, IncDir) ->
+    fun(F) ->
+            F1 = filename:join([SrcDir, F]),
+            case is_app_src(F1) of
+                false ->
+                    io:format("Compiling ~s~n", [F1]),
+                    compile:file(F1, ?COMPILE_OPTS(IncDir, EbinDir));
+                true ->
+                    AppFile = filename:join([EbinDir, app_src_to_app(F1)]),
+                    io:format("Writing ~s~n", [AppFile]),
+                    mad_utils:exec("cp", [F1, AppFile])
+            end,
+            code:add_path(EbinDir)
+    end.
+
+erl_files(Dir) ->
+    filelib:fold_files(Dir, ".erl", true, fun(F, Acc) -> [F|Acc] end, []).
+
+app_src_files(Dir) ->
+    filelib:fold_files(Dir, ".app.src", true, fun(F, Acc) -> [F|Acc] end, []).
+
+is_app_src(Filename) ->
+    Filename =/= filename:rootname(Filename, ".app.src").
+
+app_src_to_app(Filename) ->
+    filename:join([filename:basename(Filename, ".app.src") ++ ".app"]).

+ 119 - 0
src/mad_deps.erl

@@ -0,0 +1,119 @@
+-module(mad_deps).
+
+-export([path/0]).
+-export([path/1]).
+-export([clone/1]).
+-export([paths/1]).
+-export([ebins/1]).
+-export([make_dep_name/2]).
+-export([name_and_repo/1]).
+-export([checkout_to/1]).
+
+-include("mad.hrl").
+
+
+path() ->
+    ?DEPS_PATH.
+
+clone([]) ->
+    ok;
+clone([H|T]) when is_tuple(H) =:= false ->
+    clone(T);
+clone([H|T]) ->
+    {Name, Repo} = name_and_repo(H),
+    Co = case Repo of
+             {_, _, V} ->
+                 V;
+             {_, _, V, _} ->
+                 V
+         end,
+    Co1 = checkout_to(Co),
+    DepName = make_dep_name(Name, Co1),
+    case get(DepName) of
+        cloned ->
+            ok;
+        _ ->
+            clone_dep(H)
+    end,
+    clone(T).
+
+clone_dep(Dep) ->
+    {Name, Repo} = name_and_repo(Dep),
+    {Cmd, Url, Co} = case Repo of
+                         V={_, _, _} ->
+                             V;
+                         {_Cmd, _Url, _Co, _} ->
+                             {_Cmd, _Url, _Co}
+                     end,
+    Co1 = checkout_to(Co),
+    DepName = make_dep_name(Name, Co1),
+
+    %% command options: clone url path/to/dep -b Branch/Tag
+    DepPath = path(DepName),
+    Opts = ["clone", mad_utils:https_to_git(Url), DepPath],
+    io:format("dependency: ~s~n", [Name]),
+    %% clone
+    mad_utils:exec(Cmd, Opts),
+
+    %% checkout to Co1
+    Cwd = mad_utils:cwd(),
+    ok = file:set_cwd(DepPath),
+    mad_utils:exec(Cmd, ["checkout", Co1]),
+    ok = file:set_cwd(Cwd),
+
+    put(DepName, cloned),
+
+    %% check dependencies of the dependency
+    DepPath = path(DepName),
+    RebarFile = mad_utils:rebar_conf_file(DepPath),
+    Conf = mad_utils:consult(RebarFile),
+    Conf1 = mad_utils:script(DepPath, Conf),
+    clone(mad_utils:get_value(deps, Conf1, [])).
+
+paths(Deps) ->
+    paths(Deps, []).
+
+paths([], Acc) ->
+    Acc;
+paths([H|T], Acc) when is_tuple(H) =:= false ->
+    paths(T, Acc);
+paths([H|T], Acc) ->
+    {Name, Repo} = name_and_repo(H),
+    Co = case Repo of
+             {_, _, V} ->
+                 V;
+             {_, _, V, _} ->
+                 V
+         end,
+    %% branch/tag it should checkout to
+    Co1 = checkout_to(Co),
+    Name1 = make_dep_name(Name, Co1),
+    RebarFile = mad_utils:rebar_conf_file(path(Name1)),
+    Conf = mad_utils:consult(RebarFile),
+    Deps = mad_utils:get_value(deps, Conf, []),
+    Acc1 = paths(Deps, []),
+    paths(T, [path(Name1)|Acc ++ Acc1]).
+
+ebins(Deps) ->
+    ebins(paths(Deps), []).
+
+ebins([], Acc) ->
+    Acc;
+ebins([H|T], Acc) ->
+    ebins(T, [filename:join([H, "ebin"])|Acc]).
+
+make_dep_name(Name, Suffix) ->
+    %% Name-Suffix
+    mad_utils:concat([Name, "-", Suffix]).
+
+checkout_to({_, V}) -> V;
+checkout_to(Else) -> Else.
+
+name_and_repo({Name, _, Repo}) ->
+    {atom_to_list(Name), Repo};
+name_and_repo({Name, _, Repo, _}) ->
+    {atom_to_list(Name), Repo}.
+
+path(X) ->
+    %% ~/.otp/deps/X
+    filename:join([?DEPS_PATH, X]).

+ 108 - 0
src/mad_utils.erl

@@ -0,0 +1,108 @@
+-module(mad_utils).
+
+-export([cwd/0]).
+-export([exec/2]).
+-export([concat/1]).
+-export([home/0]).
+-export([rebar_conf_file/1]).
+-export([src/1]).
+-export([include/1]).
+-export([ebin/1]).
+-export([consult/1]).
+-export([deps/1]).
+-export([get_value/3]).
+-export([script/2]).
+-export([sub_dirs/2]).
+-export([lib_dirs/2]).
+-export([https_to_git/1]).
+-export([git_to_https/1]).
+
+
+%% get current working directory
+cwd() ->
+    {ok, Cwd} = file:get_cwd(),
+    Cwd.
+
+%% execute a shell command
+exec(Cmd, Opts) ->
+    Opts1 = [concat([" ", X]) || X <- Opts],
+    os:cmd(concat([Cmd, concat(Opts1)])).
+
+concat(L) ->
+    lists:concat(L).
+
+%% return $HOME
+home() ->
+    %% ~/
+    {ok, [[H|_]]} = init:get_argument(home),
+    H.
+
+rebar_conf_file(X) ->
+    filename:absname(filename:join([X, "rebar.config"])).
+
+src(X) ->
+    %% X/src
+    filename:join([X, "src"]).
+
+include(X) ->
+    %% X/include
+    filename:join([X, "include"]).
+
+ebin(X) ->
+    %% X/ebin
+    filename:join([X, "ebin"]).
+
+consult(File) ->
+    AbsFile = filename:absname(File),
+    case file:consult(AbsFile) of
+        {ok, V} ->
+            V;
+        _ ->
+            []
+    end.
+
+deps(File) ->
+    get_value(deps, consult(File), []).
+
+get_value(Key, Opts, Default) ->
+    case lists:keyfind(Key, 1, Opts) of
+        {Key, Value} ->
+            Value;
+        _ -> Default
+    end.
+
+script(Dir, Conf) ->
+    File = filename:join([Dir, "rebar.config.script"]),
+    case file:script(File, [{'CONFIG', Conf}]) of
+        {ok, Out} ->
+            Out;
+        {error, _} ->
+            Conf
+    end.
+
+sub_dirs(Cwd, Conf) ->
+    sub_dirs(Cwd, get_value(sub_dirs, Conf, []), []).
+
+sub_dirs(_, [], Acc) ->
+    Acc;
+sub_dirs(Cwd, [Dir|T], Acc) ->
+    SubDir = filename:join([Cwd, Dir]),
+    Conf = consult(rebar_conf_file(SubDir)),
+    Conf1 = script(SubDir, Conf),
+    Acc1 = sub_dirs(SubDir, get_value(sub_dirs, Conf1, []), Acc),
+    sub_dirs(Cwd, T, [SubDir|Acc1]).
+
+lib_dirs(Cwd, Conf) ->
+    lib_dirs(Cwd, get_value(lib_dirs, Conf, []), []).
+
+lib_dirs(_, [], Acc) ->
+    Acc;
+lib_dirs(Cwd, [H|T], Acc) ->
+    Dirs = filelib:wildcard(filename:join([Cwd, H, "*", "ebin"])),
+    lib_dirs(Cwd, T, Acc ++ Dirs).
+
+https_to_git(X) ->
+    re:replace(X, "https://", "git://", [{return, list}]).
+
+git_to_https(X) ->
+    re:replace(X, "git://", "https://", [{return, list}]).