diff options
Diffstat (limited to 'src/rebar_fetch.erl')
-rw-r--r-- | src/rebar_fetch.erl | 247 |
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]). |