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