Exemplo n.º 1
0
def _generate_doxyfile(*, manifest, out_dir, temp_dir, dot):
    """Creates Doxyfile_CXX from Doxyfile_CXX.in."""
    input_filename = manifest.Rlocation(
        "drake/doc/doxygen_cxx/Doxyfile_CXX.in")
    assert os.path.exists(input_filename)
    output_filename = join(temp_dir, "Doxyfile_CXX")

    cmake_configure_file = manifest.Rlocation(
        "drake/tools/workspace/cmake_configure_file")
    assert os.path.exists(cmake_configure_file)

    definitions = {}
    definitions["INPUT_ROOT"] = temp_dir
    definitions["OUTPUT_DIRECTORY"] = out_dir
    if dot:
        definitions["DOXYGEN_DOT_FOUND"] = "YES"
        definitions["DOXYGEN_DOT_EXECUTABLE"] = dot
    else:
        definitions["DOXYGEN_DOT_FOUND"] = "NO"
        definitions["DOXYGEN_DOT_EXECUTABLE"] = ""

    check_call([
        cmake_configure_file,
        "--input",
        input_filename,
        "--output",
        output_filename,
    ] + ["-D%s=%s" % (key, value) for key, value in definitions.items()])
    assert os.path.exists(output_filename)
    return output_filename
Exemplo n.º 2
0
def _build(*, out_dir, temp_dir):
    """Callback function that implements the bulk of main().
    Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    """
    # Create a hermetic copy of our input.  This helps ensure that only files
    # listed in BUILD.bazel will render onto the website.
    symlink_input("drake/doc/styleguide/jekyll_input.txt",
                  temp_dir,
                  copy=True,
                  strip_prefix=[
                      "drake/doc/styleguide/",
                      "styleguide/",
                  ])

    # Prepare the files for Jekyll.
    _add_title(temp_dir=temp_dir,
               filename="pyguide.md",
               title="Google Python Style Guide for Drake")

    # Run the documentation generator.
    check_call([
        "/usr/bin/jekyll",
        "build",
        "--source",
        temp_dir,
        "--destination",
        out_dir,
    ])

    # The filenames to suggest as the starting points for preview.
    return ["cppguide.html", "pyguide.html"]
Exemplo n.º 3
0
def _build(*, out_dir, temp_dir):
    """Callback function that implements the bulk of main().
    Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    """
    # Create a hermetic copy of our input.  This helps ensure that only files
    # listed in BUILD.bazel will render onto the website.
    symlink_input("drake/doc/pages_input.txt", temp_dir, copy=True)

    # Run the documentation generator.
    check_call([
        "jekyll",
        "build",
        "--source",
        os.path.join(temp_dir, "drake/doc"),
        "--destination",
        out_dir,
    ])

    # Tidy up.
    perl_cleanup_html_output(out_dir=out_dir)

    # The filename to suggest as the starting point for preview; in this case,
    # it's an empty filename (i.e., the index page).
    return [""]
Exemplo n.º 4
0
def _build(*, out_dir, temp_dir, modules, quick):
    """Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    """
    manifest = runfiles.Create()

    # Find drake's sources.
    drake_workspace = os.path.dirname(
        os.path.realpath(manifest.Rlocation("drake/.bazelproject")))
    assert os.path.exists(drake_workspace), drake_workspace
    assert os.path.exists(join(drake_workspace, "WORKSPACE")), drake_workspace

    # Find doxygen.
    doxygen = manifest.Rlocation("doxygen/doxygen")
    assert os.path.exists(doxygen), doxygen

    # Find dot.
    dot = "/usr/bin/dot"
    assert os.path.exists(dot), dot

    # Configure doxygen.
    doxyfile = _generate_doxyfile(manifest=manifest,
                                  out_dir=out_dir,
                                  temp_dir=temp_dir,
                                  dot=(dot if not quick else ""))

    # Prepare our input.
    symlink_input("drake/doc/doxygen_cxx/doxygen_input.txt", temp_dir)
    _symlink_headers(drake_workspace=drake_workspace,
                     temp_dir=temp_dir,
                     modules=modules)

    # Run doxygen.
    check_call([doxygen, doxyfile], cwd=temp_dir)

    # Post-process its log, and check for errors. If we are building only a
    # subset of the docs, we are likely to encounter errors due to the missing
    # sections, so we'll only enable the promotion of warnings to errors when
    # we're building all of the C++ documentation.
    check_for_errors = (len(modules) == 0)
    with open(f"{temp_dir}/doxygen.log", encoding="utf-8") as f:
        lines = [
            line.strip().replace(f"{temp_dir}/", "") for line in f.readlines()
        ]
    _postprocess_doxygen_log(lines, check_for_errors)

    # The nominal pages to offer for preview.
    return ["", "classes.html", "modules.html"]
Exemplo n.º 5
0
def _build(*, out_dir, temp_dir, modules, quick):
    """Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    """
    manifest = runfiles.Create()

    # Find drake's sources.
    drake_workspace = os.path.dirname(os.path.realpath(
        manifest.Rlocation("drake/.bazelproject")))
    assert os.path.exists(drake_workspace), drake_workspace
    assert os.path.exists(join(drake_workspace, "WORKSPACE")), drake_workspace

    # Find doxygen.
    doxygen = manifest.Rlocation("doxygen/doxygen")
    assert os.path.exists(doxygen), doxygen

    # Find dot.
    dot = "/usr/bin/dot"
    assert os.path.exists(dot), dot

    # Configure doxygen.
    doxyfile = _generate_doxyfile(
        manifest=manifest,
        out_dir=out_dir,
        temp_dir=temp_dir,
        dot=(dot if not quick else ""))

    # Prepare our input.
    symlink_input(
        "drake/doc/doxygen_cxx/doxygen_input.txt", temp_dir)
    _symlink_headers(
        drake_workspace=drake_workspace,
        temp_dir=temp_dir,
        modules=modules)

    # Run doxygen.
    check_call([doxygen, doxyfile], cwd=temp_dir)

    # The nominal pages to offer for preview.
    return ["", "classes.html", "modules.html"]
Exemplo n.º 6
0
def _generate_doxygen_header(*, doxygen, temp_dir):
    """Creates Drake's header.html based on a patch to Doxygen's default
    header template.
    """
    # This matches Doxyfile_CXX.
    header_path = f"{temp_dir}/drake/doc/doxygen_cxx/header.html"

    # Extract the default templates from the Doxygen binary. We only want the
    # header, but it forces us to create all three in this exact order.
    scratch_files = [
        "header.html.orig",
        "footer.html.orig",
        "customdoxygen.css.orig",
    ]
    check_call([doxygen, "-w", "html"] + scratch_files, cwd=temp_dir)
    shutil.copy(f"{temp_dir}/header.html.orig", header_path)
    for orig in scratch_files:
        os.remove(f"{temp_dir}/{orig}")

    # Apply our patch.
    patch_file = f"{header_path}.patch"
    check_call(["/usr/bin/patch", header_path, patch_file])
Exemplo n.º 7
0
def _build(*, out_dir, temp_dir, modules, quick):
    """Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    """
    manifest = runfiles.Create()

    # Find drake's sources.
    drake_workspace = os.path.dirname(
        os.path.realpath(manifest.Rlocation("drake/.bazelproject")))
    assert os.path.exists(drake_workspace), drake_workspace
    assert os.path.exists(join(drake_workspace, "WORKSPACE")), drake_workspace

    # Find doxygen.
    doxygen = manifest.Rlocation("doxygen/doxygen")
    assert os.path.exists(doxygen), doxygen

    # Find dot.
    dot = "/usr/bin/dot"
    assert os.path.exists(dot), dot

    # Configure doxygen.
    doxyfile = _generate_doxyfile(manifest=manifest,
                                  out_dir=out_dir,
                                  temp_dir=temp_dir,
                                  dot=(dot if not quick else ""))

    # Prepare our input.
    symlink_input("drake/doc/doxygen_cxx/doxygen_input.txt", temp_dir)
    _symlink_headers(drake_workspace=drake_workspace,
                     temp_dir=temp_dir,
                     modules=modules)

    # Run doxygen.
    check_call([doxygen, doxyfile], cwd=temp_dir)

    # Post-process its log, and check for errors. If we are building only a
    # subset of the docs, we are likely to encounter errors due to the missing
    # sections, so we'll only enable the promotion of warnings to errors when
    # we're building all of the C++ documentation.
    check_for_errors = (len(modules) == 0)
    with open(f"{temp_dir}/doxygen.log", encoding="utf-8") as f:
        lines = [
            line.strip().replace(f"{temp_dir}/", "") for line in f.readlines()
        ]
    _postprocess_doxygen_log(lines, check_for_errors)

    # Collect the list of all HTML output files.
    html_files = []
    for dirpath, _, filenames in os.walk(out_dir):
        for filename in filenames:
            if filename.endswith(".html"):
                html_files.append(relpath(join(dirpath, filename), out_dir))

    # Fix the formatting of deprecation text (see drake#15619 for an example).
    perl_statements = [
        # Remove quotes around the removal date.
        r's#(removed from Drake on or after) "(....-..-..)" *\.#\1 \2.#;',
        # Remove all quotes within the explanation text, i.e., the initial and
        # final quotes, as well as internal quotes that might be due to C++
        # multi-line string literals.
        # - The quotes must appear after a "_deprecatedNNNNNN" anchor.
        # - The quotes must appear before a "<br />" end-of-line.
        # Example lines:
        # <dl class="deprecated"><dt><b><a class="el" href="deprecated.html#_deprecated000013">Deprecated:</a></b></dt><dd>"Use RotationMatrix::MakeFromOneVector()." <br />  # noqa
        # <dd><a class="anchor" id="_deprecated000013"></a>"Use RotationMatrix::MakeFromOneVector()." <br />  # noqa
        r'while (s#(?<=_deprecated\d{6}")([^"]*)"(.*?<br)#\1\2#) {};',
    ]
    while html_files:
        # Work in batches of 100, so we don't overflow the argv limit.
        first, html_files = html_files[:100], html_files[100:]
        check_call(["perl", "-pi", "-e", "".join(perl_statements)] + first,
                   cwd=out_dir)

    # The nominal pages to offer for preview.
    return ["", "classes.html", "modules.html"]
Exemplo n.º 8
0
def _build(*, out_dir, temp_dir, modules):
    """Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.
    If modules are provided, only generate those modules and their children.
    """
    assert len(os.listdir(temp_dir)) == 0
    assert len(os.listdir(out_dir)) == 0

    sphinx_build = "/usr/share/sphinx/scripts/python3/sphinx-build"
    if not os.path.isfile(sphinx_build):
        print("Please re-run 'sudo setup/ubuntu/install_prereqs.sh' with the "
              "'--with-doc-only' flag")
        sys.exit(1)

    # Create a hermetic copy of our input.  This helps ensure that only files
    # listed in BUILD.bazel will render onto the website.
    symlink_input("drake/doc/pydrake/sphinx_input.txt",
                  temp_dir,
                  strip_prefix=["drake/doc/"])
    input_dir = join(temp_dir, "pydrake")

    # Process the command-line request for which modules to document.
    all_modules = _get_pydrake_modules()
    if not modules:
        modules_to_document = set(all_modules)
    else:
        modules_to_document = set()
        for x in modules:
            if x not in all_modules:
                print(f"error: Unknown module '{x}'")
                sys.exit(1)
            # Add the requested module and its parents.
            tokens = x.split(".")
            while tokens:
                modules_to_document.add(".".join(tokens))
                tokens.pop()
            # Add the requsted module's children.
            for y in all_modules:
                if y.startswith(x + "."):
                    modules_to_document.add(y)

    # Generate tables of contents.
    for name in sorted(list(modules_to_document)):
        if name == "pydrake":
            rst_name = "index.rst"
        else:
            rst_name = name + ".rst"
        _write_module(name, join(input_dir, rst_name))

    # Run the documentation generator.
    os.environ["LANG"] = "en_US.UTF-8"
    check_call([
        sphinx_build,
        "-b",
        "html",  # HTML output.
        "-a",
        "-E",  # Don't use caching.
        "-N",  # Disable colored output.
        "-T",  # Traceback (for plugin).
        "-d",
        join(temp_dir, "doctrees"),
        input_dir,
        out_dir,
    ])

    # The filename to suggest as the starting point for preview; in this case,
    # it's an empty filename (i.e., the index page).
    return [""]
Exemplo n.º 9
0
def _build(*, out_dir, temp_dir, quick, modules):
    """Callback function that implements the bulk of main().
    Generates into out_dir; writes scratch files into temp_dir.
    As a precondition, both directories must already exist and be empty.

    If provided, the given modules can be either API reference modules such as
    "drake.math" (C++) or "pydrake.math" (Python), or else the name of website
    sections such as "pages", "styleguide", "pydrake", "doxygen_cxx", or etc.
    """
    # Find all of our helper tools.
    manifest = runfiles.Create()
    pages_build = manifest.Rlocation("drake/doc/pages")
    styleguide_build = manifest.Rlocation("drake/doc/styleguide/build")
    pydrake_build = manifest.Rlocation("drake/doc/pydrake/build")
    doxygen_build = manifest.Rlocation("drake/doc/doxygen_cxx/build")
    for item in [pages_build, styleguide_build, pydrake_build, doxygen_build]:
        assert item and os.path.exists(item), item

    # Figure out which modules to ask for from each helper tool.
    do_pages = True
    do_styleguide = True
    do_pydrake = True
    do_doxygen = True
    do_sitemap = True
    pydrake_modules = []
    doxygen_modules = []
    if modules:
        do_pages = False
        do_styleguide = False
        do_pydrake = False
        do_doxygen = False
        do_sitemap = False
    for module in modules:
        if module in ["pages"]:
            do_pages = True
        elif module in ["styleguide", "cppguide", "pyguide"]:
            do_styleguide = True
        elif module in ["pydrake"]:
            do_pydrake = True
        elif module in ["doxygen_cxx", "doxygen", "cxx"]:
            do_doxygen = True
        elif module in ["sitemap"]:
            do_sitemap = True
        elif module.startswith("pydrake."):
            do_pydrake = True
            pydrake_modules.append(module)
        elif module.startswith("drake."):
            do_doxygen = True
            doxygen_modules.append(module)
        else:
            print(f"error: Unknown module '{module}'")
            sys.exit(1)

    # Invoke all of our helper tools.
    if do_pages:
        check_call([pages_build, f"--out_dir={out_dir}"])
    if do_styleguide:
        check_call([styleguide_build, f"--out_dir={out_dir}/styleguide"])
    if do_pydrake:
        check_call([pydrake_build, f"--out_dir={out_dir}/pydrake"] +
                   pydrake_modules)
    if do_doxygen:
        maybe_quick = ["--quick"] if quick else []
        check_call([doxygen_build, f"--out_dir={out_dir}/doxygen_cxx"] +
                   doxygen_modules + maybe_quick)
    if do_sitemap:
        _build_sitemap(out_dir)

    # The filenames to suggest as the starting point for preview.
    result = []
    result.append("") if do_pages else None
    result.append("styleguide/cppguide.html") if do_styleguide else None
    result.append("styleguide/pyguide.html") if do_styleguide else None
    result.append("pydrake/") if do_pydrake else None
    result.append("doxygen_cxx/") if do_doxygen else None
    return result