Bazel: Add unified hermetic:tidy target for workspace formatting#9811
Conversation
387baf3 to
31b6110
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a new tidy script and associated Bazel infrastructure to automate code formatting and linting for C++, Bazel, Python, and Tcl files, by adding buildifier_prebuilt, black, and tclint as dependencies. The bazel/install.sh script's default installation path was updated, and several minor C++ formatting adjustments were made. Review comments suggest improving the tidy.sh script by handling tclint errors more robustly instead of suppressing all of them, simplifying the tool path resolution logic, and making the install.sh destination directory a named constant for better maintainability. Additionally, a minor stylistic inconsistency in a C++ parameter declaration was noted.
| if command -v "$TCLINT" &> /dev/null || [ -f "$TCLINT" ]; then | ||
| "$TCLINT" src etc tools test || true |
There was a problem hiding this comment.
The || true at the end of the tclint command suppresses any errors from tclint. It would be better to check the exit code of tclint and only suppress the error if it's a specific, non-critical error (e.g., a style violation). Otherwise, a genuine error could be masked.
| if command -v "$TCLINT" &> /dev/null || [ -f "$TCLINT" ]; then | |
| "$TCLINT" src etc tools test || true | |
| "$TCLINT" src etc tools test | |
| TCLINT_STATUS=$? | |
| if [ $TCLINT_STATUS -ne 0 ]; then | |
| echo "Tclint found errors, but continuing anyway." | |
| fi |
|
|
||
| TARFILE=$(cd $BUILD_WORKSPACE_DIRECTORY; bazelisk info bazel-bin)/openroad.tar | ||
|
|
||
| DEST_DIR=${1:-${BUILD_WORKSPACE_DIRECTORY}/../install/OpenROAD/bin} | ||
| DEST_DIR=${1:-${BUILD_WORKSPACE_DIRECTORY}/bazel-install} |
| if [[ "$CLANG_FORMAT" != /* ]] && [[ "$CLANG_FORMAT" != "clang-format" ]]; then | ||
| CLANG_FORMAT="$PWD/$CLANG_FORMAT" | ||
| fi | ||
| if [[ "$BUILDIFIER" != /* ]] && [[ "$BUILDIFIER" != "buildifier" ]]; then | ||
| BUILDIFIER="$PWD/$BUILDIFIER" | ||
| fi | ||
| if [[ "$BLACK" != /* ]] && [[ "$BLACK" != "black" ]]; then | ||
| BLACK="$PWD/$BLACK" | ||
| fi | ||
| if [[ "$TCLINT" != /* ]] && [[ "$TCLINT" != "tclint" ]]; then | ||
| TCLINT="$PWD/$TCLINT" | ||
| fi |
There was a problem hiding this comment.
The script checks if the tools are not absolute paths and not equal to the tool names, and then prepends $PWD. This logic can be simplified by checking if the tool is an absolute path, and if not, using command -v to resolve the tool path. This approach would be more robust and easier to understand.
| if [[ "$CLANG_FORMAT" != /* ]] && [[ "$CLANG_FORMAT" != "clang-format" ]]; then | |
| CLANG_FORMAT="$PWD/$CLANG_FORMAT" | |
| fi | |
| if [[ "$BUILDIFIER" != /* ]] && [[ "$BUILDIFIER" != "buildifier" ]]; then | |
| BUILDIFIER="$PWD/$BUILDIFIER" | |
| fi | |
| if [[ "$BLACK" != /* ]] && [[ "$BLACK" != "black" ]]; then | |
| BLACK="$PWD/$BLACK" | |
| fi | |
| if [[ "$TCLINT" != /* ]] && [[ "$TCLINT" != "tclint" ]]; then | |
| TCLINT="$PWD/$TCLINT" | |
| fi | |
| if ! [[ "$CLANG_FORMAT" == /* ]]; then | |
| CLANG_FORMAT=$(command -v "$CLANG_FORMAT") | |
| fi | |
| if ! [[ "$BUILDIFIER" == /* ]]; then | |
| BUILDIFIER=$(command -v "$BUILDIFIER") | |
| fi | |
| if ! [[ "$BLACK" == /* ]]; then | |
| BLACK=$(command -v "$BLACK") | |
| fi | |
| if ! [[ "$TCLINT" == /* ]]; then | |
| TCLINT=$(command -v "$TCLINT") | |
| fi |
| dbId<_dbModule> T_impl::* module_field, | ||
| std::unordered_map<std::string, dbId<T_impl>> _dbModule::* hash_field) |
31b6110 to
c759b13
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
2 similar comments
|
clang-tidy review says "All clean, LGTM! 👍" |
|
clang-tidy review says "All clean, LGTM! 👍" |
cb26e78 to
d852b72
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
1 similar comment
|
clang-tidy review says "All clean, LGTM! 👍" |
d852b72 to
5a9a34d
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
@oharboe does this add anything beyond your linting PRs? |
|
@alokkumardalei-wq nice work getting multiple formatters wired up under Bazel — Why buildifier first
Reference implementationbazel-orfs already has a
Key patterns from bazel-orfs worth reusing:
What's already in place in OpenROADPRs #9734 and #9856 merged a Bazel lint framework with these entry points: bazelisk test //:lint_test # umbrella: all lint checks
bazelisk run //:fix_lint # umbrella: auto-fixCurrently it covers TCL only (tclint + tclfmt). The design is documented in What to do for this PRKeep: buildifier as a Bazel lint targetFollowing the pattern in
Drop from this PR
Follow-up PRAfter buildifier lands, add black as a second PR using the same Let me know if you have questions — happy to help work through the details. |
3f07da4 to
c011ce7
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
1 similar comment
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Thanks @oharboe for the detailed review and guidance! You were totally right—the monolithic script was getting out of hand. I've completely wiped the massive 600-file diff from this branch and stripped out I also wired I'll open a fresh, isolated follow-up PR for the black Python formatter using this exact same native Bazel pattern! |
|
Good progress stripping this down to buildifier-only — that took discipline and was the right call. You're close, but the implementation has drifted from the spec. Please read #9858 carefully — it defines exactly what targets are needed and what they should be called. Here's what needs to change: 1. Naming: use
|
| Current | Should be |
|---|---|
lint_buildifier_test |
lint_bzl_test |
tidy_buildifier |
tidy_bzl |
buildifier_test.sh |
bzl_lint_test.sh |
buildifier_fix.sh |
bzl_tidy.sh |
2. Missing fmt_bzl_test target
Issue #9858 specifies three targets, matching the TCL pattern:
| Target | Command |
|---|---|
//:lint_bzl_test |
buildifier -mode=check -lint=warn |
//:fmt_bzl_test |
buildifier -mode=check -lint=off |
//:tidy_bzl |
buildifier -mode=fix -lint=fix |
The format-only check (-lint=off) separates "your indentation is wrong" from "you have a lint warning" — same reason TCL has both lint_tcl_test and fmt_tcl_test. Look at how tcl_lint_test.sh, tcl_fmt_test.sh, and tcl_tidy.sh are structured and mirror that pattern.
3. Drop the src/sta submodule pointer change — and never touch src/sta
Your diff includes a src/sta submodule change (35aef386d6 → 2d7fc5c82b). Revert it. src/sta is the OpenSTA submodule — it is managed upstream and changes to it require careful coordination. As a rule: never include src/sta changes in a PR unless that PR is specifically about updating OpenSTA.
This also matters for your buildifier scripts: src/sta/BUILD exists and must not be reformatted by your scripts. Your scripts must exclude submodule paths. The simplest approach is to filter out paths listed in .gitmodules (currently src/sta and third-party/abc), or hard-code the exclusions. The bazel-orfs fix_lint.sh shows how to filter .bazelignore paths — adapt that pattern.
4. Add buildifier: disable comments
The existing TCL targets have # buildifier: disable=native-sh-test and # buildifier: disable=native-sh-binary comments. Add the same to your new targets — copy the pattern from the TCL section directly above yours in BUILD.bazel.
5. .buildifier.json — read it, your scripts must override it
There's a .buildifier.json at the repo root with mode: fix, lint: fix, warnings: all. Buildifier picks this up automatically. That means if your test scripts don't explicitly pass -mode=check, buildifier will fix files in place instead of just checking them — defeating the purpose of a read-only test. Your test scripts must pass explicit flags that override the config (-mode=check -lint=warn for lint, -mode=check -lint=off for fmt).
6. Update docs/contrib/LintTargets.md
Move buildifier from the "Planned additions" list to the "Available targets" table. Add all three new targets with descriptions.
7. Positional args in fix_lint.sh
The $5-$8 extension is fine for now — don't worry about it.
In short
I know this is a lot of feedback for what sounds like "just add a formatter." That's exactly the point — getting this right is shockingly complex compared to what it sounds like on the surface. Submodule exclusions, config file interactions, naming conventions, the split between read-only checks and auto-fix — there are real landmines here. But that complexity is precisely why this work is so valuable: once //:fix_lint handles buildifier correctly, every developer just runs one command and never has to think about any of this. You're absorbing the complexity so hundreds of contributors don't have to.
Take your time. The architecture is sound. The changes above are about matching the spec in #9858 and mirroring the TCL pattern exactly. Open BUILD.bazel at the TCL lint section (lines 442-479) and replicate that structure with bzl instead of tcl.
|
clang-tidy review says "All clean, LGTM! 👍" |
Hello @oharboe thank you for the feedback again I will definitely work on this and actually I am very happy to address this , and I shall ask any doubts and issues if I faced while working here and I will notify you very soon here with your suggested changes. |
44ae57d to
75aac70
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Hello @oharboe thank you so much for your time to consider this and I have addressed all 7 points from your review in the commit 75aac70:
Please have a look again when you have time and let me if anything I missed or need improvement. Thank you ! |
|
Hello @maliberty and @oharboe, all checks are passing please have look when you have time . I would be happy to address any feedback further. Thank you ! |
| @@ -94,6 +94,7 @@ bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True) | |||
| # --- Dev dependencies (not propagated to downstream consumers) --- | |||
|
|
|||
| bazel_dep(name = "bant", version = "0.2.4", dev_dependency = True) | |||
| bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) | |||
There was a problem hiding this comment.
why 6.4.0 when the current is 8.5.1.2 ?
|
@oharboe any further comments? |
75aac70 to
1ffd004
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
Implements The-OpenROAD-Project#9858 (The-OpenROAD-Project#8495). Adds three new targets mirroring the TCL lint pattern: //:lint_bzl_test - buildifier -mode=check -lint=warn (lint check) //:fmt_bzl_test - buildifier -mode=check -lint=off (format check) //:tidy_bzl - buildifier -mode=fix -lint=fix (auto-fix) Wires lint_bzl_test and fmt_bzl_test into //:lint_test, and bzl_tidy.sh + bzl_lint_test.sh into //:fix_lint. Adds buildifier_prebuilt 6.4.0 as a dev_dependency in MODULE.bazel. Uses git ls-files for file discovery, which automatically skips submodule paths (src/sta, third-party/abc). Explicit -mode=check flags override the repo-root .buildifier.json default (mode: fix), ensuring test targets remain read-only. Updates docs/contrib/LintTargets.md: moves buildifier from "Planned additions" to "Available targets". Signed-off-by: alokkumardalei-wq <alokkumardalei2@gmail.com>
1ffd004 to
8d652ae
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Hello @maliberty and @oharboe , updated version from 6.4.0 to 8.5.1.2. Please have look when you have time . Thank you ! |
d43d716
into
The-OpenROAD-Project:master
What does this PR resolves ?
Fixes #8495
Adds a unified bazelisk run :tidy target to formatting the entire codebase consistently without requiring developers to manage system-level tool installations. This significantly simplifies onboarding and standardizes code style checks.
What are key changes made??
Bazel: Added buildifier_prebuilt to
Testing/ Verification
Expected output: