Skip to content

Fix formatting of generated BUILD.bazel files#85

Merged
miridius merged 1 commit into
mainfrom
dave/tool-518-fix-formatting-of-generated-build-files
May 11, 2026
Merged

Fix formatting of generated BUILD.bazel files#85
miridius merged 1 commit into
mainfrom
dave/tool-518-fix-formatting-of-generated-build-files

Conversation

@miridius

@miridius miridius commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Problem

Generated BUILD.bazel files use non-standard formatting: all kwargs on one line, no attribute sorting, no trailing commas, #autogenerated comment header. This causes IDEs configured with buildifier to reformat the entire file on open, creating noisy diffs (banksy#15022 comment).

Solution

Rewrite the emit-bazel* multimethod system in gen_build.clj to produce output identical to buildifier --type=build. All formatting rules were verified empirically by running buildifier on sample files and by regenerating the entire banksy-3 tree (1373 BUILD files) and confirming gazelle from #84 produces byte-identical output.

  • Attribute sorting: by buildifier's NamePriority table (name first, srcs early, deps last, unknowns alphabetically)
  • List formatting: multi-element lists go multi-line (8-space indent, trailing commas); single-element stays inline
  • Label sorting: phase-based ordering matching buildifier's byStringExpr (: < // < @, then lexicographic within phase). Only for known sortable attrs (deps, srcs, resources, etc.) — ordering-sensitive attrs like aot are preserved
  • Function calls: multi-line with 4-space indent for >1 kwarg; inline for single kwarg (package(...)) or positional-only (load(...))
  • load() args sorted: positional symbol args after the bzl path go alphabetical (buildifier rule)
  • Dict literals: {"k": "v"} (no space before colon)
  • Label shortening: //path/to/pkg:pkg//path/to/pkg when the target name equals the trailing path segment
  • Standalone vectors: inline with ", " separator (multi-line still happens for kwarg-context list values)
  • Headers: triple-quote docstring replaces #autogenerated comment; load before package; final \n at end of file

A buildifier round-trip test pipes generated content through buildifier --type=build (provided as a bazel_dep via buildifier_prebuilt, resolved at test time through runfiles-env) and asserts zero diff.

Before

#autogenerated, do not edit
package(default_visibility = ["//visibility:public"])
load("@rules_clojure//:rules.bzl", "clojure_library", "clojure_test")

clojure_library(name = "build",
    deps = ["//resources:data_readers","@deps//:ns_io_github_clojure_tools_build_clojure_tools_build_api","@deps//:org_clojure_clojure"],
    resources = ["build.clj"],
    resource_strip_prefix = "dev",
    srcs = ["build.clj"],
    aot = ["build"],
    runtime_deps = ["@deps//:com_fzakaria_slf4j_timbre"],
    jvm_flags = ["-Dclojure.main.report=stderr"])

After

"""
Do not edit this file manually!
It is automatically generated by the `//:gen_srcs` target from `rules_clojure`.
"""

load("@rules_clojure//:rules.bzl", "clojure_library")

package(default_visibility = ["//visibility:public"])

clojure_library(
    name = "build",
    srcs = ["build.clj"],
    aot = ["build"],
    jvm_flags = ["-Dclojure.main.report=stderr"],
    resource_strip_prefix = "dev",
    resources = ["build.clj"],
    runtime_deps = ["@deps//:com_fzakaria_slf4j_timbre"],
    deps = [
        "//resources:data_readers",
        "@deps//:ns_io_github_clojure_tools_build_clojure_tools_build_api",
        "@deps//:org_clojure_clojure",
    ],
)

CI / build

  • MODULE.bazel gains buildifier_prebuilt as a dev_dependency; the gen-build-test target declares the buildifier binary in data and exports its runfiles path via the BUILDIFIER env var. The round-trip test resolves it through test-utils/runfiles-env — no which/PATH lookup.
  • .circleci/config.yml: no install step needed (deps come from bazel).

Reviewers: note

The first re-run of gen_srcs after this lands will produce a one-time diff on every generated BUILD.bazel (header swap + reformat + label shortening + load symbol sort). The round-trip test fails explicitly if buildifier is absent rather than silently skipping.

@miridius miridius force-pushed the dave/tool-518-fix-formatting-of-generated-build-files branch from bb0736d to e190648 Compare March 20, 2026 12:30
@miridius miridius marked this pull request as ready for review March 20, 2026 12:37
@miridius miridius requested a review from a team March 20, 2026 12:37

@english english left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice one!

@miridius miridius force-pushed the dave/tool-518-fix-formatting-of-generated-build-files branch from e190648 to 02fb218 Compare March 20, 2026 20:23
@miridius miridius force-pushed the dave/tool-518-fix-formatting-of-generated-build-files branch from 02fb218 to 105b9b0 Compare May 11, 2026 11:40
miridius added a commit that referenced this pull request May 11, 2026
Three additional rules to match `buildifier` output:

- **load() args sorted**: `load("...bzl", "clojure_library", "clojure_test")`
  not `load("...bzl", "clojure_test", "clojure_library")` — args after
  the bzl path go alphabetical.
- **`//pkg:pkg` shortened to `//pkg`**: when the target name equals the
  last path segment, omit the explicit `:name` (label-canonicalisation
  rule applied via `shorten-label` in `emit-bazel* String`).
- **trailing newline on generated BUILD.bazel**: both `gen-dir` and
  `gen-deps-build` now spit a final `\n`.

Caught dogfooding against the rules_clojure#84 Gazelle plugin output —
the remaining diffs after #85 were the buildifier rules above.
Rewrite `emit-bazel*` to produce output identical to
`buildifier --type=build`. A round-trip test pipes generated content
through buildifier and asserts zero diff.

Generated `BUILD.bazel` files previously used non-standard formatting:
all kwargs on one line, no attribute sorting, no trailing commas, an
`#autogenerated` comment header. IDEs configured with buildifier would
reformat the entire file on open, creating noisy diffs (banksy#15022
comment). The first re-run of `gen_srcs` after this lands will produce
a one-time diff on every generated BUILD.bazel.

Emission changes (gen_build.clj):
- attr-priority + sortable-list-attrs match buildifier's NamePriority +
  IsSortableListArg subsets
- label-sort-key uses phase ordering (: < // < @)
- emit-bazel-kwargs returns sorted "k = v" entry strings; multi-element
  list values render multi-line via emit-vector
- IPersistentList: inline when positional-only or single-kwarg-no-
  positional, multi-line otherwise (4-space arg indent, trailing comma)
- IPersistentList: load() args after the bzl path are sorted alphabetic
- IPersistentVector: standalone inline with ", " separator
- IPersistentMap: dict literals use `{"k": "v"}` (no space before `:`)
- emit-bazel* String: `//path/pkg:pkg` shortens to `//path/pkg`
- build-file-header replaces the "#autogenerated" comment with a
  Starlark triple-quoted docstring

Generated content layout (gen-dir, gen-deps-build):
- docstring header at top
- load() before package()
- distinct over dedupe in __clj_lib deps construction
- trailing newline at end of file

Tests (gen_build_test.clj):
- Vector inline (empty, single, multi-element)
- Inline function calls (positional-only, single-kwarg)
- Multi-kwarg multi-line with sorted attrs
- Attribute priority ordering
- Label sort phase ordering; unsortable attrs (aot) preserved
- Buildifier round-trip — fails explicitly when buildifier absent

CI / build:
- MODULE.bazel: `buildifier_prebuilt` 8.5.1.2 as a dev_dependency
- test BUILD: gen-build-test declares buildifier as a `data` dep and
  exports its runfiles path via the `BUILDIFIER` env var; the test
  resolves it through test-utils/runfiles-env — no PATH lookup
- .circleci/config.yml: no install step needed

Addresses: TOOL-518
@miridius miridius force-pushed the dave/tool-518-fix-formatting-of-generated-build-files branch from b1cf607 to 7945bd3 Compare May 11, 2026 17:33
@miridius miridius requested a review from english May 11, 2026 21:16
@miridius

miridius commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

@english — heads-up that the branch has changed substantively since your approval on e190648. The current HEAD is 7945bd3 (single squashed commit). Additions:

  1. buildifier_prebuilt is now a bazel dev_dependency. The round-trip test you reviewed relied on (shell/sh "which" "buildifier") to find buildifier on PATH, which didn't work in CI. gen-build-test now declares the binary as a data dep and the test resolves it through test-utils/runfiles-env (env var BUILDIFIER). Pins v8.5.1.2.
  2. Dict literal spacing: {"k" : "v"}{"k": "v"} (no space before colon).
  3. load() arg sort: positional symbol args after the bzl path go alphabetical (buildifier rule).
  4. Label shortening: //path/to/pkg:pkg collapses to //path/to/pkg when the target name equals the trailing path segment.
  5. Trailing newline at end of every generated BUILD.bazel.

(1) fixes a CI failure, (2)–(5) were caught dogfooding against #84's Gazelle plugin output in banksy.

Do you want to re-review or are you happy to keep the existing approval?

posted by Claude

@miridius miridius merged commit f7bef1a into main May 11, 2026
1 check passed
@miridius miridius deleted the dave/tool-518-fix-formatting-of-generated-build-files branch May 13, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants