New program libtool-next-version.

* libtool-next-version.in: New file, based on
gnulib/build-aux/libtool-next-version.
* doc/libtool.texi (Updating version info): Add sub-nodes
'Manual version info update', 'Guided version info update',
'Invoking libtool-next-version'. Note, line breaks use
partial semantic newlines.
* Makefile.am (BUILT_SOURCES): Add libtool-next-version.
(libtoolnextv_in): New variable.
(EXTRA_DIST): Add it.
(bin_SCRIPTS): Add libtool-next-version.
(libtool-next-version): New target.
(libtoolnextv_1): New variable. New target.
(dist_man1_MANS): Add it.
* NEWS: Update.
This commit is contained in:
Bruno Haible 2025-02-26 15:07:39 +01:00 committed by Ileana Dumitrescu
parent da70d4ca79
commit b31819c723
No known key found for this signature in database
GPG Key ID: 6570EA01146F7354
5 changed files with 419 additions and 14 deletions

2
.gitignore vendored
View File

@ -43,7 +43,7 @@
/gnulib-local
/gnulib-tests
/libtoolize
/libtoolize.in
/libtool-next-version
/maint.mk
/patches
/release

View File

@ -28,7 +28,7 @@ SUBDIRS = . gnulib-tests
DIST_SUBDIRS = $(SUBDIRS)
EXTRA_DIST =
BUILT_SOURCES = libtool libtoolize
BUILT_SOURCES = libtool libtoolize libtool-next-version
CLEANFILES =
MOSTLYCLEANFILES =
@ -64,7 +64,7 @@ build_scripts = $(srcdir)/$(aux_dir)/announce-gen \
EXTRA_DIST += bootstrap bootstrap.conf $(build_scripts) cfg.mk maint.mk \
GNUmakefile
CLEANFILES += libtool libtoolize
CLEANFILES += libtool libtoolize libtool-next-version
## If a file is named several times below, and especially if it
## is a distributed file created during Libtool bootstrap, we
@ -78,6 +78,7 @@ extract_trace = $(srcdir)/$(aux_dir)/extract-trace
funclib_sh = $(srcdir)/$(aux_dir)/funclib.sh
inline_source = $(srcdir)/$(aux_dir)/inline-source
libtoolize_in = $(srcdir)/libtoolize.in
libtoolnextv_in = $(srcdir)/libtool-next-version.in
ltmain_sh = $(srcdir)/$(aux_dir)/ltmain.sh
ltmain_in = $(srcdir)/$(aux_dir)/ltmain.in
libtool_m4 = $(srcdir)/$(macro_dir)/libtool.m4
@ -88,7 +89,8 @@ options_parser = $(srcdir)/$(aux_dir)/options-parser
u2d_copyright = $(srcdir)/$(aux_dir)/update-copyright
EXTRA_DIST += $(extract_trace) $(funclib_sh) $(inline_source) \
$(libtoolize_in) $(ltmain_in) $(ltmain_sh) \
$(libtoolize_in) $(libtoolnextv_in) \
$(ltmain_in) $(ltmain_sh) \
$(ltversion_in) $(ltversion_m4) $(no_bogus_macros) \
$(options_parser) $(u2d_copyright)
@ -280,6 +282,8 @@ configure_edit = $(bootstrap_edit) \
# The libtool distributor and the standalone libtool script.
bin_SCRIPTS = libtool
# The "Update version info" wizard.
bin_SCRIPTS += libtool-next-version
libtoolize: $(libtoolize_in) $(config_status)
$(AM_V_at)rm -f '$@'
@ -287,6 +291,12 @@ libtoolize: $(libtoolize_in) $(config_status)
$(AM_V_at)chmod a+x '$@'
$(AM_V_at)chmod a-w '$@'
libtool-next-version: $(libtoolnextv_in) $(config_status)
$(AM_V_at)rm -f '$@'
$(AM_V_GEN)$(bootstrap_edit) '$(libtoolnextv_in)' > '$@'
$(AM_V_at)chmod a+x '$@'
$(AM_V_at)chmod a-w '$@'
# We used to do this with a 'stamp-vcl' file, but non-gmake builds
# would rerun configure on every invocation, so now we manually
# check the version numbers from the build rule when necessary.
@ -383,6 +393,7 @@ doc_dir = $(srcdir)/doc
libtool_1 = $(doc_dir)/libtool.1
libtoolize_1 = $(doc_dir)/libtoolize.1
libtoolnextv_1 = $(doc_dir)/libtool-next-version.1
notes_texi = $(doc_dir)/notes.texi
notes_txt = $(doc_dir)/notes.txt
@ -408,7 +419,7 @@ $(notes_txt): $(notes_texi)
$(AM_V_GEN)$(MAKEINFO) -P '$(srcdir)/doc' --no-headers \
$(MAKEINFOFLAGS) -o '$@' '$(notes_texi)'
dist_man1_MANS = $(libtool_1) $(libtoolize_1)
dist_man1_MANS = $(libtool_1) $(libtoolize_1) $(libtoolnextv_1)
MAINTAINERCLEANFILES += $(dist_man1_MANS)
update_mans = \
PATH=".$(PATH_SEPARATOR)$$PATH"; export PATH; \
@ -422,6 +433,8 @@ $(libtool_1): $(ltmain_sh)
$(AM_V_GEN)$(update_mans) -n 'Provide generalized library-building support services' --help-option=--help-all libtool
$(libtoolize_1): $(libtoolize_in)
$(AM_V_GEN)$(update_mans) -n 'Prepare a package to use libtool' libtoolize
$(libtoolnextv_1): $(libtoolnextv_in)
$(AM_V_GEN)$(update_mans) -n 'Determines next version to use for a libtool library' libtool-next-version

3
NEWS
View File

@ -4,6 +4,9 @@ NEWS - list of user-visible changes between releases of GNU Libtool
** New features:
- Add a new tool, libtool-next-version, to guide users through updating
library versions.
- Add tagging for Objective-C and Objective-C++, OBJC and OBJCXX.
- Increase 5 digit limit on revision value for libraries to 19 digits,

View File

@ -40,6 +40,7 @@ the section entitled ``GNU Free Documentation License''.
@direntry
* libtool-invocation: (libtool)Invoking libtool. Running the @code{libtool} script.
* libtoolize: (libtool)Invoking libtoolize. Adding libtool support.
* libtool-next-version: (libtool)Invoking libtool-next-version. Running the @code{libtool-next-version} wizard.
@end direntry
@titlepage
@ -3158,7 +3159,7 @@ interface number.
Here are a set of rules to help you update your library version
information:
@enumerate 1
@itemize @bullet
@item
Start with version information of @samp{0:0:0} for each libtool library.
@ -3167,6 +3168,31 @@ Update the version information only immediately before a public release
of your software. More frequent updates are unnecessary, and only
guarantee that the current interface number gets larger faster.
@item
Do the update either manually, or guided by the @samp{libtool-next-version}
wizard.
@end itemize
@strong{@emph{Never}} try to set the interface numbers so that they
correspond to the release number of your package. This is an abuse that
only fosters misunderstanding of the purpose of library versions.
Instead, use the @option{-release} flag (@pxref{Release numbers}), but be
warned that every release of your package will not be binary compatible
with any other release.
@menu
* Manual version info update:: Updating version info manually.
* Guided version info update:: Using the libtool-next-version program.
* Invoking libtool-next-version:: @code{libtool-next-version} command line options.
@end menu
@node Manual version info update
@subsection Updating the version info manually
Here are the steps that you need to do, to update your library version
information:
@enumerate 1
@item
If the library source code has changed at all since the last update,
then increment @var{revision} (@samp{@var{c}:@var{r}:@var{a}} becomes
@ -3185,14 +3211,7 @@ If any interfaces have been removed or changed since the last public
release, then set @var{age} to 0.
@end enumerate
@strong{@emph{Never}} try to set the interface numbers so that they
correspond to the release number of your package. This is an abuse that
only fosters misunderstanding of the purpose of library versions.
Instead, use the @option{-release} flag (@pxref{Release numbers}), but be
warned that every release of your package will not be binary compatible
with any other release.
The following explanation may help to understand the above rules a bit
The following explanation may help to understand the steps above a bit
better: consider that there are three possible kinds of reactions from
users of your library to changes in a shared library:
@ -3222,6 +3241,79 @@ to 0.
In the above description, @emph{programs} using the library in question
may also be replaced by other libraries using it.
@node Guided version info update
@subsection Updating the version info, guided by the libtool-next-version program
The @samp{libtool-next-version} program is a wizard-like interactive tool,
that is designed to avoid mistakes in the process of the manual update:
@itemize @bullet
@item
It asks you questions, one by one, so that you can focus on one thing at a time.
@item
It prepares default answers, based on the set of symbols exported by library.
@item
It gives a couple of additional explanations and hints.
@end itemize
Before invoking the wizard,
you need to build and install the previous release and the current release
candidate of your package into different directories.
For example, assume the last release was @code{libfoo-1.4.tar.gz},
and before preparing @code{libfoo-1.5.tar.gz},
your current release candidate is @code{libfoo-1.4.99.tar.gz}.
You build them like this:
@example
$ rm -rf /tmp/prev /tmp/curr
$ tar xfz libfoo-1.4.tar.gz
$ (cd libfoo-1.4
&& ./configure --prefix=/tmp/prev
&& make
&& make install)
$ tar xfz libfoo-1.4.99.tar.gz
$ (cd libfoo-1.4.99
&& ./configure --prefix=/tmp/curr
&& make
&& make install)
@end example
Then you are ready to invoke the wizard like this:
@example
$ libtool-next-version /tmp/prev/lib/libfoo.so /tmp/curr/lib/libfoo.so
@end example
@node Invoking libtool-next-version
@subsection Invoking @command{libtool-next-version}
@pindex libtool-next-version
@cindex libtool-next-version command options
@cindex command options, libtool-next-version
@cindex options, libtool-next-version command
The @code{libtool-next-version} program determines the next version to use
for a libtool library.
It is invoked as follows:
@example
libtool-next-version [@var{option}]... @var{previous-library} @var{current-library}
@end example
@var{previous-library} is the installed library (in @code{.a} or @code{.so}
format) of the previous release.
@var{current-library} is the installed library (in @code{.a} or @code{.so}
format) of the current release candidate.
It accepts the following options:
@table @option
@item --help
Print a help message and exit.
@item --version
Print version information and exit.
@end table
@node Release numbers
@section Managing release information

297
libtool-next-version.in Normal file
View File

@ -0,0 +1,297 @@
#! /bin/sh
#
# Copyright (C) 2019-2025 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# This program is a wizard that helps a maintainer update the libtool
# version of a shared library, according to the documentation section
# 'Updating version info'
# <https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html>.
#
# Let's call the three parts of the version
# LTV_CURRENT
# LTV_REVISION
# LTV_AGE
#
# The list of steps given in this documentation section
# - If the library source code has changed at all since the last update,
# then increment LTV_REVISION.
# - If any interfaces have been added, removed, or changed since the last
# update, increment LTV_CURRENT and set LTV_REVISION to 0.
# - If any interfaces have been added since the last public release, then
# increment LTV_AGE.
# - If any interfaces have been removed or changed since the last public
# release, then set LTV_AGE to 0.
# leads to mistakes, because
# - It does not say what "interfaces" are.
# - It does not enforce that applying the second, third, or fourth rule
# is only possible after applying the first rule.
# - It does not enforce that applying the third or fourth rule is only
# possible after applying the second rule.
# func_usage
# outputs to stdout the --help usage message.
func_usage ()
{
echo "\
Usage: libtool-next-version [OPTION]... PREVIOUS-LIBRARY CURRENT-LIBRARY
Determines the next version to use for a libtool library.
PREVIOUS-LIBRARY is the installed library (in .a or .so format) of the
previous release.
CURRENT-LIBRARY is the installed library (in .a or .so format) of the current
release candidate.
Options:
--help print this help and exit
--version print version information and exit
Send patches and bug reports to <@PACKAGE_BUGREPORT@>."
}
# func_version
# outputs to stdout the --version message.
func_version ()
{
sed_extract_copyright_year='/Copyright (C)/{
s/.*\([0-9][0-9][0-9][0-9]\).*/\1/p
q
}'
copyright_year=`sed -n -e "$sed_extract_copyright_year" < "$0"`
echo "libtool-next-version (GNU @PACKAGE@) @VERSION@"
echo "Copyright (C) $copyright_year Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law."
echo
printf 'Written by %s.\n' "Bruno Haible"
}
# func_fatal_error message
# outputs to stderr a fatal error message, and terminates the program.
func_fatal_error ()
{
echo "libtool-next-version: *** $1" 1>&2
echo "libtool-next-version: *** Stop." 1>&2
exit 1
}
# func_tmpdir
# creates a temporary directory.
# Sets variable
# - tmp pathname of freshly created temporary directory
func_tmpdir ()
{
# Use the environment variable TMPDIR, falling back to /tmp. This allows
# users to specify a different temporary directory, for example, if their
# /tmp is filled up or too small.
: "${TMPDIR=/tmp}"
{
# Use the mktemp program if available. If not available, hide the error
# message.
tmp=`(umask 077 && mktemp -d -q "$TMPDIR/gtXXXXXX") 2>/dev/null` &&
test -n "$tmp" && test -d "$tmp"
} ||
{
# Use a simple mkdir command. It is guaranteed to fail if the directory
# already exists. $RANDOM is bash specific and expands to empty in shells
# other than bash, ksh and zsh. Its use does not increase security;
# rather, it minimizes the probability of failure in a very cluttered /tmp
# directory.
tmp=$TMPDIR/gt$$-$RANDOM
(umask 077 && mkdir "$tmp")
} ||
{
echo "$0: cannot create a temporary directory in $TMPDIR" >&2
{ (exit 1); exit 1; }
}
}
# func_read_yesno
# reads an answer (yes or no).
# Sets variable
# - ans yes or no
func_read_yesno ()
{
while true; do
read ans
if test yes = "$ans" || test no = "$ans"; then
break
fi
echo "Invalid answer. Please answer yes or no."
done
}
# Command-line option processing.
while test $# -gt 0; do
case $1 in
--help | --hel | --he | --h )
func_usage
exit 0 ;;
--version | --versio | --versi | --vers | --ver | --ve | --v )
func_version
exit 0 ;;
-- ) # Stop option processing
shift; break ;;
-* )
func_fatal_error "unrecognized option: $option"
;;
* )
break ;;
esac
done
test $# = 2 || {
if test $# -gt 2; then
func_fatal_error "too many arguments"
else
func_fatal_error "Usage: libtool-next-version [OPTION]... PREVIOUS-LIBRARY CURRENT-LIBRARY"
fi
}
test -f "$1" || func_fatal_error "file $1 not found"
test -f "$2" || func_fatal_error "file $2 not found"
(type nm) >/dev/null || func_fatal_error "program 'nm' not found"
# Determine how to extract a symbol list from the 'nm' output.
case `uname -s` in
Linux | FreeBSD | NetBSD | OpenBSD) nm_filter="sed -n -e 's/^.* [TWDRB] //p'" ;;
Darwin) nm_filter="sed -n -e 's/^.* [TWDRB] _//p'" ;;
Minix) nm_filter="sed -n -e 's/^.* [TDC] _//p'" ;;
AIX) nm_filter="sed -n -e 's/ *[UD] .*//p' | sed -e 's/^\\.//'" ;;
HP-UX) nm_filter="grep '|extern|\\(code\\|data\\) |' | sed -e 's/|.*//' | sed -e 's/ *$//'" ;;
IRIX) nm_filter="grep '|\\(GLOB\\|WEAK\\)' | sed -e 's/^.*|//'" ;;
SunOS)
case `uname -r` in
5.10) nm_filter="sed -n -e 's/^.* [ATWDRBV] //p'" ;;
*) nm_filter="grep '|\\(GLOB\\|WEAK\\)' | grep -v '|UNDEF' | grep -v '|SUNW' | sed -e 's/^.*|//'" ;;
esac
;;
CYGWIN*) nm_filter="sed -n -e 's/^.* T _//p'" ;;
*) func_fatal_error "unknown OS - don't know how to interpret the 'nm' output" ;;
esac
nm_filter="$nm_filter | LC_ALL=C sort -u"
func_tmpdir
eval "nm '$1' | $nm_filter > '$tmp/symlist1'"
eval "nm '$2' | $nm_filter > '$tmp/symlist2'"
echo "Please enter the libtool version of the library in the previous release."
printf "LTV_CURRENT="; read current
nondigits=`echo "$current" | tr -d '0123456789'`
{ test -n "$current" && test -z "$nondigits"; } \
|| func_fatal_error "LTV_CURRENT is invalid. It should be a nonnegative integer."
printf "LTV_REVISION="; read revision
nondigits=`echo "$revision" | tr -d '0123456789'`
{ test -n "$revision" && test -z "$nondigits"; } \
|| func_fatal_error "LTV_REVISION is invalid. It should be a nonnegative integer."
printf "LTV_AGE="; read age
nondigits=`echo "$age" | tr -d '0123456789'`
{ test -n "$age" && test -z "$nondigits"; } \
|| func_fatal_error "LTV_AGE is invalid. It should be a nonnegative integer."
echo
echo "-------------------------------------------------------------------------------"
echo "Did the library's code change at all since the previous version?"
echo "You can usually detect this by looking at the source code changes in git;"
echo "don't forget source code that is imported from other projects."
if cmp "$tmp/symlist1" "$tmp/symlist2" >/dev/null; then
echo "Please answer yes or no."
else
echo "The symbol list changed. Here are the differences:"
(cd "$tmp" && diff symlist1 symlist2 | grep '^[<>]' | sed -e 's/^/ /')
echo "Please answer yes or no (probably yes)."
fi
func_read_yesno
if test yes = "$ans"; then
revision=`expr $revision + 1`
echo
echo "-------------------------------------------------------------------------------"
echo "Have any interfaces (functions, variables, classes) been removed since the"
echo "previous release? What matters here are interfaces at the linker level;"
echo "whether macros have been removed from the include files does not matter."
if diff "$tmp/symlist1" "$tmp/symlist2" | grep '^< ' >/dev/null; then
echo "Some symbols have been removed:"
diff "$tmp/symlist1" "$tmp/symlist2" | grep '^< ' | sed -e 's/^< / /'
echo "Please answer yes or no (probably yes)."
else
echo "Please answer yes or no."
fi
func_read_yesno
if test yes = "$ans"; then
current=`expr $current + 1`
revision=0
age=0
else
echo
echo "-------------------------------------------------------------------------------"
echo "Have any interfaces (functions, variables, classes) been changed since the"
echo "previous release? This includes signature changes. It includes also details of"
echo "how functions produce their results and the values of variables, IF AND ONLY IF"
echo "users of the library are likely use these details in their test suite."
echo "Please answer yes or no."
func_read_yesno
if test yes = "$ans"; then
current=`expr $current + 1`
revision=0
age=0
else
echo
echo "-------------------------------------------------------------------------------"
echo "Have any interfaces (functions, variables, classes) been added since the"
echo "previous release? What matters here are interfaces at the linker level;"
echo "whether macros have been added to the include files does not matter."
if diff "$tmp/symlist1" "$tmp/symlist2" | grep '^> ' >/dev/null; then
echo "Some symbols have been added:"
diff "$tmp/symlist1" "$tmp/symlist2" | grep '^> ' | sed -e 's/^> / /'
echo "Please answer yes or no (probably yes)."
else
echo "Please answer yes or no."
fi
func_read_yesno
if test yes = "$ans"; then
current=`expr $current + 1`
revision=0
age=`expr $age + 1`
fi
fi
fi
fi
echo
echo "-------------------------------------------------------------------------------"
echo "This is the libtool version of the library for the new release:"
echo "LTV_CURRENT=$current"
echo "LTV_REVISION=$revision"
echo "LTV_AGE=$age"