summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2015-03-08 15:35:04 +0100
committerGeorg Brandl <georg@python.org>2015-03-08 15:35:04 +0100
commit705f6a997bf622cdc95f80868d0fd0a703124b1d (patch)
treea33a6cb96f3dd9e2a06e42d98f2aace20f54b23e
parentf2267942f3e4ff9b33a7457af0d00b9841ec842b (diff)
parentcc58f0a5e4804a0e5054d01bd07a76ed8ce17937 (diff)
downloadsphinx-git-705f6a997bf622cdc95f80868d0fd0a703124b1d.tar.gz
Merge branch 'apple-help' of git://github.com/al45tair/sphinx
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES2
-rw-r--r--doc/builders.rst24
-rw-r--r--doc/config.rst164
-rw-r--r--doc/invocation.rst5
-rw-r--r--sphinx/builders/__init__.py1
-rw-r--r--sphinx/builders/applehelp.py261
-rw-r--r--sphinx/builders/html.py34
-rw-r--r--sphinx/builders/htmlhelp.py3
-rw-r--r--sphinx/config.py33
-rw-r--r--sphinx/environment.py2
-rw-r--r--sphinx/quickstart.py12
-rw-r--r--tests/root/conf.py3
-rw-r--r--tests/root/en.lproj/localized.txt2
-rw-r--r--tests/test_build.py7
-rw-r--r--tests/test_build_applehelp.py56
16 files changed, 591 insertions, 19 deletions
diff --git a/AUTHORS b/AUTHORS
index eba3edc4b..e4219db2f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are:
+* Alastair Houghton -- Apple Help builder
* Andi Albrecht -- agogo theme
* Henrique Bastos -- SVG support for graphviz extension
* Daniel Bültmann -- todo extension
diff --git a/CHANGES b/CHANGES
index 28ee9debb..198a49291 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,8 @@ Features added
suffixes.
* Add the ability to specify source parsers by source suffix with the
:confval:`source_parsers` config value.
+* #1675: A new builder, AppleHelpBuilder, has been added that builds Apple
+ Help Books.
Bugs fixed
----------
diff --git a/doc/builders.rst b/doc/builders.rst
index 3c6f6b982..4fbd5bae6 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -76,6 +76,30 @@ The builder's "name" must be given to the **-b** command-line option of
.. _Qt help: http://qt-project.org/doc/qt-4.8/qthelp-framework.html
+.. module:: sphinx.builders.applehelp
+.. class:: AppleHelpBuilder
+
+ This builder produces an Apple Help Book based on the same output as the
+ standalone HTML builder.
+
+ If the source directory contains any ``.lproj`` folders, the one
+ corresponding to the selected language will have its contents merged with
+ the generated output. These folders will be ignored by all other
+ documentation types.
+
+ In order to generate a valid help book, this builder requires the command
+ line tool :program:`hiutil`, which is only available on Mac OS X 10.6 and
+ above. You can disable the indexing step by setting
+ :confval:`applehelp_disable_external_tools` to ``True``, in which case the
+ output will not be valid until :program:`hiutil` has been run on all of the
+ ``.lproj`` folders within the bundle.
+
+ .. autoattribute:: name
+
+ .. autoattribute:: supported_image_types
+
+ .. versionadded:: 1.3
+
.. module:: sphinx.builders.devhelp
.. class:: DevhelpBuilder
diff --git a/doc/config.rst b/doc/config.rst
index eea64ff01..0219a9ee0 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -929,6 +929,170 @@ that use Sphinx's HTMLWriter class.
Output file base name for HTML help builder. Default is ``'pydoc'``.
+.. _applehelp-options:
+
+Options for Apple Help output
+-----------------------------
+
+.. versionadded:: 1.3
+
+These options influence the Apple Help output. This builder derives from the
+HTML builder, so the HTML options also apply where appropriate.
+
+.. note::
+
+ Apple Help output will only work on Mac OS X 10.6 and higher, as it
+ requires the :program:`hiutil` and :program:`codesign` command line tools,
+ neither of which are Open Source.
+
+ You can disable the use of these tools using
+ :confval:`applehelp_disable_external_tools`, but the result will not be a
+ valid help book until the indexer is run over the ``.lproj`` folders within
+ the bundle.
+
+.. confval:: applehelp_bundle_name
+
+ The basename for the Apple Help Book. Defaults to the :confval:`project`
+ name.
+
+.. confval:: applehelp_bundle_id
+
+ The bundle ID for the help book bundle.
+
+ .. warning::
+
+ You *must* set this value in order to generate Apple Help.
+
+.. confval:: applehelp_dev_region
+
+ The development region. Defaults to ``'en-us'``, which is Apple’s
+ recommended setting.
+
+.. confval:: applehelp_bundle_version
+
+ The bundle version (as a string). Defaults to ``'1'``.
+
+.. confval:: applehelp_icon
+
+ The help bundle icon file, or ``None`` for no icon. According to Apple’s
+ documentation, this should be a 16-by-16 pixel version of the application’s
+ icon with a transparent background, saved as a PNG file.
+
+.. confval:: applehelp_kb_product
+
+ The product tag for use with :confval:`applehelp_kb_url`. Defaults to
+ :samp:`'{<project>}-{<release>}'`.
+
+.. confval:: applehelp_kb_url
+
+ The URL for your knowledgebase server,
+ e.g. ``https://example.com/kbsearch.py?p='product'&q='query'&l='lang'``.
+ Help Viewer will replace the values ``'product'``, ``'query'`` and
+ ``'lang'`` at runtime with the contents of :confval:`applehelp_kb_product`,
+ the text entered by the user in the search box and the user’s system
+ language respectively.
+
+ Defaults to ``None`` for no remote search.
+
+.. confval:: applehelp_remote_url
+
+ The URL for remote content. You can place a copy of your Help Book’s
+ ``Resources`` folder at this location and Help Viewer will attempt to use
+ it to fetch updated content.
+
+ e.g. if you set it to ``https://example.com/help/Foo/`` and Help Viewer
+ wants a copy of ``index.html`` for an English speaking customer, it will
+ look at ``https://example.com/help/Foo/en.lproj/index.html``.
+
+ Defaults to ``None`` for no remote content.
+
+.. confval:: applehelp_index_anchors
+
+ If ``True``, tell the help indexer to index anchors in the generated HTML.
+ This can be useful for jumping to a particular topic using the
+ ``AHLookupAnchor`` function or the ``openHelpAnchor:inBook:`` method in
+ your code. It also allows you to use ``help:anchor`` URLs; see the Apple
+ documentation for more information on this topic.
+
+.. confval:: applehelp_min_term_length
+
+ Controls the minimum term length for the help indexer. Defaults to
+ ``None``, which means the default will be used.
+
+.. confval:: applehelp_stopwords
+
+ Either a language specification (to use the built-in stopwords), or the
+ path to a stopwords plist, or ``None`` if you do not want to use stopwords.
+ The default stopwords plist can be found at
+ ``/usr/share/hiutil/Stopwords.plist`` and contains, at time of writing,
+ stopwords for the following languages:
+
+ ========= ====
+ Language Code
+ ========= ====
+ English en
+ German de
+ Spanish es
+ French fr
+ Swedish sv
+ Hungarian hu
+ Italian it
+ ========= ====
+
+ Defaults to :confval:`language`, or if that is not set, to :confval:`en`.
+
+.. confval:: applehelp_locale
+
+ Specifies the locale to generate help for. This is used to determine
+ the name of the ``.lproj`` folder inside the Help Book’s ``Resources``, and
+ is passed to the help indexer.
+
+ Defaults to :confval:`language`, or if that is not set, to :confval:`en`.
+
+.. confval:: applehelp_title
+
+ Specifies the help book title. Defaults to :samp:`'{<project>} Help'`.
+
+.. confval:: applehelp_codesign_identity
+
+ Specifies the identity to use for code signing, or ``None`` if code signing
+ is not to be performed.
+
+ Defaults to the value of the environment variable ``CODE_SIGN_IDENTITY``,
+ which is set by Xcode for script build phases, or ``None`` if that variable
+ is not set.
+
+.. confval:: applehelp_codesign_flags
+
+ A *list* of additional arguments to pass to :program:`codesign` when
+ signing the help book.
+
+ Defaults to a list based on the value of the environment variable
+ ``OTHER_CODE_SIGN_FLAGS``, which is set by Xcode for script build phases,
+ or the empty list if that variable is not set.
+
+.. confval:: applehelp_indexer_path
+
+ The path to the :program:`hiutil` program. Defaults to
+ ``'/usr/bin/hiutil'``.
+
+.. confval:: applehelp_codesign_path
+
+ The path to the :program:`codesign` program. Defaults to
+ ``'/usr/bin/codesign'``.
+
+.. confval:: applehelp_disable_external_tools
+
+ If ``True``, the builder will not run the indexer or the code signing tool,
+ no matter what other settings are specified.
+
+ This is mainly useful for testing, or where you want to run the Sphinx
+ build on a non-Mac OS X platform and then complete the final steps on OS X
+ for some reason.
+
+ Defaults to ``False``.
+
+
.. _epub-options:
Options for epub output
diff --git a/doc/invocation.rst b/doc/invocation.rst
index e08fbb1bc..1d0c613c3 100644
--- a/doc/invocation.rst
+++ b/doc/invocation.rst
@@ -172,6 +172,11 @@ The :program:`sphinx-build` script has several options:
Build HTML files with additional information for building a documentation
collection in one of these formats.
+ **applehelp**
+ Build an Apple Help Book. Requires :program:`hiutil` and
+ :program:`codesign`, which are not Open Source and presently only
+ available on Mac OS X 10.6 and higher.
+
**latex**
Build LaTeX sources that can be compiled to a PDF document using
:program:`pdflatex`.
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 72c27c307..2a28fd1c4 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -449,6 +449,7 @@ BUILTIN_BUILDERS = {
'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'),
'devhelp': ('devhelp', 'DevhelpBuilder'),
'qthelp': ('qthelp', 'QtHelpBuilder'),
+ 'applehelp': ('applehelp', 'AppleHelpBuilder'),
'epub': ('epub', 'EpubBuilder'),
'latex': ('latex', 'LaTeXBuilder'),
'text': ('text', 'TextBuilder'),
diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py
new file mode 100644
index 000000000..5f2f061f5
--- /dev/null
+++ b/sphinx/builders/applehelp.py
@@ -0,0 +1,261 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.applehelp
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Build Apple help books.
+
+ :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+from __future__ import print_function
+
+import os
+import codecs
+import errno
+import pipes
+
+from os import path
+
+from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.util import copy_static_entry
+from sphinx.util.osutil import copyfile, ensuredir, os_path
+from sphinx.util.console import bold
+from sphinx.util.pycompat import htmlescape
+from sphinx.util.matching import compile_matchers
+from sphinx.errors import SphinxError
+
+import plistlib
+import subprocess
+
+
+# Use plistlib.dump in 3.4 and above
+try:
+ write_plist = plistlib.dump
+except AttributeError:
+ write_plist = plistlib.writePlist
+
+
+# False access page (used because helpd expects strict XHTML)
+access_page_template = '''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>%(title)s</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex" />
+ <meta http-equiv="refresh" content="0;url=%(toc)s" />
+ </head>
+ <body>
+ </body>
+</html>
+'''
+
+
+class AppleHelpIndexerFailed(SphinxError):
+ category = 'Help indexer failed'
+
+
+class AppleHelpCodeSigningFailed(SphinxError):
+ category = 'Code signing failed'
+
+
+class AppleHelpBuilder(StandaloneHTMLBuilder):
+ """
+ Builder that outputs an Apple help book. Requires Mac OS X as it relies
+ on the ``hiutil`` command line tool.
+ """
+ name = 'applehelp'
+
+ # don't copy the reST source
+ copysource = False
+ supported_image_types = ['image/png', 'image/gif', 'image/jpeg',
+ 'image/tiff', 'image/jp2', 'image/svg+xml']
+
+ # don't add links
+ add_permalinks = False
+
+ # this is an embedded HTML format
+ embedded = True
+
+ # don't generate the search index or include the search page
+ search = False
+
+ def init(self):
+ super(AppleHelpBuilder, self).init()
+ # the output files for HTML help must be .html only
+ self.out_suffix = '.html'
+
+ if self.config.applehelp_bundle_id is None:
+ raise SphinxError('You must set applehelp_bundle_id before ' \
+ 'building Apple Help output')
+
+ self.bundle_path = path.join(self.outdir,
+ self.config.applehelp_bundle_name \
+ + '.help')
+ self.outdir = path.join(self.bundle_path,
+ 'Contents',
+ 'Resources',
+ self.config.applehelp_locale + '.lproj')
+
+ def handle_finish(self):
+ super(AppleHelpBuilder, self).handle_finish()
+
+ self.finish_tasks.add_task(self.copy_localized_files)
+ self.finish_tasks.add_task(self.build_helpbook)
+
+ def copy_localized_files(self):
+ source_dir = path.join(self.confdir,
+ self.config.applehelp_locale + '.lproj')
+ target_dir = self.outdir
+
+ if path.isdir(source_dir):
+ self.info(bold('copying localized files... '), nonl=True)
+
+ ctx = self.globalcontext.copy()
+ matchers = compile_matchers(self.config.exclude_patterns)
+ copy_static_entry(source_dir, target_dir, self, ctx,
+ exclude_matchers=matchers)
+
+ self.info('done')
+
+ def build_helpbook(self):
+ contents_dir = path.join(self.bundle_path, 'Contents')
+ resources_dir = path.join(contents_dir, 'Resources')
+ language_dir = path.join(resources_dir,
+ self.config.applehelp_locale + '.lproj')
+
+ for d in [contents_dir, resources_dir, language_dir]:
+ ensuredir(d)
+
+ # Construct the Info.plist file
+ toc = self.config.master_doc + self.out_suffix
+
+ info_plist = {
+ 'CFBundleDevelopmentRegion': self.config.applehelp_dev_region,
+ 'CFBundleIdentifier': self.config.applehelp_bundle_id,
+ 'CFBundleInfoDictionaryVersion': '6.0',
+ 'CFBundlePackageType': 'BNDL',
+ 'CFBundleShortVersionString': self.config.release,
+ 'CFBundleSignature': 'hbwr',
+ 'CFBundleVersion': self.config.applehelp_bundle_version,
+ 'HPDBookAccessPath': '_access.html',
+ 'HPDBookIndexPath': 'search.helpindex',
+ 'HPDBookTitle': self.config.applehelp_title,
+ 'HPDBookType': '3',
+ 'HPDBookUsesExternalViewer': False,
+ }
+
+ if self.config.applehelp_icon is not None:
+ info_plist['HPDBookIconPath'] \
+ = path.basename(self.config.applehelp_icon)
+
+ if self.config.applehelp_kb_url is not None:
+ info_plist['HPDBookKBProduct'] = self.config.applehelp_kb_product
+ info_plist['HPDBookKBURL'] = self.config.applehelp_kb_url
+
+ if self.config.applehelp_remote_url is not None:
+ info_plist['HPDBookRemoteURL'] = self.config.applehelp_remote_url
+
+ self.info(bold('writing Info.plist... '), nonl=True)
+ with open(path.join(contents_dir, 'Info.plist'), 'wb') as f:
+ write_plist(info_plist, f)
+ self.info('done')
+
+ # Copy the icon, if one is supplied
+ if self.config.applehelp_icon:
+ self.info(bold('copying icon... '), nonl=True)
+
+ try:
+ copyfile(path.join(self.srcdir, self.config.applehelp_icon),
+ path.join(resources_dir, info_plist['HPDBookIconPath']))
+
+ self.info('done')
+ except Exception as err:
+ self.warn('cannot copy icon file %r: %s' %
+ (path.join(self.srcdir, self.config.applehelp_icon),
+ err))
+ del info_plist['HPDBookIconPath']
+
+ # Build the access page
+ self.info(bold('building access page...'), nonl=True)
+ f = codecs.open(path.join(language_dir, '_access.html'), 'w')
+ try:
+ f.write(access_page_template % {
+ 'toc': htmlescape(toc, quote=True),
+ 'title': htmlescape(self.config.applehelp_title)
+ })
+ finally:
+ f.close()
+ self.info('done')
+
+ # Generate the help index
+ self.info(bold('generating help index... '), nonl=True)
+
+ args = [
+ self.config.applehelp_indexer_path,
+ '-Cf',
+ path.join(language_dir, 'search.helpindex'),
+ language_dir
+ ]
+
+ if self.config.applehelp_index_anchors is not None:
+ args.append('-a')
+
+ if self.config.applehelp_min_term_length is not None:
+ args += ['-m', '%s' % self.config.applehelp_min_term_length]
+
+ if self.config.applehelp_stopwords is not None:
+ args += ['-s', self.config.applehelp_stopwords]
+
+ if self.config.applehelp_locale is not None:
+ args += ['-l', self.config.applehelp_locale]
+
+ if self.config.applehelp_disable_external_tools:
+ self.info('skipping')
+
+ self.warn('you will need to index this help book with:\n %s'
+ % (' '.join([pipes.quote(arg) for arg in args])))
+ else:
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+
+ output = p.communicate()[0]
+
+ if p.returncode != 0:
+ raise AppleHelpIndexerFailed(output)
+ else:
+ self.info('done')
+
+ # If we've been asked to, sign the bundle
+ if self.config.applehelp_codesign_identity:
+ self.info(bold('signing help book... '), nonl=True)
+
+ args = [
+ self.config.applehelp_codesign_path,
+ '-s', self.config.applehelp_codesign_identity,
+ '-f'
+ ]
+
+ args += self.config.applehelp_codesign_flags
+
+ args.append(self.bundle_path)
+
+ if self.config.applehelp_disable_external_tools:
+ self.info('skipping')
+
+ self.warn('you will need to sign this help book with:\n %s'
+ % (' '.join([pipes.quote(arg) for arg in args])))
+ else:
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+
+ output = p.communicate()[0]
+
+ if p.returncode != 0:
+ raise AppleHelpCodeSigningFailed(output)
+ else:
+ self.info('done')
+
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index d53eded6d..2880f58b2 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -78,7 +78,8 @@ class StandaloneHTMLBuilder(Builder):
searchindex_filename = 'searchindex.js'
add_permalinks = True
embedded = False # for things like HTML help or Qt help: suppresses sidebar
-
+ search = True # for things like HTML help and Apple help: suppress search
+
# This is a class attribute because it is mutated by Sphinx.add_javascript.
script_files = ['_static/jquery.js', '_static/underscore.js',
'_static/doctools.js']
@@ -247,14 +248,16 @@ class StandaloneHTMLBuilder(Builder):
def prepare_writing(self, docnames):
# create the search indexer
- from sphinx.search import IndexBuilder, languages
- lang = self.config.html_search_language or self.config.language
- if not lang or lang not in languages:
- lang = 'en'
- self.indexer = IndexBuilder(self.env, lang,
- self.config.html_search_options,
- self.config.html_search_scorer)
- self.load_indexer(docnames)
+ self.indexer = None
+ if self.search:
+ from sphinx.search import IndexBuilder, languages
+ lang = self.config.html_search_language or self.config.language
+ if not lang or lang not in languages:
+ lang = 'en'
+ self.indexer = IndexBuilder(self.env, lang,
+ self.config.html_search_options,
+ self.config.html_search_scorer)
+ self.load_indexer(docnames)
self.docwriter = HTMLWriter(self)
self.docsettings = OptionParser(
@@ -485,12 +488,12 @@ class StandaloneHTMLBuilder(Builder):
self.handle_page(pagename, {}, template)
# the search page
- if self.name != 'htmlhelp':
+ if self.search:
self.info(' search', nonl=1)
self.handle_page('search', {}, 'search.html')
# the opensearch xml file
- if self.config.html_use_opensearch and self.name != 'htmlhelp':
+ if self.config.html_use_opensearch and self.search:
self.info(' opensearch', nonl=1)
fn = path.join(self.outdir, '_static', 'opensearch.xml')
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
@@ -580,9 +583,11 @@ class StandaloneHTMLBuilder(Builder):
copyfile(jsfile, path.join(self.outdir, '_static',
'translations.js'))
- # add context items for search function used in searchtools.js_t
ctx = self.globalcontext.copy()
- ctx.update(self.indexer.context_for_searchtool())
+
+ # add context items for search function used in searchtools.js_t
+ if self.indexer is not None:
+ ctx.update(self.indexer.context_for_searchtool())
# then, copy over theme-supplied static files
if self.theme:
@@ -804,7 +809,8 @@ class StandaloneHTMLBuilder(Builder):
copyfile(self.env.doc2path(pagename), source_name)
def handle_finish(self):
- self.finish_tasks.add_task(self.dump_search_index)
+ if self.indexer:
+ self.finish_tasks.add_task(self.dump_search_index)
self.finish_tasks.add_task(self.dump_inventory)
def dump_inventory(self):
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index c93f8c6c8..8085ee145 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -173,6 +173,9 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
# don't add sidebar etc.
embedded = True
+ # don't generate search index or include search page
+ search = False
+
lcid = 0x409
encoding = 'cp1252'
diff --git a/sphinx/config.py b/sphinx/config.py
index 99376247c..de77401f9 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -10,9 +10,11 @@
"""
import re
-from os import path
+from os import path, environ
+import shlex
from six import PY3, iteritems, string_types, binary_type, integer_types
+from six.moves.urllib.parse import quote as urlquote
from sphinx.errors import ConfigError
from sphinx.locale import l_
@@ -131,6 +133,35 @@ class Config(object):
# Devhelp only options
devhelp_basename = (lambda self: make_filename(self.project), None),
+ # Apple help options
+ applehelp_bundle_name = (lambda self: make_filename(self.project),
+ 'applehelp'),
+ applehelp_bundle_id = (None, 'applehelp'),
+ applehelp_dev_region = ('en-us', 'applehelp'),
+ applehelp_bundle_version = ('1', 'applehelp'),
+ applehelp_icon = (None, 'applehelp'),
+ applehelp_kb_product = (lambda self: '%s-%s' \
+ % (make_filename(self.project), self.release),
+ 'applehelp'),
+ applehelp_kb_url = (None, 'applehelp'),
+ applehelp_remote_url = (None, 'applehelp'),
+ applehelp_index_anchors = (False, 'applehelp'),
+ applehelp_min_term_length = (None, 'applehelp'),
+ applehelp_stopwords = (lambda self: self.language or 'en', 'applehelp'),
+ applehelp_locale = (lambda self: self.language or 'en', 'applehelp'),
+ applehelp_title = (lambda self: self.project + ' Help', 'applehelp'),
+ applehelp_codesign_identity = (lambda self:
+ environ.get('CODE_SIGN_IDENTITY', None),
+ 'applehelp'),
+ applehelp_codesign_flags = (lambda self:
+ shlex.split(
+ environ.get('OTHER_CODE_SIGN_FLAGS',
+ '')),
+ 'applehelp'),
+ applehelp_indexer_path = ('/usr/bin/hiutil', 'applehelp'),
+ applehelp_codesign_path = ('/usr/bin/codesign', 'applehelp'),
+ applehelp_disable_external_tools = (False, None),
+
# Epub options
epub_basename = (lambda self: make_filename(self.project), None),
epub_theme = ('epub', 'html'),
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 9ea57410b..661513041 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -460,7 +460,7 @@ class BuildEnvironment:
config.exclude_patterns[:] +
config.templates_path +
config.html_extra_path +
- ['**/_sources', '.#*']
+ ['**/_sources', '.#*', '*.lproj/**']
)
self.found_docs = set(get_matching_docs(
self.srcdir, config.source_suffix, exclude_matchers=matchers))
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index ed714d939..8f4e0ed52 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -32,6 +32,7 @@ except ImportError:
from six import PY2, PY3, text_type
from six.moves import input
+from six.moves.urllib.parse import quote as urlquote
from docutils.utils import column_width
from sphinx import __version__
@@ -86,6 +87,7 @@ QUICKSTART_CONF += u'''\
import sys
import os
+import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -496,6 +498,7 @@ help:
\t@echo " json to make JSON files"
\t@echo " htmlhelp to make HTML files and a HTML help project"
\t@echo " qthelp to make HTML files and a qthelp project"
+\t@echo " applehelp to make an Apple Help Book"
\t@echo " devhelp to make HTML files and a Devhelp project"
\t@echo " epub to make an epub"
\t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@@ -557,6 +560,14 @@ qthelp:
\t@echo "To view the help file:"
\t@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/%(project_fn)s.qhc"
+applehelp:
+\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+\t@echo
+\t@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+\t@echo "N.B. You won't be able to view it unless you put it in" \\
+\t "~/Library/Documentation/Help or install it in your application" \\
+\t "bundle."
+
devhelp:
\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
\t@echo
@@ -1278,6 +1289,7 @@ def generate(d, overwrite=True, silent=False):
d['mastertocmaxdepth'] = 2
d['project_fn'] = make_filename(d['project'])
+ d['project_url'] = urlquote(d['project'].encode('idna'))
d['project_manpage'] = d['project_fn'].lower()
d['now'] = time.asctime()
d['project_underline'] = column_width(d['project']) * '='
diff --git a/tests/root/conf.py b/tests/root/conf.py
index e8dd36138..bdf2f8c8d 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -43,6 +43,9 @@ html_context = {'hckey': 'hcval', 'hckey_co': 'wrong_hcval_co'}
htmlhelp_basename = 'SphinxTestsdoc'
+applehelp_bundle_id = 'org.sphinx-doc.Sphinx.help'
+applehelp_disable_external_tools = True
+
latex_documents = [
('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation',
'Georg Brandl \\and someone else', 'manual'),
diff --git a/tests/root/en.lproj/localized.txt b/tests/root/en.lproj/localized.txt
new file mode 100644
index 000000000..20e1b2b05
--- /dev/null
+++ b/tests/root/en.lproj/localized.txt
@@ -0,0 +1,2 @@
+This file should be included in the final bundle by the applehelp builder.
+It should be ignored by other builders.
diff --git a/tests/test_build.py b/tests/test_build.py
index 5aec0d3d8..ed39f6971 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -66,9 +66,10 @@ def test_build_all():
).encode('utf-8'))
# note: no 'html' - if it's ok with dirhtml it's ok with html
- for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo',
- 'pickle', 'json', 'text', 'htmlhelp', 'qthelp', 'epub',
- 'changes', 'xml', 'pseudoxml', 'man', 'linkcheck']:
+ for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle',
+ 'json', 'text', 'htmlhelp', 'qthelp', 'epub',
+ 'applehelp', 'changes', 'xml', 'pseudoxml', 'man',
+ 'linkcheck']:
yield verify_build, buildername, srcdir
diff --git a/tests/test_build_applehelp.py b/tests/test_build_applehelp.py
new file mode 100644
index 000000000..8d778f78f
--- /dev/null
+++ b/tests/test_build_applehelp.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+"""
+ test_build_applehelp
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Test the Apple Help builder and check its output. We don't need to
+ test the HTML itself; that's already handled by
+ :file:`test_build_html.py`.
+
+ :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import plistlib
+
+from util import with_app
+from path import path
+
+# Use plistlib.load in 3.4 and above
+try:
+ read_plist = plistlib.load
+except AttributeError:
+ read_plist = plistlib.readPlist
+
+
+def check_structure(outdir):
+ contentsdir = outdir / 'Contents'
+ assert contentsdir.isdir()
+ assert (contentsdir / 'Info.plist').isfile()
+
+ with open(contentsdir / 'Info.plist', 'rb') as f:
+ plist = read_plist(f)
+ assert plist
+ assert len(plist)
+ assert plist.get('CFBundleIdentifier', None) == 'org.sphinx-doc.Sphinx.help'
+
+ assert (contentsdir / 'Resources').isdir()
+ assert (contentsdir / 'Resources' / 'en.lproj').isdir()
+
+
+def check_localization(outdir):
+ lprojdir = outdir / 'Contents' / 'Resources' / 'en.lproj'
+ assert (lprojdir / 'localized.txt').isfile()
+
+
+@with_app(buildername='applehelp')
+def test_applehelp_output(app, status, warning):
+ app.builder.build_all()
+
+ # Have to use bundle_path, not outdir, because we alter the latter
+ # to point to the lproj directory so that the HTML arrives in the
+ # correct location.
+ bundle_path = path(app.builder.bundle_path)
+ check_structure(bundle_path)
+ check_localization(bundle_path)