summaryrefslogtreecommitdiffstats
path: root/tools/lint/perfdocs/generator.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lint/perfdocs/generator.py')
-rw-r--r--tools/lint/perfdocs/generator.py251
1 files changed, 251 insertions, 0 deletions
diff --git a/tools/lint/perfdocs/generator.py b/tools/lint/perfdocs/generator.py
new file mode 100644
index 0000000000..6d3c99d00b
--- /dev/null
+++ b/tools/lint/perfdocs/generator.py
@@ -0,0 +1,251 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+from __future__ import absolute_import
+
+import os
+import re
+import shutil
+import tempfile
+
+from perfdocs.logger import PerfDocLogger
+from perfdocs.utils import are_dirs_equal, read_file, read_yaml, save_file
+
+logger = PerfDocLogger()
+
+
+class Generator(object):
+ """
+ After each perfdocs directory was validated, the generator uses the templates
+ for each framework, fills them with the test descriptions in config and saves
+ the perfdocs in the form index.rst as index file and suite_name.rst for
+ each suite of tests in the framework.
+ """
+
+ def __init__(self, verifier, workspace, generate=False):
+ """
+ Initialize the Generator.
+
+ :param verifier: Verifier object. It should not be a fresh Verifier object,
+ but an initialized one with validate_tree() method already called
+ :param workspace: Path to the top-level checkout directory.
+ :param generate: Flag for generating the documentation
+ """
+ self._workspace = workspace
+ if not self._workspace:
+ raise Exception("PerfDocs Generator requires a workspace directory.")
+ # Template documents without added information reside here
+ self.templates_path = os.path.join(
+ self._workspace, "tools", "lint", "perfdocs", "templates"
+ )
+ self.perfdocs_path = os.path.join(
+ self._workspace, "testing", "perfdocs", "generated"
+ )
+
+ self._generate = generate
+ self._verifier = verifier
+ self._perfdocs_tree = self._verifier._gatherer.perfdocs_tree
+
+ def build_perfdocs_from_tree(self):
+ """
+ Builds up a document for each framework that was found.
+
+ :return dict: A dictionary containing a mapping from each framework
+ to the document that was built for it, i.e:
+ {
+ framework_name: framework_document,
+ ...
+ }
+ """
+
+ # Using the verified `perfdocs_tree`, build up the documentation.
+ frameworks_info = {}
+ for framework in self._perfdocs_tree:
+ yaml_content = read_yaml(os.path.join(framework["path"], framework["yml"]))
+ rst_content = read_file(
+ os.path.join(framework["path"], framework["rst"]), stringify=True
+ )
+
+ # Gather all tests and descriptions and format them into
+ # documentation content
+ documentation = []
+ suites = yaml_content["suites"]
+ for suite_name in sorted(suites.keys()):
+ suite_info = suites[suite_name]
+
+ # Add the suite section
+ documentation.extend(
+ self._verifier._gatherer.framework_gatherers[
+ yaml_content["name"]
+ ].build_suite_section(suite_name, suite_info["description"])
+ )
+
+ tests = suite_info.get("tests", {})
+ for test_name in sorted(tests.keys()):
+ documentation.extend(
+ self._verifier._gatherer.framework_gatherers[
+ yaml_content["name"]
+ ].build_test_description(
+ test_name, tests[test_name], suite_name
+ )
+ )
+ documentation.append("")
+
+ # Insert documentation into `.rst` file
+ framework_rst = re.sub(
+ r"{documentation}", os.linesep.join(documentation), rst_content
+ )
+ frameworks_info[yaml_content["name"]] = {
+ "dynamic": framework_rst,
+ "static": [],
+ }
+
+ # For static `.rst` file
+ for static_file in framework["static"]:
+ frameworks_info[yaml_content["name"]]["static"].append(
+ {
+ "file": static_file,
+ "content": read_file(
+ os.path.join(framework["path"], static_file), stringify=True
+ ),
+ }
+ )
+
+ return frameworks_info
+
+ def _create_temp_dir(self):
+ """
+ Create a temp directory as preparation of saving the documentation tree.
+ :return: str the location of perfdocs_tmpdir
+ """
+ # Build the directory that will contain the final result (a tmp dir
+ # that will be moved to the final location afterwards)
+ try:
+ tmpdir = tempfile.mkdtemp()
+ perfdocs_tmpdir = os.path.join(tmpdir, "generated")
+ os.mkdir(perfdocs_tmpdir)
+ except OSError as e:
+ logger.critical("Error creating temp file: {}".format(e))
+
+ success = False or os.path.isdir(perfdocs_tmpdir)
+ if success:
+ return perfdocs_tmpdir
+ else:
+ return success
+
+ def _create_perfdocs(self):
+ """
+ Creates the perfdocs documentation.
+ :return: str path of the temp dir it is saved in
+ """
+ # All directories that are kept in the perfdocs tree are valid,
+ # so use it to build up the documentation.
+ framework_docs = self.build_perfdocs_from_tree()
+ perfdocs_tmpdir = self._create_temp_dir()
+
+ # Save the documentation files
+ frameworks = []
+ for framework_name in sorted(framework_docs.keys()):
+ frameworks.append(framework_name)
+ save_file(
+ framework_docs[framework_name]["dynamic"],
+ os.path.join(perfdocs_tmpdir, framework_name),
+ )
+
+ for static_name in framework_docs[framework_name]["static"]:
+ save_file(
+ static_name["content"],
+ os.path.join(perfdocs_tmpdir, static_name["file"].split(".")[0]),
+ )
+
+ # Get the main page and add the framework links to it
+ mainpage = read_file(
+ os.path.join(self.templates_path, "index.rst"), stringify=True
+ )
+ fmt_frameworks = os.linesep.join(
+ [" * :doc:`%s`" % name for name in frameworks]
+ )
+ fmt_mainpage = re.sub(r"{test_documentation}", fmt_frameworks, mainpage)
+ save_file(fmt_mainpage, os.path.join(perfdocs_tmpdir, "index"))
+
+ return perfdocs_tmpdir
+
+ def _save_perfdocs(self, perfdocs_tmpdir):
+ """
+ Copies the perfdocs tree after it was saved into the perfdocs_tmpdir
+ :param perfdocs_tmpdir: str location of the temp dir where the
+ perfdocs was saved
+ """
+ # Remove the old docs and copy the new version there without
+ # checking if they need to be regenerated.
+ logger.log("Regenerating perfdocs...")
+
+ if os.path.exists(self.perfdocs_path):
+ shutil.rmtree(self.perfdocs_path)
+
+ try:
+ saved = shutil.copytree(perfdocs_tmpdir, self.perfdocs_path)
+ if saved:
+ logger.log(
+ "Documentation saved to {}/".format(
+ re.sub(".*testing", "testing", self.perfdocs_path)
+ )
+ )
+ except Exception as e:
+ logger.critical(
+ "There was an error while saving the documentation: {}".format(e)
+ )
+
+ def generate_perfdocs(self):
+ """
+ Generate the performance documentation.
+
+ If `self._generate` is True, then the documentation will be regenerated
+ without any checks. Otherwise, if it is False, the new documentation will be
+ prepare and compare with the existing documentation to determine if
+ it should be regenerated.
+
+ :return bool: True/False - For True, if `self._generate` is True, then the
+ docs were regenerated. If `self._generate` is False, then True will mean
+ that the docs should be regenerated, and False means that they do not
+ need to be regenerated.
+ """
+
+ def get_possibly_changed_files():
+ """
+ Returns files that might have been modified
+ (used to output a linter warning for regeneration)
+ :return: list - files that might have been modified
+ """
+ # Returns files that might have been modified
+ # (used to output a linter warning for regeneration)
+ files = []
+ for entry in self._perfdocs_tree:
+ files.extend(
+ [
+ os.path.join(entry["path"], entry["yml"]),
+ os.path.join(entry["path"], entry["rst"]),
+ ]
+ )
+ return files
+
+ # Throw a warning if there's no need for generating
+ if not os.path.exists(self.perfdocs_path) and not self._generate:
+ # If they don't exist and we are not generating, then throw
+ # a linting error and exit.
+ logger.warning(
+ "PerfDocs need to be regenerated.", files=get_possibly_changed_files()
+ )
+ return True
+
+ perfdocs_tmpdir = self._create_perfdocs()
+ if self._generate:
+ self._save_perfdocs(perfdocs_tmpdir)
+ else:
+ # If we are not generating, then at least check if they
+ # should be regenerated by comparing the directories.
+ if not are_dirs_equal(perfdocs_tmpdir, self.perfdocs_path):
+ logger.warning(
+ "PerfDocs are outdated, run ./mach lint -l perfdocs --fix` to update them.",
+ files=get_possibly_changed_files(),
+ )