1#! /bin/sh
2#
3# Copyright (C) 2019-2021 Free Software Foundation, Inc.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <https://www.gnu.org/licenses/>.
17#
18
19# This program is a wizard that helps a maintainer update the libtool
20# version of a shared library, according to the documentation section
21# 'Updating version info'
22# <https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html>.
23#
24# Let's call the three parts of the version
25#   LTV_CURRENT
26#   LTV_REVISION
27#   LTV_AGE
28#
29# The list of steps given in this documentation section
30#   - If the library source code has changed at all since the last update,
31#     then increment LTV_REVISION.
32#   - If any interfaces have been added, removed, or changed since the last
33#     update, increment LTV_CURRENT and set LTV_REVISION to 0.
34#   - If any interfaces have been added since the last public release, then
35#     increment LTV_AGE.
36#   - If any interfaces have been removed or changed since the last public
37#     release, then set LTV_AGE to 0.
38# leads to mistakes, because
39#   - It does not say what "interfaces" are.
40#   - It does not enforce that applying the second, third, or fourth rule
41#     is only possible after applying the first rule.
42#   - It does not enforce that applying the third or fourth rule is only
43#     possible after applying the second rule.
44
45scriptversion=2021-04-11
46
47# func_usage
48# outputs to stdout the --help usage message.
49func_usage ()
50{
51  echo "\
52Usage: libtool-next-version [OPTION]... PREVIOUS-LIBRARY CURRENT-LIBRARY
53
54Determines the next version to use for a libtool library.
55
56PREVIOUS-LIBRARY is the installed library (in .a or .so format) of the
57previous release.
58
59CURRENT-LIBRARY is the installed library (in .a or .so format) of the current
60release candidate.
61
62Options:
63      --help           print this help and exit
64      --version        print version information and exit
65
66Report bugs to <bruno@clisp.org>."
67}
68
69# func_version
70# outputs to stdout the --version message.
71func_version ()
72{
73  copyright_year=`echo "$scriptversion" | sed -e 's/[^0-9].*//'`
74  echo "libtool-next-version (GNU gnulib) $scriptversion"
75  echo "Copyright (C) ${copyright_year} Free Software Foundation, Inc.
76License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
77This is free software: you are free to change and redistribute it.
78There is NO WARRANTY, to the extent permitted by law."
79  echo
80  printf 'Written by %s.\n' "Bruno Haible"
81}
82
83# func_fatal_error message
84# outputs to stderr a fatal error message, and terminates the program.
85func_fatal_error ()
86{
87  echo "libtool-next-version: *** $1" 1>&2
88  echo "libtool-next-version: *** Stop." 1>&2
89  exit 1
90}
91
92# func_tmpdir
93# creates a temporary directory.
94# Sets variable
95# - tmp             pathname of freshly created temporary directory
96func_tmpdir ()
97{
98  # Use the environment variable TMPDIR, falling back to /tmp. This allows
99  # users to specify a different temporary directory, for example, if their
100  # /tmp is filled up or too small.
101  : ${TMPDIR=/tmp}
102  {
103    # Use the mktemp program if available. If not available, hide the error
104    # message.
105    tmp=`(umask 077 && mktemp -d -q "$TMPDIR/gtXXXXXX") 2>/dev/null` &&
106    test -n "$tmp" && test -d "$tmp"
107  } ||
108  {
109    # Use a simple mkdir command. It is guaranteed to fail if the directory
110    # already exists.  $RANDOM is bash specific and expands to empty in shells
111    # other than bash, ksh and zsh.  Its use does not increase security;
112    # rather, it minimizes the probability of failure in a very cluttered /tmp
113    # directory.
114    tmp=$TMPDIR/gt$$-$RANDOM
115    (umask 077 && mkdir "$tmp")
116  } ||
117  {
118    echo "$0: cannot create a temporary directory in $TMPDIR" >&2
119    { (exit 1); exit 1; }
120  }
121}
122
123# func_read_yesno
124# reads an answer (yes or no).
125# Sets variable
126# - ans             yes or no
127func_read_yesno ()
128{
129  while true; do
130    read ans
131    if test yes = "$ans" || test no = "$ans"; then
132      break
133    fi
134    echo "Invalid answer. Please answer yes or no."
135  done
136}
137
138# Command-line option processing.
139while test $# -gt 0; do
140  case "$1" in
141    --help | --hel | --he | --h )
142      func_usage
143      exit 0 ;;
144   --version | --versio | --versi | --vers | --ver | --ve | --v )
145      func_version
146      exit 0 ;;
147    -- )      # Stop option processing
148      shift; break ;;
149    -* )
150      func_fatal_error "unrecognized option: $option"
151      ;;
152    * )
153      break ;;
154  esac
155done
156
157test $# = 2 || {
158  if test $# -gt 2; then
159    func_fatal_error "too many arguments"
160  else
161    func_fatal_error "Usage: libtool-next-version [OPTION]... PREVIOUS-LIBRARY CURRENT-LIBRARY"
162  fi
163}
164
165test -f "$1" || func_fatal_error "file $1 not found"
166test -f "$2" || func_fatal_error "file $2 not found"
167
168(type nm) >/dev/null || func_fatal_error "program 'nm' not found"
169# Determine how to extract a symbol list from the 'nm' output.
170case `uname -s` in
171  Linux | FreeBSD | NetBSD | OpenBSD) nm_filter="sed -n -e 's/^.* [TWDRB] //p'" ;;
172  Darwin) nm_filter="sed -n -e 's/^.* [TWDRB] _//p'" ;;
173  Minix) nm_filter="sed -n -e 's/^.* [TDC] _//p'" ;;
174  AIX) nm_filter="sed -n -e 's/  *[UD] .*//p' | sed -e 's/^\\.//'" ;;
175  HP-UX) nm_filter="grep '|extern|\\(code\\|data\\)   |' | sed -e 's/|.*//' | sed -e 's/ *$//'" ;;
176  IRIX) nm_filter="grep '|\\(GLOB\\|WEAK\\)' | sed -e 's/^.*|//'" ;;
177  SunOS)
178    case `uname -r` in
179      5.10) nm_filter="sed -n -e 's/^.* [ATWDRBV] //p'" ;;
180      *) nm_filter="grep '|\\(GLOB\\|WEAK\\)' | grep -v '|UNDEF' | grep -v '|SUNW' | sed -e 's/^.*|//'" ;;
181    esac
182    ;;
183  CYGWIN*) nm_filter="sed -n -e 's/^.* T _//p'" ;;
184  *) func_fatal_error "unknown OS - don't know how to interpret the 'nm' output" ;;
185esac
186nm_filter="$nm_filter | LC_ALL=C sort -u"
187
188func_tmpdir
189eval "nm '$1' | $nm_filter > '$tmp/symlist1'"
190eval "nm '$2' | $nm_filter > '$tmp/symlist2'"
191
192echo "Please enter the libtool version of the library in the previous release."
193
194printf "LTV_CURRENT="; read current
195nondigits=`echo "$current" | tr -d '0123456789'`
196{ test -n "$current" && test -z "$nondigits"; } \
197  || func_fatal_error "LTV_CURRENT is invalid. It should be a nonnegative integer."
198
199printf "LTV_REVISION="; read revision
200nondigits=`echo "$revision" | tr -d '0123456789'`
201{ test -n "$revision" && test -z "$nondigits"; } \
202  || func_fatal_error "LTV_REVISION is invalid. It should be a nonnegative integer."
203
204printf "LTV_AGE="; read age
205nondigits=`echo "$age" | tr -d '0123456789'`
206{ test -n "$age" && test -z "$nondigits"; } \
207  || func_fatal_error "LTV_AGE is invalid. It should be a nonnegative integer."
208
209echo
210echo "-------------------------------------------------------------------------------"
211echo "Did the library's code change at all since the previous version?"
212echo "You can usually detect this by looking at the source code changes in git;"
213echo "don't forget source code that is imported from other projects."
214if cmp "$tmp/symlist1" "$tmp/symlist2" >/dev/null; then
215  echo "Please answer yes or no."
216else
217  echo "The symbol list changed. Here are the differences:"
218  (cd "$tmp" && diff symlist1 symlist2 | grep '^[<>]' | sed -e 's/^/  /')
219  echo "Please answer yes or no (probably yes)."
220fi
221func_read_yesno
222if test "$ans" = yes; then
223
224  revision=`expr $revision + 1`
225
226  echo
227  echo "-------------------------------------------------------------------------------"
228  echo "Have any interfaces (functions, variables, classes) been removed since the"
229  echo "previous release? What matters here are interfaces at the linker level;"
230  echo "whether macros have been removed from the include files does not matter."
231  if diff "$tmp/symlist1" "$tmp/symlist2" | grep '^< ' >/dev/null; then
232    echo "Some symbols have been removed:"
233    diff "$tmp/symlist1" "$tmp/symlist2" | grep '^< ' | sed -e 's/^< /  /'
234    echo "Please answer yes or no (probably yes)."
235  else
236    echo "Please answer yes or no."
237  fi
238  func_read_yesno
239
240  if test "$ans" = yes; then
241
242    current=`expr $current + 1`
243    revision=0
244    age=0
245
246  else
247
248    echo
249    echo "-------------------------------------------------------------------------------"
250    echo "Have any interfaces (functions, variables, classes) been changed since the"
251    echo "previous release? This includes signature changes. It includes also details of"
252    echo "how functions produce their results and the values of variables, IF AND ONLY IF"
253    echo "users of the library are likely use these details in their test suite."
254    echo "Please answer yes or no."
255    func_read_yesno
256
257    if test "$ans" = yes; then
258
259      current=`expr $current + 1`
260      revision=0
261      age=0
262
263    else
264
265      echo
266      echo "-------------------------------------------------------------------------------"
267      echo "Have any interfaces (functions, variables, classes) been added since the"
268      echo "previous release? What matters here are interfaces at the linker level;"
269      echo "whether macros have been added to the include files does not matter."
270      if diff "$tmp/symlist1" "$tmp/symlist2" | grep '^> ' >/dev/null; then
271        echo "Some symbols have been added:"
272        diff "$tmp/symlist1" "$tmp/symlist2" | grep '^> ' | sed -e 's/^> /  /'
273        echo "Please answer yes or no (probably yes)."
274      else
275        echo "Please answer yes or no."
276      fi
277      func_read_yesno
278
279      if test "$ans" = yes; then
280
281        current=`expr $current + 1`
282        revision=0
283        age=`expr $age + 1`
284
285      fi
286    fi
287  fi
288fi
289
290echo
291echo "-------------------------------------------------------------------------------"
292echo "This is the libtool version of the library for the new release:"
293echo "LTV_CURRENT=$current"
294echo "LTV_REVISION=$revision"
295echo "LTV_AGE=$age"
296