update_autogen 9.25 KB
Newer Older
1
#!/usr/bin/env bash
Glenn Morris's avatar
Glenn Morris committed
2
### update_autogen - update some auto-generated files in the Emacs tree
3

Paul Eggert's avatar
Paul Eggert committed
4
## Copyright (C) 2011-2020 Free Software Foundation, Inc.
5 6

## Author: Glenn Morris <rgm@gnu.org>
Glenn Morris's avatar
Glenn Morris committed
7
## Maintainer: emacs-devel@gnu.org
8 9 10 11 12 13 14 15 16 17 18 19 20 21

## This file is part of GNU Emacs.

## GNU Emacs 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.

## GNU Emacs 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
22
## along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
23 24 25

### Commentary:

Glenn Morris's avatar
Glenn Morris committed
26 27
## This is a helper script to update some generated files in the Emacs
## repository.  This is suitable for running from cron.
28
## Only Emacs maintainers need use this, so it uses bash features.
29
##
Glenn Morris's avatar
Glenn Morris committed
30 31
## By default, it updates the versioned loaddefs-like files in lisp,
## except ldefs-boot.el.
32 33 34

### Code:

35
die ()                 # write error to stderr and exit
36 37 38 39 40 41 42 43 44 45
{
    [ $# -gt 0 ] && echo "$PN: $@" >&2
    exit 1
}

PN=${0##*/}                     # basename of script
PD=${0%/*}

[ "$PD" = "$0" ] && PD=.        # if PATH includes PWD

Glenn Morris's avatar
Glenn Morris committed
46
## This should be the admin directory.
47 48
cd $PD
cd ../
Glenn Morris's avatar
Glenn Morris committed
49
[ -d admin ] || die "Could not locate admin directory"
50

51
[ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1 || die "Not in a git repository"
52

53
usage ()
54 55
{
    cat 1>&2 <<EOF
56
Usage: ${PN} [-f] [-c] [-q] [-A dir] [-I] [-L] [-C] [-- make-flags]
Glenn Morris's avatar
Glenn Morris committed
57 58 59
Update some auto-generated files in the Emacs tree.
By default, only does the versioned loaddefs-like files in lisp/.
This requires a build.  Passes any non-option args to make (eg -- -j2).
60 61 62 63 64
Options:
-f: force an update even if the source files are locally modified.
-c: if the update succeeds and the generated files are modified,
    commit them (caution).
-q: be quiet; only give error messages, not status messages.
Glenn Morris's avatar
Glenn Morris committed
65
-A: only update autotools files, copying into specified dir.
66
-H: also update ChangeLog.${changelog_n}
67
-I: also update info/dir.
68
-L: also update ldefs-boot.el.
69
-C: start from a clean state.  Slower, but more correct.
70 71 72 73 74 75 76 77 78 79
EOF
    exit 1
}


## Defaults.

force=
commit=
quiet=
80
clean=
Glenn Morris's avatar
Glenn Morris committed
81 82
autogendir=                     # was "autogen"
ldefs_flag=1
83
lboot_flag=
84
info_flag=
85
changelog_flag=
86 87

## Parameters.
88
ldefs_in=lisp/loaddefs.el
89
ldefs_out=lisp/ldefs-boot.el
90
changelog_n=$(sed -n 's/CHANGELOG_HISTORY_INDEX_MAX *= *//p' Makefile.in)
91
changelog_files="ChangeLog.$changelog_n"
92
sources="configure.ac lib/Makefile.am"
Glenn Morris's avatar
Glenn Morris committed
93 94
## Files to copy into autogendir.
## Everything:
95
genfiles="
Paul Eggert's avatar
Paul Eggert committed
96 97 98
  configure aclocal.m4 src/config.in
  build-aux/config.guess build-aux/config.sub
  build-aux/install-sh
99
"
Glenn Morris's avatar
Glenn Morris committed
100
## msdos-only:
Paul Eggert's avatar
Paul Eggert committed
101
genfiles="src/config.in"
102 103 104 105 106 107 108 109 110 111 112 113

for g in $genfiles; do
    basegen="$basegen ${g##*/}"
done

[ "$basegen" ] || die "internal error"

tempfile=/tmp/$PN.$$

trap "rm -f $tempfile 2> /dev/null" EXIT


114
while getopts ":hcfqA:HCIL" option ; do
115 116 117 118 119 120 121 122 123
    case $option in
        (h) usage ;;

        (c) commit=1 ;;

        (f) force=1 ;;

        (q) quiet=1 ;;

Glenn Morris's avatar
Glenn Morris committed
124 125 126 127
        (A) autogendir=$OPTARG
            [ -d "$autogendir" ] || die "No autogen directory: $autogendir"
            ;;

128 129
        (C) clean=1 ;;

130 131
        (H) changelog_flag=1 ;;

132 133
        (I) info_flag=1 ;;

134 135
        (L) lboot_flag=1 ;;

136 137 138 139 140 141 142 143 144 145 146
        (\?) die "Bad option -$OPTARG" ;;

        (:) die "Option -$OPTARG requires an argument" ;;

        (*) die "getopts error" ;;
    esac
done
shift $(( --OPTIND ))
OPTIND=1


147
## Does not work 100% because a lot of Emacs batch output comes on stderr (?).
148
[ "$quiet" ] && exec 1> /dev/null
149 150


151 152 153
## Run status on inputs, list modified files on stdout.
status ()
{
154
    git status -s "$@" >| $tempfile || die "git status error for $@"
155

156
    local stat file modified
157

158
    while read stat file; do
159

160 161 162
        [ "$stat" != "M" ] && \
            die "Unexpected status ($stat) for generated $file"
        modified="$modified $file"
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    done < $tempfile

    echo "$modified"

    return 0
}                               # function status


echo "Checking input file status..."

## The lisp portion could be more permissive, eg only care about .el files.
modified=$(status ${autogendir:+$sources} ${ldefs_flag:+lisp} ${info_flag:+doc}) || die

[ "$modified" ] && {
    echo "Locally modified: $modified"
    [ "$force" ] || die "There are local modifications"
}
181 182


183 184 185 186 187 188 189 190 191 192 193 194
## Probably this is overkill, and there's no need to "bootstrap" just
## for making autoloads.
[ "$clean" ] && {

    echo "Running 'make maintainer-clean'..."

    make maintainer-clean #|| die "Cleaning error"

    rm -f $ldefs_in
}


195
echo "Running autoreconf..."
196

197
autoreconf ${clean:+-f} -i -I m4 2>| $tempfile
198 199 200 201 202 203 204 205 206 207 208

retval=$?

## Annoyingly, autoreconf puts the "installing `./foo' messages on stderr.
if [ "$quiet" ]; then
    grep -v 'installing `\.' $tempfile 1>&2
else
    cat "$tempfile" 1>&2
fi

[ $retval -ne 0 ] && die "autoreconf error"
209 210


211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
## Uses global $commit.
commit ()
{
    local type=$1
    shift

    [ $# -gt 0 ] || {
        echo "No files were modified"
        return 0
    }

    echo "Modified file(s): $@"

    [ "$commit" ] || return 0

    echo "Committing..."

228
    git commit -m "; Auto-commit of $type files." "$@" || return $?
229

230 231 232
    ## In case someone else pushed something while we were working.
    git pull --rebase || return $?
    git push || return $?
233 234 235 236 237

    echo "Committed files: $@"
}                               # function commit


238 239
## No longer used since info/dir is now generated at install time if needed,
## and is not in the repository any more.
240 241
info_dir ()
{
242
    local basefile=build-aux/dir_top outfile=info/dir
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

    echo "Regenerating info/dir..."

    ## Header contains non-printing characters, so this is more
    ## reliable than using echo.
    rm -f $outfile
    cp $basefile $outfile

    local topic file dircat dirent

    ## FIXME inefficient looping.
    for topic in "Texinfo documentation system" "Emacs" "GNU Emacs Lisp" \
        "Emacs editing modes" "Emacs network features" "Emacs misc features" \
        "Emacs lisp libraries"; do

        cat - <<EOF >> $outfile

$topic
EOF
        ## Bit faster than doc/*/*.texi.
        for file in doc/emacs/emacs.texi doc/lispintro/*.texi \
            doc/lispref/elisp.texi doc/misc/*.texi; do

            ## FIXME do not ignore w32 if OS is w32.
            case $file in
                *-xtra.texi|*efaq-w32.texi) continue ;;
            esac

271
            dircat=$(sed -n -e 's/@value{emacsname}/Emacs/' -e 's/^@dircategory //p' $file)
272

273 274
            ## TODO warn about unknown topics (check-info in top-level
            ## Makefile does this).
275 276 277 278 279 280 281 282 283 284 285 286
            [ "$dircat" = "$topic" ] || continue

            sed -n -e 's/@value{emacsname}/Emacs/' \
                -e 's/@acronym{\([A-Z]*\)}/\1/' \
                -e '/^@direntry/,/^@end direntry/ s/^\([^@]\)/\1/p' \
                $file >> $outfile

        done
    done

    local modified

287
    modified=$(status $outfile) || die
288

289
    commit "info/dir" $modified || die "commit error"
290 291 292
}                               # function info_dir


Glenn Morris's avatar
Glenn Morris committed
293 294 295 296 297 298 299 300 301 302
[ "$autogendir" ] && {

    oldpwd=$PWD

    cp $genfiles $autogendir/

    cd $autogendir || die "cd error for $autogendir"

    echo "Checking status of generated files..."

303
    modified=$(status $basegen) || die
Glenn Morris's avatar
Glenn Morris committed
304

305
    commit "generated" $modified || die "commit error"
Glenn Morris's avatar
Glenn Morris committed
306 307 308

    exit 0
}                               # $autogendir
309 310


311 312 313
[ "$info_flag" ] && info_dir


314 315 316 317 318
[ "$ldefs_flag" ] || exit 0


echo "Finding loaddef targets..."

319 320 321
find lisp -name '*.el' -exec grep '^;.*generated-autoload-file:' {} + | \
    sed -e '/loaddefs\|esh-groups/d' -e 's|/[^/]*: "|/|' -e 's/"//g' \
    >| $tempfile || die "Error finding targets"
322 323 324 325 326

genfiles=

while read genfile; do

327 328 329 330 331
    ## Or we can just use sort -u when making tempfile...
    case " $genfiles " in
        *" $genfile "*) continue ;;
    esac

332
    [ -r $genfile ] || die "Unable to read $genfile"
333 334 335 336 337 338 339 340 341 342 343

    genfiles="$genfiles $genfile"
done < $tempfile


[ "$genfiles" ] || die "Error setting genfiles"


[ -e Makefile ] || {
    echo "Running ./configure..."

344 345
    ## Minimize required packages.
    ./configure --without-x || die "configure error"
346 347 348
}


349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
## Build the minimum needed to get the autoloads.
echo "Running lib/ make..."

make -C lib "$@" all || die "make lib error"


echo "Running src/ make..."

make -C src "$@" bootstrap-emacs || die "make src error"


echo "Running lisp/ make..."

make -C lisp "$@" autoloads EMACS=../src/bootstrap-emacs || die "make src error"

364 365 366 367 368 369 370

## Ignore comment differences.
[ ! "$lboot_flag" ] || \
    diff -q -I '^;' $ldefs_in $ldefs_out || \
    cp $ldefs_in $ldefs_out || die "cp ldefs_boot error"


371 372 373 374 375
echo "Checking status of loaddef files..."

## It probably would be fine to just check+commit lisp/, since
## making autoloads should not effect any other files.  But better
## safe than sorry.
376
modified=$(status $genfiles $ldefs_out) || die
377 378


379
commit "loaddefs" $modified || die "commit error"
380 381


382 383 384 385 386 387 388 389
## Less important than the other stuff, so do it last.
[ ! "$changelog_flag" ] || {
    make change-history-nocommit || die "make change-history error"
    modified=$(status $changelog_files) || die
    commit "ChangeLog" $modified || die "commit error"
}


390
exit 0
391 392

### update_autogen ends here