summaryrefslogtreecommitdiff
path: root/src/rebar_fetch.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_fetch.erl')
-rw-r--r--src/rebar_fetch.erl247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
new file mode 100644
index 0000000..6ed0f3f
--- /dev/null
+++ b/src/rebar_fetch.erl
@@ -0,0 +1,247 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% -------------------------------------------------------------------
+-module(rebar_fetch).
+
+-export([new/4,
+ current_ref/2,
+ download_source/2,
+ update_source1/2,
+ source_engine_avail/1,
+ source_engine_avail/2,
+ has_vcs_dir/2,
+ print_source/1,
+ format_source/2]).
+
+-include("rebar.hrl").
+
+-type dep_name() :: atom().
+-type dep_vsn() :: ec_semver:any_version().
+-type dep_source() :: {atom(), string(), ref()}.
+-type ref() :: string() | {atom(), string()}.
+
+-type dep() :: { dep_name(), dep_vsn(), dep_source() }.
+
+-record(p4_settings, {
+ client=undefined,
+ transport="tcp4:perforce:1666",
+ username,
+ password
+ }).
+
+new(Dir, App, Vsn, Source) ->
+ Ref = current_ref(Dir, Source),
+ {App, Vsn, setelement(3, Source, Ref)}.
+
+init_p4_settings(Basename) ->
+ #p4_settings{client =
+ case inet:gethostname() of
+ {ok,HostName} ->
+ HostName ++ "-"
+ ++ os:getenv("USER") ++ "-"
+ ++ Basename
+ ++ "-Rebar-automated-download"
+ end}.
+
+current_ref(AppDir, {git, _, _}) ->
+ string:strip(os:cmd("git --git-dir='" ++ AppDir ++ "/.git' rev-parse --verify HEAD"), both, $\n).
+
+download_source(AppDir, {p4, Url}) ->
+ download_source(AppDir, {p4, Url, "#head"});
+download_source(AppDir, {p4, Url, Rev}) ->
+ download_source(AppDir, {p4, Url, Rev, init_p4_settings(filename:basename(AppDir))});
+download_source(AppDir, {p4, Url, _Rev, Settings}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh_send("p4 client -i",
+ ?FMT("Client: ~s~n"
+ ++"Description: generated by Rebar~n"
+ ++"Root: ~s~n"
+ ++"View:~n"
+ ++" ~s/... //~s/...~n",
+ [Settings#p4_settings.client,
+ AppDir,
+ Url,
+ Settings#p4_settings.client]),
+ []),
+ rebar_utils:sh(?FMT("p4 -c ~s sync -f", [Settings#p4_settings.client]), []);
+download_source(AppDir, {hg, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("hg update ~s", [Rev]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url}) ->
+ download_source(AppDir, {git, Url, {branch, "HEAD"}});
+download_source(AppDir, {git, Url, ""}) ->
+ download_source(AppDir, {git, Url, {branch, "HEAD"}});
+download_source(AppDir, {git, Url, {branch, Branch}}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url, {tag, Tag}}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, AppDir}]);
+download_source(AppDir, {bzr, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("bzr branch -r ~s ~s ~s",
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]);
+download_source(AppDir, {svn, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("svn checkout -r ~s ~s ~s",
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]);
+download_source(AppDir, {rsync, Url}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s", [Url, AppDir]), []);
+download_source(AppDir, {fossil, Url}) ->
+ download_source(AppDir, {fossil, Url, ""});
+download_source(AppDir, {fossil, Url, Version}) ->
+ Repository = filename:join(AppDir, filename:basename(AppDir) ++ ".fossil"),
+ ok = filelib:ensure_dir(Repository),
+ ok = file:set_cwd(AppDir),
+ rebar_utils:sh(?FMT("fossil clone ~s ~s", [Url, Repository]),
+ [{cd, AppDir}]),
+ rebar_utils:sh(?FMT("fossil open ~s ~s --nested", [Repository, Version]),
+ []).
+
+update_source1(AppDir, Args) when element(1, Args) =:= p4 ->
+ download_source(AppDir, Args);
+update_source1(AppDir, {git, Url}) ->
+ update_source1(AppDir, {git, Url, {branch, "HEAD"}});
+update_source1(AppDir, {git, Url, ""}) ->
+ update_source1(AppDir, {git, Url, {branch, "HEAD"}});
+update_source1(AppDir, {git, _Url, {branch, Branch}}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Branch]), ShOpts),
+ rebar_utils:sh(
+ ?FMT("git pull --ff-only --no-rebase -q origin ~s", [Branch]),ShOpts);
+update_source1(AppDir, {git, _Url, {tag, Tag}}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts);
+update_source1(AppDir, {git, _Url, Refspec}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Refspec]), ShOpts);
+update_source1(AppDir, {svn, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {hg, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {bzr, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {rsync, Url}) ->
+ rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]);
+update_source1(AppDir, {fossil, Url}) ->
+ update_source1(AppDir, {fossil, Url, ""});
+update_source1(AppDir, {fossil, _Url, Version}) ->
+ ok = file:set_cwd(AppDir),
+ rebar_utils:sh("fossil pull", [{cd, AppDir}]),
+ rebar_utils:sh(?FMT("fossil update ~s", [Version]), []).
+
+%% ===================================================================
+%% Source helper functions
+%% ===================================================================
+
+source_engine_avail(Source) ->
+ Name = element(1, Source),
+ source_engine_avail(Name, Source).
+
+source_engine_avail(Name, Source)
+ when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync;
+ Name == fossil; Name == p4 ->
+ case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of
+ true ->
+ true;
+ false ->
+ ?ABORT("Rebar requires version ~p or higher of ~s to process ~p\n",
+ [required_vcs_client_vsn(Name), Name, Source])
+ end.
+
+vcs_client_vsn(false, _VsnArg, _VsnRegex) ->
+ false;
+vcs_client_vsn(Path, VsnArg, VsnRegex) ->
+ {ok, Info} = rebar_utils:sh(Path ++ VsnArg, [{env, [{"LANG", "C"}]},
+ {use_stdout, false}]),
+ case re:run(Info, VsnRegex, [{capture, all_but_first, list}]) of
+ {match, Match} ->
+ list_to_tuple([list_to_integer(S) || S <- Match]);
+ _ ->
+ false
+ end.
+
+required_vcs_client_vsn(p4) -> {2013, 1};
+required_vcs_client_vsn(hg) -> {1, 1};
+required_vcs_client_vsn(git) -> {1, 5};
+required_vcs_client_vsn(bzr) -> {2, 0};
+required_vcs_client_vsn(svn) -> {1, 6};
+required_vcs_client_vsn(rsync) -> {2, 0};
+required_vcs_client_vsn(fossil) -> {1, 0}.
+
+vcs_client_vsn(p4) ->
+ vcs_client_vsn(rebar_utils:find_executable("p4"), " -V",
+ "Rev\\. .*/(\\d+)\\.(\\d)/");
+vcs_client_vsn(hg) ->
+ vcs_client_vsn(rebar_utils:find_executable("hg"), " --version",
+ "version (\\d+).(\\d+)");
+vcs_client_vsn(git) ->
+ vcs_client_vsn(rebar_utils:find_executable("git"), " --version",
+ "git version (\\d+).(\\d+)");
+vcs_client_vsn(bzr) ->
+ vcs_client_vsn(rebar_utils:find_executable("bzr"), " --version",
+ "Bazaar \\(bzr\\) (\\d+).(\\d+)");
+vcs_client_vsn(svn) ->
+ vcs_client_vsn(rebar_utils:find_executable("svn"), " --version",
+ "svn, version (\\d+).(\\d+)");
+vcs_client_vsn(rsync) ->
+ vcs_client_vsn(rebar_utils:find_executable("rsync"), " --version",
+ "rsync version (\\d+).(\\d+)");
+vcs_client_vsn(fossil) ->
+ vcs_client_vsn(rebar_utils:find_executable("fossil"), " version",
+ "version (\\d+).(\\d+)").
+
+has_vcs_dir(p4, _) ->
+ true;
+has_vcs_dir(git, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".git"));
+has_vcs_dir(hg, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".hg"));
+has_vcs_dir(bzr, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".bzr"));
+has_vcs_dir(svn, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".svn"))
+ orelse filelib:is_dir(filename:join(Dir, "_svn"));
+has_vcs_dir(rsync, _) ->
+ true;
+has_vcs_dir(_, _) ->
+ true.
+
+print_source({App, _, Source}) ->
+ ?CONSOLE("~s~n", [format_source(App, Source)]).
+
+format_source(App, {p4, Url}) ->
+ format_source(App, {p4, Url, "#head"});
+format_source(App, {git, Url}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
+format_source(App, {git, Url, ""}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
+format_source(App, {git, Url, {branch, Branch}}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, Branch, Url]);
+format_source(App, {git, Url, {tag, Tag}}) ->
+ ?FMT("~p TAG ~s ~s", [App, Tag, Url]);
+format_source(App, {_, Url, Rev}) ->
+ ?FMT("~p REV ~s ~s", [App, Rev, Url]);
+format_source(App, undefined) ->
+ ?FMT("~p", [App]).