diff options
Diffstat (limited to 'make')
-rw-r--r-- | make/emd2exml.in | 6 | ||||
-rw-r--r-- | make/ose_lm.mk.in | 75 | ||||
-rw-r--r-- | make/otp.mk.in | 7 | ||||
-rw-r--r-- | make/otp_ded.mk.in | 1 | ||||
-rw-r--r-- | make/otp_default_release_path.mk | 24 | ||||
-rw-r--r-- | make/otp_release_targets.mk | 4 | ||||
-rw-r--r-- | make/otp_released_app.mk | 43 | ||||
-rw-r--r-- | make/otp_subdir.mk | 13 | ||||
-rw-r--r-- | make/output.mk.in | 6 | ||||
-rw-r--r-- | make/run_make.mk | 2 | ||||
-rwxr-xr-x | make/verify_runtime_dependencies | 313 |
11 files changed, 487 insertions, 7 deletions
diff --git a/make/emd2exml.in b/make/emd2exml.in index 5bfe89894e..48473349d0 100644 --- a/make/emd2exml.in +++ b/make/emd2exml.in @@ -334,6 +334,8 @@ text(Line) -> text("%ERTS-VSN%" ++ Cs, Acc) -> text(Cs, ["@ERTS_VSN@"|Acc]); +text("%OTP-VSN%" ++ Cs, Acc) -> + text(Cs, ["@OTP_VSN@"|Acc]); text("%OTP-REL%" ++ Cs, Acc) -> text(Cs, ["@OTP_REL@"|Acc]); @@ -357,6 +359,8 @@ put_text(#state{c = CTag, emphasis = EmTag} = S, Line) -> put_text(S, "%ERTS-VSN%"++Cs, CTag, EmTag, Acc) -> put_text(S, Cs, CTag, EmTag, ["@ERTS_VSN@"|Acc]); +put_text(S, "%OTP-VSN%"++Cs, CTag, EmTag, Acc) -> + put_text(S, Cs, CTag, EmTag, ["@OTP_VSN@"|Acc]); put_text(S, "%OTP-REL%"++Cs, CTag, EmTag, Acc) -> put_text(S, Cs, CTag, EmTag, ["@OTP_REL@"|Acc]); @@ -560,6 +564,8 @@ code(Line) -> code("%ERTS-VSN%" ++ Cs, Acc) -> code(Cs, ["@ERTS_VSN@"|Acc]); +code("%OTP-VSN%" ++ Cs, Acc) -> + code(Cs, ["@OTP_VSN@"|Acc]); code("%OTP-REL%" ++ Cs, Acc) -> code(Cs, ["@OTP_REL@"|Acc]); diff --git a/make/ose_lm.mk.in b/make/ose_lm.mk.in new file mode 100644 index 0000000000..5455ad94c6 --- /dev/null +++ b/make/ose_lm.mk.in @@ -0,0 +1,75 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode +# ---------------------------------------------------- +# Template target for generating an OSE5 load module +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Author: Petre Pircalabu +# ---------------------------------------------------- + +# ---------------------------------------------------- +# build-ose-load-module +# Creates an OSE5 load module +# params: +# $(1) - The output target +# $(2) - Objects +# $(3) - Libraries +# $(4) - LM configuration file +# ---------------------------------------------------- + +ifeq ($(findstring ose,$(TARGET)),ose) +LDR1FLAGS = @erl_xcomp_ose_ldflags_pass1@ +LDR2FLAGS = @erl_xcomp_ose_ldflags_pass2@ +OSEROOT = @erl_xcomp_ose_OSEROOT@ +LCF = @erl_xcomp_ose_LM_LCF@ +BEAM_LMCONF = @erl_xcomp_ose_BEAM_LM_CONF@ +EPMD_LMCONF = @erl_xcomp_ose_EPMD_LM_CONF@ +RUN_ERL_LMCONF = @erl_xcomp_ose_RUN_ERL_LM_CONF@ +STRIP = @erl_xcomp_ose_STRIP@ +LM_POST_LINK = @erl_xcomp_ose_LM_POST_LINK@ +LM_SET_CONF = @erl_xcomp_ose_LM_SET_CONF@ +LM_ELF_SIZE = @erl_xcomp_ose_LM_ELF_SIZE@ +OSE_CONFD = @erl_xcomp_ose_CONFD@ +CRT0_LM = @erl_xcomp_ose_CRT0_LM@ +endif + +define build-ose-load-module + @echo " --- Linking $(1)" + + @echo " --- Linking $(1) (pass 1)" + $(ld_verbose)$(PURIFY) $(LD) -o $(1)_unconfigured_ro -r \ + $(2) --start-group $(3) --end-group --cref --discard-none -M > $(1)_1.map + + @echo " --- Linking $(1) (pass 2)" + $(ld_verbose)$(PURIFY) $(LD) -o $(1)_unconfigured \ + $(1)_unconfigured_ro -T $(LCF) -n --emit-relocs -e crt0_lm --cref \ + --discard-none -M > $(1)_2.map + + @echo " --- Inserting configuration" + $(ld_verbose) $(LM_SET_CONF) $(1)_unconfigured < $(4) + + @echo " --- Striping $(1)" +# $(ld_verbose) $(STRIP) $(1)_unconfigured + + @echo " --- Postlinking $(1)" + $(ld_verbose) $(LM_POST_LINK) $(1)_unconfigured + + @echo " --- Sizing $(1)" + $(ld_verbose) $(LM_ELF_SIZE) $(1)_unconfigured + mv $(1)_unconfigured $(1) +endef diff --git a/make/otp.mk.in b/make/otp.mk.in index 785926b997..a89bc37820 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -30,6 +30,13 @@ include $(ERL_TOP)/make/output.mk # ---------------------------------------------------- +# Version +# ---------------------------------------------------- + +OTP_VERSION = @OTP_VERSION@ +SYSTEM_VSN = @SYSTEM_VSN@ + +# ---------------------------------------------------- # Cross Compiling # ---------------------------------------------------- CROSS_COMPILING = @CROSS_COMPILING@ diff --git a/make/otp_ded.mk.in b/make/otp_ded.mk.in index 0c9a8a087f..c534209a25 100644 --- a/make/otp_ded.mk.in +++ b/make/otp_ded.mk.in @@ -38,6 +38,7 @@ DED_THR_DEFS = @DED_THR_DEFS@ DED_EMU_THR_DEFS = @DED_EMU_THR_DEFS@ DED_WARN_FLAGS = @WFLAGS@ DED_CFLAGS = @WERRORFLAGS@ @WFLAGS@ @DED_EMU_THR_DEFS@ @DED_CFLAGS@ +DED_STATIC_CFLAGS = @WERRORFLAGS@ @WFLAGS@ @DED_EMU_THR_DEFS@ @DED_STATIC_CFLAGS@ DED_LIBS = @LIBS@ DED_EXT = @DED_EXT@ ERLANG_OSTYPE = @ERLANG_OSTYPE@ diff --git a/make/otp_default_release_path.mk b/make/otp_default_release_path.mk new file mode 100644 index 0000000000..932bbb2f6d --- /dev/null +++ b/make/otp_default_release_path.mk @@ -0,0 +1,24 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +# Where to release to by default +# + +OTP_DEFAULT_RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk index b6afcd1c8b..fcac2ff695 100644 --- a/make/otp_release_targets.mk +++ b/make/otp_release_targets.mk @@ -17,6 +17,8 @@ # %CopyrightEnd% # +include $(ERL_TOP)/make/otp_default_release_path.mk + # ---------------------------------------------------- # Targets for the new documentation support # ---------------------------------------------------- @@ -137,7 +139,7 @@ endif ifeq ($(TESTROOT),) release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" \ + $(MAKE) $(MFLAGS) RELEASE_PATH=$(OTP_DEFAULT_RELEASE_PATH) \ $(TARGET_MAKEFILE) $@_spec else diff --git a/make/otp_released_app.mk b/make/otp_released_app.mk new file mode 100644 index 0000000000..fb5205ab23 --- /dev/null +++ b/make/otp_released_app.mk @@ -0,0 +1,43 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk +include $(APP_PWD)/vsn.mk +include $(ERL_TOP)/make/otp_default_release_path.mk + +RELEASED_APP_VSN=$(APP)-$($(APP_VSN)) +ifeq ($(TESTROOT),) +REL_DIR=$(OTP_DEFAULT_RELEASE_PATH)/releases/$(SYSTEM_VSN) +else +REL_DIR=$(TESTROOT)/releases/$(SYSTEM_VSN) +endif +INST_APP_VSNS=$(REL_DIR)/installed_application_versions + +.PHONY: update + +update: + test -d "$(REL_DIR)" || mkdir -p "$(REL_DIR)" ; \ + if test ! -f "$(INST_APP_VSNS)" ; then \ + echo "$(RELEASED_APP_VSN)" > "$(INST_APP_VSNS)" || exit 1; \ + else \ + if test x = x`grep $(RELEASED_APP_VSN) "$(INST_APP_VSNS)"` ; then \ + echo $(RELEASED_APP_VSN) >> "$(INST_APP_VSNS)" || exit 1; \ + fi ; \ + fi + diff --git a/make/otp_subdir.mk b/make/otp_subdir.mk index 07294c272d..e6a75cce17 100644 --- a/make/otp_subdir.mk +++ b/make/otp_subdir.mk @@ -19,12 +19,12 @@ # Make include file for otp .PHONY: debug opt release docs release_docs tests release_tests \ - clean depend valgrind + clean depend valgrind static_lib # # Targets that don't affect documentation directories # -opt debug release docs release_docs tests release_tests clean depend valgrind: +opt debug release docs release_docs tests release_tests clean depend valgrind static_lib: @set -e ; \ app_pwd=`pwd` ; \ if test -f vsn.mk; then \ @@ -44,5 +44,14 @@ opt debug release docs release_docs tests release_tests clean depend valgrind: fi ; \ done ; \ if test -f vsn.mk; then \ + if test release = $@ && test ! -f SKIP; then \ + app=`basename $$app_pwd` ; \ + app_vsn=`echo $$app | sed "y|abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ|"` ; \ + app_vsn=$${app_vsn}_VSN ; \ + ( $(MAKE) -f "$(ERL_TOP)/make/otp_released_app.mk" \ + APP_PWD="$$app_pwd" APP_VSN=$$app_vsn APP=$$app \ + TESTROOT="$(TESTROOT)" update) \ + || exit $$? ; \ + fi ; \ echo "=== Leaving application" `basename $$app_pwd` ; \ fi diff --git a/make/output.mk.in b/make/output.mk.in index 51d9401280..f2f738a2ce 100644 --- a/make/output.mk.in +++ b/make/output.mk.in @@ -65,9 +65,9 @@ cc_verbose_0 = @echo " CC "$@; cc_verbose = $(cc_verbose_$(V)) V_CC = $(cc_verbose)$(CC) -cpp_verbose_0 = @echo " CPP "$@; -cpp_verbose = $(cpp_verbose_$(V)) -V_CPP = $(cpp_verbose)$(CPP) +cxx_verbose_0 = @echo " CXX "$@; +cxx_verbose = $(cxx_verbose_$(V)) +V_CXX = $(cxx_verbose)$(CXX) # For the diameter compiler. dia_verbose_0 = @echo " DIA "$@; diff --git a/make/run_make.mk b/make/run_make.mk index bb0da6743c..01ab257006 100644 --- a/make/run_make.mk +++ b/make/run_make.mk @@ -37,7 +37,7 @@ plain smp frag smp_frag: $(make_verbose)$(MAKE) -f $(TARGET)/Makefile FLAVOR=$@ clean generate depend docs release release_spec release_docs release_docs_spec \ - tests release_tests release_tests_spec: + tests release_tests release_tests_spec static_lib: $(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@ diff --git a/make/verify_runtime_dependencies b/make/verify_runtime_dependencies new file mode 100755 index 0000000000..b8eea06b6e --- /dev/null +++ b/make/verify_runtime_dependencies @@ -0,0 +1,313 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% @author Rickard Green <[email protected]> +%%% @copyright (C) 2014, Rickard Green +%%% @doc +%%% Verify runtime dependencies when patching OTP applications. +%%% @end +%%% Created : 4 Mar 2014 by Rickard Green <[email protected]> +%%%------------------------------------------------------------------- + +-mode(compile). + +-export([main/1]). + +main(Args) -> + {Force, Release, SourceDir, TargetDir, AppList} = parse_args(Args, + false, + [], + [], + [], + []), + SourceAppInfo = read_source_app_info(AppList, SourceDir), + AppVsnsTab0 = current_target_app_vsns(TargetDir, Release), + AppVsnsTab1 = add_source_app_vsns(SourceAppInfo, AppVsnsTab0), + case verify_runtime_deps(SourceAppInfo, AppVsnsTab1, true) of + true -> + ok; + false -> + case Force of + true -> + warn("Your OTP development system was updated with " + "unfulfilled runtime dependencies. The system " + "may not be working as expected.", []); + false -> + err("Unfulfilled runtime dependencies. " + "See warnings above.~n", []) + end + end, + halt(0). + +parse_args(["-force" | Args], _, Release, SourceDir, TargetDir, Apps) -> + parse_args(Args, true, Release, SourceDir, TargetDir, Apps); +parse_args(["-release", Release | Args], Force, _, SourceDir, TargetDir, Apps) -> + parse_args(Args, Force, Release, SourceDir, TargetDir, Apps); +parse_args(["-source", SourceDir | Args], Force, Release, _, TargetDir, Apps) -> + parse_args(Args, Force, Release, SourceDir, TargetDir, Apps); +parse_args(["-target", TargetDir | Args], Force, Release, SourceDir, _, Apps) -> + parse_args(Args, Force, Release, SourceDir, TargetDir, Apps); +parse_args([App | Args], Force, Release, SourceDir, TargetDir, OldApps) -> + parse_args(Args, Force, Release, SourceDir, TargetDir, [App | OldApps]); +parse_args([], _, [], _, _, _) -> + err("Missing release~n", []); +parse_args([], _, _, [], _, _) -> + err("Missing source directory~n", []); +parse_args([], _, _, _, [], _) -> + err("Missing target directory~n", []); +parse_args([], _, _, _, _, []) -> + err("Missing applications~n"); +parse_args([], Force, Release, SourceDir, TargetDir, Apps) -> + {Force, Release, SourceDir, TargetDir, Apps}. + + +%warn(Format) -> +% warn(Format, []). + +warn(Format, Args) -> + io:format(standard_error, "WARNING: " ++ Format, Args). + +err(Format) -> + err(Format, []). + +err(Format, Args) -> + io:format(standard_error, "ERROR: " ++ Format, Args), + halt(1). + +read_file(FileName) -> + case file:read_file(FileName) of + {ok, Content} -> + binary_to_list(Content); + {error, Error} -> + err("Failed to read ~s: ~p~n", [FileName, Error]) + end. + +consult_file(FileName) -> + case file:consult(FileName) of + {ok, Terms} -> + Terms; + {error, Error} -> + err("Failed to consult ~s: ~p~n", [FileName, Error]) + end. + +current_target_app_vsns(TargetDir, Release) -> + IAV = read_file(filename:join([TargetDir, "releases", Release, + "installed_application_versions"])), + DirList = string:tokens(IAV, "\n\r\t "), + LibDir = filename:join(TargetDir, "lib"), + make_app_vsns_tab(DirList, LibDir, gb_trees:empty()). + +make_app_vsns_tab([], _LibDir, GBT) -> + GBT; +make_app_vsns_tab([AppVer | AppVsns], LibDir, GBT0) -> + GBT1 = try + case file:read_file_info(filename:join(LibDir, AppVer)) of + {ok, _FInfo} -> + [App, Vsn] = string:tokens(AppVer, "-"), + add_app_vsn(App, Vsn, GBT0); + _ -> + GBT0 + end + catch + _:_ -> + warn("Unexpected directory: ~p~n", + [filename:join(LibDir, AppVer)]), + GBT0 + end, + make_app_vsns_tab(AppVsns, LibDir, GBT1). + +add_app_vsn(App, VsnList, GBT) when is_atom(App) -> + Vsn = parse_vsn(VsnList), + case gb_trees:lookup(App, GBT) of + none -> + gb_trees:insert(App, [Vsn], GBT); + {value, Vsns} -> + gb_trees:update(App, [Vsn | Vsns], GBT) + end; +add_app_vsn(AppStr, VsnList, GBT) -> + add_app_vsn(list_to_atom(AppStr), VsnList, GBT). + +add_source_app_vsns([], AppVsnsTab) -> + AppVsnsTab; +add_source_app_vsns([{App, Vsn, _IReqs} | AI], AppVsnsTab) -> + add_source_app_vsns(AI, add_app_vsn(App, Vsn, AppVsnsTab)). + +read_source_app_info([], _SourceDir) -> + []; +read_source_app_info([App | Apps], SourceDir) -> + AppFile = case App of + "erts" -> + filename:join([SourceDir, "erts", "preloaded", "ebin", + "erts.app"]); + _ -> + filename:join([SourceDir, "lib", App, "ebin", + App ++ ".app"]) + end, + AppAtom = list_to_atom(App), + case consult_file(AppFile) of + [{application, AppAtom, InfoList}] -> + Vsn = case lists:keyfind(vsn, 1, InfoList) of + {vsn, V} -> + V; + _ -> + err("Missing vsn in ~p~n", AppFile) + end, + AI = case lists:keyfind(runtime_dependencies, 1, InfoList) of + {runtime_dependencies, IReqs} -> + case parse_inst_reqs(IReqs) of + error -> + err("Failed to parse runtime_dependencies in ~p~n", + [AppFile]); + ParsedIReqs -> + {AppAtom, Vsn, ParsedIReqs} + end; + _ -> + {AppAtom, Vsn, []} + end, + [AI | read_source_app_info(Apps, SourceDir)]; + _ -> + err("Failed to parse ~p~n", [AppFile]) + end. + +parse_vsn(VsnStr) -> + list_to_tuple(lists:map(fun (IL) -> + list_to_integer(IL) + end, string:tokens(VsnStr, "."))). + +parse_inst_reqs(InstReqs) -> + try + parse_inst_reqs_aux(InstReqs) + catch + _ : _ -> + error + end. + +parse_inst_reqs_aux([]) -> + []; +parse_inst_reqs_aux([IR | IRs]) -> + [App, VsnStr] = string:tokens(IR, "-"), + [{list_to_atom(App), parse_vsn(VsnStr)} | parse_inst_reqs_aux(IRs)]. + +make_app_vsn_str({App, VsnTup}) -> + make_app_vsn_str(tuple_to_list(VsnTup), [atom_to_list(App), $-]). + +make_app_vsn_str([I], Acc) -> + lists:flatten([Acc, integer_to_list(I)]); +make_app_vsn_str([I | Is], Acc) -> + make_app_vsn_str(Is, [Acc, integer_to_list(I), $.]). + +missing_min_req(App, AppVsn, IReq) -> + warn("Unfulfilled runtime dependency for application ~p-~s: ~s~n", + [App, AppVsn, make_app_vsn_str(IReq)]). + +verify_runtime_deps([], _AppVsnsTab, Res) -> + Res; +verify_runtime_deps([{App, Vsn, IReqs} | SAIs], AppVsnsTab, Res0) -> + Res = lists:foldl( + fun ({IRApp, IRMinVsn} = InstReq, AccRes) -> + case gb_trees:lookup(IRApp, AppVsnsTab) of + none -> + missing_min_req(App, Vsn, InstReq), + false; + {value, AppVsns} -> + try + lists:foreach( + fun (AppVsn) -> + case meets_min_req(AppVsn, IRMinVsn) of + true -> + throw(true); + false -> + false + end + end, + AppVsns), + missing_min_req(App, Vsn, InstReq), + false + catch + throw : true -> + AccRes + end + end + end, + Res0, + IReqs), + verify_runtime_deps(SAIs, AppVsnsTab, Res). + +meets_min_req(Vsn, Vsn) -> + true; +meets_min_req({X}, VsnReq) -> + meets_min_req({X, 0, 0}, VsnReq); +meets_min_req({X, Y}, VsnReq) -> + meets_min_req({X, Y, 0}, VsnReq); +meets_min_req(Vsn, {X}) -> + meets_min_req(Vsn, {X, 0, 0}); +meets_min_req(Vsn, {X, Y}) -> + meets_min_req(Vsn, {X, Y, 0}); +meets_min_req({X, _Y, _Z}, {XReq, _YReq, _ZReq}) when X > XReq -> + true; +meets_min_req({X, Y, _Z}, {X, YReq, _ZReq}) when Y > YReq -> + true; +meets_min_req({X, Y, Z}, {X, Y, ZReq}) when Z > ZReq -> + true; +meets_min_req({_X, _Y, _Z}, {_XReq, _YReq, _ZReq}) -> + false; +meets_min_req(Vsn, VsnReq) -> + gp_meets_min_req(mk_gp_vsn_list(Vsn), mk_gp_vsn_list(VsnReq)). + +gp_meets_min_req([X, Y, Z | _Vs], [X, Y, Z]) -> + true; +gp_meets_min_req([X, Y, Z | _Vs], [XReq, YReq, ZReq]) -> + meets_min_req({X, Y, Z}, {XReq, YReq, ZReq}); +gp_meets_min_req([X, Y, Z | Vs], [X, Y, Z | VReqs]) -> + gp_meets_min_req_tail(Vs, VReqs); +gp_meets_min_req(_Vsn, _VReq) -> + %% Versions on different version branches, i.e., the minimum + %% required functionality is not included in Vsn. + false. + +gp_meets_min_req_tail([V | Vs], [V | VReqs]) -> + gp_meets_min_req_tail(Vs, VReqs); +gp_meets_min_req_tail([], []) -> + true; +gp_meets_min_req_tail([_V | _Vs], []) -> + true; +gp_meets_min_req_tail([V | _Vs], [VReq]) when V > VReq -> + true; +gp_meets_min_req_tail(_Vs, _VReqs) -> + %% Versions on different version branches, i.e., the minimum + %% required functionality is not included in Vsn. + false. + +mk_gp_vsn_list(Vsn) -> + [X, Y, Z | Tail] = tuple_to_list(Vsn), + [X, Y, Z | remove_trailing_zeroes(Tail)]. + +remove_trailing_zeroes([]) -> + []; +remove_trailing_zeroes([0 | Vs]) -> + case remove_trailing_zeroes(Vs) of + [] -> []; + NewVs -> [0 | NewVs] + end; +remove_trailing_zeroes([V | Vs]) -> + [V | remove_trailing_zeroes(Vs)]. |