Hack together a quick doc generator for IDL files.

- Changed the IDL parser to mine the Comment nodes, and process them for
parameter comments.
- Added hacks to the doc builder to parse the IDL files and spit out a
generated JSON file before running the generator.

BUG=no
TEST=no

Review URL: https://chromiumcodereview.appspot.com/10082038

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132474 0039d316-1c4b-4281-b951-d872f2087c98
parent bf0fa427
...@@ -27,6 +27,8 @@ _extension_api_dir = os.path.normpath(_base_dir + "/../api") ...@@ -27,6 +27,8 @@ _extension_api_dir = os.path.normpath(_base_dir + "/../api")
_extension_api_json_schemas = glob.glob(_extension_api_dir + _extension_api_json_schemas = glob.glob(_extension_api_dir +
'/[a-zA-Z0-9]*.json') '/[a-zA-Z0-9]*.json')
_extension_api_idl_schemas = glob.glob(_extension_api_dir +
'/[a-zA-Z0-9]*.idl')
_api_template_html = _template_dir + "/api_template.html" _api_template_html = _template_dir + "/api_template.html"
_page_shell_html = _template_dir + "/page_shell.html" _page_shell_html = _template_dir + "/page_shell.html"
_generator_html = _build_dir + "/generator.html" _generator_html = _build_dir + "/generator.html"
...@@ -193,7 +195,8 @@ def main(): ...@@ -193,7 +195,8 @@ def main():
dump_render_tree = FindDumpRenderTree() dump_render_tree = FindDumpRenderTree()
# Load the manifest of existing API Methods # Load the manifest of existing API Methods
api_manifest = ApiManifest(_extension_api_json_schemas) api_manifest = ApiManifest(_extension_api_json_schemas,
_extension_api_idl_schemas)
# Read static file names # Read static file names
static_names = GetStaticFileNames() static_names = GetStaticFileNames()
...@@ -212,6 +215,9 @@ def main(): ...@@ -212,6 +215,9 @@ def main():
raise Exception("--page-name argument must be one of %s." % raise Exception("--page-name argument must be one of %s." %
', '.join(sorted(page_names))) ', '.join(sorted(page_names)))
# Write temporary JSON files based on the IDL inputs
api_manifest.generateJSONFromIDL()
# Render a manifest file containing metadata about all the extension samples # Render a manifest file containing metadata about all the extension samples
samples_manifest = SamplesManifest(_samples_dir, _base_dir, api_manifest) samples_manifest = SamplesManifest(_samples_dir, _base_dir, api_manifest)
samples_manifest.writeToFile(_samples_json) samples_manifest.writeToFile(_samples_json)
...@@ -243,6 +249,9 @@ def main(): ...@@ -243,6 +249,9 @@ def main():
if (os.path.isfile(debug_log)): if (os.path.isfile(debug_log)):
os.remove(debug_log) os.remove(debug_log)
# Cleanup our temporary IDL->JSON files
api_manifest.cleanupGeneratedFiles()
if 'EX_OK' in dir(os): if 'EX_OK' in dir(os):
return os.EX_OK return os.EX_OK
else: else:
......
...@@ -18,6 +18,14 @@ import json_minify as minify ...@@ -18,6 +18,14 @@ import json_minify as minify
# default C locale. # default C locale.
locale.setlocale(locale.LC_ALL, 'C') locale.setlocale(locale.LC_ALL, 'C')
import sys
_script_path = os.path.realpath(__file__)
_build_dir = os.path.dirname(_script_path)
_base_dir = os.path.normpath(_build_dir + "/..")
sys.path.insert(0, os.path.normpath(_base_dir +
"/../../../../tools/json_schema_compiler"))
import idl_schema
def sorted_walk(path): def sorted_walk(path):
""" A version of os.walk that yields results in order sorted by name. """ A version of os.walk that yields results in order sorted by name.
...@@ -58,18 +66,55 @@ def parse_json_file(path, encoding="utf-8"): ...@@ -58,18 +66,55 @@ def parse_json_file(path, encoding="utf-8"):
return json_obj return json_obj
def parse_idl_file(path):
""" Load the specified file and parse it as IDL.
Args:
path: Path to a file containing JSON-encoded data.
"""
api_def = idl_schema.Load(path)
return api_def
def write_json_to_file(manifest, path):
""" Writes the contents of this manifest file as a JSON-encoded text file.
Args:
manifest: The manifest structure to write.
path: The path to write the manifest file to.
Raises:
Exception: If the file could not be written.
"""
manifest_text = json.dumps(manifest, indent=2,
sort_keys=True, separators=(',', ': '))
output_path = os.path.realpath(path)
try:
output_file = open(output_path, 'w')
except IOError, msg:
raise Exception("Failed to write the samples manifest file."
"The specific error was: %s." % msg)
output_file.write(manifest_text)
output_file.close()
class ApiManifest(object): class ApiManifest(object):
""" Represents the list of API methods contained in the extension API JSON """ """ Represents the list of API methods contained in the extension API JSON """
def __init__(self, manifest_paths): def __init__(self, json_paths, idl_paths):
""" Read the supplied manifest file and parse its contents. """ Read the supplied json files and idl files and parse their contents.
Args: Args:
manifest_paths: Array of paths to API schemas. json_paths: Array of paths to .json API schemas.
idl_paths: Array of paths to .idl API schemas.
""" """
self._manifest = []; self._manifest = []
for path in manifest_paths: self._temporary_json_files = []
for path in json_paths:
self._manifest.extend(parse_json_file(path)) self._manifest.extend(parse_json_file(path))
for path in idl_paths:
module = parse_idl_file(path)
json_path = os.path.realpath(path.replace('.idl', '.json'))
self._temporary_json_files.append((module, json_path))
self._manifest.extend(module)
def _parseModuleDocLinksByKeyTypes(self, module, key): def _parseModuleDocLinksByKeyTypes(self, module, key):
""" """
...@@ -186,6 +231,22 @@ class ApiManifest(object): ...@@ -186,6 +231,22 @@ class ApiManifest(object):
api_dict.update(self._parseModuleDocLinksByKeyTypes(module, 'events')) api_dict.update(self._parseModuleDocLinksByKeyTypes(module, 'events'))
return api_dict return api_dict
def generateJSONFromIDL(self):
""" Writes temporary .json files for every .idl file we have read, for
use by the documentation generator.
"""
for (module, json_path) in self._temporary_json_files:
if os.path.exists(json_path):
print ("WARNING: Overwriting existing file '%s'"
" with generated content." % (json_path))
write_json_to_file(module, json_path)
def cleanupGeneratedFiles(self):
""" Removes the temporary .json files we generated from .idl before.
"""
for (module, json_path) in self._temporary_json_files:
os.remove(json_path)
class SamplesManifest(object): class SamplesManifest(object):
""" Represents a manifest file containing information about the sample """ Represents a manifest file containing information about the sample
extensions available in the codebase. """ extensions available in the codebase. """
...@@ -262,16 +323,7 @@ class SamplesManifest(object): ...@@ -262,16 +323,7 @@ class SamplesManifest(object):
Args: Args:
path: The path to write the samples manifest file to. path: The path to write the samples manifest file to.
""" """
manifest_text = json.dumps(self._manifest_data, indent=2, write_json_to_file(self._manifest_data, path)
sort_keys=True, separators=(',', ': '))
output_path = os.path.realpath(path)
try:
output_file = open(output_path, 'w')
except IOError, msg:
raise Exception("Failed to write the samples manifest file."
"The specific error was: %s." % msg)
output_file.write(manifest_text)
output_file.close()
def writeZippedSamples(self): def writeZippedSamples(self):
""" For each sample in the current manifest, create a zip file with the """ For each sample in the current manifest, create a zip file with the
......
...@@ -95,6 +95,18 @@ ...@@ -95,6 +95,18 @@
"chrome.experimental.alarms.onAlarm": "experimental.alarms.html#event-onAlarm", "chrome.experimental.alarms.onAlarm": "experimental.alarms.html#event-onAlarm",
"chrome.experimental.app.clearAllNotifications": "experimental.app.html#method-clearAllNotifications", "chrome.experimental.app.clearAllNotifications": "experimental.app.html#method-clearAllNotifications",
"chrome.experimental.app.notify": "experimental.app.html#method-notify", "chrome.experimental.app.notify": "experimental.app.html#method-notify",
"chrome.experimental.bluetooth.connect": "experimental.bluetooth.html#method-connect",
"chrome.experimental.bluetooth.disconnect": "experimental.bluetooth.html#method-disconnect",
"chrome.experimental.bluetooth.getAddress": "experimental.bluetooth.html#method-getAddress",
"chrome.experimental.bluetooth.getDevicesWithService": "experimental.bluetooth.html#method-getDevicesWithService",
"chrome.experimental.bluetooth.getOutOfBandPairingData": "experimental.bluetooth.html#method-getOutOfBandPairingData",
"chrome.experimental.bluetooth.isAvailable": "experimental.bluetooth.html#method-isAvailable",
"chrome.experimental.bluetooth.isPowered": "experimental.bluetooth.html#method-isPowered",
"chrome.experimental.bluetooth.onAvailabilityChanged": "experimental.bluetooth.html#event-onAvailabilityChanged",
"chrome.experimental.bluetooth.onPowerChanged": "experimental.bluetooth.html#event-onPowerChanged",
"chrome.experimental.bluetooth.read": "experimental.bluetooth.html#method-read",
"chrome.experimental.bluetooth.setOutOfBandPairingData": "experimental.bluetooth.html#method-setOutOfBandPairingData",
"chrome.experimental.bluetooth.write": "experimental.bluetooth.html#method-write",
"chrome.experimental.devtools.audits.AuditCategory.onAuditStarted": "experimental.devtools.audits.html#event-AuditCategory-onAuditStarted", "chrome.experimental.devtools.audits.AuditCategory.onAuditStarted": "experimental.devtools.audits.html#event-AuditCategory-onAuditStarted",
"chrome.experimental.devtools.audits.AuditResultNode.addChild": "experimental.devtools.audits.html#method-AuditResultNode-addChild", "chrome.experimental.devtools.audits.AuditResultNode.addChild": "experimental.devtools.audits.html#method-AuditResultNode-addChild",
"chrome.experimental.devtools.audits.AuditResults.addResult": "experimental.devtools.audits.html#method-AuditResults-addResult", "chrome.experimental.devtools.audits.AuditResults.addResult": "experimental.devtools.audits.html#method-AuditResults-addResult",
...@@ -106,6 +118,7 @@ ...@@ -106,6 +118,7 @@
"chrome.experimental.devtools.console.addMessage": "experimental.devtools.console.html#method-addMessage", "chrome.experimental.devtools.console.addMessage": "experimental.devtools.console.html#method-addMessage",
"chrome.experimental.devtools.console.getMessages": "experimental.devtools.console.html#method-getMessages", "chrome.experimental.devtools.console.getMessages": "experimental.devtools.console.html#method-getMessages",
"chrome.experimental.devtools.console.onMessageAdded": "experimental.devtools.console.html#event-onMessageAdded", "chrome.experimental.devtools.console.onMessageAdded": "experimental.devtools.console.html#event-onMessageAdded",
"chrome.experimental.dns.resolve": "experimental.dns.html#method-resolve",
"chrome.experimental.downloads.download": "experimental.downloads.html#method-download", "chrome.experimental.downloads.download": "experimental.downloads.html#method-download",
"chrome.experimental.fontSettings.getDefaultFixedFontSize": "experimental.fontSettings.html#method-getDefaultFixedFontSize", "chrome.experimental.fontSettings.getDefaultFixedFontSize": "experimental.fontSettings.html#method-getDefaultFixedFontSize",
"chrome.experimental.fontSettings.getDefaultFontSize": "experimental.fontSettings.html#method-getDefaultFontSize", "chrome.experimental.fontSettings.getDefaultFontSize": "experimental.fontSettings.html#method-getDefaultFontSize",
...@@ -133,6 +146,17 @@ ...@@ -133,6 +146,17 @@
"chrome.experimental.runtime.getBackgroundPage": "experimental.runtime.html#method-getBackgroundPage", "chrome.experimental.runtime.getBackgroundPage": "experimental.runtime.html#method-getBackgroundPage",
"chrome.experimental.runtime.onBackgroundPageUnloadingSoon": "experimental.runtime.html#event-onBackgroundPageUnloadingSoon", "chrome.experimental.runtime.onBackgroundPageUnloadingSoon": "experimental.runtime.html#event-onBackgroundPageUnloadingSoon",
"chrome.experimental.runtime.onInstalled": "experimental.runtime.html#event-onInstalled", "chrome.experimental.runtime.onInstalled": "experimental.runtime.html#event-onInstalled",
"chrome.experimental.serial.close": "experimental.serial.html#method-close",
"chrome.experimental.serial.open": "experimental.serial.html#method-open",
"chrome.experimental.serial.read": "experimental.serial.html#method-read",
"chrome.experimental.serial.write": "experimental.serial.html#method-write",
"chrome.experimental.socket.connect": "experimental.socket.html#method-connect",
"chrome.experimental.socket.create": "experimental.socket.html#method-create",
"chrome.experimental.socket.destroy": "experimental.socket.html#method-destroy",
"chrome.experimental.socket.disconnect": "experimental.socket.html#method-disconnect",
"chrome.experimental.socket.onEvent": "experimental.socket.html#event-onEvent",
"chrome.experimental.socket.read": "experimental.socket.html#method-read",
"chrome.experimental.socket.write": "experimental.socket.html#method-write",
"chrome.experimental.speechInput.isRecording": "experimental.speechInput.html#method-isRecording", "chrome.experimental.speechInput.isRecording": "experimental.speechInput.html#method-isRecording",
"chrome.experimental.speechInput.onError": "experimental.speechInput.html#event-onError", "chrome.experimental.speechInput.onError": "experimental.speechInput.html#event-onError",
"chrome.experimental.speechInput.onResult": "experimental.speechInput.html#event-onResult", "chrome.experimental.speechInput.onResult": "experimental.speechInput.html#event-onResult",
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import json import json
import os.path import os.path
import sys import sys
import re
# This file is a peer to json_schema.py. Each of these files understands a # This file is a peer to json_schema.py. Each of these files understands a
# certain format describing APIs (either JSON or IDL), reads files written # certain format describing APIs (either JSON or IDL), reads files written
...@@ -22,18 +23,45 @@ if idl_generators_path not in sys.path: ...@@ -22,18 +23,45 @@ if idl_generators_path not in sys.path:
sys.path.insert(0, idl_generators_path) sys.path.insert(0, idl_generators_path)
import idl_parser import idl_parser
def ProcessComment(comment):
'''
Given the string from a Comment node, parse it into a tuple that looks
like:
(
"The processed comment, minus all |parameter| mentions.",
{
'parameter_name_1': "The comment that followed |parameter_name_1|:",
...
}
)
'''
# Find all the parameter comments of the form "|name|: comment".
parameter_comments = re.findall(r'\n *\|([^|]*)\| *: *(.*)', comment)
# Get the parent comment (everything before the first parameter comment.
parent_comment = re.sub(r'\n *\|.*', '', comment)
parent_comment = parent_comment.replace('\n', '').strip()
parsed = {}
for (name, comment) in parameter_comments:
parsed[name] = comment.replace('\n', '').strip()
return (parent_comment, parsed)
class Callspec(object): class Callspec(object):
''' '''
Given a Callspec node representing an IDL function declaration, converts into Given a Callspec node representing an IDL function declaration, converts into
a name/value pair where the value is a list of function parameters. a name/value pair where the value is a list of function parameters.
''' '''
def __init__(self, callspec_node): def __init__(self, callspec_node, comment):
self.node = callspec_node self.node = callspec_node
self.comment = comment
def process(self, callbacks): def process(self, callbacks):
parameters = [] parameters = []
for node in self.node.children: for node in self.node.children:
parameters.append(Param(node).process(callbacks)) parameter = Param(node).process(callbacks)
if parameter['name'] in self.comment:
parameter['description'] = self.comment[parameter['name']]
parameters.append(parameter)
return self.node.GetName(), parameters return self.node.GetName(), parameters
class Param(object): class Param(object):
...@@ -84,10 +112,15 @@ class Member(object): ...@@ -84,10 +112,15 @@ class Member(object):
if self.node.GetProperty('nodoc'): if self.node.GetProperty('nodoc'):
properties['nodoc'] = True properties['nodoc'] = True
is_function = False is_function = False
parameter_comments = {}
for node in self.node.children:
if node.cls == 'Comment':
(parent_comment, parameter_comments) = ProcessComment(node.GetName())
properties['description'] = parent_comment
for node in self.node.children: for node in self.node.children:
if node.cls == 'Callspec': if node.cls == 'Callspec':
is_function = True is_function = True
name, parameters = Callspec(node).process(callbacks) name, parameters = Callspec(node, parameter_comments).process(callbacks)
properties['parameters'] = parameters properties['parameters'] = parameters
properties['name'] = name properties['name'] = name
if is_function: if is_function:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment