def _write_module(name, f_name): """Writes an rst file for module `name` into `f_name`. """ if verbose(): print("Write: {}".format(name)) subs = _get_submodules(name) with open(f_name, 'w') as f: f.write("\n") rst_name = name.replace("_", "\\_") f.write("{}\n".format(rst_name)) f.write("=" * len(rst_name) + "\n") f.write("\n") if len(subs) > 0: f.write(".. toctree::\n") f.write(" :maxdepth: 1\n") f.write("\n") for sub in subs: f.write(" {}\n".format(sub)) f.write("\n\n") f.write(".. automodule:: {}\n".format(name)) f.write(" :members:\n") # See if there's a corresponding private CcPybind library. if _has_cc_imported_symbols(name): f.write(" :imported-members:\n") f.write(" :undoc-members:\n") f.write(" :show-inheritance:\n")
def _symlink_headers(*, drake_workspace, temp_dir, modules): """Prepare the input and output folders. We will copy the requested input file(s) into a temporary scratch directory, so that Doxygen doesn't scan the drake_workspace directly (which is extremely slow). """ # Locate the default top-level modules. unwanted_top_level_dirs = [ ".*", # There is no C++ code here. "bazel-*", # Ignore Bazel build artifacts. "build", # Ignore CMake build artifacts. "cmake", # There is no C++ code here. "debian", # Ignore Debian build artifacts. "doc", # There is no C++ code here. "gen", # Ignore setup artifacts. "setup", # There is no C++ code here. "third_party", # Only document first-party Drake code. "tools", # There is no C++ code here. "tutorials", # There is no C++ code here. ] default_modules = [ f"drake.{x}" for x in os.listdir(drake_workspace) if os.path.isdir(join(drake_workspace, x)) and not any([ fnmatch(x, unwanted) for unwanted in unwanted_top_level_dirs ]) ] # Iterate modules one by one. for module in (modules or default_modules): if verbose(): print(f"Symlinking {module} ...") prefix = "drake." if not module.startswith(prefix): print("error: Doxygen modules must start with 'drake'," f" not {module}") sys.exit(1) module_as_subdir = module[len(prefix):].replace('.', '/') module_workspace = join(drake_workspace, module_as_subdir) if not os.path.isdir(module_workspace): print(f"error: Unknown module {module}") sys.exit(1) for dirpath, dirs, files in os.walk(module_workspace): subdir = os.path.relpath(dirpath, drake_workspace) os.makedirs(join(temp_dir, "drake", subdir)) for item in files: if any([module.startswith("drake.doc"), "images" in subdir, item.endswith(".h")]): dest = join(temp_dir, "drake", subdir, item) if not os.path.exists(dest): os.symlink(join(dirpath, item), dest)
def _postprocess_doxygen_log(original_lines, check_for_errors): """If check_for_errors is true, then looks for any important warnings and fails-fast if any were found. When in verbose mode, also dumps the log to the console. """ # Throw away useless lines. # # Specifically, we remove stanzas that looks like this: # # The following parameters of DiscardZeroGradient(...) are not documented: # parameter 'auto_diff_matrix' # # The pattern to remove will be the "are not documented" line, followed by # some list of one or more parameter names. lines = [] is_ignoring_parameters = False for line in original_lines: if is_ignoring_parameters: if line.startswith("parameter "): continue is_ignoring_parameters = False if line.endswith(" are not documented:"): is_ignoring_parameters = True continue lines.append(line) # Print all of the warnings (when requested). if verbose(): for line in lines: print("[doxygen] " + line) # Check for important warnings (when requested). errors = [] if check_for_errors: for line in lines: if _is_important_warning(line): errors.append(line.replace("warning:", "error:")) if errors: message = "\n".join(["Problems found by Doxygen:"] + sorted(errors)) raise RuntimeError(message)