From 41f1a891d9f126e4b0c3846c40e881ac77328734 Mon Sep 17 00:00:00 2001 From: Nathaniel Waisbrot Date: Tue, 17 Sep 2013 11:22:05 -0400 Subject: [PATCH] Add 'p4' (Perforce) as a dependency type This calls the 'p4' command-line tool to checkout and sync Perforce trees. It involves significantly more special code in Rebar than using 'git p4', but it eliminates the indirection of Rebar->Git->Python->Perforce --- rebar.config.sample | 5 +++-- src/rebar_deps.erl | 49 +++++++++++++++++++++++++++++++++++++++++++-- src/rebar_utils.erl | 20 ++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/rebar.config.sample b/rebar.config.sample index 515ed00..47812c1 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -155,7 +155,7 @@ %% name as an atom, eg. mochiweb, a name and a version (from the .app file), or %% an application name, a version and the SCM details on how to fetch it (SCM %% type, location and revision). -%% Rebar currently supports git, hg, bzr, svn, rsync, and fossil. +%% Rebar currently supports git, hg, bzr, svn, rsync, fossil, and p4. {deps, [app_name, {rebar, "1.0.*"}, {rebar, ".*", @@ -188,7 +188,8 @@ {app_name, ".*", {svn, "svn://svn.example.org/url"}}, {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}}, {app_name, ".*", {fossil, "https://www.example.org/url"}}, - {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}]}. + {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}, + {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}. %% == Subdirectories == diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl index 43bde04..392882c 100644 --- a/src/rebar_deps.erl +++ b/src/rebar_deps.erl @@ -277,7 +277,8 @@ info_help(Description) -> {app_name, ".*", {svn, "svn://svn.example.org/url"}}, {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}}, {app_name, ".*", {fossil, "https://www.example.org/url"}}, - {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}]} + {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}, + {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]} ]). %% Added because of trans deps, @@ -507,6 +508,40 @@ use_source(Config, Dep, Count) -> use_source(Config, Dep#dep { dir = TargetDir }, Count-1) end. +-record(p4_settings, { + client=undefined, + transport="tcp4:perforce:1666", + username, + password + }). +init_p4_settings(Basename) -> + #p4_settings{client = + case inet:gethostname() of + {ok,HostName} -> + HostName ++ "-" + ++ os:getenv("USER") ++ "-" + ++ Basename + ++ "-Rebar-automated-download" + end}. + +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)]), @@ -573,6 +608,8 @@ update_source(Config, Dep) -> Dep end. +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, ""}) -> @@ -696,7 +733,7 @@ source_engine_avail(Source) -> source_engine_avail(Name, Source) when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync; - Name == fossil -> + Name == fossil; Name == p4 -> case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of true -> true; @@ -717,6 +754,7 @@ vcs_client_vsn(Path, VsnArg, VsnRegex) -> 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}; @@ -724,6 +762,9 @@ 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+)"); @@ -743,6 +784,8 @@ 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) -> @@ -760,6 +803,8 @@ has_vcs_dir(_, _) -> print_source(#dep{app=App, source=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, ""}) -> diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 2d227b6..6b8e874 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -31,6 +31,7 @@ get_arch/0, wordsize/0, sh/2, + sh_send/3, find_files/2, find_files/3, now_str/0, ensure_dir/1, @@ -87,6 +88,24 @@ wordsize() -> integer_to_list(8 * erlang:system_info(wordsize)) end. +sh_send(Command0, String, Options0) -> + ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~s < ~s\n", [get_cwd(), Command0, String]), + ?DEBUG("\topts: ~p\n", [Options0]), + + DefaultOptions = [use_stdout, abort_on_error], + Options = [expand_sh_flag(V) + || V <- proplists:compact(Options0 ++ DefaultOptions)], + + Command = patch_on_windows(Command0, proplists:get_value(env, Options, [])), + PortSettings = proplists:get_all_values(port_settings, Options) ++ + [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], + Port = open_port({spawn, Command}, PortSettings), + + %% allow us to send some data to the shell command's STDIN + %% Erlang doesn't let us get any reply after sending an EOF, though... + Port ! {self(), {command, String}}, + port_close(Port). + %% %% Options = [Option] -- defaults to [use_stdout, abort_on_error] %% Option = ErrorOption | OutputOption | {cd, string()} | {env, Env} @@ -478,6 +497,7 @@ vcs_vsn_1(Vcs, Dir) -> end. vcs_vsn_cmd(git) -> "git describe --always --tags"; +vcs_vsn_cmd(p4) -> "echo #head"; vcs_vsn_cmd(hg) -> "hg identify -i"; vcs_vsn_cmd(bzr) -> "bzr revno"; vcs_vsn_cmd(svn) -> "svnversion";