summaryrefslogtreecommitdiff
path: root/tools/yelp-build.in
diff options
context:
space:
mode:
Diffstat (limited to 'tools/yelp-build.in')
-rwxr-xr-xtools/yelp-build.in1403
1 files changed, 814 insertions, 589 deletions
diff --git a/tools/yelp-build.in b/tools/yelp-build.in
index f3e21d8..d53db52 100755
--- a/tools/yelp-build.in
+++ b/tools/yelp-build.in
@@ -1,8 +1,7 @@
-#!/bin/sh
-# -*- indent-tabs-mode: nil -*-
+#!/bin/python3
#
# yelp-build
-# Copyright (C) 2010-2015 Shaun McCance <shaunm@gnome.org>
+# Copyright (C) 2010-2020 Shaun McCance <shaunm@gnome.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,590 +17,816 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-xsl_mal_cache='@XSL_MAL_CACHE@'
-xsl_db2html='@XSL_DB2HTML@'
-xsl_db2xhtml='@XSL_DB2XHTML@'
-xsl_mal2html='@XSL_MAL2HTML@'
-xsl_mal2xhtml='@XSL_MAL2XHTML@'
-yelp_icon_dir='@YELP_ICON_DIR@'
-yelp_js_dir='@YELP_JS_DIR@'
-xsl_mal_epub='@DATADIR@/xslt/mal-epub.xsl'
-xsl_mal_opf='@DATADIR@/xslt/mal-opf.xsl'
-xsl_mal_ncx='@DATADIR@/xslt/mal-ncx.xsl'
-xsl_mal_files='@DATADIR@/xslt/mal-files.xsl'
-
-mkdir_p () {
- if [ ! -d "$1" ]; then
- mkdir_p `dirname "$1"`
- mkdir "$1"
- fi
-}
-
-urlencode () {
- LC_ALL=C awk '
-BEGIN {
- for (i = 1; i <= 255; i++) chars[sprintf("%c", i)] = i;
-}
-{
- ret = "";
- for (i = 1; i <= length($0); i++) {
- c = substr($0, i, 1);
- if (c ~ /[\/a-zA-Z0-9._-]/)
- ret = ret c;
- else
- ret = ret sprintf("%%%X%X", int(chars[c] / 16), chars[c] % 16);
- }
- print ret;
-}'
-}
-
-urldecode () {
- LC_ALL=C awk '
-BEGIN {
- for(i = 0; i < 10; i++) hex[i] = i;
- hex["A"] = hex["a"] = 10;
- hex["B"] = hex["b"] = 11;
- hex["C"] = hex["c"] = 12;
- hex["D"] = hex["d"] = 13;
- hex["E"] = hex["e"] = 14;
- hex["F"] = hex["f"] = 15;
-}
-{
- ret = "";
- for (i = 1; i <= length($0); i++) {
- c = substr($0, i, 1);
- if (c == "+") {
- ret = ret " ";
- }
- else if (c == "%") {
- c = sprintf("%c", hex[substr($0, i + 1, 1)] * 16 + hex[substr($0, i + 2, 1)]);
- ret = ret c;
- i += 2;
- }
- else {
- ret = ret c;
- }
- }
- print ret;
-}'
-}
-
-yelp_usage () {
- (
- echo "Usage: yelp-build <COMMAND> [OPTIONS] [FILES]"
- echo ""
- echo "Commands:"
- echo " cache Create a Mallard cache file"
- echo " epub Create an EPUB file for Mallard"
- echo " html Convert input files to HTML"
- echo " xhtml Convert input files to XHTML"
- ) 1>&2
-}
-yelp_usage_cache () {
- (
- echo "Usage: yelp-build cache <FILES>"
- echo ""
- echo " Create a Mallard cache file from the page files FILES."
- echo " If FILES contains directories, all .page files in those"
- echo " directories will be used."
- echo ""
- echo "Options:"
- echo " -o OUT Output cache to the file OUT"
- echo " -s Treat pages as belonging to a Mallard site"
- ) 1>&2
-}
-yelp_usage_epub () {
- (
- echo "Usage: yelp-build epub [OPTIONS] <FILES>"
- echo ""
- echo " Create and EPUB file from the Mallard page files FILES."
- echo ""
- echo "Options:"
- echo " -c CACHE Use the existing Mallard cache CACHE"
- echo " -o OUT Output en EPUB file named OUT"
- echo " -x CUSTOM Import the custom XSLT file CUSTOM"
- echo " -p PATHS Extra paths to search for files"
- echo " -i Ignore missing media files"
- ) 1>&2
-}
-yelp_usage_html () {
- (
- echo "Usage: yelp-build <html|xhtml> [OPTIONS] <FILES>"
- echo ""
- echo " Create HTML or XHTML from the input files FILES."
- echo " FILES can be DocBook files, Mallard page files,"
- echo " or directories containing Mallard page files."
- echo ""
- echo "Options:"
- echo " -c CACHE Use the existing Mallard cache CACHE"
- echo " -o OUT Output files in the directory OUT"
- echo " -x CUSTOM Import the custom XSLT file CUSTOM"
- echo " -p PATHS Extra paths to search for files"
- echo " -i Ignore missing media files"
- ) 1>&2
-}
-
-if [ $# = 0 ]; then
- yelp_usage
- exit 1
-fi
-
-yelp_paths_normalize () {
- for path in $(echo "$1" | sed -e 's/[: ]/\n/g'); do
- (cd "$path" && pwd)
- done | tr '\n' ' '
-}
-
-yelp_cache_in_page () {
- fbase=$(basename "$1")
- ext=$(echo "$fbase" | sed -e 's/.*\.//')
- fdir=$( (cd $(dirname "$1") && pwd) )
- sdir=${fdir##${cache_site_root}}/
- url=file://$(echo "$fdir/$fbase" | urlencode)
- if [ "x$cache_site" = "x1" ]; then
- siteattr=' site:dir="'"$sdir"'"'
- fi
- if [ "x$ext" = "xstack" ]; then
- echo '<stack cache:href="'"$url"'"'"$siteattr"'/>'
- else
- echo '<page cache:href="'"$url"'"'"$siteattr"'/>'
- fi
-}
-
-yelp_cache_in_site () {
- for dir in "$1"/*; do
- if [ -d "$dir" ]; then
- if [ $(basename "$dir") != "__pintail__" ]; then
- yelp_cache_in_site "$dir"
- fi
- fi
- done
- for page in "$1"/*.page "$1"/*.stack; do
- if [ -e "$page" ]; then
- yelp_cache_in_page "$page"
- fi
- done
-}
-
-yelp_cache_in () {
- echo '<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"'
- if [ "x$cache_site" = "x1" ]; then
- echo ' xmlns:site="http://projectmallard.org/site/1.0/"'
- fi
- echo ' xmlns="http://projectmallard.org/1.0/">'
- for page in "$@"; do
- if [ -d "$page" ]; then
- if [ "x$cache_site" = "x1" ]; then
- yelp_cache_in_site "$page"
- else
- for sub in "$page"/*.page "$page"/*.stack; do
- if [ -e "$sub" ]; then
- yelp_cache_in_page "$sub"
- fi
- done
- fi
- else
- yelp_cache_in_page "$page"
- fi
- done
- echo '</cache:cache>'
-}
-
-yelp_cache () {
- cache_out="index.cache"
- while [ "$#" != "0" ]; do
- case "$1" in
- "-o")
- shift
- cache_out="$1"
- shift
- ;;
- "-s")
- cache_site="1"
- cache_site_root=$(pwd)
- shift
- ;;
- *)
- break
- ;;
- esac
- done
- if [ "$#" = "0" -o "x$1" = "x--help" -o "x$1" = "x-h" ]; then
- yelp_usage_cache
- exit 1
- fi
- yelp_cache_in "$@" | xsltproc --path "$html_paths" \
- --xinclude -o "$cache_out" \
- "$xsl_mal_cache" -
-}
-
-yelp_html_xsl_common () {
- if [ "x$html_internal_datadir" != "x" ]; then
- echo '<xsl:variable name="yelp.internal.datadir" select="'"'$html_internal_datadir'"'"/>'
- echo '<xsl:param name="html.css.root" select="$yelp.internal.datadir"/>'
- echo '<xsl:param name="html.js.root" select="$yelp.internal.datadir"/>'
- else
- echo '<xsl:variable name="yelp.internal.datadir" select="'"''"'"/>'
- fi
- if [ "x$html_internal_xsl" != "x" ]; then
- echo '<xsl:include href="file://'`echo "$html_internal_xsl" | urlencode`'"/>'
- fi
- if [ "x$html_custom" != "x" ]; then
- echo '<xsl:include href="file://'`echo "$html_custom" | urlencode`'"/>'
- fi
- echo '<xsl:template name="html.css">'
- echo ' <xsl:param name="node" select="."/>'
- echo ' <xsl:variable name="yelp.locale">'
- echo ' <xsl:choose>'
- echo ' <xsl:when test="$node/@xml:lang != '"''"'">'
- echo ' <xsl:value-of select="$node/@xml:lang"/>'
- echo ' </xsl:when>'
- echo ' <xsl:when test="$node/@lang != '"''"'">'
- echo ' <xsl:value-of select="$node/@lang"/>'
- echo ' </xsl:when>'
- echo ' <xsl:otherwise>'
- echo ' <xsl:text>C</xsl:text>'
- echo ' </xsl:otherwise>'
- echo ' </xsl:choose>'
- echo ' </xsl:variable>'
- echo ' <exsl:document href="{$yelp.internal.datadir}{$yelp.locale}.css" method="text">'
- echo ' <xsl:call-template name="html.css.content">'
- echo ' <xsl:with-param name="node" select="$node"/>'
- echo ' <xsl:with-param name="direction">'
- echo ' <xsl:call-template name="l10n.direction">'
- echo ' <xsl:with-param name="lang" select="$yelp.locale"/>'
- echo ' </xsl:call-template>'
- echo ' </xsl:with-param>'
- echo ' </xsl:call-template>'
- echo ' </exsl:document>'
- echo ' <link rel="stylesheet" type="text/css" href="{$html.css.root}{$yelp.locale}.css"/>'
- echo '</xsl:template>'
- echo '<xsl:template name="html.js.script">'
- echo ' <xsl:param name="node" select="."/>'
- echo ' <exsl:document href="{$yelp.internal.datadir}yelp.js" method="text">'
- echo ' <xsl:call-template name="html.js.content">'
- echo ' <xsl:with-param name="node" select="$node"/>'
- echo ' </xsl:call-template>'
- echo ' </exsl:document>'
- echo ' <script type="text/javascript" src="{$html.js.root}yelp.js"/>'
- echo '</xsl:template>'
-}
-yelp_html_db2html () {
- for xml in "$@"; do
- xmldir=`dirname "$xml"`
- xmldir=`(cd "$xmldir" && pwd)`
- # Output HTML
- (
- echo '<xsl:stylesheet'
- echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
- echo ' xmlns:exsl="http://exslt.org/common"'
- echo ' xmlns="http://www.w3.org/1999/xhtml"'
- echo ' extension-element-prefixes="exsl"'
- echo ' version="1.0">'
- if [ "x$is_xhtml" = "x1" ]; then
- xsl='file://'`echo "$xsl_db2xhtml" | urlencode`
- else
- xsl='file://'`echo "$xsl_db2html" | urlencode`
- fi
- echo '<xsl:import href="'"$xsl"'"/>'
- yelp_html_xsl_common
- echo '</xsl:stylesheet>'
- ) | xsltproc --path "$html_paths" --xinclude -o "$html_out"/ - "$xml"
- # Copy media
- if [ "x$xmldir" != "x$html_out" ]; then
- (
- echo '<xsl:stylesheet'
- echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
- echo ' xmlns:db="http://docbook.org/ns/docbook"'
- echo ' exclude-result-prefixes="db"'
- echo ' version="1.0">'
- echo '<xsl:output method="text"/>'
- echo '<xsl:template match="/">'
- echo ' <xsl:for-each select="'
- echo ' //audiodata | //imagedata | //videodata |'
- echo ' //db:audiodata | //db:imagedata | //db:videodata">'
- echo ' <xsl:value-of select="concat(@fileref, '"'&#x000A;'"')"/>'
- echo ' </xsl:for-each>'
- echo '</xsl:template>'
- echo '</xsl:stylesheet>'
- ) | xsltproc --path "$html_paths" --xinclude - "$xml" | while read media; do
- mfile=`echo "$media" | urldecode`
- minput="$xmldir/$mfile"
- moutput="$html_out/$mfile"
- mkdir_p `dirname "$moutput"`
- if [ ! -f "$minput" -a "x$html_paths" != "x" ]; then
- minput_rel=${minput#"$(pwd)/"}
- if [ "x$minput_rel" != "x$minput_src" ]; then
- for path in $html_paths; do
- if [ -f "$path/$minput_rel" ]; then
- minput="$path/$minput_rel"
- break
- fi
- done
- fi
- fi
- if [ -f "$minput" -o "x$html_ignore_media" != "x1" ]; then
- cp "$minput" "$moutput"
- fi
- done
- fi
- # Copy JavaScript
- cp "${yelp_js_dir}/highlight.pack.js" "$html_out/$html_internal_datadir"
- done
-}
-
-yelp_html_mal2html () {
- if [ "x$html_cache_file" != "x" ]; then
- html_cache_file=`(cd $(dirname "$html_cache_file") && pwd)`/`basename "$html_cache_file"`
- else
- html_cache_file_is_tmp="yes"
- html_cache_file=`mktemp "${TMPDIR:-/tmp}"/yelp-XXXXXXXX`
- yelp_cache -o "$html_cache_file" "$@"
- fi
- html_tmp_infile=`mktemp "${TMPDIR:-/tmp}"/yelp-XXXXXXXX`
- yelp_cache_in "$@" > "$html_tmp_infile"
- # Output HTML
- (
- echo '<xsl:stylesheet'
- echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
- echo ' xmlns:mal="http://projectmallard.org/1.0/"'
- echo ' xmlns:cache="http://projectmallard.org/cache/1.0/"'
- echo ' xmlns:exsl="http://exslt.org/common"'
- echo ' xmlns="http://www.w3.org/1999/xhtml"'
- echo ' exclude-result-prefixes="mal cache"'
- echo ' extension-element-prefixes="exsl"'
- echo ' version="1.0">'
- if [ "x$is_xhtml" = "x1" ]; then
- xsl='file://'`echo "$xsl_mal2xhtml" | urlencode`
- else
- xsl='file://'`echo "$xsl_mal2html" | urlencode`
- fi
- echo '<xsl:import href="'"$xsl"'"/>'
- echo '<xsl:include href="'"$xsl_mal_files"'"/>'
- echo '<xsl:output method="text"/>'
- yelp_html_xsl_common
- html_cache_url='file://'`echo "$html_cache_file" | urlencode`
- echo '<xsl:param name="mal.cache.file" select="'"'$html_cache_url'"'"/>'
- echo '<xsl:template match="/">'
- echo '<xsl:for-each select="cache:cache/mal:page | cache:cache/mal:stack">'
- echo '<xsl:variable name="href" select="@cache:href"/>'
- echo '<xsl:for-each select="document(@cache:href)">'
- echo '<xsl:for-each select="mal:page | mal:stack/mal:page">'
- echo '<xsl:call-template name="html.output"/>'
- echo '<xsl:call-template name="mal.files.copy">'
- echo ' <xsl:with-param name="href" select="substring-after($href, '\''file://'\'')"/>'
- echo '</xsl:call-template>'
- echo '</xsl:for-each>'
- echo '</xsl:for-each>'
- echo '</xsl:for-each>'
- echo '</xsl:template>'
- echo '</xsl:stylesheet>'
- ) | (cd "$html_out" && xsltproc $html_profile \
- --path "$html_paths" --xinclude \
- - "$html_tmp_infile") | sort | uniq | \
- while read line; do
- # Copy media from paths output by HTML transform
- line_src=$(echo "$line" | cut -d' ' -f1 | urldecode)
- line_dest="$html_out/"$(echo "$line" | cut -d' ' -f2)
- if [ ! -f "$line_src" -a "x$html_paths" != "x" ]; then
- line_src_rel=${line_src#"$(pwd)/"}
- if [ "x$line_src_rel" != "x$line_src" ]; then
- for path in $html_paths; do
- if [ -f "$path/$line_src_rel" ]; then
- line_src="$path/$line_src_rel"
+import configparser
+import os
+import sys
+import shutil
+import subprocess
+import tempfile
+import urllib.parse
+import uuid
+
+import lxml.etree
+import lxml.ElementInclude
+
+
+XSL_DB2HTML = '@XSL_DB2HTML@'
+XSL_DB2XHTML = '@XSL_DB2XHTML@'
+XSL_MALCACHE = '@XSL_MAL_CACHE@'
+XSL_MAL2HTML = '@XSL_MAL2HTML@'
+XSL_MAL2XHTML = '@XSL_MAL2XHTML@'
+XSL_MAL_OPF='@DATADIR@/xslt/mal-opf.xsl'
+XSL_MAL_NCX='@DATADIR@/xslt/mal-ncx.xsl'
+YELP_JS_DIR = '@YELP_JS_DIR@'
+
+XSLCOMMON = ('''
+<xsl:variable name="yelp.internal.datadir" select="'{intdatadir}'"/>
+<xsl:param name="html.css.root" select="$yelp.internal.datadir"/>
+<xsl:param name="html.js.root" select="$yelp.internal.datadir"/>
+{includes}
+<xsl:template name="html.css">
+ <xsl:param name="node" select="."/>
+ <xsl:variable name="yelp.locale">
+ <xsl:choose>
+ <xsl:when test="$node/@xml:lang != ''">
+ <xsl:value-of select="$node/@xml:lang"/>
+ </xsl:when>
+ <xsl:when test="$node/@lang != ''">
+ <xsl:value-of select="$node/@lang"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>C</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <exsl:document href="{{$yelp.internal.datadir}}{{$yelp.locale}}.css" method="text">
+ <xsl:call-template name="html.css.content">
+ <xsl:with-param name="node" select="$node"/>
+ <xsl:with-param name="direction">
+ <xsl:call-template name="l10n.direction">
+ <xsl:with-param name="lang" select="$yelp.locale"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </exsl:document>
+ <link rel="stylesheet" type="text/css" href="{{$html.css.root}}{{$yelp.locale}}.css"/>
+</xsl:template>
+<xsl:template name="html.js.script">
+ <xsl:param name="node" select="."/>
+ <exsl:document href="{{$yelp.internal.datadir}}yelp.js" method="text">
+ <xsl:call-template name="html.js.content">
+ <xsl:with-param name="node" select="$node"/>
+ </xsl:call-template>
+ </exsl:document>
+ <script type="text/javascript" src="{{$html.js.root}}yelp.js"/>
+</xsl:template>
+''')
+
+DB2HTML = ('''
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns="http://www.w3.org/1999/xhtml"
+ extension-element-prefixes="exsl"
+ version="1.0">
+<xsl:import href="file://{xslfile}"/>
+'''
++ XSLCOMMON +
+'''
+</xsl:stylesheet>
+''')
+
+
+MAL2HTML = ('''
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:mal="http://projectmallard.org/1.0/"
+ xmlns:cache="http://projectmallard.org/cache/1.0/"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns="http://www.w3.org/1999/xhtml"
+ exclude-result-prefixes="mal cache"
+ extension-element-prefixes="exsl"
+ version="1.0">
+<xsl:import href="file://{xslfile}"/>
+<xsl:param name="mal.cache.file" select="'file://{cachefile}'"/>
+'''
++ XSLCOMMON +
+'''
+<xsl:template match="/">
+ <xsl:for-each select="cache:cache/mal:page | cache:cache/mal:stack">
+ <xsl:variable name="href" select="@cache:href"/>
+ <xsl:for-each select="document(@cache:href)">
+ <xsl:for-each select="mal:page | mal:stack/mal:page">
+ <xsl:call-template name="html.output"/>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:for-each>
+</xsl:template>
+</xsl:stylesheet>
+''')
+
+
+
+class InputFile:
+ def __init__(self, filepath, filename, sitedir=None):
+ self.filepath = filepath
+ self.filename = filename
+ self.absfile = os.path.join(filepath, filename)
+ self.absdir = os.path.dirname(self.absfile)
+ self.sitedir = sitedir or ''
+ self.sitefilename = self.sitedir + self.filename
+
+
+class PathResolver(lxml.etree.Resolver):
+ def __init__(self, srcdir, path):
+ if srcdir.endswith('/'):
+ self.srcdir = srcdir
+ else:
+ self.srcdir = srcdir + '/'
+ self.path = path
+
+ def resolve(self, uri, id, context):
+ if os.path.exists(uri):
+ return self.resolve_filename(uri, context)
+ if uri.startswith(self.srcdir):
+ ref = uri[len(self.srcdir):]
+ else:
+ ref = uri
+ for p in self.path:
+ tryfile = os.path.join(p, ref)
+ if os.path.exists(tryfile):
+ return self.resolve_filename(tryfile, context)
+ return None
+
+
+class Builder:
+ name = None
+ desc = None
+ blurb = None
+ formats = []
+ arguments = []
+ postblurb = None
+ config = None
+
+ def __init__(self, yelpbuild):
+ self.yelpbuild = yelpbuild
+ self.options = {}
+ self.fileargs = []
+ self.tmpdir = None
+
+ def __del__(self):
+ if self.tmpdir is not None:
+ shutil.rmtree(self.tmpdir)
+ self.tmpdir = None
+
+ def parse_args(self, args):
+ while len(args) > 0:
+ argdef = None
+ if args[0].startswith('--'):
+ for arg_ in self.arguments:
+ if args[0] == '--' + arg_[0]:
+ argdef = arg_
+ break
+ if argdef is None:
+ self.print_help()
+ return 1
+ elif args[0].startswith('-'):
+ for arg_ in self.arguments:
+ if args[0] == arg_[1]:
+ argdef = arg_
+ break
+ if argdef is None:
+ self.print_help()
+ return 1
+ if argdef is not None:
+ takesarg = (argdef[2] is not None)
+ if takesarg:
+ if len(args) < 2:
+ self.print_help()
+ return 1
+ self.options.setdefault(argdef[0], [])
+ self.options[argdef[0]].append(args[1])
+ args = args[2:]
+ else:
+ self.options[argdef[0]] = True
+ args = args[1:]
+ else:
+ self.fileargs.append(args[0])
+ args = args[1:]
+ cfgfile = None
+ if len(self.fileargs) > 0:
+ cfgfile = os.path.join(os.path.dirname(self.fileargs[0]), '.yelp-tools.cfg')
+ if not os.path.exists(cfgfile):
+ cfgfile = None
+ if cfgfile is None:
+ cfgfile = os.path.join(os.getcwd(), '.yelp-tools.cfg')
+ if os.path.exists(cfgfile):
+ self.config = configparser.ConfigParser()
+ try:
+ self.config.read(cfgfile)
+ except Exception as e:
+ print(e, file=sys.stderr)
+ sys.exit(1)
+ return 0
+
+ def get_option_bool(self, arg):
+ if arg in self.options:
+ return self.options[arg] == True
+ if self.config is not None:
+ val = self.config.get('build:' + self.name, arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
+ val = self.config.get('build', arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return (val == 'true')
+ return False
+
+ def get_option_str(self, arg):
+ if arg in self.options:
+ if isinstance(self.options[arg], list):
+ return self.options[arg][-1]
+ if self.config is not None:
+ val = self.config.get('build:' + self.name, arg, fallback=None)
+ if val is not None:
+ return val
+ val = self.config.get('build', arg, fallback=None)
+ if val is not None:
+ return val
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return val
+ return None
+
+ def get_option_list(self, arg):
+ if arg in self.options:
+ if isinstance(self.options[arg], list):
+ ret = []
+ for opt in self.options[arg]:
+ ret.extend(opt.replace(',', ' ').split())
+ return ret
+ if self.config is not None:
+ val = self.config.get('build:' + self.name, arg, fallback=None)
+ if val is not None:
+ return val.replace(',', ' ').split()
+ val = self.config.get('build', arg, fallback=None)
+ if val is not None:
+ return val.replace(',', ' ').split()
+ val = self.config.get('default', arg, fallback=None)
+ if val is not None:
+ return val.replace(',', ' ').split()
+ return None
+
+ def get_xml(self, infile, path):
+ parser = lxml.etree.XMLParser()
+ parser.resolvers.add(PathResolver(os.path.realpath(infile.absdir), path))
+ tree = lxml.etree.parse(infile.absfile, parser=parser)
+ def pathloader(href, parse, encoding=None):
+ usefile = os.path.join(infile.absdir, href)
+ if not os.path.exists(href):
+ usefile = None
+ if usefile is None:
+ absdir = infile.absdir
+ if not absdir.endswith('/'):
+ absdir = absdir + '/'
+ ref = href
+ if ref.startswith(absdir):
+ ref = ref[len(absdir):]
+ for p in path:
+ tryfile = os.path.join(p, ref)
+ if os.path.exists(tryfile):
+ usefile = tryfile
+ break
+ if usefile is not None:
+ if parse == 'xml':
+ return lxml.etree.parse(usefile, parser=parser).getroot()
+ elif parse == 'text':
+ return open(usefile).read()
+ return None
+ lxml.ElementInclude.include(tree, loader=pathloader)
+ return tree
+
+ def iter_files(self, sitedir=None):
+ issite = self.get_option_bool('site')
+ if len(self.fileargs) == 0:
+ self.fileargs.append('.')
+ for filearg in self.fileargs:
+ if os.path.isdir(filearg):
+ if issite:
+ for infile in self.iter_site(filearg, '/'):
+ yield infile
+ else:
+ for fname in os.listdir(filearg):
+ if fname.endswith('.page'):
+ yield InputFile(filearg, fname)
+ else:
+ if issite:
+ # FIXME: should do some normalization here, I guess.
+ # It's hard to get this perfect without a defined start dir
+ yield InputFile(os.getcwd(), filearg, '/' + os.path.dirname(filearg))
+ else:
+ yield InputFile(os.getcwd(), filearg)
+
+ def iter_site(self, filepath, sitedir):
+ for fname in os.listdir(filepath):
+ newpath = os.path.join(filepath, fname)
+ if os.path.isdir(newpath):
+ # FIXME https://github.com/projectmallard/pintail/issues/36
+ if fname == '__pintail__':
+ continue
+ for infile in self.iter_site(newpath, sitedir + fname + '/'):
+ yield infile
+ elif fname.endswith('.page'):
+ yield InputFile(filepath, fname, sitedir)
+
+ def create_tmpdir(self):
+ if self.tmpdir is None:
+ self.tmpdir = tempfile.mkdtemp()
+
+ def print_help(self):
+ print('Usage: yelp-build ' + self.name + ' [OPTIONS] [FILES]')
+ print('Formats: ' + ' '.join(self.formats) + '\n')
+ #FIXME: prettify names of formats
+ if self.blurb is not None:
+ print(self.blurb + '\n')
+ print('Options:')
+ maxarglen = 2
+ args = []
+ for arg in self.arguments:
+ argkey = '--' + arg[0]
+ if arg[1] is not None:
+ argkey = arg[1] + ', ' + argkey
+ if arg[2] is not None:
+ argkey = argkey + ' ' + arg[2]
+ args.append((argkey, arg[3]))
+ for arg in args:
+ maxarglen = max(maxarglen, len(arg[0]) + 1)
+ for arg in args:
+ print(' ' + (arg[0]).ljust(maxarglen) + ' ' + arg[1])
+ if self.postblurb is not None:
+ print(self.postblurb)
+
+ def main(self, args):
+ pass
+
+
+class CacheBuilder (Builder):
+ name = 'cache'
+ desc = 'Convert a Mallard cache file'
+ blurb = ('Create a Mallard cache file from the page files FILES.\n' +
+ 'If FILES contains directories, all .page files in those\n' +
+ 'directories will be used.')
+ formats = ['mallard']
+ arguments = [
+ ('help', '-h', None, 'Show this help and exit'),
+ ('output', '-o', 'OUT', 'Output files in the directory OUT'),
+ ('path', '-p', 'PATH', 'Extra directories to search for files'),
+ ('site', '-s', None, 'Treat pages as belonging to a Mallard site')
+ ]
+
+ def build_cache_in(self, filename):
+ with open(filename, 'w') as cachein:
+ print('<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"' +
+ ' xmlns:site="http://projectmallard.org/site/1.0/"'
+ ' xmlns="http://projectmallard.org/1.0/">',
+ file=cachein)
+ for infile in self.iter_files():
+ if infile.filename.endswith('.page'):
+ page = '<page'
+ elif infile.filename.endswith('.stack'):
+ page = '<stack'
+ else:
+ continue
+ page += ' cache:href="file://' + urllib.parse.quote(os.path.realpath(infile.absfile)) + '"'
+ if self.get_option_bool('site'):
+ page += ' site:dir="' + infile.sitedir + '"'
+ page += '/>'
+ print(page, file=cachein)
+ print('</cache:cache>', file=cachein)
+
+ def main(self, args, output=None, path=None):
+ if self.parse_args(args) != 0:
+ return 1
+ if 'help' in self.options:
+ self.print_help()
+ return 0
+
+ retcode = 0
+ self.create_tmpdir()
+ cacheinfile = os.path.join(self.tmpdir, 'index.cache.in')
+ self.build_cache_in(cacheinfile)
+ if output is None:
+ output = self.get_option_str('output')
+ if output is None:
+ output = 'index.cache'
+ if path is None:
+ path = self.get_option_list('path')
+ if path is None:
+ path = ':'
+ else:
+ path = ':'.join(path)
+ retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
+ '--path', path,
+ XSL_MALCACHE, cacheinfile])
+ return retcode
+
+
+class XhtmlBuilder (Builder):
+ name = 'xhtml'
+ desc = 'Convert input files to XHTML'
+ blurb = ('Create XHTML output from the input files FILES.\n' +
+ 'FILES can be DocBook files, Mallard page files,\n' +
+ 'or directories containing Mallard page files.')
+ formats = ['docbook4', 'docbook5', 'mallard']
+ arguments = [
+ ('help', '-h', None, 'Show this help and exit'),
+ ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
+ ('output', '-o', 'OUT', 'Output files in the directory OUT'),
+ ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
+ ('path', '-p', 'PATH', 'Extra directories to search for files'),
+ ('ignore', '-i', None, 'Ignore missing media files')
+ ]
+
+ def __init__(self, yelpbuild, xhtml=True, epub=False):
+ super().__init__(yelpbuild)
+ self.mal2html = None
+ self.db2html = None
+ self.xhtml = xhtml
+ self.epub = epub
+ if self.epub:
+ self.intdatadir = 'yelp'
+ else:
+ self.intdatadir = ''
+ self.cacheinfile = None
+
+
+ def build_mallard_all(self, cache=None, output=None, xsl=None, path=None):
+ if self.mal2html is not None:
+ # We build all the pages on the first call, because it's faster
+ return 0
+ if path is None:
+ path = self.get_option_list('path')
+ self.create_tmpdir()
+ if cache is None:
+ cachefile = self.get_option_str('cache')
+ else:
+ cachefile = cache
+ cachebuilder = CacheBuilder(self.yelpbuild)
+ if cachefile is None:
+ cachefile = os.path.join(self.tmpdir, 'index.cache')
+ retcode = cachebuilder.main(self.fileargs, output=cachefile, path=path)
+ if retcode != 0:
+ return retcode
+ self.cacheinfile = cachefile
+ else:
+ cachefile = os.path.realpath(cachefile)
+ self.cacheinfile = os.path.join(self.tmpdir, 'index.cache.in')
+ cachebuilder.parse_args(self.fileargs)
+ cachebuilder.build_cache_in(self.cacheinfile)
+ self.mal2html = os.path.join(self.tmpdir, 'mal2html.xsl')
+ with open(self.mal2html, 'w') as xslout:
+ if self.xhtml:
+ xslfile = XSL_MAL2XHTML
+ else:
+ xslfile = XSL_MAL2HTML
+ includes = ''
+ if xsl is None:
+ customxsl = self.get_option_str('xsl')
+ else:
+ customxsl = xsl
+ if customxsl is not None:
+ customxsl = urllib.parse.quote(os.path.realpath(customxsl))
+ includes += '<xsl:include href="file://' + customxsl + '"/>'
+ if self.epub:
+ includes += '''<xsl:param name="mal.if.target" select="'target:epub target:html target:xhtml'"/>'''
+ includes += '''<xsl:template mode="html.header.mode" match="mal:page"/>'''
+ includes += '''<xsl:template mode="html.footer.mode" match="mal:page"/>'''
+ xslout.write(MAL2HTML.format(xslfile=xslfile,
+ cachefile=cachefile,
+ includes=includes,
+ intdatadir=self.intdatadir))
+ if output is None:
+ output = self.get_option_str('output')
+ if output is None:
+ output = os.getcwd()
+ else:
+ if not os.path.isdir(output):
+ print('Output must be a directory', file=sys.stderr)
+ return 1
+ if not output.endswith('/'):
+ # xsltproc is picky about this
+ output = output + '/'
+ if path is None:
+ pathstr = ':'
+ else:
+ pathstr = ':'.join(path)
+ retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
+ '--path', pathstr,
+ '--stringparam', 'mal.cache.file', cachefile,
+ self.mal2html, self.cacheinfile])
+ return retcode
+
+
+ def build_docbook(self, infile, output=None, xsl=None, path=None):
+ if self.db2html is None:
+ self.create_tmpdir()
+ self.db2html = os.path.join(self.tmpdir, 'db2html.xsl')
+ with open(self.db2html, 'w') as xslout:
+ if self.xhtml:
+ xslfile = XSL_DB2XHTML
+ else:
+ xslfile = XSL_DB2HTML
+ includes = ''
+ if xsl is not None:
+ customxsl = xsl
+ else:
+ customxsl = self.get_option_str('xsl')
+ if customxsl is not None:
+ customxsl = urllib.parse.quote(os.path.realpath(customxsl))
+ includes += '<xsl:include href="file://' + customxsl + '"/>'
+ xslout.write(DB2HTML.format(xslfile=xslfile,
+ includes=includes,
+ intdatadir=self.intdatadir))
+ if output is None:
+ output = self.get_option_str('output')
+ if output is None:
+ output = os.getcwd()
+ else:
+ if not os.path.isdir(output):
+ print('Output must be a directory', file=sys.stderr)
+ return 1
+ if path is None:
+ path = self.get_option_list('path')
+ if path is None:
+ pathstr = ':'
+ else:
+ pathstr = ':'.join(path)
+ retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
+ '--path', pathstr,
+ self.db2html, infile.absfile])
+ return retcode
+
+
+ def main(self, args, cache=None, output=None, xsl=None, path=None, ignore=None):
+ if self.parse_args(args) != 0:
+ return 1
+ if 'help' in self.options:
+ self.print_help()
+ return 0
+
+ if path is None:
+ pathopt = self.get_option_list('path')
+ else:
+ pathopt = path
+ path = []
+ if pathopt is not None:
+ for p in pathopt:
+ path.extend(p.split(':'))
+ if output is None:
+ output = self.get_option_str('output')
+ srcs = {}
+ for infile in self.iter_files():
+ if infile.filename.endswith('.page') or infile.filename.endswith('.stack'):
+ retcode = self.build_mallard_all(cache=cache, output=output, xsl=xsl, path=path)
+ if retcode != 0:
+ return retcode
+ if output is not None:
+ tree = self.get_xml(infile, path)
+ if tree is None:
+ return 1
+ for el in tree.xpath('//*[@src]'):
+ src = el.get('src')
+ srcs.setdefault(src, [])
+ orig = os.path.join(os.path.realpath(infile.absdir), src)
+ if orig not in srcs[src]:
+ srcs[src].append(orig)
+ elif infile.filename.endswith('.docbook') or infile.filename.endswith('.xml'):
+ retcode = self.build_docbook(infile, output=output, xsl=xsl, path=path)
+ if retcode != 0:
+ return retcode
+ if output is not None:
+ tree = self.get_xml(infile, path)
+ if tree is None:
+ return 1
+ for el in tree.xpath('//*[@fileref]'):
+ src = el.get('fileref')
+ srcs.setdefault(src, [])
+ orig = os.path.join(os.path.realpath(infile.absdir), src)
+ if orig not in srcs[src]:
+ srcs[src].append(orig)
+ else:
+ print('Error: No builder for ' + infile.filename)
+ return 1
+
+ if ignore is None:
+ ignore = self.get_option_bool('ignore')
+ tocopy = {}
+ for src in srcs:
+ useorig = None
+ for orig in srcs[src]:
+ if os.path.exists(orig):
+ if useorig is None:
+ useorig = orig
+ else:
+ print('Warning: Multiple sources for ' + src + '. Using first.',
+ file=sys.stderr)
+ if useorig is None:
+ for p in path:
+ tryorig = os.path.join(p, src)
+ if os.path.exists(tryorig):
+ useorig = tryorig
break
- fi
- done
- fi
- fi
- line_dest=`echo "$line_dest" | urldecode`
- if [ "$line_src" != "$line_dest" ]; then
- line_dir=`dirname "$line_dest"`
- mkdir_p "$line_dir"
- if [ -f "$line_src" -o "x$html_ignore_media" != "x1" ]; then
- cp "$line_src" "$line_dest"
- fi
- fi
- done
- # Copy JavaScript
- cp "${yelp_js_dir}/highlight.pack.js" "$html_out/$html_internal_datadir"
- # Clean up
- rm "$html_tmp_infile"
- if [ "x$html_cache_file_is_tmp" = "xyes" ]; then
- rm "$html_cache_file"
- fi
-}
-
-yelp_html () {
- while [ "$#" != "0" ]; do
- case "$1" in
- "-c")
- shift
- html_cache_file="$1"
- shift
- ;;
- "-o")
- shift
- html_out="$1"
- shift
- ;;
- "-x")
- shift
- html_custom="$1"
- shift
- ;;
- "-p")
- shift
- html_paths=$(yelp_paths_normalize "$1")
- shift
- ;;
- "-i")
- shift
- html_ignore_media="1"
- ;;
- "--profile")
- html_profile="--profile"
- shift
- ;;
- *)
- break
- ;;
- esac
- done
- if [ "x$html_out" = "x" ]; then
- html_out="."
- elif [ ! -d "$html_out" ]; then
- echo "Error: output must be a directory." 1>&2
- exit 1
- fi
- html_out=`(cd "$html_out" && pwd)`
- if [ "x$html_custom" != "x" ]; then
- html_custom_dir=`dirname "$html_custom"`
- html_custom_dir=`(cd "$html_custom_dir" && pwd)`
- html_custom="$html_custom_dir"/`basename "$html_custom"`
- fi
- if [ "$#" = "0" -o "x$1" = "x--help" -o "x$1" = "x-h" ]; then
- yelp_usage_html
- exit 1
- fi
- ext=`echo "$1" | sed -e 's/.*\.//'`
- if [ "x$ext" = "xxml" -o "x$ext" = "xdocbook" ]; then
- yelp_html_db2html "$@"
- else
- yelp_html_mal2html "$@"
- fi
-}
-
-yelp_epub () {
- while [ "$#" != "0" ]; do
- case "$1" in
- "-c")
- shift
- epub_cache_file="$1"
- shift
- ;;
- "-o")
- shift
- epub_out="$1"
- shift
- ;;
- "-x")
- shift
- html_custom="$1"
- shift
- ;;
- "-p")
- shift
- html_paths=$(yelp_paths_normalize "$1")
- shift
- ;;
- "-i")
- shift
- html_ignore_media="1"
- ;;
- *)
- break
- ;;
- esac
- done
- if [ "$#" = "0" -o "x$1" = "x--help" -o "x$1" = "x-h" ]; then
- yelp_usage_epub
- exit 1
- fi
- if [ "x$epub_cache_file" != "x" ]; then
- epub_cache_file=`(cd $(dirname "$epub_cache_file") && pwd)`/`basename "$epub_cache_file"`
- else
- epub_cache_file_is_tmp="yes"
- epub_cache_file=`mktemp "${TMPDIR:-/tmp}"/yelp-XXXXXXXX`
- yelp_cache -o "$epub_cache_file" "$@"
- fi
- html_cache_file="$epub_cache_file"
- epub_data_out=`mktemp -d "${TMPDIR:-/tmp}"/yelp-XXXXXXXX`
- html_out="$epub_data_out/OPS"
- mkdir "$html_out"
- mkdir "$html_out/yelp"
- html_internal_datadir="yelp/"
- html_internal_xsl="$xsl_mal_epub"
- yelp_html_mal2html "$@"
-
- epub_id=`uuidgen`
- epub_data=`(cd "$html_out" && ls yelp/*.*)`
- xsltproc \
- --path "$html_paths" \
- -o "$html_out/opf.opf" \
- --stringparam opf.id "$epub_id" \
- --stringparam opf.data "$epub_data" \
- "$xsl_mal_opf" "$epub_cache_file"
- xsltproc \
- --path "$html_paths" \
- -o "$html_out/ncx.ncx" \
- --stringparam ncx.id "$epub_id" \
- "$xsl_mal_ncx" "$epub_cache_file"
-
- echo "application/epub+zip" > "$epub_data_out/mimetype"
-
- mkdir "$epub_data_out/META-INF"
- (
- echo "<?xml version='1.0' encoding='UTF-8'?>"
- echo "<container version='1.0' xmlns='urn:oasis:names:tc:opendocument:xmlns:container'>"
- echo "<rootfiles>"
- echo "<rootfile full-path='OPS/opf.opf' media-type='application/oebps-package+xml'/>"
- echo "</rootfiles>"
- echo "</container>"
- ) > "$epub_data_out/META-INF/container.xml"
-
- if [ "x$epub_out" = "x" ]; then
- epub_out=`pwd`/index.epub
- else
- epub_out=`(cd $(dirname "$epub_out") && pwd)`/`basename "$epub_out"`
- fi
- (cd "$epub_data_out" && zip -q -r "$epub_out" mimetype META-INF OPS)
-
- if [ "x$epub_cache_file_is_tmp" = "xyes" ]; then
- rm "$epub_cache_file"
- fi
- rm -rf "$html_out"
-}
-
-cmd="$1"
-shift
-case "x$cmd" in
- "xcache")
- yelp_cache "$@"
- ;;
- "xepub")
- is_xhtml=1
- yelp_epub "$@"
- ;;
- "xhtml")
- is_xhtml=0
- yelp_html "$@"
- ;;
- "xxhtml")
- is_xhtml=1
- yelp_html "$@"
- ;;
- *)
- yelp_usage
- ;;
-esac
+ if useorig is None:
+ if ignore:
+ print('Warning: No source found for ' + src, file=sys.stderr)
+ else:
+ print('Error: No source found for ' + src, file=sys.stderr)
+ return 1
+ if useorig is not None:
+ destfile = os.path.join(output, src)
+ destdir = os.path.dirname(destfile)
+ os.makedirs(destdir, exist_ok=True)
+ shutil.copyfile(useorig, destfile)
+
+ if output is None:
+ shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'),
+ os.path.join(self.intdatadir, 'highlight.pack.js'))
+ else:
+ shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'),
+ os.path.join(output, self.intdatadir, 'highlight.pack.js'))
+
+ return 0
+
+
+class HtmlBuilder (Builder):
+ name = 'html'
+ desc = 'Convert input files to HTML'
+ blurb = ('Create HTML output from the input files FILES.\n' +
+ 'FILES can be DocBook files, Mallard page files,\n' +
+ 'or directories containing Mallard page files.')
+ formats = ['docbook4', 'docbook5', 'mallard']
+ arguments = [
+ ('help', '-h', None, 'Show this help and exit'),
+ ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
+ ('output', '-o', 'OUT', 'Output files in the directory OUT'),
+ ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
+ ('path', '-p', 'PATH', 'Extra directories to search for files'),
+ ('ignore', '-i', None, 'Ignore missing media files')
+ ]
+
+ def __init__(self, yelpbuild):
+ super().__init__(yelpbuild)
+ self.xhtmlbuilder = XhtmlBuilder(yelpbuild, xhtml=False)
+
+ def main(self, args):
+ if self.parse_args(args) != 0:
+ return 1
+ if 'help' in self.options:
+ self.print_help()
+ return 0
+
+ return self.xhtmlbuilder.main(args)
+
+
+class EpubBuilder (Builder):
+ name = 'epub'
+ desc = 'Create an EPUB file for Mallard'
+ blurb = ('Create an EPUB file from the Mallard page files FILES')
+ formats = ['mallard']
+ arguments = [
+ ('help', '-h', None, 'Show this help and exit'),
+ ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
+ ('output', '-o', 'OUT', 'Output files in the directory OUT'),
+ ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
+ ('path', '-p', 'PATH', 'Extra directories to search for files'),
+ ('ignore', '-i', None, 'Ignore missing media files'),
+ ('nozip', None, None, 'Do not zip the output directory')
+ ]
+
+ def __init__(self, yelpbuild):
+ super().__init__(yelpbuild)
+
+ def main(self, args):
+ if self.parse_args(args) != 0:
+ return 1
+ if 'help' in self.options:
+ self.print_help()
+ return 0
+
+ output = self.get_option_str('output')
+ nozip = self.get_option_bool('nozip')
+ if nozip:
+ if output is None:
+ output = 'EPUB'
+ if os.path.isfile(output):
+ print('Error: Output must be a directory', file=sys.stderr)
+ sys.exit(1)
+ epubdir = output
+ else:
+ self.create_tmpdir()
+ if output is None:
+ output = 'index.epub'
+ if os.path.isdir(output):
+ print('Error: Output must be a file', file=sys.stderr)
+ sys.exit(1)
+ epubdir = os.path.join(self.tmpdir, 'EPUB')
+ os.makedirs(epubdir, exist_ok=True)
+ os.makedirs(os.path.join(epubdir, 'OPS', 'yelp'), exist_ok=True)
+
+ xhtmlbuilder = XhtmlBuilder(self.yelpbuild, epub=True)
+ retcode = xhtmlbuilder.main(self.fileargs,
+ cache=self.get_option_str('cache'),
+ output=os.path.join(epubdir, 'OPS'),
+ xsl=self.get_option_str('xsl'),
+ path=self.get_option_list('path'),
+ ignore=self.get_option_bool('ignore'))
+ if retcode != 0:
+ return retcode
+
+ with open(os.path.join(epubdir, 'mimetype'), 'w') as fd:
+ fd.write('application/epub+zip\n')
+
+ os.makedirs(os.path.join(epubdir, 'META-INF'), exist_ok=True)
+
+ with open(os.path.join(epubdir, 'META-INF', 'container.xml'), 'w') as fd:
+ fd.write('<?xml version="1.0" encoding="UTF-8"?>')
+ fd.write('<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">')
+ fd.write('<rootfiles>')
+ fd.write('<rootfile full-path="OPS/opf.opf" media-type="application/oebps-package+xml"/>')
+ fd.write('</rootfiles>')
+ fd.write('</container>\n')
+
+ path = self.get_option_list('path')
+ if path is None:
+ pathstr = ':'
+ else:
+ pathstr = ':'.join(path)
+ epubid = str(uuid.uuid4())
+ opfdata = ''
+ for fname in os.listdir(os.path.join(epubdir, 'OPS', 'yelp')):
+ opfdata += ' OPS/yelp/' + urllib.parse.quote(fname)
+ retcode = subprocess.call(['xsltproc', '--xinclude',
+ '-o', os.path.join(epubdir, 'OPS', 'opf.opf'),
+ '--path', pathstr,
+ '--stringparam', 'opf.id', epubid,
+ '--stringparam', 'opf.data', opfdata,
+ XSL_MAL_OPF, xhtmlbuilder.cacheinfile])
+ if retcode != 0:
+ return retcode
+ retcode = subprocess.call(['xsltproc', '--xinclude',
+ '-o', os.path.join(epubdir, 'OPS', 'ncx.ncx'),
+ '--path', pathstr,
+ '--stringparam', 'ncx.id', epubid,
+ XSL_MAL_NCX, xhtmlbuilder.cacheinfile])
+ if retcode != 0:
+ return retcode
+
+ if not nozip:
+ retcode = subprocess.call(['zip', '-q', '-r', os.path.realpath(output),
+ 'mimetype', 'META-INF', 'OPS'],
+ cwd=os.path.realpath(epubdir))
+ if retcode != 0:
+ return retcode
+ return 0
+
+
+class YelpBuild:
+ def __init__(self):
+ pass
+
+ def main(self):
+ if len(sys.argv) < 2:
+ self.print_usage()
+ return 1
+
+ builder = None
+ for cls in Builder.__subclasses__():
+ if sys.argv[1] == cls.name:
+ builder = cls(self)
+
+ if builder is None:
+ print('Unrecognized command: ' + sys.argv[1], file=sys.stderr)
+ return 1
+
+ return builder.main(sys.argv[2:])
+
+ def print_usage(self):
+ print('Usage: yelp-builder <COMMAND> [OPTIONS] [FILES]')
+ namelen = 2
+ builders = []
+ for cls in sorted(Builder.__subclasses__(), key=(lambda cls: cls.name or '')):
+ namelen = max(namelen, len(cls.name) + 2)
+ builders.append(cls)
+
+ print('\nCommands:')
+ for cls in builders:
+ print(' ' + cls.name.ljust(namelen) + cls.desc)
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(YelpBuild().main())
+ except KeyboardInterrupt:
+ sys.exit(1)