All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Cc: "Daniel P. Berrangé" <berrange@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Michael Roth" <mdroth@linux.vnet.ibm.com>,
	"Stefan Hajnoczi" <stefanha@redhat.com>,
	"John Snow" <jsnow@redhat.com>
Subject: [PATCH 28/29] scripts/qapi: Remove texinfo generation support
Date: Thu,  6 Feb 2020 17:30:39 +0000	[thread overview]
Message-ID: <20200206173040.17337-29-peter.maydell@linaro.org> (raw)
In-Reply-To: <20200206173040.17337-1-peter.maydell@linaro.org>

We no longer use the generated texinfo format documentation,
so delete the code that generates it, and the test case for
the generation.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile               |   1 -
 tests/Makefile.include |  15 +-
 scripts/qapi-gen.py    |   2 -
 scripts/qapi/doc.py    | 303 -----------------------------------------
 scripts/qapi/gen.py    |   7 -
 5 files changed, 1 insertion(+), 327 deletions(-)
 delete mode 100644 scripts/qapi/doc.py

diff --git a/Makefile b/Makefile
index 159ac78dd24..8213dfec013 100644
--- a/Makefile
+++ b/Makefile
@@ -595,7 +595,6 @@ qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
 qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
 $(SRC_PATH)/scripts/qapi/commands.py \
 $(SRC_PATH)/scripts/qapi/common.py \
-$(SRC_PATH)/scripts/qapi/doc.py \
 $(SRC_PATH)/scripts/qapi/error.py \
 $(SRC_PATH)/scripts/qapi/events.py \
 $(SRC_PATH)/scripts/qapi/expr.py \
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 2f1cafed720..ee766a77091 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -32,7 +32,6 @@ export SRC_PATH
 qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
 $(SRC_PATH)/scripts/qapi/commands.py \
 $(SRC_PATH)/scripts/qapi/common.py \
-$(SRC_PATH)/scripts/qapi/doc.py \
 $(SRC_PATH)/scripts/qapi/error.py \
 $(SRC_PATH)/scripts/qapi/events.py \
 $(SRC_PATH)/scripts/qapi/expr.py \
@@ -486,16 +485,8 @@ tests/test-qapi-gen-timestamp: \
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
 		-o tests -p "test-" $<, \
 		"GEN","$(@:%-timestamp=%)")
-	@rm -f tests/test-qapi-doc.texi
 	@>$@
 
-tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-		-o tests/qapi-schema -p "doc-good-" $<, \
-		"GEN","$@")
-	@mv tests/qapi-schema/doc-good-qapi-doc.texi $@
-	@rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch]
-
 tests/qtest/dbus-vmstate1.h tests/qtest/dbus-vmstate1.c: tests/qtest/dbus-vmstate1-gen-timestamp ;
 tests/qtest/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/qtest/dbus-vmstate1.xml
 	$(call quiet-command,$(GDBUS_CODEGEN) $< \
@@ -847,10 +838,6 @@ check-tests/qapi-schema/frontend: $(addprefix $(SRC_PATH)/, $(check-qapi-schema-
 	  PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py $^, \
 	  TEST, check-qapi-schema)
 
-.PHONY: check-tests/qapi-schema/doc-good.texi
-check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
-	@diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
-
 .PHONY: check-decodetree
 check-decodetree:
 	$(call quiet-command, \
@@ -898,7 +885,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR)
 # Consolidated targets
 
 .PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean
-check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi
+check-qapi-schema: check-tests/qapi-schema/frontend
 check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
 ifeq ($(CONFIG_TOOLS),y)
 check-block: $(patsubst %,check-%, $(check-block-y))
diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py
index f93f3c7c233..2d39714fa36 100755
--- a/scripts/qapi-gen.py
+++ b/scripts/qapi-gen.py
@@ -11,7 +11,6 @@ import re
 import sys
 
 from qapi.commands import gen_commands
-from qapi.doc import gen_doc
 from qapi.events import gen_events
 from qapi.introspect import gen_introspect
 from qapi.schema import QAPIError, QAPISchema
@@ -52,7 +51,6 @@ def main(argv):
     gen_commands(schema, args.output_dir, args.prefix)
     gen_events(schema, args.output_dir, args.prefix)
     gen_introspect(schema, args.output_dir, args.prefix, args.unmask)
-    gen_doc(schema, args.output_dir, args.prefix)
 
 
 if __name__ == '__main__':
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
deleted file mode 100644
index 96346c9b14f..00000000000
--- a/scripts/qapi/doc.py
+++ /dev/null
@@ -1,303 +0,0 @@
-# QAPI texi generator
-#
-# This work is licensed under the terms of the GNU LGPL, version 2+.
-# See the COPYING file in the top-level directory.
-"""This script produces the documentation of a qapi schema in texinfo format"""
-
-from __future__ import print_function
-import re
-from qapi.gen import QAPIGenDoc, QAPISchemaVisitor
-
-
-MSG_FMT = """
-@deftypefn {type} {{}} {name}
-
-{body}{members}{features}{sections}
-@end deftypefn
-
-""".format
-
-TYPE_FMT = """
-@deftp {{{type}}} {name}
-
-{body}{members}{features}{sections}
-@end deftp
-
-""".format
-
-EXAMPLE_FMT = """@example
-{code}
-@end example
-""".format
-
-
-def subst_strong(doc):
-    """Replaces *foo* by @strong{foo}"""
-    return re.sub(r'\*([^*\n]+)\*', r'@strong{\1}', doc)
-
-
-def subst_emph(doc):
-    """Replaces _foo_ by @emph{foo}"""
-    return re.sub(r'\b_([^_\n]+)_\b', r'@emph{\1}', doc)
-
-
-def subst_vars(doc):
-    """Replaces @var by @code{var}"""
-    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
-
-
-def subst_braces(doc):
-    """Replaces {} with @{ @}"""
-    return doc.replace('{', '@{').replace('}', '@}')
-
-
-def texi_example(doc):
-    """Format @example"""
-    # TODO: Neglects to escape @ characters.
-    # We should probably escape them in subst_braces(), and rename the
-    # function to subst_special() or subs_texi_special().  If we do that, we
-    # need to delay it until after subst_vars() in texi_format().
-    doc = subst_braces(doc).strip('\n')
-    return EXAMPLE_FMT(code=doc)
-
-
-def texi_format(doc):
-    """
-    Format documentation
-
-    Lines starting with:
-    - |: generates an @example
-    - =: generates @section
-    - ==: generates @subsection
-    - 1. or 1): generates an @enumerate @item
-    - */-: generates an @itemize list
-    """
-    ret = ''
-    doc = subst_braces(doc)
-    doc = subst_vars(doc)
-    doc = subst_emph(doc)
-    doc = subst_strong(doc)
-    inlist = ''
-    lastempty = False
-    for line in doc.split('\n'):
-        line = line.strip()
-        empty = line == ''
-
-        # FIXME: Doing this in a single if / elif chain is
-        # problematic.  For instance, a line without markup terminates
-        # a list if it follows a blank line (reaches the final elif),
-        # but a line with some *other* markup, such as a = title
-        # doesn't.
-        #
-        # Make sure to update section "Documentation markup" in
-        # docs/devel/qapi-code-gen.txt when fixing this.
-        if line.startswith('| '):
-            line = EXAMPLE_FMT(code=line[2:])
-        elif line.startswith('= '):
-            line = '@section ' + line[2:]
-        elif line.startswith('== '):
-            line = '@subsection ' + line[3:]
-        elif re.match(r'^([0-9]*\.) ', line):
-            if not inlist:
-                ret += '@enumerate\n'
-                inlist = 'enumerate'
-            ret += '@item\n'
-            line = line[line.find(' ')+1:]
-        elif re.match(r'^[*-] ', line):
-            if not inlist:
-                ret += '@itemize %s\n' % {'*': '@bullet',
-                                          '-': '@minus'}[line[0]]
-                inlist = 'itemize'
-            ret += '@item\n'
-            line = line[2:]
-        elif lastempty and inlist:
-            ret += '@end %s\n\n' % inlist
-            inlist = ''
-
-        lastempty = empty
-        ret += line + '\n'
-
-    if inlist:
-        ret += '@end %s\n\n' % inlist
-    return ret
-
-
-def texi_body(doc):
-    """Format the main documentation body"""
-    return texi_format(doc.body.text)
-
-
-def texi_if(ifcond, prefix='\n', suffix='\n'):
-    """Format the #if condition"""
-    if not ifcond:
-        return ''
-    return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
-
-
-def texi_enum_value(value, desc, suffix):
-    """Format a table of members item for an enumeration value"""
-    return '@item @code{%s}\n%s%s' % (
-        value.name, desc, texi_if(value.ifcond, prefix='@*'))
-
-
-def texi_member(member, desc, suffix):
-    """Format a table of members item for an object type member"""
-    typ = member.type.doc_type()
-    membertype = ': ' + typ if typ else ''
-    return '@item @code{%s%s}%s%s\n%s%s' % (
-        member.name, membertype,
-        ' (optional)' if member.optional else '',
-        suffix, desc, texi_if(member.ifcond, prefix='@*'))
-
-
-def texi_members(doc, what, base=None, variants=None,
-                 member_func=texi_member):
-    """Format the table of members"""
-    items = ''
-    for section in doc.args.values():
-        # TODO Drop fallbacks when undocumented members are outlawed
-        if section.text:
-            desc = texi_format(section.text)
-        elif (variants and variants.tag_member == section.member
-              and not section.member.type.doc_type()):
-            values = section.member.type.member_names()
-            members_text = ', '.join(['@t{"%s"}' % v for v in values])
-            desc = 'One of ' + members_text + '\n'
-        else:
-            desc = 'Not documented\n'
-        items += member_func(section.member, desc, suffix='')
-    if base:
-        items += '@item The members of @code{%s}\n' % base.doc_type()
-    if variants:
-        for v in variants.variants:
-            when = ' when @code{%s} is @t{"%s"}%s' % (
-                variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")"))
-            if v.type.is_implicit():
-                assert not v.type.base and not v.type.variants
-                for m in v.type.local_members:
-                    items += member_func(m, desc='', suffix=when)
-            else:
-                items += '@item The members of @code{%s}%s\n' % (
-                    v.type.doc_type(), when)
-    if not items:
-        return ''
-    return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
-
-
-def texi_arguments(doc, boxed_arg_type):
-    if boxed_arg_type:
-        assert not doc.args
-        return ('\n@b{Arguments:} the members of @code{%s}\n'
-                % boxed_arg_type.name)
-    return texi_members(doc, 'Arguments')
-
-
-def texi_features(doc):
-    """Format the table of features"""
-    items = ''
-    for section in doc.features.values():
-        desc = texi_format(section.text)
-        items += '@item @code{%s}\n%s' % (section.name, desc)
-    if not items:
-        return ''
-    return '\n@b{Features:}\n@table @asis\n%s@end table\n' % (items)
-
-
-def texi_sections(doc, ifcond):
-    """Format additional sections following arguments"""
-    body = ''
-    for section in doc.sections:
-        if section.name:
-            # prefer @b over @strong, so txt doesn't translate it to *Foo:*
-            body += '\n@b{%s:}\n' % section.name
-        if section.name and section.name.startswith('Example'):
-            body += texi_example(section.text)
-        else:
-            body += texi_format(section.text)
-    body += texi_if(ifcond, suffix='')
-    return body
-
-
-def texi_type(typ, doc, ifcond, members):
-    return TYPE_FMT(type=typ,
-                    name=doc.symbol,
-                    body=texi_body(doc),
-                    members=members,
-                    features=texi_features(doc),
-                    sections=texi_sections(doc, ifcond))
-
-
-def texi_msg(typ, doc, ifcond, members):
-    return MSG_FMT(type=typ,
-                   name=doc.symbol,
-                   body=texi_body(doc),
-                   members=members,
-                   features=texi_features(doc),
-                   sections=texi_sections(doc, ifcond))
-
-
-class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
-    def __init__(self, prefix):
-        self._prefix = prefix
-        self._gen = QAPIGenDoc(self._prefix + 'qapi-doc.texi')
-        self.cur_doc = None
-
-    def write(self, output_dir):
-        self._gen.write(output_dir)
-
-    def visit_enum_type(self, name, info, ifcond, members, prefix):
-        doc = self.cur_doc
-        self._gen.add(texi_type('Enum', doc, ifcond,
-                                texi_members(doc, 'Values',
-                                             member_func=texi_enum_value)))
-
-    def visit_object_type(self, name, info, ifcond, base, members, variants,
-                          features):
-        doc = self.cur_doc
-        if base and base.is_implicit():
-            base = None
-        self._gen.add(texi_type('Object', doc, ifcond,
-                                texi_members(doc, 'Members', base, variants)))
-
-    def visit_alternate_type(self, name, info, ifcond, variants):
-        doc = self.cur_doc
-        self._gen.add(texi_type('Alternate', doc, ifcond,
-                                texi_members(doc, 'Members')))
-
-    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
-                      success_response, boxed, allow_oob, allow_preconfig,
-                      features):
-        doc = self.cur_doc
-        self._gen.add(texi_msg('Command', doc, ifcond,
-                               texi_arguments(doc,
-                                              arg_type if boxed else None)))
-
-    def visit_event(self, name, info, ifcond, arg_type, boxed):
-        doc = self.cur_doc
-        self._gen.add(texi_msg('Event', doc, ifcond,
-                               texi_arguments(doc,
-                                              arg_type if boxed else None)))
-
-    def symbol(self, doc, entity):
-        if self._gen._body:
-            self._gen.add('\n')
-        self.cur_doc = doc
-        entity.visit(self)
-        self.cur_doc = None
-
-    def freeform(self, doc):
-        assert not doc.args
-        if self._gen._body:
-            self._gen.add('\n')
-        self._gen.add(texi_body(doc) + texi_sections(doc, None))
-
-
-def gen_doc(schema, output_dir, prefix):
-    vis = QAPISchemaGenDocVisitor(prefix)
-    vis.visit_begin(schema)
-    for doc in schema.docs:
-        if doc.symbol:
-            vis.symbol(doc, schema.lookup_entity(doc.symbol))
-        else:
-            vis.freeform(doc)
-    vis.write(output_dir)
diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
index 95afae0615a..7712d2d49f7 100644
--- a/scripts/qapi/gen.py
+++ b/scripts/qapi/gen.py
@@ -177,13 +177,6 @@ def ifcontext(ifcond, *args):
         arg.end_if()
 
 
-class QAPIGenDoc(QAPIGen):
-
-    def _top(self):
-        return (QAPIGen._top(self)
-                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
-
-
 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
 
     def __init__(self, prefix, what, blurb, pydoc):
-- 
2.20.1



  parent reply	other threads:[~2020-02-06 17:50 UTC|newest]

Thread overview: 77+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-06 17:30 [PATCH 00/29] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
2020-02-06 17:30 ` [PATCH 01/29] configure: Allow user to specify sphinx-build binary Peter Maydell
2020-02-06 17:30 ` [PATCH 02/29] configure: Check that sphinx-build is using Python 3 Peter Maydell
2020-02-07 16:17   ` Markus Armbruster
2020-02-07 16:30     ` Peter Maydell
2020-02-08  7:50       ` Markus Armbruster
2020-02-08 13:11         ` Peter Maydell
2020-02-06 17:30 ` [PATCH 03/29] Makefile: Fix typo in dependency list for interop manpages Peter Maydell
2020-02-06 17:30 ` [PATCH 04/29] qga/qapi-schema.json: Fix missing '-' in GuestDiskBusType doc comment Peter Maydell
2020-02-07  8:16   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 05/29] qga/qapi-schema.json: Fix indent level on doc comments Peter Maydell
2020-02-07  8:18   ` Markus Armbruster
2020-02-07  8:22     ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 06/29] qga/qapi-schema.json: minor format fixups for rST Peter Maydell
2020-02-07  8:32   ` Markus Armbruster
2020-02-13 16:20     ` Peter Maydell
2020-02-14 13:16       ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 07/29] qapi/block-core.json: Use literal block for ascii art Peter Maydell
2020-02-07  8:50   ` Markus Armbruster
2020-02-07  9:27     ` Peter Maydell
2020-02-06 17:30 ` [PATCH 08/29] qapi: Use ':' after @argument in doc comments Peter Maydell
2020-02-07  9:28   ` Markus Armbruster
2020-02-07  9:33     ` Max Reitz
2020-02-07 10:24     ` Kevin Wolf
2020-02-07 11:05       ` Peter Maydell
2020-02-07 14:43         ` Markus Armbruster
2020-02-07 15:01           ` Max Reitz
2020-02-07 15:40             ` Kevin Wolf
2020-02-07 15:24           ` Peter Maydell
2020-02-08  7:54             ` Markus Armbruster
2020-02-08 13:22               ` Peter Maydell
2020-02-06 17:30 ` [PATCH 09/29] qapi: Fix indent level on doc comments in json files Peter Maydell
2020-02-07  9:31   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 10/29] qapi: Remove hardcoded tabs Peter Maydell
2020-02-07  9:32   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 11/29] qapi/ui.json: Put input-send-event body text in the right place Peter Maydell
2020-02-07  9:45   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 12/29] qapi: Explicitly put "foo: dropped in n.n" notes into Notes section Peter Maydell
2020-02-07 10:06   ` Markus Armbruster
2020-02-07 14:23     ` Eric Blake
2020-02-06 17:30 ` [PATCH 13/29] qapi/ui.json: Avoid `...' texinfo style quoting Peter Maydell
2020-02-07 10:13   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 14/29] qapi/block-core.json: Use explicit bulleted lists Peter Maydell
2020-02-07 12:07   ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 15/29] qapi/ui.json: " Peter Maydell
2020-02-06 17:30 ` [PATCH 16/29] qapi/{block, misc, tmp}.json: " Peter Maydell
2020-02-07 10:33   ` Philippe Mathieu-Daudé
2020-02-06 17:30 ` [PATCH 17/29] qapi: Add blank lines before " Peter Maydell
2020-02-07 10:11   ` Philippe Mathieu-Daudé
2020-02-06 17:30 ` [PATCH 18/29] qapi/migration.json: Replace _this_ with *this* Peter Maydell
2020-02-07 16:54   ` Markus Armbruster
2020-02-07 17:00     ` Peter Maydell
2020-02-08 14:24       ` Markus Armbruster
2020-02-06 17:30 ` [PATCH 19/29] qapi/qapi-schema.json: Put headers in their own doc-comment blocks Peter Maydell
2020-02-07 15:34   ` Markus Armbruster
2020-02-07 16:13     ` Peter Maydell
2020-02-08 14:10       ` Markus Armbruster
2020-02-08 14:43         ` Peter Maydell
2020-02-06 17:30 ` [PATCH 20/29] qapi/machine.json: Escape a literal '*' in doc comment Peter Maydell
2020-02-06 17:30 ` [PATCH 21/29] scripts/qapi: Move doc-comment whitespace stripping to doc.py Peter Maydell
2020-02-06 17:30 ` [PATCH 22/29] scripts/qapi/parser.py: improve doc comment indent handling Peter Maydell
2020-02-06 17:30 ` [PATCH 23/29] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
2020-02-06 17:30 ` [PATCH 24/29] docs/interop: Convert qemu-ga-ref to rST Peter Maydell
2020-02-06 17:30 ` [PATCH 25/29] docs/interop: Convert qemu-qmp-ref " Peter Maydell
2020-02-06 17:30 ` [PATCH 26/29] qapi: Use rST markup for literal blocks Peter Maydell
2020-02-06 17:30 ` [PATCH 27/29] qga/qapi-schema.json: Add some headings Peter Maydell
2020-02-06 17:30 ` Peter Maydell [this message]
2020-02-06 17:59   ` [PATCH 28/29] scripts/qapi: Remove texinfo generation support Peter Maydell
2020-02-06 17:30 ` [PATCH 29/29] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions Peter Maydell
2020-02-06 18:47 ` [PATCH 00/29] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
2020-02-06 19:53 ` no-reply
2020-02-06 19:56 ` no-reply
2020-02-07 17:00 ` Markus Armbruster
2020-02-07 17:10   ` Peter Maydell
2020-02-08 14:15     ` Markus Armbruster
2020-02-08 14:59       ` Peter Maydell
2020-02-10  0:34 ` Aleksandar Markovic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200206173040.17337-29-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.org \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.