Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

work

  • Loading branch information...
commit 9b6668937e0e9cbc9f46066f8c43328933ce3294 1 parent d61a5d1
Dmytro Lytovchenko authored
View
6 .gitignore
@@ -1 +1,7 @@
ebin/*.beam
+deps/*
+deps
+ebin/*
+epm
+*.iml
+.idea
View
28 README.markdown → README.md
@@ -1,6 +1,30 @@
-# How did this project get here?
+# Erlang Package Manager (epm)
-http://hyperthunk.wordpress.com/2012/05/28/does-erlangotp-need-a-new-package-management-solution/
+This is client/server package manager for prebuilt binary Erlang packages, but
+with source support. See requirements below.
+
+Project is in development. Do not use.
+
+## Requirements
+
+These are things I expect `epm` to be able to do:
+
+### Client
+
+* Support rebar config and possibly own format for dependencies. Similar to ruby
+ version lock file.
+* Be able to fetch binary and source packages; if commit hash is locked, then
+ only source is available.
+* Store configs and cache of downloaded binary packages in user home
+
+### Server
+
+* Binary repo running on a commodity web server with index and subdirectories
+ like those Linux repos do;
+
+### Future Plans
+
+* Store index in Git(hub) with pull-requests and signing
--------------------
View
6 ebin/epm.app
@@ -4,10 +4,10 @@
{modules, [ epm,
epm_core,
epm_package,
- epm_util,
+ epm_util, epm_cache, epm_deps,
api_behavior,
- github_api,
- bitbucket_api,
+ %github_api,
+ %bitbucket_api,
yaws_html
]},
{registered, []},
View
15 ebin/epm_cfg.erl
@@ -0,0 +1,15 @@
+%%%-------------------------------------------------------------------
+%%% @author Dmytro Lytovchenko <dmytro.lytovchenko@gmail.com>
+%%% @doc Configuration global storage
+%%% @end
+%%% Created : 08. Feb 2014 1:56 PM
+%%%-------------------------------------------------------------------
+-module(epm_cfg).
+
+%% API
+-export([]).
+
+init(Config) ->
+ ets:new().
+
+set(Key, Value) ->
View
20 include/epm.hrl
@@ -1,7 +1,23 @@
-define(epm_version, "0.1.1").
-define(epm_year, 2014).
+-define(DEFAULT_API_MODULES, [github_api]).
-define(EXIT(Format, Args), exit(lists:flatten(io_lib:format(Format, Args)))).
--record(repository, {name, owner, description, homepage, followers, pushed, api_module}).
--record(package, {user, name, vsn=undefined, app_vsn, install_dir, deps=[], args=[], repo=#repository{}}).
+ , owner
+ , description
+ , homepage
+ , followers
+ , pushed
+ , api_module
+ }).
+-record(package, {user
+ , name
+ , vsn=undefined
+ , app_vsn
+ , install_dir
+ , deps=[]
+ , args=[]
+ , repo=#repository{}
+ }).
View
0  src/bitbucket_api.erl → src/bitbucket_api.bak
File renamed without changes
View
4 src/epm.erl
@@ -11,7 +11,7 @@ main(Args) ->
application:set_env(sasl, sasl_error_logger, false),
lists:map(fun application:start/1, [sasl, crypto, public_key, ssl, ibrowse, epm]),
- case (catch main1(Args)) of
+ case (catch main_internal(Args)) of
{'EXIT', ErrorMsg} when is_list(ErrorMsg) ->
io:format("- ~s~n", [ErrorMsg]);
{'EXIT', Other} ->
@@ -23,7 +23,7 @@ main(Args) ->
dets:close(epm_index),
io:format("~n").
-main1(Args) ->
+main_internal(Args) ->
Home = epm_util:home_dir(),
EpmHome = epm_util:epm_home_dir(Home),
epm_util:open_dets_table(Home, EpmHome),
View
376 src/epm_cache.erl
@@ -0,0 +1,376 @@
+%%%-------------------------------------------------------------------
+%%% @author Dmytro Lytovchenko <dmytro.lytovchenko@gmail.com>
+%%% @doc Represents local index of packages and binary package storage
+%%% @end
+%%% Created : 07. Feb 2014 7:16 PM
+%%%-------------------------------------------------------------------
+-module(epm_cache).
+
+%% API
+-export([filter_installed_packages/1
+ , retrieve_remote_repo/3
+ , retrieve_remote_repos/4
+ , installed_packages/0
+ , installed_packages/1
+ , split_package/1
+ , read_vsn_from_args/2
+ , print_not_installed_package_info/2
+ , print_installed_package_info/1
+ , update_package/2
+ , remove_package/2
+ , install_package/2
+ , install/3
+ ]).
+
+-include("epm.hrl").
+
+%%------------------------------------------------------------------------------
+filter_installed_packages(Packages) ->
+ filter_installed_packages(Packages, [], []).
+
+%% @private
+filter_installed_packages([], Installed, NotInstalled) ->
+ {lists:reverse(Installed), NotInstalled};
+filter_installed_packages([Package|Tail], Installed, NotInstalled) ->
+ case local_package_info(Package) of
+ [] -> filter_installed_packages(Tail, Installed, [Package|NotInstalled]);
+ [P|_] -> filter_installed_packages(Tail, [P|Installed], NotInstalled)
+ end.
+
+
+split_package(Raw) -> split_package(Raw, []).
+split_package([], Package) -> {Package, none};
+split_package([47 | Package], User) -> {Package, User};
+split_package([A | Tail], User) -> split_package(Tail, User ++ [A]).
+
+
+installed_packages() ->
+ [Package || [{_,Package}] <- dets:match(epm_index, '$1')].
+
+installed_packages(Packages) ->
+ Installed = dict:to_list(installed_packages_internal(Packages, dict:new())),
+ [V || {_K,V} <- Installed].
+
+%% @private
+installed_packages_internal([], Dict) ->
+ Dict;
+installed_packages_internal([Package|Tail], Dict) ->
+ Dict1 =
+ case local_package_info(Package) of
+ [] ->
+ Dict;
+ List ->
+ FoldF = fun(InstalledPackage, TempDict) ->
+ NewValue = { InstalledPackage#package.user
+ , InstalledPackage#package.name
+ , InstalledPackage#package.vsn },
+ TempDict1 = dict:store(NewValue, InstalledPackage, TempDict),
+ DependantPackages = dependant_installed_packages(InstalledPackage),
+ installed_packages_internal(DependantPackages, TempDict1)
+ end,
+ lists:foldl(FoldF, Dict, List)
+ end,
+ installed_packages_internal(Tail, Dict1).
+
+
+dependant_installed_packages(Package) ->
+ dependant_installed_packages(Package, [], dets:match(epm_index, '$1')).
+
+dependant_installed_packages(_Package, Acc, []) -> Acc;
+dependant_installed_packages(#package{user = User
+ , name = Name
+ , vsn = Vsn} = Package
+ , Acc
+ , [[{_, #package{deps = Deps} = InstalledPackage}]|Tail]) ->
+ F = fun({U, N, V}) ->
+ (U == User orelse U == none) andalso
+ (N == Name) andalso
+ (V == Vsn)
+ end,
+ Acc1 = case lists:filter(F, Deps) of
+ [] -> Acc;
+ [_] -> [InstalledPackage|Acc]
+ end,
+ dependant_installed_packages(Package, Acc1, Tail).
+
+
+retrieve_remote_repo([], _, ProjectName) ->
+ ?EXIT("failed to locate remote repo for ~s", [ProjectName]);
+
+retrieve_remote_repo([Module|Tail], none, ProjectName) ->
+ case apply(Module, search, [ProjectName]) of
+ [] ->
+ retrieve_remote_repo(Tail, none, ProjectName);
+ Repos when is_list(Repos) ->
+ case lists:filter(fun(R1) -> R1#repository.name == ProjectName end
+ , Repos) of
+ [R0|_] -> R0;
+ [] -> retrieve_remote_repo(Tail, none, ProjectName)
+ end;
+ Err ->
+ ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
+ end;
+
+retrieve_remote_repo([Module|Tail], User, ProjectName) ->
+ case apply(Module, info, [User, ProjectName]) of
+ Repo when is_record(Repo, repository) ->
+ Repo;
+ undefined ->
+ retrieve_remote_repo(Tail, User, ProjectName);
+ Err ->
+ ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
+ end.
+
+retrieve_remote_repos(Modules, User, ProjectName, IsExact) ->
+ retrieve_remote_repos(Modules, User, ProjectName, IsExact, []).
+
+retrieve_remote_repos([], _, _, _, Acc) -> Acc;
+
+retrieve_remote_repos([Module|Tail], none, ProjectName, IsExact, Acc) ->
+ case apply(Module, search, [ProjectName]) of
+ [] ->
+ retrieve_remote_repos(Tail, none, ProjectName, IsExact, Acc);
+ Repos when is_list(Repos), IsExact == true ->
+ case lists:filter(fun(R1) -> R1#repository.name == ProjectName end
+ , Repos) of
+ [] -> retrieve_remote_repos(Tail, none, ProjectName, IsExact, Acc);
+ R0s ->
+ retrieve_remote_repos(Tail, none, ProjectName, IsExact
+ , lists:append(Acc, R0s))
+ end;
+ Repos when is_list(Repos) ->
+ retrieve_remote_repos(Tail, none, ProjectName, IsExact
+ , lists:append(Acc, Repos));
+ Err ->
+ ?EXIT("failed to locate remote repos for ~s: ~p", [ProjectName, Err])
+ end;
+
+retrieve_remote_repos([Module|Tail], User, ProjectName, IsExact, Acc) ->
+ case apply(Module, info, [User, ProjectName]) of
+ Repo when is_record(Repo, repository) ->
+ retrieve_remote_repos(Tail, User, ProjectName, IsExact, Acc ++ [Repo]);
+ undefined ->
+ retrieve_remote_repos(Tail, User, ProjectName, IsExact, Acc);
+ Err ->
+ ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
+ end.
+
+%% -----------------------------------------------------------------------------
+%% package info
+%% -----------------------------------------------------------------------------
+%% @private
+local_package_info(#package{ user = none
+ , name = ProjectName
+ , vsn = undefined}) ->
+ case dets:match(epm_index, {{'_', ProjectName, '_'}, '$1'}) of
+ [] -> [];
+ List -> [Package || [Package] <- List]
+ end;
+local_package_info(#package{ user = none
+ , name = ProjectName
+ , vsn = Vsn}) ->
+ case dets:match(epm_index, {{'_', ProjectName, Vsn}, '$1'}) of
+ [] -> [];
+ List -> [Package || [Package] <- List]
+ end;
+local_package_info(#package{ user = User
+ , name = ProjectName
+ , vsn = undefined}) ->
+ case dets:match(epm_index, {{User, ProjectName, '_'}, '$1'}) of
+ [] -> [];
+ List -> [Package || [Package] <- List]
+ end;
+local_package_info(#package{ user = User
+ , name = ProjectName
+ , vsn = Vsn}) ->
+ case dets:match(epm_index, {{User, ProjectName, Vsn}, '$1'}) of
+ [] -> [];
+ List -> [Package || [Package] <- List]
+ end.
+
+
+%% -----------------------------------------------------------------------------
+%% Read vsn
+%% -----------------------------------------------------------------------------
+read_vsn_from_args([{tag, Tag}|_], _) -> Tag;
+read_vsn_from_args([{branch, Branch}|_], _) -> Branch;
+read_vsn_from_args([{sha, Sha}|_], _) -> Sha;
+read_vsn_from_args([_|Tail], Default) -> read_vsn_from_args(Tail, Default);
+read_vsn_from_args([], Default) -> Default.
+
+%% -----------------------------------------------------------------------------
+%% Print package info
+%% -----------------------------------------------------------------------------
+print_installed_package_info(Package) ->
+ Repo = Package#package.repo,
+ [io:format(" ~s: ~s~n", [Field, if Value==undefined -> ""; true -> Value end]) || {Field, Value} <- [
+ {"name", Repo#repository.name},
+ {"owner", Repo#repository.owner},
+ {"vsn", Package#package.vsn},
+ {"pushed", Repo#repository.pushed},
+ {"install dir", Package#package.install_dir},
+ {"homepage", Repo#repository.homepage},
+ {"description", Repo#repository.description}
+ ]],
+ case Package#package.deps of
+ [] -> ok;
+ Deps ->
+ io:format(" dependencies: ~n ~s~n", [string:join([
+ case U of
+ none -> lists:flatten(io_lib:format("~s/~s", [N,V]));
+ _ -> lists:flatten(io_lib:format("~s/~s/~s", [U,N,V]))
+ end || {U,N,V} <- Deps], "\n ")])
+ end.
+
+print_not_installed_package_info(GlobalConfig, Packages) ->
+ print_not_installed_package_info(GlobalConfig, Packages, false).
+
+print_not_installed_package_info(GlobalConfig, Packages, IsExact) ->
+ RepoPlugins = proplists:get_value(repo_plugins, GlobalConfig, ?DEFAULT_API_MODULES),
+ write_not_installed_package_info1(Packages, RepoPlugins, IsExact).
+
+write_not_installed_package_info1(Packages, RepoPlugins, IsExact) ->
+ case fetch_not_installed_package_info(Packages, RepoPlugins, [], IsExact) of
+ [] ->
+ io:format("- not found~n");
+ Repos ->
+ io:format("===============================~n"),
+ io:format("AVAILABLE~n"),
+ io:format("===============================~n"),
+ lists:foldl(
+ fun(Repo, Count) ->
+ Tags = apply(Repo#repository.api_module, tags, [Repo#repository.owner, Repo#repository.name]),
+ Branches = apply(Repo#repository.api_module, branches, [Repo#repository.owner, Repo#repository.name]),
+ case Count of
+ 0 -> ok;
+ _ -> io:format("~n")
+ end,
+ [io:format(" ~s: ~s~n", [Field, if Value == undefined -> ""; true ->
+ Value end ]) || {Field, Value} <- [
+ {"name", Repo#repository.name} ,
+ {"owner", Repo#repository.owner} ,
+ {"followers", Repo#repository.followers} ,
+ {"pushed", Repo#repository.pushed} ,
+ {"homepage", Repo#repository.homepage} ,
+ {"description", Repo#repository.description} ,
+ {"repo plugin", atom_to_list(Repo#repository.api_module)}
+ ]],
+ if
+ Tags =/= [] ->
+ io:format(" tags:~n"),
+ [io:format(" ~s~n", [Tag]) || Tag <- Tags];
+ true -> ok
+ end,
+ if
+ Branches =/= [] ->
+ io:format(" branches:~n"),
+ [io:format(" ~s~n", [Branch]) || Branch <- Branches];
+ true -> ok
+ end,
+ Count + 1
+ end , 0, Repos)
+ end.
+
+fetch_not_installed_package_info([], _, Acc, _) -> Acc;
+fetch_not_installed_package_info([#package{user=User,name=ProjectName}|Tail]
+ , RepoPlugins, Acc, IsExact) ->
+ Repos = epm_cache:retrieve_remote_repos(
+ RepoPlugins, User, ProjectName, IsExact),
+ fetch_not_installed_package_info(
+ Tail, RepoPlugins, lists:append(Acc, Repos), IsExact).
+
+
+
+%% -----------------------------------------------------------------------------
+%% INSTALL
+%% -----------------------------------------------------------------------------
+install_package(GlobalConfig, Package) ->
+ Repo = Package#package.repo,
+ User = Repo#repository.owner,
+ Name = Repo#repository.name,
+ Vsn = Package#package.vsn,
+ %% switch to build home dir
+ epm_util:set_cwd_build_home(GlobalConfig),
+
+ %% download correct version of package
+ LocalProjectDir = apply(Repo#repository.api_module, download_package, [Repo, Vsn]),
+
+ %% switch to project dir
+ epm_util:set_cwd_build_home(GlobalConfig),
+ epm_util:set_cwd(LocalProjectDir),
+
+ %% build/install project
+ InstallDir = "not-building-anything", %build_project(GlobalConfig, Package),
+
+ %% switch to build home dir and delete cloned project
+ epm_util:set_cwd_build_home(GlobalConfig),
+ epm_util:del_dir(LocalProjectDir),
+
+ dets:insert(epm_index, {{User, Name, Vsn}, Package#package{install_dir = InstallDir}}),
+
+ ok.
+
+%% -----------------------------------------------------------------------------
+%% REMOVE
+%% -----------------------------------------------------------------------------
+remove_package(_GlobalConfig, #package{user = User, name = Name, vsn = Vsn, install_dir = InstallDir}) ->
+ io:format("+ removing package ~s-~s-~s from ~s~n", [User, Name, Vsn, InstallDir]),
+ RemoveCmd = "rm -rf " ++ InstallDir,
+ epm_util:print_cmd_output("~s~n", [RemoveCmd]),
+ epm_util:do_cmd(RemoveCmd, fail),
+ dets:delete(epm_index, {User, Name, Vsn}).
+
+%% -----------------------------------------------------------------------------
+%% UPDATE
+%% -----------------------------------------------------------------------------
+update_package(GlobalConfig, Package) ->
+ Repo = Package#package.repo,
+ Vsn = Package#package.vsn,
+ %% switch to build home dir
+ epm_util:set_cwd_build_home(GlobalConfig),
+
+ %% download correct version of package
+ LocalProjectDir = apply(Repo#repository.api_module, download_package
+ , [Repo, Vsn]),
+
+ %% switch to project dir
+ epm_util:set_cwd_build_home(GlobalConfig),
+ epm_util:set_cwd(LocalProjectDir),
+
+ %% build/install project
+ %_InstallDir = build_project(GlobalConfig, Package),
+
+ %% switch to build home dir and delete cloned project
+ epm_util:set_cwd_build_home(GlobalConfig),
+ epm_util:del_dir(LocalProjectDir).
+
+
+install(ProjectName, Config, undefined) ->
+ install(ProjectName, Config, code:lib_dir());
+
+install(ProjectName, _Config, LibDir) ->
+ Vsn =
+ case file:consult("ebin/" ++ ProjectName ++ ".app") of
+ {ok, [{application, _, Props}]} ->
+ proplists:get_value(vsn, Props);
+ _ ->
+ undefined
+ end,
+ Dir =
+ case Vsn of
+ undefined -> LibDir ++ "/" ++ ProjectName;
+ _ -> LibDir ++ "/" ++ ProjectName ++ "-" ++ Vsn
+ end,
+ InstallCmd = "mkdir -p " ++ Dir ++ "; cp -R ./* " ++ Dir,
+ io:format("+ running ~s install command~n", [ProjectName]),
+ epm_util:print_cmd_output("~s~n", [InstallCmd]),
+ epm_util:do_cmd(InstallCmd, fail),
+ Ebin = Dir ++ "/ebin",
+ case code:add_pathz(Ebin) of
+ true -> ok;
+ Err ->
+ M = io_lib:format("failed to add path for ~s (~s): ~p"
+ , [ProjectName, Ebin, Err]),
+ exit(lists:flatten(M))
+ end,
+ Dir.
View
587 src/epm_core.erl
@@ -3,13 +3,12 @@
-include("epm.hrl").
--define(DEFAULT_API_MODULES, [github_api]).
-
execute(GlobalConfig0, ["install" | Args]) ->
{GlobalConfig, Packages, Flags} = collect_args(install, Args, GlobalConfig0),
+ %% TODO: Remove put/get
put(verbose, lists:member(verbose, Flags)),
- Deps = package_dependencies(GlobalConfig, Packages),
- {Installed, NotInstalled} = filter_installed_packages(Deps),
+ Deps = epm_deps:package_dependencies(GlobalConfig, Packages),
+ {Installed, NotInstalled} = epm_cache:filter_installed_packages(Deps),
case NotInstalled of
[] ->
io:format("+ nothing to do: packages and dependencies already installed~n");
@@ -32,7 +31,8 @@ execute(GlobalConfig0, ["install" | Args]) ->
case io:get_chars("", 1) of
C when C == "y"; C == "\n" ->
io:format("~n"),
- [install_package(GlobalConfig, Package) || Package <- NotInstalled];
+ [epm_cache:install_package(GlobalConfig, Package)
+ || Package <- NotInstalled];
_ -> ok
end
end;
@@ -40,7 +40,7 @@ execute(GlobalConfig0, ["install" | Args]) ->
execute(GlobalConfig0, ["remove" | Args]) ->
{GlobalConfig, Packages, Flags} = collect_args(remove, Args, GlobalConfig0),
put(verbose, lists:member(verbose, Flags)),
- Installed = installed_packages(Packages),
+ Installed = epm_cache:installed_packages(Packages),
case Installed of
[] ->
io:format("+ nothing to remove: no matching packages installed~n");
@@ -48,12 +48,14 @@ execute(GlobalConfig0, ["remove" | Args]) ->
io:format("===============================~n"),
io:format("Remove the following packages?~n"),
io:format("===============================~n"),
- [io:format(" + ~s-~s-~s~n", [U, N, V]) || #package{user = U, name = N, vsn = V} <- Installed],
+ [io:format(" + ~s-~s-~s~n", [U, N, V])
+ || #package{user = U, name = N, vsn = V} <- Installed],
io:format("~n([y]/n) "),
case io:get_chars("", 1) of
C when C == "y"; C == "\n" ->
io:format("~n"),
- [remove_package(GlobalConfig, Package) || Package <- Installed];
+ [epm_cache:remove_package(GlobalConfig, Package)
+ || Package <- Installed];
_ -> ok
end
end;
@@ -61,7 +63,7 @@ execute(GlobalConfig0, ["remove" | Args]) ->
execute(GlobalConfig0, ["update" | Args]) ->
{GlobalConfig, Packages, Flags} = collect_args(update, Args, GlobalConfig0),
put(verbose, lists:member(verbose, Flags)),
- Installed = installed_packages(Packages),
+ Installed = epm_cache:installed_packages(Packages),
case Installed of
[] ->
io:format("- nothing to update~n");
@@ -69,19 +71,21 @@ execute(GlobalConfig0, ["update" | Args]) ->
io:format("===============================~n"),
io:format("Update the following packages?~n"),
io:format("===============================~n"),
- [io:format(" + ~s-~s-~s~n", [U, N, V]) || #package{user = U, name = N, vsn = V} <- Installed],
+ [io:format(" + ~s-~s-~s~n", [U, N, V])
+ || #package{user = U, name = N, vsn = V} <- Installed],
io:format("~n([y]/n) "),
case io:get_chars("", 1) of
C when C == "y"; C == "\n" ->
io:format("~n"),
- [update_package(GlobalConfig, Package) || Package <- Installed];
+ [epm_cache:update_package(GlobalConfig, Package)
+ || Package <- Installed];
_ -> ok
end
end;
execute(GlobalConfig0, ["info" | Args]) ->
{GlobalConfig, Packages, _Flags} = collect_args(info, Args, GlobalConfig0),
- {Installed, NotInstalled} = filter_installed_packages(Packages),
+ {Installed, NotInstalled} = epm_cache:filter_installed_packages(Packages),
case Installed of
[] -> ok;
_ ->
@@ -95,7 +99,7 @@ execute(GlobalConfig0, ["info" | Args]) ->
0 -> ok;
_ -> io:format("~n")
end,
- write_installed_package_info(Package),
+ epm_cache:print_installed_package_info(Package),
Count + 1
end , 0, lists:reverse(Installed))
end,
@@ -107,15 +111,17 @@ execute(GlobalConfig0, ["info" | Args]) ->
[] -> ok;
_ -> io:format("~n")
end,
- write_not_installed_package_info(GlobalConfig, NotInstalled, true)
+ epm_cache:print_not_installed_package_info(GlobalConfig
+ , NotInstalled, true)
end;
execute(GlobalConfig0, ["search" | Args]) ->
- {GlobalConfig, Packages, _Flags} = collect_args(search, Args, GlobalConfig0),
- write_not_installed_package_info(GlobalConfig, lists:reverse(Packages));
+ {GlobalConfig, Packages, _Flags} = collect_args(search, Args, GlobalConfig0),
+ epm_cache:print_not_installed_package_info(GlobalConfig
+ , lists:reverse(Packages));
execute(_GlobalConfig, ["list" | _Args]) ->
- Installed = installed_packages(),
+ Installed = epm_cache:installed_packages(),
case Installed of
[] ->
io:format("- no packages installed~n");
@@ -130,7 +136,7 @@ execute(_GlobalConfig, ["list" | _Args]) ->
0 -> ok;
_ -> io:format("~n")
end,
- write_installed_package_info(Package),
+ epm_cache:print_installed_package_info(Package),
Count + 1
end , 0, lists:reverse(Installed))
end;
@@ -227,17 +233,22 @@ collect_args(_, [], GlobalConfig, Packages, Flags) ->
collect_args(Target, [Arg | Rest], GlobalConfig, Packages, Flags) ->
case parse_tag(Target, Arg) of
undefined -> %% if not a tag then must be a project name
- {ProjectName, User} = split_package(Arg), %% split into user and project
- collect_args(Target, Rest, GlobalConfig, [#package{user = User, name = ProjectName}|Packages], Flags);
+ %% split into user and project
+ {ProjectName, User} = epm_cache:split_package(Arg),
+ collect_args(Target, Rest, GlobalConfig
+ , [#package{user = User, name = ProjectName}|Packages]
+ , Flags);
{Type, Tag, 0} -> %% tag with no trailing value
case Type of
project ->
[#package{args = Args} = Package|OtherPackages] = Packages,
- collect_args(Target, Rest, GlobalConfig, [Package#package{args = Args ++ [Tag]}|OtherPackages], Flags);
+ collect_args(Target, Rest, GlobalConfig
+ , [Package#package{args = Args ++ [Tag]}|OtherPackages]
+ , Flags);
global ->
collect_args(Target, Rest, GlobalConfig, Packages, [Tag|Flags])
end;
- {Type, Tag, NumVals} when is_integer(NumVals) -> %% tag with trailing value(s)
+ {Type, Tag, NumVals} when is_integer(NumVals) -> % tag with trailing value(s)
if
length(Rest) < NumVals ->
exit("poorly formatted command");
@@ -251,12 +262,17 @@ collect_args(Target, [Arg | Rest], GlobalConfig, Packages, Flags) ->
end,
case Type of
project ->
- [#package{args = Args} = Package|OtherPackages] = Packages, %% this tag applies to the last project on the stack
+ %% this tag applies to the last project on the stack
+ [#package{args = Args} = Package|OtherPackages] = Packages,
Vsn = if
Tag == tag; Tag == branch; Tag == sha -> Vals1;
true -> Package#package.vsn
end,
- collect_args(Target, Rest1, GlobalConfig, [Package#package{vsn = Vsn, args = Args ++ [{Tag, Vals1}]}|OtherPackages], Flags);
+ collect_args( Target, Rest1, GlobalConfig
+ , [Package#package{ vsn = Vsn
+ , args = Args ++ [{Tag, Vals1}]
+ } | OtherPackages]
+ , Flags);
global ->
GlobalConfig1 =
if
@@ -301,220 +317,6 @@ parse_tag(_, "--config-set") -> {global, config_set, 2};
parse_tag(_, _) -> undefined.
-split_package(Raw) -> split_package(Raw, []).
-split_package([], Package) -> {Package, none};
-split_package([47 | Package], User) -> {Package, User};
-split_package([A | Tail], User) -> split_package(Tail, User ++ [A]).
-
-%% -----------------------------------------------------------------------------
-%% package info
-%% -----------------------------------------------------------------------------
-local_package_info(#package{user = none, name = ProjectName, vsn = undefined}) ->
- case dets:match(epm_index, {{'_', ProjectName, '_'}, '$1'}) of
- [] -> [];
- List -> [Package || [Package] <- List]
- end;
-local_package_info(#package{user = none, name = ProjectName, vsn = Vsn}) ->
- case dets:match(epm_index, {{'_', ProjectName, Vsn}, '$1'}) of
- [] -> [];
- List -> [Package || [Package] <- List]
- end;
-local_package_info(#package{user = User, name = ProjectName, vsn = undefined}) ->
- case dets:match(epm_index, {{User, ProjectName, '_'}, '$1'}) of
- [] -> [];
- List -> [Package || [Package] <- List]
- end;
-local_package_info(#package{user = User, name = ProjectName, vsn = Vsn}) ->
- case dets:match(epm_index, {{User, ProjectName, Vsn}, '$1'}) of
- [] -> [];
- List -> [Package || [Package] <- List]
- end.
-
-installed_packages() ->
- [Package || [{_,Package}] <- dets:match(epm_index, '$1')].
-
-installed_packages(Packages) ->
- [V || {_K,V} <- dict:to_list(installed_packages1(Packages, dict:new()))].
-
-installed_packages1([], Dict) ->
- Dict;
-
-installed_packages1([Package|Tail], Dict) ->
- Dict1 =
- case local_package_info(Package) of
- [] ->
- Dict;
- List ->
- lists:foldl(
- fun(InstalledPackage, TempDict) ->
- TempDict1 = dict:store( {
- InstalledPackage#package.user,
- InstalledPackage#package.name,
- InstalledPackage#package.vsn }, InstalledPackage, TempDict),
- DependantPackages = dependant_installed_packages(InstalledPackage),
- installed_packages1(DependantPackages, TempDict1)
- end , Dict, List)
- end,
- installed_packages1(Tail, Dict1).
-
-dependant_installed_packages(Package) ->
- dependant_installed_packages(Package, [], dets:match(epm_index, '$1')).
-
-dependant_installed_packages(_Package, Acc, []) -> Acc;
-dependant_installed_packages(#package{user = User, name = Name, vsn = Vsn} = Package
- , Acc
- , [[{_, #package{deps = Deps} = InstalledPackage}]|Tail]) ->
- F = fun({U, N, V}) ->
- (U == User orelse U == none) andalso
- (N == Name) andalso
- (V == Vsn)
- end,
- Acc1 = case lists:filter(F, Deps) of
- [] -> Acc;
- [_] -> [InstalledPackage|Acc]
- end,
- dependant_installed_packages(Package, Acc1, Tail).
-
-%% -----------------------------------------------------------------------------
-%% Print package info
-%% -----------------------------------------------------------------------------
-write_installed_package_info(Package) ->
- Repo = Package#package.repo,
- [io:format(" ~s: ~s~n", [Field, if Value==undefined -> ""; true -> Value end]) || {Field, Value} <- [
- {"name", Repo#repository.name},
- {"owner", Repo#repository.owner},
- {"vsn", Package#package.vsn},
- {"pushed", Repo#repository.pushed},
- {"install dir", Package#package.install_dir},
- {"homepage", Repo#repository.homepage},
- {"description", Repo#repository.description}
- ]],
- case Package#package.deps of
- [] -> ok;
- Deps ->
- io:format(" dependencies: ~n ~s~n", [string:join([
- case U of
- none -> lists:flatten(io_lib:format("~s/~s", [N,V]));
- _ -> lists:flatten(io_lib:format("~s/~s/~s", [U,N,V]))
- end || {U,N,V} <- Deps], "\n ")])
- end.
-
-write_not_installed_package_info(GlobalConfig, Packages) ->
- write_not_installed_package_info(GlobalConfig, Packages, false).
-
-write_not_installed_package_info(GlobalConfig, Packages, IsExact) ->
- RepoPlugins = proplists:get_value(repo_plugins, GlobalConfig, ?DEFAULT_API_MODULES),
- write_not_installed_package_info1(Packages, RepoPlugins, IsExact).
-
-write_not_installed_package_info1(Packages, RepoPlugins, IsExact) ->
- case fetch_not_installed_package_info(Packages, RepoPlugins, [], IsExact) of
- [] ->
- io:format("- not found~n");
- Repos ->
- io:format("===============================~n"),
- io:format("AVAILABLE~n"),
- io:format("===============================~n"),
- lists:foldl(
- fun(Repo, Count) ->
- Tags = apply(Repo#repository.api_module, tags, [Repo#repository.owner, Repo#repository.name]),
- Branches = apply(Repo#repository.api_module, branches, [Repo#repository.owner, Repo#repository.name]),
- case Count of
- 0 -> ok;
- _ -> io:format("~n")
- end,
- [io:format(" ~s: ~s~n", [Field, if Value == undefined -> ""; true ->
- Value end ]) || {Field, Value} <- [
- {"name", Repo#repository.name} ,
- {"owner", Repo#repository.owner} ,
- {"followers", Repo#repository.followers} ,
- {"pushed", Repo#repository.pushed} ,
- {"homepage", Repo#repository.homepage} ,
- {"description", Repo#repository.description} ,
- {"repo plugin", atom_to_list(Repo#repository.api_module)}
- ]],
- if
- Tags =/= [] ->
- io:format(" tags:~n"),
- [io:format(" ~s~n", [Tag]) || Tag <- Tags];
- true -> ok
- end,
- if
- Branches =/= [] ->
- io:format(" branches:~n"),
- [io:format(" ~s~n", [Branch]) || Branch <- Branches];
- true -> ok
- end,
- Count + 1
- end , 0, Repos)
- end.
-
-fetch_not_installed_package_info([], _, Acc, _) -> Acc;
-fetch_not_installed_package_info([#package{user=User,name=ProjectName}|Tail], RepoPlugins, Acc, IsExact) ->
- Repos = retrieve_remote_repos(RepoPlugins, User, ProjectName, IsExact),
- fetch_not_installed_package_info(Tail, RepoPlugins, lists:append(Acc, Repos), IsExact).
-
-%% -----------------------------------------------------------------------------
-%% INSTALL
-%% -----------------------------------------------------------------------------
-install_package(GlobalConfig, Package) ->
- Repo = Package#package.repo,
- User = Repo#repository.owner,
- Name = Repo#repository.name,
- Vsn = Package#package.vsn,
- %% switch to build home dir
- epm_util:set_cwd_build_home(GlobalConfig),
-
- %% download correct version of package
- LocalProjectDir = apply(Repo#repository.api_module, download_package, [Repo, Vsn]),
-
- %% switch to project dir
- epm_util:set_cwd_build_home(GlobalConfig),
- epm_util:set_cwd(LocalProjectDir),
-
- %% build/install project
- InstallDir = build_project(GlobalConfig, Package),
-
- %% switch to build home dir and delete cloned project
- epm_util:set_cwd_build_home(GlobalConfig),
- epm_util:del_dir(LocalProjectDir),
-
- dets:insert(epm_index, {{User, Name, Vsn}, Package#package{install_dir = InstallDir}}),
-
- ok.
-
-%% -----------------------------------------------------------------------------
-%% REMOVE
-%% -----------------------------------------------------------------------------
-remove_package(_GlobalConfig, #package{user = User, name = Name, vsn = Vsn, install_dir = InstallDir}) ->
- io:format("+ removing package ~s-~s-~s from ~s~n", [User, Name, Vsn, InstallDir]),
- RemoveCmd = "rm -rf " ++ InstallDir,
- epm_util:print_cmd_output("~s~n", [RemoveCmd]),
- epm_util:do_cmd(RemoveCmd, fail),
- dets:delete(epm_index, {User, Name, Vsn}).
-
-%% -----------------------------------------------------------------------------
-%% UPDATE
-%% -----------------------------------------------------------------------------
-update_package(GlobalConfig, Package) ->
- Repo = Package#package.repo,
- Vsn = Package#package.vsn,
- %% switch to build home dir
- epm_util:set_cwd_build_home(GlobalConfig),
-
- %% download correct version of package
- LocalProjectDir = apply(Repo#repository.api_module, download_package, [Repo, Vsn]),
-
- %% switch to project dir
- epm_util:set_cwd_build_home(GlobalConfig),
- epm_util:set_cwd(LocalProjectDir),
-
- %% build/install project
- _InstallDir = build_project(GlobalConfig, Package),
-
- %% switch to build home dir and delete cloned project
- epm_util:set_cwd_build_home(GlobalConfig),
- epm_util:del_dir(LocalProjectDir).
-
%% -----------------------------------------------------------------------------
%% Replace epm script with most recent
%% -----------------------------------------------------------------------------
@@ -574,245 +376,70 @@ write_config_file(GlobalConfig) ->
?EXIT("failed to update .epm config (~s): ~p", [FileLoc, Reason])
end.
-%% -----------------------------------------------------------------------------
-%% Read vsn
-%% -----------------------------------------------------------------------------
-read_vsn_from_args([{tag, Tag}|_], _) -> Tag;
-read_vsn_from_args([{branch, Branch}|_], _) -> Branch;
-read_vsn_from_args([{sha, Sha}|_], _) -> Sha;
-read_vsn_from_args([_|Tail], Default) -> read_vsn_from_args(Tail, Default);
-read_vsn_from_args([], Default) -> Default.
-
-build_project(GlobalConfig, Package) ->
- ProjectName = (Package#package.repo)#repository.name,
- Props = Package#package.args,
- Config =
- case file:consult(ProjectName ++ ".epm") of
- {ok, [Config0]} -> Config0;
- _ -> []
- end,
- UserSuppliedPrebuildCommand = proplists:get_value(prebuild_command, Props),
- UserSuppliedBuildCommand = proplists:get_value(build_command, Props),
- UserSuppliedTestCommand = proplists:get_value(test_command, Props),
- prebuild(ProjectName, Config, UserSuppliedPrebuildCommand),
- build(ProjectName, Config, UserSuppliedBuildCommand),
- test(ProjectName, Config, UserSuppliedTestCommand),
- install(ProjectName, Config, proplists:get_value(install_dir, GlobalConfig)).
-
-prebuild(ProjectName, Config, undefined) ->
- case proplists:get_value(prebuild_command, Config) of
- undefined -> ok;
- PrebuildCmd -> prebuild1(ProjectName, PrebuildCmd)
- end;
-prebuild(ProjectName, _Config, PrebuildCmd) ->
- prebuild1(ProjectName, PrebuildCmd).
-
-prebuild1(ProjectName, PrebuildCmd) ->
- io:format("+ running ~s prebuild command~n", [ProjectName]),
- epm_util:print_cmd_output("~s~n", [PrebuildCmd]),
- epm_util:do_cmd(PrebuildCmd, fail).
-
-build(ProjectName, Config, undefined) ->
- case proplists:get_value(build_command, Config) of
- undefined ->
- case filelib:is_regular("Makefile") of
- true ->
- build1(ProjectName, "make");
- false ->
- case os:find_executable("rebar") of
- false ->
- exit("failed to build package: No Makefile and rebar not installed");
- RebarExec ->
- io:format("+ compiling with rebar...~n"),
- build1(ProjectName, RebarExec ++ " compile")
- end
- end;
- Cmd ->
- build1(ProjectName, Cmd)
- end;
-build(ProjectName, _Config, BuildCmd) ->
- build1(ProjectName, BuildCmd).
-
-build1(ProjectName, BuildCmd) ->
- io:format("+ running ~s build command~n", [ProjectName]),
- epm_util:print_cmd_output("~s~n", [BuildCmd]),
- epm_util:do_cmd(BuildCmd, fail).
-
-test(ProjectName, Config, undefined) ->
- case proplists:get_value(test_command, Config) of
- undefined -> ok;
- TestCmd -> test1(ProjectName, TestCmd)
- end;
-test(ProjectName, _Config, TestCmd) ->
- test1(ProjectName, TestCmd).
-
-test1(ProjectName, TestCmd) ->
- io:format("+ running ~s test command~n", [ProjectName]),
- epm_util:print_cmd_output("~s~n", [TestCmd]),
- epm_util:do_cmd(TestCmd, fail).
-
-install(ProjectName, Config, undefined) ->
- install(ProjectName, Config, code:lib_dir());
-
-install(ProjectName, _Config, LibDir) ->
- Vsn =
- case file:consult("ebin/" ++ ProjectName ++ ".app") of
- {ok, [{application, _, Props}]} ->
- proplists:get_value(vsn, Props);
- _ ->
- undefined
- end,
- Dir =
- case Vsn of
- undefined -> LibDir ++ "/" ++ ProjectName;
- _ -> LibDir ++ "/" ++ ProjectName ++ "-" ++ Vsn
- end,
- InstallCmd = "mkdir -p " ++ Dir ++ "; cp -R ./* " ++ Dir,
- io:format("+ running ~s install command~n", [ProjectName]),
- epm_util:print_cmd_output("~s~n", [InstallCmd]),
- epm_util:do_cmd(InstallCmd, fail),
- Ebin = Dir ++ "/ebin",
- case code:add_pathz(Ebin) of
- true ->
- ok;
- Err ->
- exit(lists:flatten(io_lib:format("failed to add path for ~s (~s): ~p", [ProjectName, Ebin, Err])))
- end,
- Dir.
-
-%% -----------------------------------------------------------------------------
-%% Compile list of dependencies
-%% -----------------------------------------------------------------------------
-package_dependencies(GlobalConfig, Packages) ->
- RepoPlugins = proplists:get_value(repo_plugins, GlobalConfig, ?DEFAULT_API_MODULES),
- G = digraph:new(),
- UpdatedPackages = package_dependencies1(Packages, RepoPlugins, G, undefined, dict:new()),
- Deps = digraph_utils:topsort(G),
- digraph:delete(G),
- [dict:fetch(Dep, UpdatedPackages) || Dep <- Deps].
-
-package_dependencies1([], _, _, _, Dict) -> Dict;
-package_dependencies1([Package|Tail], RepoPlugins, G, Parent, Dict) ->
- Repo = retrieve_remote_repo(RepoPlugins, Package#package.user, Package#package.name),
- WithoutDeps = lists:member(without_deps, Package#package.args),
- Key = {Repo#repository.owner, Repo#repository.name, Package#package.vsn},
-
- digraph:add_vertex(G, Key),
-
- case Parent of
- undefined -> ok;
- {_, ParentProjectName, _} ->
- digraph:add_edge(G, Parent, Key),
- case digraph_utils:is_acyclic(G) of
- true ->
- ok;
- false ->
- ?EXIT("circular dependency detected: ~s <--> ~s", [ParentProjectName, Repo#repository.name])
- end
- end,
-
- PkgVsn =
- case Package#package.vsn of
- undefined -> apply(Repo#repository.api_module, default_vsn, []);
- _ -> Package#package.vsn
- end,
-
- {Deps, Dict1} =
- case WithoutDeps of
- true ->
- {[], Dict};
- false ->
- Deps0 = apply(Repo#repository.api_module, package_deps, [Repo#repository.owner, Repo#repository.name, PkgVsn]),
- lists:mapfoldl(
- fun({Dep, Args}, TempDict) ->
- {DepName, DepUser} = split_package(Dep),
- DepVsn = read_vsn_from_args(Args, apply(Repo#repository.api_module, default_vsn, [])),
- Package0 = #package{
- user = DepUser,
- name = DepName,
- vsn = DepVsn ,
- args = Args
- },
- TempDict1 = package_dependencies1([Package0], RepoPlugins, G, Key, TempDict),
- {{DepUser, DepName, DepVsn}, TempDict1}
- end , Dict, Deps0)
- end,
-
- Package1 = Package#package{ user = Repo#repository.owner
- , name = Repo#repository.name
- , vsn = PkgVsn
- , deps = Deps
- , repo = Repo
- },
- package_dependencies1(Tail, RepoPlugins, G, Parent, dict:store(Key, Package1, Dict1)).
-
-filter_installed_packages(Packages) ->
- filter_installed_packages(Packages, [], []).
-
-filter_installed_packages([], Installed, NotInstalled) ->
- {lists:reverse(Installed), NotInstalled};
-
-filter_installed_packages([Package|Tail], Installed, NotInstalled) ->
- case local_package_info(Package) of
- [] -> filter_installed_packages(Tail, Installed, [Package|NotInstalled]);
- [P|_] -> filter_installed_packages(Tail, [P|Installed], NotInstalled)
- end.
-
-retrieve_remote_repo([], _, ProjectName) ->
- ?EXIT("failed to locate remote repo for ~s", [ProjectName]);
-
-retrieve_remote_repo([Module|Tail], none, ProjectName) ->
- case apply(Module, search, [ProjectName]) of
- [] ->
- retrieve_remote_repo(Tail, none, ProjectName);
- Repos when is_list(Repos) ->
- case lists:filter (fun(R1) ->
- R1#repository.name == ProjectName end, Repos) of
- [R0|_] -> R0;
- [] -> retrieve_remote_repo(Tail, none, ProjectName)
- end;
- Err ->
- ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
- end;
-
-retrieve_remote_repo([Module|Tail], User, ProjectName) ->
- case apply(Module, info, [User, ProjectName]) of
- Repo when is_record(Repo, repository) ->
- Repo;
- undefined ->
- retrieve_remote_repo(Tail, User, ProjectName);
- Err ->
- ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
- end.
-
-retrieve_remote_repos(Modules, User, ProjectName, IsExact) ->
- retrieve_remote_repos(Modules, User, ProjectName, IsExact, []).
-
-retrieve_remote_repos([], _, _, _, Acc) -> Acc;
-
-retrieve_remote_repos([Module|Tail], none, ProjectName, IsExact, Acc) ->
- case apply(Module, search, [ProjectName]) of
- [] ->
- retrieve_remote_repos(Tail, none, ProjectName, IsExact, Acc);
- Repos when is_list(Repos), IsExact == true ->
- case lists:filter (fun(R1) ->
- R1#repository.name == ProjectName end, Repos) of
- [] -> retrieve_remote_repos(Tail, none, ProjectName, IsExact, Acc);
- R0s ->
- retrieve_remote_repos(Tail, none, ProjectName, IsExact, lists:append(Acc, R0s))
- end;
- Repos when is_list(Repos) ->
- retrieve_remote_repos(Tail, none, ProjectName, IsExact, lists:append(Acc, Repos));
- Err ->
- ?EXIT("failed to locate remote repos for ~s: ~p", [ProjectName, Err])
- end;
-
-retrieve_remote_repos([Module|Tail], User, ProjectName, IsExact, Acc) ->
- case apply(Module, info, [User, ProjectName]) of
- Repo when is_record(Repo, repository) ->
- retrieve_remote_repos(Tail, User, ProjectName, IsExact, Acc ++ [Repo]);
- undefined ->
- retrieve_remote_repos(Tail, User, ProjectName, IsExact, Acc);
- Err ->
- ?EXIT("failed to locate remote repo for ~s: ~p", [ProjectName, Err])
- end.
+%% build_project(GlobalConfig, Package) ->
+%% ProjectName = (Package#package.repo)#repository.name,
+%% Props = Package#package.args,
+%% Config =
+%% case file:consult(ProjectName ++ ".epm") of
+%% {ok, [Config0]} -> Config0;
+%% _ -> []
+%% end,
+%% UserSuppliedPrebuildCommand = proplists:get_value(prebuild_command, Props),
+%% UserSuppliedBuildCommand = proplists:get_value(build_command, Props),
+%% UserSuppliedTestCommand = proplists:get_value(test_command, Props),
+%% prebuild(ProjectName, Config, UserSuppliedPrebuildCommand),
+%% build(ProjectName, Config, UserSuppliedBuildCommand),
+%% test(ProjectName, Config, UserSuppliedTestCommand),
+%% install(ProjectName, Config, proplists:get_value(install_dir, GlobalConfig)).
+
+%% prebuild(ProjectName, Config, undefined) ->
+%% case proplists:get_value(prebuild_command, Config) of
+%% undefined -> ok;
+%% PrebuildCmd -> prebuild1(ProjectName, PrebuildCmd)
+%% end;
+%% prebuild(ProjectName, _Config, PrebuildCmd) ->
+%% prebuild1(ProjectName, PrebuildCmd).
+
+%% prebuild1(ProjectName, PrebuildCmd) ->
+%% io:format("+ running ~s prebuild command~n", [ProjectName]),
+%% epm_util:print_cmd_output("~s~n", [PrebuildCmd]),
+%% epm_util:do_cmd(PrebuildCmd, fail).
+
+%% build(ProjectName, Config, undefined) ->
+%% case proplists:get_value(build_command, Config) of
+%% undefined ->
+%% case filelib:is_regular("Makefile") of
+%% true ->
+%% build1(ProjectName, "make");
+%% false ->
+%% case os:find_executable("rebar") of
+%% false ->
+%% exit("failed to build package: No Makefile and rebar not installed");
+%% RebarExec ->
+%% io:format("+ compiling with rebar...~n"),
+%% build1(ProjectName, RebarExec ++ " compile")
+%% end
+%% end;
+%% Cmd ->
+%% build1(ProjectName, Cmd)
+%% end;
+%% build(ProjectName, _Config, BuildCmd) ->
+%% build1(ProjectName, BuildCmd).
+
+%% build1(ProjectName, BuildCmd) ->
+%% io:format("+ running ~s build command~n", [ProjectName]),
+%% epm_util:print_cmd_output("~s~n", [BuildCmd]),
+%% epm_util:do_cmd(BuildCmd, fail).
+
+%% test(ProjectName, Config, undefined) ->
+%% case proplists:get_value(test_command, Config) of
+%% undefined -> ok;
+%% TestCmd -> test1(ProjectName, TestCmd)
+%% end;
+%% test(ProjectName, _Config, TestCmd) ->
+%% test1(ProjectName, TestCmd).
+
+%% test1(ProjectName, TestCmd) ->
+%% io:format("+ running ~s test command~n", [ProjectName]),
+%% epm_util:print_cmd_output("~s~n", [TestCmd]),
+%% epm_util:do_cmd(TestCmd, fail).
View
82 src/epm_deps.erl
@@ -0,0 +1,82 @@
+%%%-------------------------------------------------------------------
+%%% @author Dmytro Lytovchenko <dmytro.lytovchenko@gmail.com>
+%%% @doc Represents package dependency graph and operations
+%%% @end
+%%% Created : 07. Feb 2014 7:25 PM
+%%%-------------------------------------------------------------------
+-module(epm_deps).
+
+%% API
+-export([package_dependencies/2]).
+
+-include("epm.hrl").
+
+%% -----------------------------------------------------------------------------
+%% Compile list of dependencies
+%% -----------------------------------------------------------------------------
+package_dependencies(GlobalConfig, Packages) ->
+ RepoPlugins = proplists:get_value(repo_plugins, GlobalConfig
+ , ?DEFAULT_API_MODULES),
+ G = digraph:new(),
+ UpdatedPackages = package_dependencies1(Packages, RepoPlugins, G, undefined
+ , dict:new()),
+ Deps = digraph_utils:topsort(G),
+ digraph:delete(G),
+ [dict:fetch(Dep, UpdatedPackages) || Dep <- Deps].
+
+%% @private
+package_dependencies1([], _, _, _, Dict) -> Dict;
+package_dependencies1([Package|Tail], RepoPlugins, G, Parent, Dict) ->
+ Repo = epm_cache:retrieve_remote_repo(
+ RepoPlugins, Package#package.user, Package#package.name),
+ WithoutDeps = lists:member(without_deps, Package#package.args),
+ Key = {Repo#repository.owner, Repo#repository.name, Package#package.vsn},
+
+ digraph:add_vertex(G, Key),
+
+ case Parent of
+ undefined -> ok;
+ {_, ParentProjectName, _} ->
+ digraph:add_edge(G, Parent, Key),
+ case digraph_utils:is_acyclic(G) of
+ true -> ok;
+ false -> ?EXIT("circular dependency detected: ~s <--> ~s"
+ , [ParentProjectName, Repo#repository.name])
+ end
+ end,
+
+ PkgVsn =
+ case Package#package.vsn of
+ undefined -> apply(Repo#repository.api_module, default_vsn, []);
+ _ -> Package#package.vsn
+ end,
+
+ {Deps, Dict1} =
+ case WithoutDeps of
+ true ->
+ {[], Dict};
+ false ->
+ Deps0 = apply(Repo#repository.api_module, package_deps
+ , [Repo#repository.owner, Repo#repository.name, PkgVsn]),
+ F = fun({Dep, Args}, TempDict) ->
+ {DepName, DepUser} = epm_cache:split_package(Dep),
+ DepVsn = epm_cache:read_vsn_from_args(
+ Args, apply(Repo#repository.api_module, default_vsn, [])),
+ Package0 = #package{user = DepUser
+ , name = DepName
+ , vsn = DepVsn
+ , args = Args },
+ TempDict1 = package_dependencies1([Package0], RepoPlugins, G, Key
+ , TempDict),
+ {{DepUser, DepName, DepVsn}, TempDict1}
+ end,
+ lists:mapfoldl(F, Dict, Deps0)
+ end,
+
+ Package1 = Package#package{ user = Repo#repository.owner
+ , name = Repo#repository.name
+ , vsn = PkgVsn
+ , deps = Deps
+ , repo = Repo },
+ package_dependencies1(Tail, RepoPlugins, G, Parent
+ , dict:store(Key, Package1, Dict1)).
View
9 src/github_api.erl → src/github_api.bak
@@ -1,7 +1,14 @@
-module(github_api).
-behaviour(api_behavior).
--export([package_deps/3, search/1, info/2, tags/2, branches/2, download_package/2, default_vsn/0]).
+-export([ package_deps/3
+ , search/1
+ , info/2
+ , tags/2
+ , branches/2
+ , download_package/2
+ , default_vsn/0
+ ]).
-include_lib("xmerl/include/xmerl.hrl").
-include("epm.hrl").
Please sign in to comment.
Something went wrong with that request. Please try again.