Skip to content

Commit

Permalink
command line flags, help
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmytro Lytovchenko committed Feb 9, 2014
1 parent 2976ccf commit a957756
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 296 deletions.
4 changes: 4 additions & 0 deletions Makefile
Expand Up @@ -2,6 +2,10 @@
compile:
rebar get-deps compile escriptize

.PHONY: clean
clean:
rm ebin/* *.log

#-------------------------------------------------------------------------------
OTP_PLT = .epm_otp.plt
COMBO_PLT = .epm_combo.plt
Expand Down
27 changes: 16 additions & 11 deletions include/epm.hrl
Expand Up @@ -2,36 +2,41 @@
-define(epm_year, 2014).
-define(DEFAULT_API_MODULES, [github_api]).

-define(any_author, any_author).
-define(any_platform, any_platform).
-define(any_vsn, any_vsn).
-define(any_erlang_vsn, any_erlang_vsn).
-define(any_name, any_n).
-define(any_author, any_a).
-define(any_platform, any_p).
-define(any_vsn, any_v).
-define(any_erlang_vsn, any_e).

-define(EPM_FAIL(Format, Args), begin exit({error, epm:s(Format, Args)}) end).
-define(EPM_EXIT(Format, Args), begin exit({ok, epm:s(Format, Args)}) end).

-type platform() :: x86 | x64 | ?any_platform.
-type erlangvsn() :: binary().
-type api_module() :: epm_api_github.
-type pkg_arg() :: source | {tag|branch|hash, string()}.

-record(repoid, { name :: string()
}).
-type repoid() :: #repoid{}.

%% Unified global identifier for package
-record(pkgid, { author=?any_author :: string() | ?any_author
, pkg_name :: string()
, platform=?any_platform :: platform()
, vsn=?any_vsn :: string() | ?any_vsn
, erlang_vsn=?any_erlang_vsn :: erlangvsn() | ?any_erlang_vsn
}).
-record(pkgid, { author = ?any_author :: string() | ?any_author
, pkg_name = ?any_name :: string() | ?any_name
, platform = ?any_platform :: platform()
, vsn = ?any_vsn :: string() | ?any_vsn
, erlang_vsn = ?any_erlang_vsn :: erlangvsn() | ?any_erlang_vsn
%% arguments passed from command line, like: source, {tag|branch|hash, x}
, args=[] :: [pkg_arg()]
}).
-type pkgid() :: #pkgid{}.

-record(repo, { id :: repoid()
, description :: string()
, url :: string()
%, followers
%, pushed
, api_module
, api_module=epm_api_github :: api_module()
}).
-type repo() :: #repo{}.

Expand Down
17 changes: 15 additions & 2 deletions src/epm.erl
Expand Up @@ -8,7 +8,7 @@
, matches/2
, platform/1
, p/3, p/2, p/1
, s/2, pkgid_match_spec/1]).
, s/2, pkgid_match_spec/1, args/1, arg_bool/2, arg/2, set_arg_bool/3]).

-include("epm.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
Expand Down Expand Up @@ -58,15 +58,28 @@ platform(#pkgid{platform=X}) -> X.
erlang_vsn(#pkg{id=#pkgid{erlang_vsn=X}}) -> X;
erlang_vsn(#pkgid{erlang_vsn=X}) -> X.

args(#pkgid{args=X}) -> X.

arg_bool(Arg, #pkgid{args=X}) -> lists:member(Arg, X).

arg(Arg, #pkgid{args=X}) -> proplists:get_value(Arg, X).

set_arg_bool(Arg, false, #pkgid{args=Args}=Pkgid) ->
Pkgid#pkgid{args = lists:delete(Arg, Args)};
set_arg_bool(Arg, true, #pkgid{args=Args}=Pkgid) ->
Pkgid#pkgid{args = [Arg | lists:delete(Arg, Args)]}.

%%------------------------------------------------------------------------------
as_string(#pkg{id=Id}) -> as_string(Id);
as_string(#pkgid{author=A, pkg_name=N, vsn=V, platform=P, erlang_vsn=E}) ->
as_string(#pkgid{ author=A, pkg_name=N, vsn=V, platform=P, erlang_vsn=E
, args=Args}) ->
"["
++ case A of ?any_author -> ""; _ -> s("~s/", [A]) end
++ N
++ case V of ?any_vsn -> ""; _ -> s(", ~s", [V]) end
++ case P of ?any_platform -> ""; _ -> s(", ~s", [P]) end
++ case E of ?any_erlang_vsn -> ""; _ -> s(", ~s", [E]) end
++ case Args of [] -> ""; _ -> s(" ~p", [Args]) end
++ "]";
as_string(#repo{id=I, description=_D, url=U}) ->
s("[Repo ~s url=~s]", [I, U]);
Expand Down
23 changes: 23 additions & 0 deletions src/epm_api_github.erl
@@ -0,0 +1,23 @@
%%%-------------------------------------------------------------------
%%% @author Dmytro Lytovchenko <dmytro.lytovchenko@gmail.com>
%%% @doc Handles Github repositories
%%% @end
%%% Created : 09. Feb 2014 8:16 PM
%%%-------------------------------------------------------------------
-module(epm_api_github).
-behaviour(gen_epm_api).

%% API
-export([get_source/2]).

-include("epm.hrl").

-spec get_source(Pkg :: pkg(), DestDir :: string()) -> ok | {error, any()}.
get_source(Pkg=#pkg{id=#pkgid{author=A, pkg_name=N}}, DestDir) ->
Url = get_vcs_url(Pkg),
epm_util:git(["clone", Url, DestDir ++ epm:s("/~s_~s", [A, N])]).

get_vcs_url(#pkg{id=#pkgid{author=?any_author}}) -> {error, author};
get_vcs_url(#pkg{id=#pkgid{pkg_name=?any_name}}) -> {error, pkg_name};
get_vcs_url(#pkg{id=#pkgid{author=A, pkg_name=N}}) ->
epm:s("https://github.com/~s/~s.git", [A, N]).
152 changes: 75 additions & 77 deletions src/epm_core.erl
Expand Up @@ -151,51 +151,50 @@ execute(_State=#epm_state{}, ["config" | Args]) ->
end;

execute(_State=#epm_state{}, _) ->
io:format("Usage: epm commands~n~n"),
io:format(" install [<user>/]<project> {project options}, ... {global options}~n"),
io:format(" project options:~n"),
io:format(" --tag <tag>~n"),
io:format(" --branch <branch>~n"),
io:format(" --sha <sha>~n"),
io:format(" --with-deps (default)~n"),
io:format(" --without-deps~n"),
io:format(" --prebuild-command <cmd>~n"),
io:format(" --build-command <cmd>~n"),
io:format(" --test-command <cmd>~n"),
io:format(" global options:~n"),
io:format(" --verbose~n"),
io:format(" --config-set <key> <value>~n~n"),
io:format(" remove [<user>/]<project> {project options}, ... {global options}~n"),
io:format(" project options:~n"),
io:format(" --tag <tag>~n"),
io:format(" --branch <branch>~n"),
io:format(" --sha <sha>~n"),
io:format(" global options:~n"),
io:format(" --verbose~n~n"),
io:format(" --config-set <key> <value>~n~n"),
io:format(" update [<user>/]<project> {project options}, ... {global options}~n"),
io:format(" project options:~n"),
io:format(" --tag <tag>~n"),
io:format(" --branch <branch>~n"),
io:format(" --sha <sha>~n"),
io:format(" --with-deps~n"),
io:format(" --without-deps (default)~n"),
io:format(" global options:~n"),
io:format(" --verbose~n~n"),
io:format(" --config-set <key> <value>~n~n"),
io:format(" info [<user>/]<project>, ... {global options}~n"),
io:format(" global options:~n"),
io:format(" --config-set <key> <value>~n~n"),
io:format(" search <project>, ... {global options}~n"),
io:format(" global options:~n"),
io:format(" --config-set <key> <value>~n~n"),
io:format(" list~n~n"),
io:format(" latest~n~n"),
io:format(" config {options}~n"),
io:format(" options:~n"),
io:format(" --get (default)~n"),
io:format(" --set <key> <value>~n"),
io:format(" --remove <key>~n"),
epm:p("Usage: epm commands~n~n"
" install [<author>/]<project> {project options}, ... {global options}~n"
" project options:~n"
" --source :: requests source install (does not build)~n"
" --platform x86|x64, --vsn <v>, --tag <t>, --branch <b>, --hash <h>~n"
%% " --with-deps (default)~n"
%% " --without-deps~n"
%% " --prebuild-command <cmd>~n"
%% " --build-command <cmd>~n"
%% " --test-command <cmd>~n"
" global options:~n"
%% " --verbose~n"
" --config-set <key> <value>~n~n"
%% " remove [<user>/]<project> {project options}, ... {global options}~n"
%% " project options:~n"
%% " --tag <tag>~n"
%% " --branch <branch>~n"
%% " --sha <sha>~n"
%% " global options:~n"
%% " --verbose~n~n"
%% " --config-set <key> <value>~n~n"
%% " update [<user>/]<project> {project options}, ... {global options}~n"
%% " project options:~n"
%% " --tag <tag>~n"
%% " --branch <branch>~n"
%% " --sha <sha>~n"
%% " --with-deps~n"
%% " --without-deps (default)~n"
%% " global options:~n"
%% " --verbose~n~n"
%% " --config-set <key> <value>~n~n"
%% " info [<user>/]<project>, ... {global options}~n"
%% " global options:~n"
%% " --config-set <key> <value>~n~n"
%% " search <project>, ... {global options}~n"
%% " global options:~n"
%% " --config-set <key> <value>~n~n"
%% " list~n~n"
%% " latest~n~n"
" config {options}~n"
" options:~n"
" --get (default)~n"
" --set <key> <value>~n"
" --remove <key>~n"),
ok.

%% -----------------------------------------------------------------------------
Expand All @@ -209,32 +208,32 @@ collect_args(Target, Args) ->

-spec collect_args_internal(Target :: atom()
, Args :: [string()]
, Packages :: [#pkgid{}]
, Pkgids :: [#pkgid{}]
, Flags :: [atom()])
-> {[#pkgid{}], [atom()]}.
collect_args_internal(_, [], Packages, Flags) ->
{lists:reverse(Packages), lists:reverse(Flags)};
collect_args_internal(Target, [Arg | Rest], Packages, Flags) ->
collect_args_internal(Target, [Arg | Rest], Pkgids, Flags) ->
case parse_tag(Target, Arg) of
undefined -> %% if not a tag then must be a project name
%% split into user and project
{ProjectName, User} = epm_ops:split_package(Arg),
collect_args_internal(Target, Rest
, [#pkgid{author=User, pkg_name=ProjectName}|Packages]
, [#pkgid{author=User, pkg_name=ProjectName}|Pkgids]
, Flags);
{Type, Tag, 0} -> %% tag with no trailing value
case Type of
%% project ->
%% [#pkg{args = Args} = Package|OtherPackages] = Packages,
%% collect_args_internal(Target, Rest
%% , [Package#pkg{args = Args ++ [Tag]}|OtherPackages]
%% , Flags);
project ->
%% Update head of Pkgids with additional arg
[#pkgid{args=Args} = Pkgid|Tail] = Pkgids,
Pkgid1 = Pkgid#pkgid{ args = Args ++ [Tag] },
collect_args_internal(Target, Rest, [Pkgid1 | Tail], Flags);
global ->
collect_args_internal(Target, Rest, Packages, [Tag|Flags])
collect_args_internal(Target, Rest, Pkgids, [Tag|Flags])
end;
{Type, Tag, NumVals} when is_integer(NumVals) -> % tag with trailing value(s)
if length(Rest) < NumVals ->
exit("poorly formatted command");
?EPM_FAIL("poorly formatted command", []);
true -> ok
end,
{Vals, Rest1} = lists:split(NumVals, Rest),
Expand All @@ -243,17 +242,15 @@ collect_args_internal(Target, [Arg | Rest], Packages, Flags) ->
_ -> Vals
end,
case Type of
%% project ->
%% %% this tag applies to the last project on the stack
%% [#pkg{args=Args}=Package | OtherPackages] = Packages,
%% Vsn = if Tag == tag; Tag == branch; Tag == sha -> Vals1;
%% true -> Package#pkg.vsn
%% end,
%% collect_args_internal( Target, Rest1
%% , [Package#pkg{ vsn = Vsn
%% , args = Args ++ [{Tag, Vals1}]
%% } | OtherPackages]
%% , Flags);
project ->
%% Update head of Pkgids with additional arg
%% this tag applies to the last project on the stack
[#pkgid{args=Args}=Pkgid | Tail] = Pkgids,
Vsn = if Tag == tag; Tag == branch; Tag == hash -> Vals1;
true -> Pkgid#pkgid.vsn
end,
Pkgid2 = Pkgid#pkgid{ vsn = Vsn, args = Args ++ [{Tag, Vals1}] },
collect_args_internal( Target, Rest1, [Pkgid2 | Tail], Flags);
global ->
if Tag == config_set ->
[K, V1] = Vals1,
Expand All @@ -264,7 +261,7 @@ collect_args_internal(Target, [Arg | Rest], Packages, Flags) ->
end;
true -> ok
end,
collect_args_internal(Target, Rest1, Packages, [{Tag, Vals1}|Flags])
collect_args_internal(Target, Rest1, Pkgids, [{Tag, Vals1}|Flags])
end
end.

Expand All @@ -273,19 +270,20 @@ collect_args_internal(Target, [Arg | Rest], Packages, Flags) ->
%% Arg = string()
%% Tag = atom()
%% HasValue = bool()
parse_tag(install, "--source") -> {project, source, 0};
parse_tag(install, "--tag") -> {project, tag, 1};
parse_tag(install, "--branch") -> {project, branch, 1};
parse_tag(install, "--sha") -> {project, sha, 1};
parse_tag(install, "--prebuild-command") -> {project, prebuild_command, 1};
parse_tag(install, "--build-command") -> {project, build_command, 1};
parse_tag(install, "--test-command") -> {project, test_command, 1};
parse_tag(install, "--hash") -> {project, hash, 1};
%% parse_tag(install, "--prebuild-command") -> {project, prebuild_command, 1};
%% parse_tag(install, "--build-command") -> {project, build_command, 1};
%% parse_tag(install, "--test-command") -> {project, test_command, 1};

parse_tag(info, "--tag") -> {project, tag, 1};
parse_tag(info, "--branch") -> {project, branch, 1};
parse_tag(info, "--sha") -> {project, sha, 1};
%% parse_tag(info, "--tag") -> {project, tag, 1};
%% parse_tag(info, "--branch") -> {project, branch, 1};
%% parse_tag(info, "--sha") -> {project, sha, 1};

parse_tag(_, "--with-deps") -> {project, with_deps, 0};
parse_tag(_, "--without-deps") -> {project, without_deps, 0};
%% parse_tag(_, "--with-deps") -> {project, with_deps, 0};
%% parse_tag(_, "--without-deps") -> {project, without_deps, 0};

parse_tag(config, "--get") -> {global, get, 0};
parse_tag(config, "--set") -> {global, set, 2};
Expand Down Expand Up @@ -314,7 +312,7 @@ update_epm(_State) ->
{ok, {{_, 200, _}, _, Body}} ->
case file:write_file(File, Body) of
ok ->
io:format("+ updated epm (~s) to latest version~n", [File]);
epm:p("+ updated epm (~s) to latest version~n", [File]);
{error, Reason} ->
exit(lists:flatten(io_lib:format("failed to overwrite epm executable ~s: ~p~n", [File, Reason])))
end;
Expand Down
11 changes: 9 additions & 2 deletions src/epm_deps.erl
Expand Up @@ -25,9 +25,16 @@ resolve_dependencies(Pkgids) ->
resolve_dependencies_internal(Pkgids, Pkgids) -> Pkgids;
resolve_dependencies_internal(Pkgids, _Previous) ->
F = fun(Pkgid=#pkgid{}, A) ->
SourceFlag = epm:arg_bool(source, Pkgid),
PkgList = epm_index:list_global_matching(Pkgid),
lists:foldl(fun(Dep, A1) -> ordsets:add_element(Dep, A1) end
, A, lists:flatten([Pkg#pkg.deps || Pkg <- PkgList]))
lists:foldl(fun(Dep, A1) ->
% source flag spreads over all deps
Dep1 = epm:set_arg_bool(source, SourceFlag, Dep),
ordsets:add_element(Dep1, A1)
end, A, lists:flatten([Pkg#pkg.deps || Pkg <- PkgList]))
end,
%% For all the package ids get matching packages (TODO: Get 1 best candidate)
%% and merge it with all the dependency ids

PkgidsPlusDeps = lists:foldl(F, ordsets:from_list(Pkgids), Pkgids),
resolve_dependencies_internal(PkgidsPlusDeps, Pkgids).

0 comments on commit a957756

Please sign in to comment.