Merge from origin/emacs-29
[emacs.git] / admin / quick-install-emacs
blob985e71d997705c44f1fcc18b81e3a6744f83018d
1 #!/bin/sh
2 ### quick-install-emacs --- do a halfway-decent job of installing emacs quickly
4 ## Copyright (C) 2001-2024 Free Software Foundation, Inc.
6 ## Author: Miles Bader <miles@gnu.org>
8 ## This file is part of GNU Emacs.
10 ## GNU Emacs is free software: you can redistribute it and/or modify
11 ## it under the terms of the GNU General Public License as published by
12 ## the Free Software Foundation, either version 3 of the License, or
13 ## (at your option) any later version.
15 ## GNU Emacs is distributed in the hope that it will be useful,
16 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ## GNU General Public License for more details.
20 ## You should have received a copy of the GNU General Public License
21 ## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24 ### Commentary:
26 ## This script is mainly intended for emacs maintainer or pretesters who
27 ## install emacs very often. See the --help output for more details.
30 PUBLIC_LIBSRC_BINARIES='emacsclient etags ctags ebrowse'
32 AVOID="CVS -DIC README COPYING ChangeLog ~ [.]orig$ [.]rej$ Makefile$ Makefile.in$ makefile$ makefile.w32-in$ stamp-subdir [.]cvsignore [.]arch-ids [{]arch[}] [.][cho]$ make-docfile"
34 # Prune old binaries lying around in the source tree
35 PRUNE=no
36 # Re-install files even if they already exist
37 FORCE=no
38 # Command verbose flag
39 VERBOSE=''
41 me="`basename $0`"
43 # Install commands (if the user specifies the '--verbose' option, it is
44 # passed to these commands, so that feature only works if these commands
45 # implement it too)
46 LINK='cp -lf'
47 COPY='cp -f'
48 REMOVE='rm -r'
49 MKDIR='mkdir -p'
51 # Used to execute commands once we create them
52 EXEC='sh'
54 NAWK=/usr/bin/nawk
56 # avoid non-standard command output from non-C locales
57 unset LANG LC_ALL LC_MESSAGES
59 # Some messages
60 USAGE="Usage: $me [OPTION...] BUILD_TREE [PREFIX]"
61 TRY="Try '$me --help' for more information."
63 # Parse command-line options
64 while :; do
65 case "$1" in
66 -n|--dry-run)
67 EXEC=cat; shift;;
68 -p|--prune)
69 PRUNE=yes; shift;;
70 -P|--no-prune)
71 PRUNE=no; shift;;
72 --prune-only)
73 PRUNE=only; shift;;
74 -f|--force)
75 FORCE=yes; shift;;
76 -v|--verbose)
77 VERBOSE="-v"; shift;;
78 --help)
79 cat <<EOF
80 $USAGE
81 Install emacs quickly
83 -n, --dry-run print installation commands instead of
84 executing them
86 -f, --force install even files that haven't changed
87 -v, --verbose print messages describing what is done
89 -p, --prune prune old generated files
90 -P, --no-prune don't prune old generated files (default)
91 --prune-only prune old generated files, but don't install
93 --help display this help and exit
94 --version output version information and exit
96 $me install emacs "incrementally", that is, it will
97 install only those files that have changed since the last time it was
98 invoked, and remove any obsolete files from the installation
99 directories. It also uses hard-links into the source and build trees to
100 do the install, so it uses much less space than the default Makefile
101 install target; however, this also means that $me can
102 not install onto a disk partition other than the one on which the source
103 and build directories reside.
105 Optionally, $me can also remove old versions of
106 automatically generated files that are version-specific (such as the
107 versioned emacs executables in the 'src' directory).
108 The latter action is called "pruning", and
109 can be enabled using the '-p' or '--prune' options.
111 exit 0
113 --version)
114 cat <<EOF
115 $me 1.6
117 Written by Miles Bader <miles@gnu.org>
119 exit 0
121 -[!-]?*)
122 # split concatenated single-letter options apart
123 FIRST="$1"; shift
124 set -- `printf '%s\n' "$FIRST" | sed 's/-\(.\)\(.*\)/-\1 -\2/'` "$@"
127 printf '%s\n' >&2 "$me: unrecognized option '$1'"
128 printf '%s\n' >&2 "$TRY"
129 exit 1
132 break;
133 esac
134 done
136 LINK_CMD="$LINK $VERBOSE"
137 REMOVE_CMD="$REMOVE $VERBOSE"
139 case $# in
140 1) BUILD="$1";;
141 2) BUILD="$1"; prefix="$2";;
143 printf '%s\n' >&2 "$USAGE"
144 printf '%s\n' >&2 "$TRY"
145 exit 1
147 esac
149 if test ! -d "$BUILD"; then
150 printf '%s\n' >&2 "$me: $BUILD: Build tree not found"
151 exit 2
152 elif test ! -r "$BUILD/config.status"; then
153 printf '%s\n' >&2 \
154 "$me: $BUILD: Not a proper build tree, config.status not found"
155 exit 2
158 CONFIG_STATUS="$BUILD/config.status"
159 get_config_var ()
161 { sed -n "s/^S[[]\"$1\"[]]=\"\([^\"]*\)\"/\1/p" $CONFIG_STATUS | sed q | grep ''; } ||
162 { sed -n "s/^s\(.\)@$1@\1\(|#_!!_#|\)*\(.*\)\1.*$/\3/p" $CONFIG_STATUS | sed q | grep ''; } ||
164 printf '%s\n' >&2 \
165 "$me: $1: Configuration variable not found in $CONFIG_STATUS"
166 exit 4
170 test x"$SRC" = x && { SRC="`get_config_var srcdir`" || exit 4 ; }
171 test x"$prefix" = x && { prefix="`get_config_var prefix`" || exit 4 ; }
172 test x"$ARCH" = x && { ARCH="`get_config_var host`" || exit 4 ; }
174 VERSION=`
175 sed -n 's/^AC_INIT([ ]*\[*emacs]*[ ]*,[ ]*\[*\([^] ),]*\).*/\1/p' <$SRC/configure.ac
176 ` || exit 4
177 test -n "$VERSION" || VERSION=`
178 sed -n 's/^AC_INIT([ ]*\[*GNU Emacs]*[ ]*,[ ]*\[*\([^] ),]*\).*/\1/p' <$SRC/configure.ac
179 ` || exit 4
180 test -n "$VERSION" || { printf '%s\n' >&2 "$me: no version in configure.ac"; exit 4; }
182 DST_SHARE="$prefix/share/emacs/$VERSION"
183 DST_BIN="$prefix/bin"
184 DST_LIBEXEC="$prefix/libexec/emacs/$VERSION/$ARCH"
186 # There are various common places for the info dir to be, so try to
187 # use whatever's already there, defaulting to (and preferring)
188 # .../share/info.
190 DST_INFO=''
191 for D in "$prefix/share/info" "$prefix/info"; do
192 if test -d "$D"; then
193 DST_INFO="$D"
194 break
196 done
197 DST_INFO=${DST_INFO:-"$prefix/share/info"}
199 maybe_mkdir ()
201 if ! test -d "$1"; then
202 $MKDIR $VERBOSE "$1" 2>&1 | sed "s/^mkdir:/$me:/" 1>&2
206 maybe_mkdir "$DST_BIN"
207 maybe_mkdir "$DST_SHARE"
208 maybe_mkdir "$DST_SHARE/site-lisp"
209 maybe_mkdir "$DST_LIBEXEC"
210 maybe_mkdir "$DST_INFO"
212 ( # start of command-generating sub-shell
214 PRUNED=""
215 if test x"$PRUNE" != xno; then
216 for D in `ls -1t $BUILD/src/emacs-$VERSION.* | sed 1d`; do
217 printf '%s\n' "$REMOVE_CMD $D"
218 PRUNED="$PRUNED $D"
219 done
222 test x"$PRUNE" = xonly && exit 0
224 maybe_emit_copy ()
226 if test "$FORCE" = yes || ! cmp -s $1 $2; then
227 printf '%s\n' "$LINK_CMD $1 $2"
231 maybe_emit_copy $BUILD/src/emacs $DST_BIN/emacs
232 maybe_emit_copy $BUILD/src/emacs $DST_BIN/emacs-$VERSION
234 for F in $PUBLIC_LIBSRC_BINARIES; do
235 maybe_emit_copy $BUILD/lib-src/$F $DST_BIN/$F
236 done
238 if test x"$SRC" = x"$BUILD"; then
239 PFXS="$BUILD"
240 else
241 PFXS="$SRC $BUILD"
244 for SUBDIR in lisp leim etc lib-src info; do
245 # defaults
246 SHARED=no
247 FORCED=''
248 AVOID_PAT="`printf '%s\n' "($AVOID)" | tr ' ' '|'`"
250 # Set subdir-specific values
251 case $SUBDIR in
252 lisp|leim)
253 DST="$DST_SHARE/$SUBDIR"
255 etc)
256 DST="$DST_SHARE/$SUBDIR"
257 # COPYING is in the avoid list, but there should be a copy of it in
258 # the install etc dir, so make that here.
259 FORCED="$DST/COPYING"
261 lib-src)
262 DST="$DST_LIBEXEC"
263 AVOID_PAT="`printf '%s\n' "($AVOID ($PUBLIC_LIBSRC_BINARIES)\$)" | tr ' ' '|'`"
265 info)
266 DST="$DST_INFO"
267 SHARED=yes
269 esac
271 for PFX in $PFXS; do
272 if [ -d $PFX/$SUBDIR ]; then
273 for DIR in `(cd $PFX/$SUBDIR; find . -type d -print | sed 's@^./@@')`; do
274 if [ -d $DST/$DIR ]; then
275 printf '%s\n' "Directory $DST/$DIR exists"
276 else
277 printf '%s\n' "Directory $DST/$DIR non-existent"
278 if [ "`printf '%s\n' "$DIR" | grep -Ev "$AVOID_PAT"`" ]; then
279 maybe_mkdir $DST/$DIR
282 done
283 diff -sqr $PFX/$SUBDIR $DST
285 done | $NAWK '
286 BEGIN {
287 src_pat = "^'"$SRC"'/'"$SUBDIR"'/"
288 build_pat = "^'"$BUILD"'/'"$SUBDIR"'/"
289 dst_pat = "^'"$DST"'/"
290 dst_pfx = "'"$DST"'/"
291 avoid_pat = "'"$AVOID_PAT"'"
292 force = ("'"$FORCE"'" == "yes")
293 shared = ("'"$SHARED"'" == "yes")
294 init_bool_array(pruned, "'"$PRUNED"'")
295 init_bool_array(forced, "'"$FORCED"'")
297 function init_bool_array(array, string, a,k)
299 split (string, a)
300 for (k in a)
301 array[a[k]] = 1
303 function install(src, dst)
305 if (! (src in pruned)) {
306 cp[src] = dst;
307 from[dst] = src;
308 delete rm[dst];
311 function update(src, dst, copy)
313 if (src in pruned) {
314 rm[dst] = 1;
315 delete from[dst]
316 } else {
317 if (copy)
318 cp[src] = dst;
319 from[dst] = src;
320 delete rm[dst];
323 function uninstall(dst)
325 if (!(dst in from))
326 rm[dst] = 1;
328 /^Directory / {
329 if ($2 ~ avoid_pat) {
330 if ($NF == "exists")
331 uninstall($2)
332 } else
333 update(0, $2, 0)
334 next
336 /^Files / {
337 if ($4 ~ avoid_pat && !($4 in forced))
338 uninstall($4)
339 else if ($NF == "identical")
340 update($2, $4, force)
341 else
342 update($2, $4, 1)
343 next
345 /^Only / {
346 pfx = $3
347 sub (/:$/, "/", pfx)
349 if (pfx ~ dst_pat) {
350 if (! shared)
351 uninstall(pfx $4)
352 } else {
353 subdir = pfx
354 if (subdir ~ src_pat)
355 sub (src_pat, "", subdir)
356 else
357 sub (build_pat, "", subdir)
359 dst = dst_pfx subdir $4
360 if (! (dst ~ avoid_pat))
361 install(pfx $4, dst)
363 next
365 END {
366 for (f in rm)
367 print "'"$REMOVE_CMD"' " f
368 for (f in cp)
369 print "'"$LINK_CMD"' " f " " cp[f]
372 done
374 ) | eval $EXEC