LDP/LDP/defunct/builder-2016/produce-ldp-outputs

347 lines
10 KiB
Bash
Executable File

#! /bin/bash
#
# -- Program to locate all DocBook and LinuxDoc files in The Linux
# Documentation Project (TLDP) Version Control System (VCS) and
# produce HTML, PDF and text outputs (with companion Makefile)
#
# -- License: GPLv2
#
# -- written for TLDP in January 2016; Martin A. Brown <martin@linux-ip.net>
#
set -e
# set -x
SELFNAME="$( readlink --canonicalize ${0})"
ME="${SELFNAME##*/}" # -- basename
DIR="${SELFNAME%/*}" # -- dirname
MAKEFILE="${DIR}/Makefile"
SOURCETREE="${1:-$HOME/vcs/LDP/LDP}" && shift
SOURCETREE="$( readlink --canonicalize $SOURCETREE )"
OUTPUTTREE="${1:-$PWD/output}" && shift
OUTPUTTREE="$( readlink --canonicalize $OUTPUTTREE )"
SOURCEDIRS=""
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/faq/linuxdoc"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/guide/linuxdoc"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/howto/linuxdoc"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/faq/docbook"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/guide/docbook"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/howto/docbook"
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/ref/docbook"
# -- my favorite shell functions (and their offspring)
#
gripe () { printf >&2 "%s\n" "$@"; }
debug () { : ; } # -- NOOP for now
notice () { gripe "${ME}[$$]: $@"; }
abort () {
EXIT_CODE="${1}" && shift;
gripe "E: $@";
exit $EXIT_CODE;
}
# -- defaults for logging output; override these in environment
# if you care
#
: "${LOG_FACILITY:=daemon}"
: "${LOG_PRIO:=err}"
: "${LOGGER_OPTS:=-t ${ME}[$$] -p ${LOG_FACILITY}.${LOG_PRIO}}"
# -- if we are connected to a terminal, then send logging output
# to STDERR, as well
# -- create logger as a shell function
#
logger () {
tty -s && LOGGER_OPTS="-s $LOGGER_OPTS"
command logger $LOGGER_OPTS -- "$@" ;
}
usage () {
gripe "usage: ${ME} [<ldp_checkout> [<destdir>]]" \
" default ldp_checkout=${SOURCETREE}" \
" default destdir=${OUTPUTTREE}" \
""
abort 1 "$@";
}
# - - - - - - - - - - - -
function select_source_files () {
# - - - - - - - - - - - -
#
# -- return a list of filenames in any directory which are (likely)
# to be documents for TLDP
# args 1: file extension sought
# args +2: base directories in which to seek
local ext=${1} && shift
for d in $@; do
test -e "$d" || abort 2 "ENOENT (2): No such file or directory: ${d}"
test -d "$d" || abort 20 "ENOTDIR (20): Not a directory: ${d}";
# -- locate each solo text document, e.g. XWindow-User-HOWTO.xml
#
find "$d" -mindepth 1 -maxdepth 1 -type f -name \*."${ext}" -printf "%p\n"
# -- locate each document owning a directory, e.g. 8021X-HOWTO/8021X-HOWTO.sgml
#
{
find "$d" -mindepth 1 -maxdepth 1 -type d -printf "%p\n" | while read DIR; do
# -- get just the stem of the document name from the directory, e.g. 8021X-HOWTO
stem=${DIR##*/}
# -- construct an abspath, e.g. /full/path/to/8021X-HOWTO/8021X-HOWTO.sgml
doc="${DIR}/${stem}.${ext}"
# -- see if file exists, and if so, print its name
# File existence test logic is reversed, because this script runs
# with "set -e". If the test for existence fails, then the script
# stops (this should be familiar to those who have written
# Makefiles).
#
test ! -e "${doc}" || printf "%s\n" "${doc}"
done
}
done
}
# - - - - - - - - - - - -
list_output_documents () {
# - - - - - - - - - - - -
#
# -- by definition, each directory in the output tree
# is named by the STEM, so this produces a list of the
# stem names of the documents that have been produced
#
find "$@" -mindepth 1 -maxdepth 1 -printf "%P\n";
}
# - - - - - - - - - - - -
list_source_documents () {
# - - - - - - - - - - - -
#
select_source_files xml "$@";
select_source_files sgml "$@";
}
# - - - - - - - - - - - -
get_stem () {
# - - - - - - - - - - - -
#
# -- filter filenames and produce stem names
# - no args
# - STDIN: A filename (can be full filename)
# - STDOUT: Just the stem name.
#
while read FULLNAME ; do
fn="${FULLNAME##*/}"
printf "%s\n" "${fn%.*}";
done
}
# - - - - - - - - - - - -
sort_by_stem () {
# - - - - - - - - - - - -
#
# -- typical decorate, sort, undecorate pattern, though,
# admittedly, not common in shell
# - no args
# - STDIN: stream of strings with full path
# - STDOUT: stem-sorted stream of strings with full path
#
{
{
while read OBJ; do
stem=$( printf "%s\n" "$OBJ" | get_stem )
printf "%s %s\n" "${stem}" "$OBJ"
done;
} | sort;
} | awk '{print $2}';
}
# - - - - - - - - - - - -
find_sort_by_mtime () {
# - - - - - - - - - - - -
#
local SOURCE="${1}" && shift
local OUTPUT="${1}" && shift
{
# -- directory age does not count, only file age!
#
test -e "${SOURCE}" \
|| abort 2 "ENOENT (2): we thought this directory just existed: ${SOURCE}"
find "$SOURCE" -type f -printf "%T@ SOURCE %p\n";
test -d "${OUTPUT}" \
&& find "$OUTPUT" -type f -printf "%T@ OUTPUT %p\n";
} | sort -gr;
}
# - - - - - - - - - - - -
function is_output_current () {
# - - - - - - - - - - - -
#
# -- return a list of filenames in any directory which are (likely)
# to be documents for TLDP
# args 1: input file name
# args 2: OUTPUTTREE
#
# -- there are two (probable) cases for the input, A) a single standalone
# file or B) the main document source file in a dedicated directory;
# below is a description of the logic....
#
# Case A) single standalone file; we don't want to find all files from
# the parent directory
#
# OBJ=/home/fred/vcs/LDP/LDP/howto/docbook/Software-Release-Practice-HOWTO.xml
# OUTPUTTREE=/home/fred/wip/tldp/output
# fn=Software-Release-Practice-HOWTO.xml
# stem=Software-Release-Practice-HOWTO
# parentdir=/home/fred/vcs/LDP/LDP/howto/docbook
# parentstem=docbook
#
# Case B) main document source file in a dedicated directory; we do want
# to look for all files starting from the parent directory
#
# OBJ=/home/fred/vcs/LDP/LDP/howto/docbook/Jabber-Server-Farming-HOWTO/Jabber-Server-Farming-HOWTO.xml
# OUTPUTTREE=/home/fred/wip/tldp/output
# fn=Jabber-Server-Farming-HOWTO.xml
# stem=Jabber-Server-Farming-HOWTO
# parentdir=/home/fred/vcs/LDP/LDP/howto/docbook/Jabber-Server-Farming-HOWTO
# parentstem=Jabber-Server-Farming-HOWTO
#
local stem="${1}" && shift
local OBJ="${1}" && shift
local OUTPUTTREE="${1}" && shift
local parentdir="${OBJ%/*}"
local parentstem="${parentdir##*/}"
startloc="$OBJ"
test "${stem}" == "${parentstem}" \
&& startloc="${parentdir}"
local outdir="${OUTPUTTREE}/${stem}"
result=$( find_sort_by_mtime "$startloc" "$outdir" ) \
|| abort 1 "Could not retrieve files for $stem sorted by mtime."
newest=$( head -n 1 <<<"$result" ) \
|| abort 1 "Could not get first result for $stem files."
DECISION=$( sed -e '/ OUTPUT /,$d' <<<"$newest" ) \
|| abort 1 "Could not check if the SOURCE files are newest...."
if test -z "$DECISION" ; then
debug "${stem} No new source files"
return 0
else
# -- squawk about it, to the user
#
while read TS SOURCE FNAME ; do
ts=$( date +%F-%T --date @"$TS" )
notice "${stem} updated file found ($ts): $FNAME"
done <<< "$DECISION"
return 1
fi
}
# - - - - - - - - - - - -
generate_new_content () {
# - - - - - - - - - - - -
#
local stem="${1}" && shift
local OBJ="${1}" && shift
local OUTPUTTREE="${1}" && shift
#local makefile="${1}" && shift
# -- make a stab at processing the document, but don't let the driver
# stop just because a single document has an error
#
{
{
exec 2>&1;
make -f "$MAKEFILE" OBJ="${OBJ}" DESTDIR="${OUTPUTTREE}";
test $? -eq 0 \
|| gripe "FAILURE" "FAILURE producing all outputs from $OBJ" "FAILURE";
} | sed -ue "s/^/${stem} /";
} | command logger -s ${LOGGER_OPTS} --
}
# - - - - - - - - - - - -
list_orphaned_outputs () {
# - - - - - - - - - - - -
#
local OUTPUTTREE="${1}" && shift
local SOURCEDIRS="$@"
{
SOURCEDOCS=$( list_source_documents "${SOURCEDIRS}" | get_stem | sort )
OUTPUTDOCS=$( list_output_documents "${OUTPUTTREE}" | sort )
# -- list documents in source tree that do not have corresponding
# output directory
#
diff -Nur -- <( printf "%s\n" "$SOURCEDOCS" ) <( printf "%s\n" "$OUTPUTDOCS" ) \
| tail -n +2 \
| grep -- '^-' \
| sed -e 's|^-|Unbuilt source document |';
# -- list documents in output tree that do not have corresponding
# input directory
#
diff -Nur -- <( printf "%s\n" "$OUTPUTDOCS" ) <( printf "%s\n" "$SOURCEDOCS" ) \
| tail -n +2 \
| grep -- '^-' \
| sed -e 's|^-|Extra output document |';
} | command logger -s ${LOGGER_OPTS} --;
}
# - - - - - - - - - - - -
produce_ldp_outputs () {
# - - - - - - - - - - - -
#
local OUTPUTTREE="${1}" && shift
local SOURCEDIRS="$@"
list_source_documents "$SOURCEDIRS" | sort_by_stem | while read OBJ; do
stem=$( printf "%s\n" "$OBJ" | get_stem )
notice "${stem} checking if rebuild required for $OBJ"
# -- N.B. there's a dangerous looking ||: at the end of the line
# calling the Makefile. This is intentional. The Makefile
# may not be able to process the input successfully, but we
# would like to continue, if possible, and process the next
# available document.
#
is_output_current "${stem}" "$OBJ" "$OUTPUTTREE" \
|| (generate_new_content "${stem}" "${OBJ}" "${OUTPUTTREE}" || :)
done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# main
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
test -d "${OUTPUTTREE}" \
|| usage "Output directory must already exist, quitting."
produce_ldp_outputs "$OUTPUTTREE" "$SOURCEDIRS"
list_orphaned_outputs "$OUTPUTTREE" "$SOURCEDIRS"
# -- end of file