36fc2307a795ccac4f107fe3fb556ade1af9e2fa
   1#!/bin/sh
   2#
   3# Build two documentation trees and diff the resulting formatted output.
   4# Compared to a source diff, this can reveal mistakes in the formatting.
   5# For example:
   6#
   7#   ./doc-diff origin/master HEAD
   8#
   9# would show the differences introduced by a branch based on master.
  10
  11OPTIONS_SPEC="\
  12doc-diff [options] <from> <to> [-- <diff-options>]
  13doc-diff (-c|--clean)
  14--
  15j=n                     parallel argument to pass to make
  16f                       force rebuild; do not rely on cached results
  17c,clean                 cleanup temporary working files
  18from-asciidoc           use asciidoc with the 'from'-commit
  19from-asciidoctor        use asciidoctor with the 'from'-commit
  20asciidoc                use asciidoc with both commits
  21to-asciidoc             use asciidoc with the 'to'-commit
  22to-asciidoctor          use asciidoctor with the 'to'-commit
  23asciidoctor             use asciidoctor with both commits
  24"
  25SUBDIRECTORY_OK=1
  26. "$(git --exec-path)/git-sh-setup"
  27
  28parallel=
  29force=
  30clean=
  31from_program=
  32to_program=
  33while test $# -gt 0
  34do
  35        case "$1" in
  36        -j)
  37                parallel=$2; shift ;;
  38        -c|--clean)
  39                clean=t ;;
  40        -f)
  41                force=t ;;
  42        --from-asciidoctor)
  43                from_program=-asciidoctor ;;
  44        --to-asciidoctor)
  45                to_program=-asciidoctor ;;
  46        --asciidoctor)
  47                from_program=-asciidoctor
  48                to_program=-asciidoctor ;;
  49        --from-asciidoc)
  50                from_program=-asciidoc ;;
  51        --to-asciidoc)
  52                to_program=-asciidoc ;;
  53        --asciidoc)
  54                from_program=-asciidoc
  55                to_program=-asciidoc ;;
  56        --)
  57                shift; break ;;
  58        *)
  59                usage ;;
  60        esac
  61        shift
  62done
  63
  64tmp="$(git rev-parse --show-toplevel)/Documentation/tmp-doc-diff" || exit 1
  65
  66if test -n "$clean"
  67then
  68        test $# -eq 0 || usage
  69        git worktree remove --force "$tmp/worktree" 2>/dev/null
  70        rm -rf "$tmp"
  71        exit 0
  72fi
  73
  74if test -z "$parallel"
  75then
  76        parallel=$(getconf _NPROCESSORS_ONLN 2>/dev/null)
  77        if test $? != 0 || test -z "$parallel"
  78        then
  79                parallel=1
  80        fi
  81fi
  82
  83test $# -gt 1 || usage
  84from=$1; shift
  85to=$1; shift
  86
  87from_oid=$(git rev-parse --verify "$from") || exit 1
  88to_oid=$(git rev-parse --verify "$to") || exit 1
  89
  90if test -n "$force"
  91then
  92        rm -rf "$tmp"
  93fi
  94
  95# We'll do both builds in a single worktree, which lets "make" reuse
  96# results that don't differ between the two trees.
  97if ! test -d "$tmp/worktree"
  98then
  99        git worktree add -f --detach "$tmp/worktree" "$from" &&
 100        dots=$(echo "$tmp/worktree" | sed 's#[^/]*#..#g') &&
 101        ln -s "$dots/config.mak" "$tmp/worktree/config.mak"
 102fi
 103
 104construct_makemanflags () {
 105        if test "$1" = "-asciidoc"
 106        then
 107                echo USE_ASCIIDOCTOR=
 108        elif test "$1" = "-asciidoctor"
 109        then
 110                echo USE_ASCIIDOCTOR=YesPlease
 111        fi
 112}
 113
 114from_makemanflags=$(construct_makemanflags "$from_program") &&
 115to_makemanflags=$(construct_makemanflags "$to_program") &&
 116
 117from_dir=$from_oid$from_program &&
 118to_dir=$to_oid$to_program &&
 119
 120# generate_render_makefile <srcdir> <dstdir>
 121generate_render_makefile () {
 122        find "$1" -type f |
 123        while read src
 124        do
 125                dst=$2/${src#$1/}
 126                printf 'all:: %s\n' "$dst"
 127                printf '%s: %s\n' "$dst" "$src"
 128                printf '\t@echo >&2 "  RENDER $(notdir $@)" && \\\n'
 129                printf '\tmkdir -p $(dir $@) && \\\n'
 130                printf '\tMANWIDTH=80 man $< >$@+ && \\\n'
 131                printf '\tmv $@+ $@\n'
 132        done
 133}
 134
 135# render_tree <committish_oid> <directory_name> <makemanflags>
 136render_tree () {
 137        # Skip install-man entirely if we already have an installed directory.
 138        # We can't rely on make here, since "install-man" unconditionally
 139        # copies the files (spending effort, but also updating timestamps that
 140        # we then can't rely on during the render step). We use "mv" to make
 141        # sure we don't get confused by a previous run that failed partway
 142        # through.
 143        oid=$1 &&
 144        dname=$2 &&
 145        makemanflags=$3 &&
 146        if ! test -d "$tmp/installed/$dname"
 147        then
 148                git -C "$tmp/worktree" checkout --detach "$oid" &&
 149                make -j$parallel -C "$tmp/worktree" \
 150                        $makemanflags \
 151                        GIT_VERSION=omitted \
 152                        SOURCE_DATE_EPOCH=0 \
 153                        DESTDIR="$tmp/installed/$dname+" \
 154                        install-man &&
 155                mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
 156        fi &&
 157
 158        # As with "installed" above, we skip the render if it's already been
 159        # done.  So using make here is primarily just about running in
 160        # parallel.
 161        if ! test -d "$tmp/rendered/$dname"
 162        then
 163                generate_render_makefile "$tmp/installed/$dname" \
 164                        "$tmp/rendered/$dname+" |
 165                make -j$parallel -f - &&
 166                mv "$tmp/rendered/$dname+" "$tmp/rendered/$dname"
 167        fi
 168}
 169
 170render_tree $from_oid $from_dir $from_makemanflags &&
 171render_tree $to_oid $to_dir $to_makemanflags &&
 172git -C $tmp/rendered diff --no-index "$@" $from_dir $to_dir