bazel: build and package man pages as runfiles#9766
bazel: build and package man pages as runfiles#9766alokkumardalei-wq wants to merge 1 commit intoThe-OpenROAD-Project:masterfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new Bazel rule to build and package man pages, making them available to the openroad binary as runfiles. This is a valuable addition that improves the build process. My review focuses on enhancing the robustness and maintainability of the new Bazel rule and improving the portability of the C++ code that locates the generated documentation at runtime.
bazel/man_pages.bzl
Outdated
| html_dir = ctx.actions.declare_directory("html") | ||
|
|
||
| # Build a map of script basename -> path for easy lookup | ||
| scripts_by_name = {f.basename: f.path for f in ctx.files.scripts} |
There was a problem hiding this comment.
The use of .get(script_name, "") on lines 77-80 can lead to cryptic cp errors if a required script is not provided in the scripts attribute. A more robust approach would be to explicitly check for the required scripts and fail with a clear error message if any are missing. After this check, you can directly access the script paths without .get().
For example, you could add this check after building scripts_by_name:
required_scripts = [
"link_readmes.sh",
"md_roff_compat.py",
"manpage.py",
"extract_utils.py",
]
for script in required_scripts:
if script not in scripts_by_name:
fail("Script '{}' is missing from the 'scripts' attribute".format(script))And then in the .format() call, use direct access like scripts_by_name["link_readmes.sh"].
bazel/man_pages.bzl
Outdated
| command = """ | ||
| set -euo pipefail | ||
|
|
||
| # Ensure system tools like pandoc and nroff are available | ||
| export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" | ||
|
|
||
| WORK_DIR=$(mktemp -d) | ||
| trap "rm -rf $WORK_DIR" EXIT | ||
| EXECROOT=$(pwd) | ||
|
|
||
| # Set up docs directory structure | ||
| mkdir -p "$WORK_DIR/md/man1" | ||
| mkdir -p "$WORK_DIR/md/man2" | ||
| mkdir -p "$WORK_DIR/md/man3" | ||
| mkdir -p "$WORK_DIR/src/scripts" | ||
|
|
||
| # Copy scripts and Makefile | ||
| cp {link_readmes} "$WORK_DIR/src/scripts/link_readmes.sh" | ||
| cp {md_roff_compat} "$WORK_DIR/src/scripts/md_roff_compat.py" | ||
| cp {manpage} "$WORK_DIR/src/scripts/manpage.py" | ||
| cp {extract_utils} "$WORK_DIR/src/scripts/extract_utils.py" | ||
| cp {makefile} "$WORK_DIR/Makefile" | ||
|
|
||
| # Copy man1 sources | ||
| for f in {man1_srcs}; do | ||
| cp "$f" "$WORK_DIR/md/man1/" | ||
| done | ||
|
|
||
| # Copy man2 README sources (named by module directory) | ||
| for f in {man2_srcs}; do | ||
| module=$(basename $(dirname $f)) | ||
| cp "$f" "$WORK_DIR/md/man2/${{module}}.md" | ||
| done | ||
|
|
||
| # Run the Python preprocessor to generate per-function man2/man3 pages | ||
| cd "$WORK_DIR" | ||
| python3 src/scripts/md_roff_compat.py | ||
|
|
||
| # Remove any malformed output files (e.g. '#.md' from READMEs with bad function names) | ||
| find "$WORK_DIR/md" -name '#*.md' -delete 2>/dev/null || true | ||
|
|
||
| # Build all man page formats; use -k to continue past nroff warnings | ||
| make all -k -j$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2) || true | ||
|
|
||
| # Ensure output subdirs exist even if some pages failed to build | ||
| mkdir -p "$WORK_DIR/cat/cat1" "$WORK_DIR/cat/cat2" "$WORK_DIR/cat/cat3" | ||
| mkdir -p "$WORK_DIR/html/html1" "$WORK_DIR/html/html2" "$WORK_DIR/html/html3" | ||
|
|
||
| # Copy outputs into the Bazel-declared output directories (paths are execroot-relative) | ||
| mkdir -p "$EXECROOT/{cat_out}" "$EXECROOT/{html_out}" | ||
| cp -r "$WORK_DIR/cat/." "$EXECROOT/{cat_out}/" | ||
| cp -r "$WORK_DIR/html/." "$EXECROOT/{html_out}/" | ||
| """.format( | ||
| link_readmes = scripts_by_name.get("link_readmes.sh", ""), | ||
| md_roff_compat = scripts_by_name.get("md_roff_compat.py", ""), | ||
| manpage = scripts_by_name.get("manpage.py", ""), | ||
| extract_utils = scripts_by_name.get("extract_utils.py", ""), | ||
| makefile = ctx.file.makefile.path, | ||
| man1_srcs = " ".join([f.path for f in ctx.files.man1_srcs]), | ||
| man2_srcs = " ".join([f.path for f in ctx.files.man2_srcs]), | ||
| cat_out = cat_dir.path, | ||
| html_out = html_dir.path, | ||
| ), |
There was a problem hiding this comment.
bazel/man_pages.bzl
Outdated
| set -euo pipefail | ||
|
|
||
| # Ensure system tools like pandoc and nroff are available | ||
| export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" |
There was a problem hiding this comment.
Hardcoding paths like /opt/homebrew/bin can make the build less portable, as tool locations can vary between operating systems, distributions, and user configurations. While this might work for the current primary development environments, a more robust solution would be to rely on the tools being in the system PATH.
src/OpenRoad.cc
Outdated
| const std::filesystem::path runfiles_docs | ||
| = std::filesystem::path(exe + ".runfiles") / "openroad" / "docs"; | ||
| if (std::filesystem::exists(runfiles_docs)) { | ||
| return runfiles_docs; | ||
| } |
There was a problem hiding this comment.
Manually constructing the runfiles path with exe + ".runfiles" is not fully portable (e.g., it differs on Windows) and can be brittle. Since this target already depends on the Bazel runfiles library, it would be more robust to use it to locate the docs directory. This would abstract away the platform-specific details of the runfiles layout.
Example using the runfiles library:
#include "tools/cpp/runfiles/runfiles.h"
// ... in a function that has access to argv[0] or exe path
std::string error;
std::unique_ptr<bazel::tools::cpp::runfiles::Runfiles> runfiles(
bazel::tools::cpp::runfiles::Runfiles::Create(getExePath(), &error));
// ... error handling ...
std::string docs_path = runfiles->Rlocation("openroad/docs");
if (!docs_path.empty() && std::filesystem::exists(docs_path)) {
return docs_path;
}This change would make your code more portable and less dependent on the specifics of Bazel's runfiles implementation.
|
clang-tidy review says "All clean, LGTM! 👍" |
b02b33d to
cb4284b
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
abb37f2 to
4f974a0
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
1 similar comment
|
clang-tidy review says "All clean, LGTM! 👍" |
4f974a0 to
20a70f9
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
20a70f9 to
57a1471
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
57a1471 to
693980d
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
When I try this I get: and no files are created in the runfiles. |
|
clang-tidy review says "All clean, LGTM! 👍" |
068e6ad to
0070f75
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Hello @gadfort I think this is due to PATH environment problem in Bazel's sandboxed execution on macOS. I have fixed that I want you to have look into this when you have time and happy to address further feedback. Thank you! |
|
Hello @gadfort all checks are passing now ,please have a look onto this when you have time . |
|
It builds, but |
|
clang-tidy review says "All clean, LGTM! 👍" |
0070f75 to
693980d
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Hello @gadfort, Please have a look onto this and happy to address any further feedback. Thank you! |
|
clang-tidy review says "All clean, LGTM! 👍" |
693980d to
8fa8bfe
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
e1fa556 to
bc10b91
Compare
Signed-off-by: alokkumardalei-wq <alokkumardalei2@gmail.com>
bc10b91 to
3eb3e7a
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
1 similar comment
|
clang-tidy review says "All clean, LGTM! 👍" |
What does this PR do?
Fixes #7117 — Bazel build now generates and packages man pages
The Bazel build was missing man pages for both the man command and the GUI help browser.
Added a man_pages Bazel rule (
man_pages.bzl
) that runs the existing docs Makefile to generate cat/ and html/ pages from the module READMEs. The output is packaged as runfiles alongside the openroad binary.
Updated getDocsPath() to check for the Bazel runfiles layout (.runfiles/openroad/docs/) before falling back to the installed path.
Verification/Testing
Expected output: