readutmp, boot-time: Work around a Cygwin 3.5.3 bug.
[gnulib.git] / gnulib-tool
blob56c447331818e127c885ff3281a885d7db07cb7b
1 #! /bin/sh
3 # Copyright (C) 2002-2024 Free Software Foundation, Inc.
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.
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.
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/>.
19 # This program dispatches among
20 # - the shell implementation and
21 # - the Python implementation
22 # of gnulib-tool, according to the environment variable GNULIB_TOOL_IMPL.
24 # The environment variable GNULIB_TOOL_IMPL can have four possible values:
25 # - GNULIB_TOOL_IMPL=sh chooses the shell implementation.
26 # - GNULIB_TOOL_IMPL=py chooses the Python implementation.
27 # - GNULIB_TOOL_IMPL= chooses the default (currently sh).
28 # - GNULIB_TOOL_IMPL=sh+py runs both implementations and compares the
29 # results.
31 progname=$0
33 # func_exit STATUS
34 # exits with a given status.
35 # This function needs to be used, rather than 'exit', when a 'trap' handler is
36 # in effect that refers to $?.
37 func_exit ()
39 (exit $1); exit $1
42 # func_fatal_error message
43 # outputs to stderr a fatal error message, and terminates the program.
44 # Input:
45 # - progname name of this program
46 func_fatal_error ()
48 echo "$progname: *** $1" 1>&2
49 echo "$progname: *** Stop." 1>&2
50 func_exit 1
53 # func_readlink SYMLINK
54 # outputs the target of the given symlink.
55 if (type readlink) > /dev/null 2>&1; then
56 func_readlink ()
58 # Use the readlink program from GNU coreutils.
59 readlink "$1"
61 else
62 func_readlink ()
64 # Use two sed invocations. A single sed -n -e 's,^.* -> \(.*\)$,\1,p'
65 # would do the wrong thing if the link target contains " -> ".
66 LC_ALL=C ls -l "$1" | sed -e 's, -> ,#%%#,' | sed -n -e 's,^.*#%%#\(.*\)$,\1,p'
70 # func_gnulib_dir
71 # locates the directory where the gnulib repository lives
72 # Input:
73 # - progname name of this program
74 # Sets variables
75 # - self_abspathname absolute pathname of gnulib-tool
76 # - gnulib_dir absolute pathname of gnulib repository
77 func_gnulib_dir ()
79 case "$progname" in
80 /* | ?:*) self_abspathname="$progname" ;;
81 */*) self_abspathname=`pwd`/"$progname" ;;
83 # Look in $PATH.
84 # Iterate through the elements of $PATH.
85 # We use IFS=: instead of
86 # for d in `echo ":$PATH:" | sed -e 's/:::*/:.:/g' | sed -e 's/:/ /g'`
87 # because the latter does not work when some PATH element contains spaces.
88 # We use a canonicalized $pathx instead of $PATH, because empty PATH
89 # elements are by definition equivalent to '.', however field splitting
90 # according to IFS=: loses empty fields in many shells:
91 # - /bin/sh on OSF/1 and Solaris loses all empty fields (at the
92 # beginning, at the end, and in the middle),
93 # - /bin/sh on IRIX and /bin/ksh on IRIX and OSF/1 lose empty fields
94 # at the beginning and at the end,
95 # - GNU bash, /bin/sh on AIX and HP-UX, and /bin/ksh on AIX, HP-UX,
96 # Solaris lose empty fields at the end.
97 # The 'case' statement is an optimization, to avoid evaluating the
98 # explicit canonicalization command when $PATH contains no empty fields.
99 self_abspathname=
100 if test "$PATH_SEPARATOR" = ";"; then
101 # On Windows, programs are searched in "." before $PATH.
102 pathx=".;$PATH"
103 else
104 # On Unix, we have to convert empty PATH elements to ".".
105 pathx="$PATH"
106 case :$PATH: in
107 *::*)
108 pathx=`echo ":$PATH:" | sed -e 's/:::*/:.:/g' -e 's/^://' -e 's/:\$//'`
110 esac
112 saved_IFS="$IFS"
113 IFS="$PATH_SEPARATOR"
114 for d in $pathx; do
115 IFS="$saved_IFS"
116 test -z "$d" && d=.
117 if test -x "$d/$progname" && test ! -d "$d/$progname"; then
118 self_abspathname="$d/$progname"
119 break
121 done
122 IFS="$saved_IFS"
123 if test -z "$self_abspathname"; then
124 func_fatal_error "could not locate the gnulib-tool program - how did you invoke it?"
127 esac
128 while test -h "$self_abspathname"; do
129 # Resolve symbolic link.
130 linkval=`func_readlink "$self_abspathname"`
131 test -n "$linkval" || break
132 case "$linkval" in
133 /* | ?:* ) self_abspathname="$linkval" ;;
134 * ) self_abspathname=`echo "$self_abspathname" | sed -e 's,/[^/]*$,,'`/"$linkval" ;;
135 esac
136 done
137 gnulib_dir=`echo "$self_abspathname" | sed -e 's,/[^/]*$,,'`
140 func_gnulib_dir
142 case "$GNULIB_TOOL_IMPL" in
144 # Use the Python implementation if a suitable Python version is found
145 # in $PATH. This is the same Python version test as in gnulib-tool.py.
146 if (python3 -c 'import sys; sys.exit(not sys.version_info >= (3,7))') 2>/dev/null; then
147 exec "$gnulib_dir/gnulib-tool.py" "$@"
148 else
149 echo "gnulib-tool: warning: python3 not found or too old, using the slow shell-based implementation" 1>&2
150 exec "$gnulib_dir/gnulib-tool.sh" "$@"
154 exec "$gnulib_dir/gnulib-tool.sh" "$@" ;;
156 exec "$gnulib_dir/gnulib-tool.py" "$@" ;;
157 sh+py)
158 case " $* " in
159 *" --import"* | *" --add-import"* | *" --remove-import"* | *" --update"* | *" --copy-file"*)
160 # A gnulib-tool invocation that produces files in the current directory.
161 # Create a temporary directory in the parent directory.
162 tmpdir=`cd .. && pwd`
164 # Use the mktemp program if available. If not available, hide the error
165 # message.
166 tmp=`(umask 077 && mktemp -d "$tmpdir/glpyXXXXXX") 2>/dev/null` &&
167 test -n "$tmp" && test -d "$tmp"
168 } ||
170 # Use a simple mkdir command. It is guaranteed to fail if the directory
171 # already exists.
172 tmp=$tmpdir/glpy$$
173 (umask 077 && mkdir "$tmp")
174 } ||
176 echo "$progname: cannot create a temporary directory in $tmpdir" >&2
177 func_exit 1
179 # Copy the current directory into the the temporary directory.
180 { tar cf - . | (cd "$tmp" && tar xf -); } ||
182 echo "$progname: failed to clone the current directory" >&2
183 func_exit 1
185 # Execute gnulib-tool.py in the clone directory.
186 (cd "$tmp" && "$gnulib_dir/gnulib-tool.py" "$@" >"$tmp-py-out" 2>"$tmp-py-err")
187 pyrc=$?
188 # Execute gnulib-tool.sh in the current directory.
189 "$gnulib_dir/gnulib-tool.sh" "$@" >"$tmp-sh-out" 2>"$tmp-sh-err"
190 shrc=$?
191 if test $shrc != 0; then
192 if test $pyrc = 0; then
193 func_fatal_error "gnulib-tool.sh failed but gnulib-tool.py succeeded! Inspect $tmp-sh-err and $tmp-py-err."
194 else
195 cat "$tmp-sh-out"
196 cat "$tmp-sh-err" >&2
197 rm -rf "$tmp" "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
198 exit $shrc
201 if test $pyrc != 0; then
202 func_fatal_error "gnulib-tool.sh succeeded but gnulib-tool.py failed! Inspect $tmp/ and $tmp-py-err."
204 # Compare the two results on the file system.
205 # GNU diffutils 3.3 or newer support option --no-dereference. This
206 # option avoids errors on dangling links.
207 if LC_ALL=C diff --help 2>/dev/null | grep no-dereference >/dev/null; then
208 diff_options='--no-dereference'
209 else
210 diff_options=
212 diff -r $diff_options --exclude=__pycache__ -q . "$tmp" >/dev/null ||
213 func_fatal_error "gnulib-tool.py produced different files than gnulib-tool.sh! Compare `pwd` and $tmp."
214 # Compare the two outputs.
215 diff -q "$tmp-sh-out" "$tmp-py-out" >/dev/null ||
216 func_fatal_error "gnulib-tool.py produced different output than gnulib-tool.sh! Compare $tmp-sh-out and $tmp-py-out."
217 # Same results.
218 cat "$tmp-sh-out"
219 cat "$tmp-sh-err" >&2
220 rm -rf "$tmp" "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
221 exit 0
223 *" --create-testdir"* | *" --create-megatestdir"*)
224 # A gnulib-tool invocation that produces a new directory with files.
225 # Extract the --dir value.
226 dir=`echo " $* " | sed -n -e 's/^.* --dir=//p' | sed -e 's/ .*//'`
227 if test -z "$dir"; then
228 dir=`echo " $* " | sed -n -e 's/^.* --dir *//p' | sed -e 's/ .*//'`
229 if test -z "$dir"; then
230 func_fatal_error "could not extract --dir value"
233 # Find another directory name.
234 tmp="$dir-glpy$$"
235 # Execute gnulib-tool.py, creating a different directory.
236 "$gnulib_dir/gnulib-tool.py" "$@" --dir="$tmp" >"$tmp-py-out" 2>"$tmp-py-err"
237 pyrc=$?
238 # Execute gnulib-tool.sh, creating the intended directory.
239 "$gnulib_dir/gnulib-tool.sh" "$@" >"$tmp-sh-out" 2>"$tmp-sh-err"
240 shrc=$?
241 if test $shrc != 0; then
242 if test $pyrc = 0; then
243 func_fatal_error "gnulib-tool.sh failed but gnulib-tool.py succeeded! Inspect $tmp-sh-err and $tmp-py-err."
244 else
245 cat "$tmp-sh-out"
246 cat "$tmp-sh-err" >&2
247 rm -rf "$tmp" "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
248 exit $shrc
251 if test $pyrc != 0; then
252 func_fatal_error "gnulib-tool.sh succeeded but gnulib-tool.py failed! Inspect $tmp/ and $tmp-py-err."
254 # Compare the two results on the file system.
255 # GNU diffutils 3.3 or newer support option --no-dereference. This
256 # option avoids errors on dangling links.
257 if LC_ALL=C diff --help 2>/dev/null | grep no-dereference >/dev/null; then
258 diff_options='--no-dereference'
259 else
260 diff_options=
262 diff -r $diff_options -q "$dir" "$tmp" >/dev/null ||
263 func_fatal_error "gnulib-tool.py produced different files than gnulib-tool.sh! Compare $dir and $tmp."
264 # Compare the two outputs.
265 diff -q "$tmp-sh-out" "$tmp-py-out" >/dev/null ||
266 func_fatal_error "gnulib-tool.py produced different output than gnulib-tool.sh! Compare $tmp-sh-out and $tmp-py-out."
267 # Same results.
268 cat "$tmp-sh-out"
269 cat "$tmp-sh-err" >&2
270 rm -rf "$tmp" "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
271 exit 0
274 # A gnulib-tool invocation that produces only output, no files.
275 tmp="glpy$$"
276 # Execute gnulib-tool.py.
277 "$gnulib_dir/gnulib-tool.py" "$@" >"$tmp-py-out" 2>"$tmp-py-err"
278 pyrc=$?
279 # Execute gnulib-tool.sh.
280 "$gnulib_dir/gnulib-tool.sh" "$@" >"$tmp-sh-out" 2>"$tmp-sh-err"
281 shrc=$?
282 if test $shrc != 0; then
283 if test $pyrc = 0; then
284 func_fatal_error "gnulib-tool.sh failed but gnulib-tool.py succeeded! Inspect $tmp-sh-err and $tmp-py-err."
285 else
286 cat "$tmp-sh-out"
287 cat "$tmp-sh-err" >&2
288 rm -rf "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
289 exit $shrc
292 if test $pyrc != 0; then
293 func_fatal_error "gnulib-tool.sh succeeded but gnulib-tool.py failed! Inspect $tmp-py-err."
295 # Compare the two outputs.
296 diff -q "$tmp-sh-out" "$tmp-py-out" >/dev/null ||
297 func_fatal_error "gnulib-tool.py produced different output than gnulib-tool.sh! Compare $tmp-sh-out and $tmp-py-out."
298 # Same results.
299 cat "$tmp-sh-out"
300 cat "$tmp-sh-err" >&2
301 rm -rf "$tmp-sh-out" "$tmp-sh-err" "$tmp-py-out" "$tmp-py-err"
302 exit 0
304 esac
307 func_fatal_error "invalid value of GNULIB_TOOL_IMPL: $GNULIB_TOOL_IMPL" ;;
308 esac