aboutsummaryrefslogtreecommitdiffstats
path: root/src/rcl_release.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rcl_release.erl')
-rw-r--r--src/rcl_release.erl415
1 files changed, 0 insertions, 415 deletions
diff --git a/src/rcl_release.erl b/src/rcl_release.erl
deleted file mode 100644
index 9ed741e..0000000
--- a/src/rcl_release.erl
+++ /dev/null
@@ -1,415 +0,0 @@
-%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
-%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
-%%%
-%%% This file is provided to you under the Apache License,
-%%% Version 2.0 (the "License"); you may not use this file
-%%% except in compliance with the License. You may obtain
-%%% a copy of the License at
-%%%
-%%% http://www.apache.org/licenses/LICENSE-2.0
-%%%
-%%% Unless required by applicable law or agreed to in writing,
-%%% software distributed under the License is distributed on an
-%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-%%% KIND, either express or implied. See the License for the
-%%% specific language governing permissions and limitations
-%%% under the License.
-%%%---------------------------------------------------------------------------
-%%% @author Eric Merritt <[email protected]>
-%%% @copyright (C) 2012 Erlware, LLC.
-%%%
-%%% @doc This module represents a release and its metadata and is used to
-%%% manipulate the release metadata.
--module(rcl_release).
-
--export([new/2,
- new/3,
- relfile/1,
- relfile/2,
- erts/2,
- erts/1,
- goals/2,
- goals/1,
- name/1,
- vsn/1,
- realize/3,
- applications/1,
- application_details/1,
- application_details/2,
- realized/1,
- metadata/1,
- canonical_name/1,
- format/1,
- format/2,
- format_error/1]).
-
--export_type([t/0,
- name/0,
- vsn/0,
- app_name/0,
- app_vsn/0,
- app_type/0,
- application_spec/0,
- application_goal/0]).
-
--include_lib("relcool/include/relcool.hrl").
-
--record(release_t, {name :: atom(),
- vsn :: ec_semver:any_version(),
- erts :: ec_semver:any_version(),
- goals = [] :: [rcl_depsolver:constraint()],
- realized = false :: boolean(),
- annotations = undefined :: annotations(),
- applications = [] :: [application_spec()],
- relfile :: undefined | string(),
- app_detail = [] :: [rcl_app_info:t()]}).
-
-%%============================================================================
-%% types
-%%============================================================================
--type name() :: atom().
--type vsn() :: string().
--type app_name() :: atom().
--type app_vsn() :: string().
--type app_type() :: permanent | transient | temporary | load | none.
--type incl_apps() :: [app_name()].
-
--type application_spec() :: {app_name(), app_vsn()} |
- {app_name(), app_vsn(), app_type() | incl_apps()} |
- {app_name(), app_vsn(), app_type(), incl_apps()}.
-
--type application_constraint() :: rcl_depsolver:raw_constraint() | string() | binary().
--type application_goal() :: application_constraint()
- | {application_constraint(), app_type() | incl_apps()}
- | {application_constraint(), app_type(), incl_apps() | none}.
-
--type annotations() :: ec_dictionary:dictionary(app_name(),
- {app_type(), incl_apps() | none}).
-
-
--opaque t() :: record(release_t).
-
-%%============================================================================
-%% API
-%%============================================================================
--spec new(atom(), string(), undefined | file:name()) -> t().
-new(ReleaseName, ReleaseVsn, Relfile) ->
- #release_t{name=to_atom(ReleaseName), vsn=ReleaseVsn,
- relfile = Relfile,
- annotations=ec_dictionary:new(ec_dict)}.
-
--spec new(atom(), string()) -> t().
-new(ReleaseName, ReleaseVsn) ->
- new(ReleaseName, ReleaseVsn, undefined).
-
-
--spec relfile(t()) -> file:name() | undefined.
-relfile(#release_t{relfile=Relfile}) ->
- Relfile.
-
--spec relfile(t(), file:name()) -> t().
-relfile(Release, Relfile) ->
- Release#release_t{relfile=Relfile}.
-
--spec name(t()) -> atom().
-name(#release_t{name=Name}) ->
- Name.
-
--spec vsn(t()) -> string().
-vsn(#release_t{vsn=Vsn}) ->
- Vsn.
-
--spec erts(t(), app_vsn()) -> t().
-erts(Release, Vsn) ->
- Release#release_t{erts=Vsn}.
-
--spec erts(t()) -> app_vsn().
-erts(#release_t{erts=Vsn}) ->
- Vsn.
-
--spec goals(t(), [application_goal()]) -> {ok, t()} | relcool:error().
-goals(Release, Goals0) ->
- lists:foldl(fun parse_goal0/2,
- {ok, Release}, Goals0).
-
--spec goals(t()) -> [application_goal()].
-goals(#release_t{goals=Goals}) ->
- Goals.
-
--spec realize(t(), [{app_name(), app_vsn()}], [rcl_app_info:t()]) ->
- {ok, t()} | relcool:error().
-realize(Rel, Pkgs0, World0) ->
- World1 = subset_world(Pkgs0, World0),
- case rcl_topo:sort_apps(World1) of
- {ok, Pkgs1} ->
- process_specs(realize_erts(Rel), Pkgs1);
- Error={error, _} ->
- Error
- end.
-
-%% @doc this gives the application specs for the release. This can only be
-%% populated by the 'realize' call in this module.
--spec applications(t()) -> [application_spec()].
-applications(#release_t{applications=Apps}) ->
- Apps.
-
-%% @doc this gives the rcl_app_info objects representing the applications in
-%% this release. These should only be populated by the 'realize' call in this
-%% module or by reading an existing rel file.
--spec application_details(t()) -> [rcl_app_info:t()].
-application_details(#release_t{app_detail=App}) ->
- App.
-
-%% @doc this is only expected to be called by a process building a new release
-%% from an existing rel file.
--spec application_details(t(), [rcl_app_info:t()]) -> t().
-application_details(Release, AppDetail) ->
- Release#release_t{app_detail=AppDetail}.
-
--spec realized(t()) -> boolean().
-realized(#release_t{realized=Realized}) ->
- Realized.
-
--spec metadata(t()) -> term().
-metadata(#release_t{name=Name, vsn=Vsn, erts=ErtsVsn, applications=Apps,
- realized=Realized}) ->
- case Realized of
- true ->
- {ok, {release, {erlang:atom_to_list(Name), Vsn}, {erts, ErtsVsn},
- Apps}};
- false ->
- ?RCL_ERROR({not_realized, Name, Vsn})
- end.
-
-%% @doc produce the canonical name (<name>-<vsn>) for this release
--spec canonical_name(t()) -> string().
-canonical_name(#release_t{name=Name, vsn=Vsn}) ->
- erlang:binary_to_list(erlang:iolist_to_binary([erlang:atom_to_list(Name), "-",
- Vsn])).
-
--spec format(t()) -> iolist().
-format(Release) ->
- format(0, Release).
-
--spec format(non_neg_integer(), t()) -> iolist().
-format(Indent, #release_t{name=Name, vsn=Vsn, erts=ErtsVsn, realized=Realized,
- goals = Goals, applications=Apps}) ->
- BaseIndent = rcl_util:indent(Indent),
- [BaseIndent, "release: ", rcl_util:to_string(Name), "-", Vsn, "\n",
- rcl_util:indent(Indent + 1), " erts-", ErtsVsn,
- ", realized = ", erlang:atom_to_list(Realized), "\n",
- BaseIndent, "goals: \n",
- [[rcl_util:indent(Indent + 1), format_goal(Goal), ",\n"] || Goal <- Goals],
- case Realized of
- true ->
- [BaseIndent, "applications: \n",
- [[rcl_util:indent(Indent + 1), io_lib:format("~p", [App]), ",\n"] ||
- App <- Apps]];
- false ->
- []
- end].
-
--spec format_goal(application_goal()) -> iolist().
-format_goal({Constraint, AppType}) ->
- io_lib:format("~p", [{rcl_depsolver:format_constraint(Constraint), AppType}]);
-format_goal({Constraint, AppType, AppInc}) ->
- io_lib:format("~p", [{rcl_depsolver:format_constraint(Constraint), AppType, AppInc}]);
-format_goal(Constraint) ->
- rcl_depsolver:format_constraint(Constraint).
-
--spec format_error(Reason::term()) -> iolist().
-format_error({topo_error, E}) ->
- rcl_topo:format_error(E);
-format_error({failed_to_parse, Con}) ->
- io_lib:format("Failed to parse constraint ~p", [Con]);
-format_error({invalid_constraint, _, Con}) ->
- io_lib:format("Invalid constraint specified ~p", [Con]);
-format_error({not_realized, Name, Vsn}) ->
- io_lib:format("Unable to produce metadata release: ~p-~s has not been realized",
- [Name, Vsn]).
-
-%%%===================================================================
-%%% Internal Functions
-%%%===================================================================
--spec realize_erts(t()) -> t().
-realize_erts(Rel=#release_t{erts=undefined}) ->
- Rel#release_t{erts=erlang:system_info(version)};
-realize_erts(Rel) ->
- Rel.
-
--spec process_specs(t(), [rcl_app_info:t()]) ->
- {ok, t()}.
-process_specs(Rel=#release_t{annotations=Annots,
- goals=Goals}, World) ->
- ActiveApps = lists:flatten([rcl_app_info:active_deps(El) || El <- World] ++
- [case get_app_name(Goal) of
- {error, _} -> [];
- G -> G
- end || Goal <- Goals]),
- LibraryApps = lists:flatten([rcl_app_info:library_deps(El) || El <- World]),
- Specs = [create_app_spec(Annots, App, ActiveApps, LibraryApps) || App <- World],
- {ok, Rel#release_t{annotations=Annots,
- applications=Specs,
- app_detail=World,
- realized=true}}.
-
--spec create_app_spec(annotations(), rcl_app_info:t(), [app_name()],
- [app_name()]) ->
- application_spec().
-create_app_spec(Annots, App, ActiveApps, LibraryApps) ->
- %% If the app only exists as a dependency in a library app then it should
- %% get the 'load' annotation unless the release spec has provided something
- %% else
- AppName = rcl_app_info:name(App),
- TypeAnnot =
- case (lists:member(AppName, LibraryApps) and
- (not lists:member(AppName, ActiveApps))) of
- true ->
- load;
- false ->
- none
- end,
- BaseAnnots =
- try
- case ec_dictionary:get(AppName, Annots) of
- {none, Incld} ->
- {TypeAnnot, Incld};
- Else ->
- Else
- end
- catch
- throw:not_found ->
- {TypeAnnot, none}
- end,
- Vsn = rcl_app_info:vsn_as_string(App),
- case BaseAnnots of
- {none, none} ->
- {AppName, Vsn};
- {Type, none} ->
- {AppName, Vsn, Type};
- {none, Incld0} ->
- {AppName, Vsn, Incld0};
- {Type, Incld1} ->
- {AppName, Vsn, Type, Incld1}
- end.
-
--spec subset_world([{app_name(), app_vsn()}], [rcl_app_info:t()]) -> [rcl_app_info:t()].
-subset_world(Pkgs, World) ->
- [get_app_info(Pkg, World) || Pkg <- Pkgs].
-
--spec get_app_info({app_name(), app_vsn()}, [rcl_app_info:t()]) -> rcl_app_info:t().
-get_app_info({PkgName, PkgVsn}, World) ->
- {ok, WorldEl} =
- ec_lists:find(fun(El) ->
- rcl_app_info:name(El) =:= PkgName andalso
- rcl_app_info:vsn(El) =:= PkgVsn
- end, World),
- WorldEl.
-
-parse_goal0({Constraint0, Annots}, {ok, Release})
- when Annots =:= permanent;
- Annots =:= transient;
- Annots =:= temporary;
- Annots =:= load;
- Annots =:= none ->
- case parse_constraint(Constraint0) of
- {ok, Constraint1} ->
- parse_goal1(Release, Constraint1, {Annots, none});
- Error ->
- Error
- end;
-parse_goal0({Constraint0, Annots, Incls}, {ok, Release})
- when (Annots =:= permanent orelse
- Annots =:= transient orelse
- Annots =:= temporary orelse
- Annots =:= load orelse
- Annots =:= none),
- erlang:is_list(Incls) ->
- case parse_constraint(Constraint0) of
- {ok, Constraint1} ->
- parse_goal1(Release, Constraint1, {Annots, Incls});
- Error ->
- Error
- end;
-parse_goal0(Constraint0, {ok, Release}) ->
- case parse_constraint(Constraint0) of
- {ok, Constraint1} ->
- parse_goal1(Release, Constraint1, {none, none});
- Error ->
- Error
- end;
-parse_goal0(_, E = {error, _}) ->
- E;
-parse_goal0(Constraint, _) ->
- ?RCL_ERROR({invalid_constraint, 1, Constraint}).
-
-parse_goal1(Release = #release_t{annotations=Annots, goals=Goals},
- Constraint, NewAnnots) ->
- case get_app_name(Constraint) of
- E1 = {error, _} ->
- E1;
- AppName ->
- {ok,
- Release#release_t{annotations=ec_dictionary:add(AppName, NewAnnots, Annots),
- goals = [Constraint | Goals]}}
- end.
-
--spec parse_constraint(application_constraint()) ->
- rcl_depsolver:constraint() | relcool:error().
-parse_constraint(Constraint0)
- when erlang:is_list(Constraint0); erlang:is_binary(Constraint0) ->
- case rcl_goal:parse(Constraint0) of
- {fail, _} ->
- ?RCL_ERROR({failed_to_parse, Constraint0});
- {ok, Constraint1} ->
- {ok, Constraint1}
- end;
-parse_constraint(Constraint0)
- when erlang:is_tuple(Constraint0);
- erlang:is_atom(Constraint0) ->
- Constraint1 = parse_version(Constraint0),
- case rcl_depsolver:is_valid_constraint(Constraint1) of
- false ->
- ?RCL_ERROR({invalid_constraint, 2, Constraint0});
- true ->
- {ok, Constraint1}
- end;
-parse_constraint(Constraint) ->
- ?RCL_ERROR({invalid_constraint, 3, Constraint}).
-
--spec get_app_name(rcl_depsolver:raw_constraint()) ->
- AppName::atom() | relcool:error().
-get_app_name(AppName) when erlang:is_atom(AppName) ->
- AppName;
-get_app_name({AppName, _}) when erlang:is_atom(AppName) ->
- AppName;
-get_app_name({AppName, _, _}) when erlang:is_atom(AppName) ->
- AppName;
-get_app_name({AppName, _, _, _}) when erlang:is_atom(AppName) ->
- AppName;
-get_app_name(V) ->
- ?RCL_ERROR({invalid_constraint, 4, V}).
-
--spec parse_version(rcl_depsolver:raw_constraint()) ->
- rcl_depsolver:constraint().
-parse_version({AppName, Version})
- when erlang:is_binary(Version);
- erlang:is_list(Version) ->
- {AppName, rcl_depsolver:parse_version(Version)};
-parse_version({AppName, Version, Constraint})
- when erlang:is_binary(Version);
- erlang:is_list(Version) ->
- {AppName, rcl_depsolver:parse_version(Version), Constraint};
-parse_version({AppName, Version, Constraint0, Constraint1})
- when erlang:is_binary(Version);
- erlang:is_list(Version) ->
- {AppName, rcl_depsolver:parse_version(Version), Constraint1, Constraint0};
-parse_version(Constraint) ->
- Constraint.
-
-to_atom(RelName)
- when erlang:is_list(RelName) ->
- erlang:list_to_atom(RelName);
-to_atom(Else)
- when erlang:is_atom(Else) ->
- Else.