Skip to content

Replace cc_static_library with a deprecation stub - #274

Merged
pcrumley merged 2 commits into
mainfrom
deprecate-cc-static-library
Sep 14, 2026
Merged

pcrumley merged 2 commits into
mainfrom
deprecate-cc-static-library

Conversation

@pcrumley

@pcrumley pcrumley commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Supersedes #273, which fixed a bug in this rule. The better fix is not to ship the rule.

Why

Every shipping consumer already loads cc_static_library from rules_cc, each via a one line change to the load statement:

  • starling c55b8e434d (AP-5401, 2026-07-09)
  • libpal swift-nav/libpal#407 (2026-06-29)
  • starling-core swift-nav/starling-core#1736 (2026-07-17)

Org-wide code search for the symbol returns 7 files: our two, and those three repos — all already on upstream. As a control, a search for rules_swiftnav//cc:defs.bzl returns 202 files, so the index is live and broad; 7 is a real number rather than an empty result.

Upstream also cannot hit the bug #273 fixed, because it never merges archives. It collects the object files of the transitive deps and runs one archive action over them, so input archives are never nested as members — the failure ours produced on macOS, where the toolchain archiver is llvm-ar and llvm-ar rc out.a a.a b.a yields an archive the linker rejects with archive member 'libfoo.a' not a mach-o file.

Why a stub rather than deleting the symbol

Code search only indexes default branches. Walking every branch of the three consumer repos found 1,528 branches still loading this from us:

repo branches on upstream still on ours no usage
starling 2,851 84 1,159 1,606
starling-core 483 32 276 172
libpal 218 11 93 112

Most are abandoned personal branches going back to March 2023. But some are active release lines — orion-v30.0-release (last commit 2026-09-03), orion-v31.0-release (2026-08-20), starling-v1.93.0-release and others.

Their exposure today is nil, because they pin old versions (starling@orion-v30.0-release → rules_swiftnav 0.10.0, @orion-v31.0-release → 0.18.1; starling-core 0.11.0 and 0.19.0; current is 0.29.0). They keep building against what they pin. But whenever one of them is bumped, this stub gives them a message naming the target and the exact load line to use, instead of an opaque missing-symbol error:

ERROR: .../BUILD.bazel:3:18: in cc_static_library
Error in fail: cc_static_library was removed from rules_swiftnav; target 'merged'
must load it from rules_cc instead:
    load("@rules_cc//cc:cc_static_library.bzl", "cc_static_library")
Upstream emits libmerged.a rather than merged.a, and provides no CcInfo
(wrap it in cc_import to use it as a dep).

Both migration gotchas are in the message because neither is guessable. Suggest keeping the stub for more than one release, given the release branches above.

cc/defs.bzl still loads normally for every other symbol — only calling the macro fails. That matters, since 202 files in the org load that file.

Upstream is a drop-in for the two cgo consumers

These were the reason to keep the rule. Built upstream cc_static_library on the same deps and compared:

phoebe //:gnss_cgo CSL //:orion_cgo
members 832 vs 832, diff empty 832 vs 832, diff empty
nested .a 0 0
defined symbols 73,123 vs 73,123, diff empty 72,395 vs 72,395, diff empty
symbol_check passes passes
cgo build + tests green green

Neither closure contains any cc_import or prebuilt archive, so upstream's "deps without object files are dropped" behaviour doesn't apply. Follow-ups: swift-nav/phoebe#1929 and the CSL PR.

Tests

The regression example from #273 is kept, retargeted at upstream cc_static_library. The rule is gone, but the failure it caught was never really the rule's — the archiver comes from whichever toolchain is registered, and the llvm20 toolchains here wrap llvm-ar. A wrapper or toolchain change that regresses the archiver would reintroduce nested archives silently, because an archive full of nested archives still builds fine and only fails at link time.

Both halves of the guard survive:

  • merged_members_test — the archive holds .o members and no nested .a
  • merged_link_test — a cc_test links against the merged archive alone and calls into every folded library, including the transitive dep (fibonacci → add)

The archive now arrives through srcs out of necessity rather than care: upstream provides no CcInfo, so it cannot go in deps at all.

I checked the member assertion isn't vacuous by building a nested archive by hand and confirming the grep rejects it:

$ ar rc nested.a liba.a && ar t nested.a
liba.a
RESULT: guard fires -- nested .a detected, genrule would exit 1

Full run: bazel test //... in examples/small_world is 9/9 green, and the two static library tests pass under --nocache_test_results against the upstream rule (output confirmed as libmerged.a with members sign.pic.o, fibonacci.pic.o, add.pic.o). Buildifier reports the same 3 pre-existing warnings as main, none new. Confirmed by hand that calling the macro produces the error above, and that loading cc/defs.bzl for other symbols is unaffected.

The macOS job is renamed strip-macos → archive-tools-macos, since it now guards both strip and the archiver — both Mach-O-only failure modes the Linux job cannot catch. main has no required status checks configured, so the rename breaks nothing.

Verification was on macOS; Linux CI covers the other path.

Not included

No version bump: bazel run //tools:release owns that.

Every shipping consumer already loads cc_static_library from rules_cc:
starling (c55b8e434d), libpal (#407) and starling-core (#1736) each
migrated in June/July with a one line change to the load statement. Our
copy has no users left on any default branch in the org.

Upstream cannot hit the bug ours had, because it never merges archives.
It collects the object files of the transitive deps and runs a single
archive action over them, so there is no nesting of input archives as
members -- the failure ours produced on macOS, where the toolchain
archiver is llvm-ar and `llvm-ar rc out.a a.a b.a` yields an archive the
linker rejects with "archive member 'libfoo.a' not a mach-o file".

Delete the rule but keep the macro name failing loudly for one release
rather than removing the symbol outright. Org code search only indexes
default branches, and a branch walk of starling-core found ~39 stale
branches still loading this from us. Those get a message naming the
target and the exact load line to use instead, rather than an opaque
missing symbol error, whenever they are rebased. Loading cc/defs.bzl for
any other symbol is unaffected, which matters because 202 files in the
org do exactly that.

Verified upstream is a drop in for the two cgo consumers that were the
reason to keep this rule. For both phoebe (//:gnss_cgo, 7 deps) and
caster_session_logger (//:orion_cgo), the upstream archive has an
identical member list (832 members, no nested archives) and an identical
defined symbol set (73123 and 72395 respectively), passes symbol_check,
and links and tests green through cgo on macOS.

No version bump; `bazel run //tools:release` owns that.
@pcrumley
pcrumley requested a review from a team as a code owner September 11, 2026 23:14
Ports the regression example from #273 to upstream cc_static_library. The
rule is gone, but the failure it hit was never really the rule's: the
archiver comes from whichever toolchain is registered, and the llvm20
toolchains here wrap llvm-ar. Invoked wrongly llvm-ar nests input
archives as members rather than merging their objects, and nothing
notices until link time, where macOS reports "archive member 'libfoo.a'
not a mach-o file".

Keeps both halves of the guard. merged_members_test asserts the archive
holds .o members and no nested .a; merged_link_test links a cc_test
against the merged archive alone and calls into every folded library,
including the transitive dep. The archive has to arrive through srcs
now, since upstream provides no CcInfo at all.

Confirmed the member assertion has teeth by building a nested archive by
hand and checking the grep rejects it, rather than trusting that a
passing test means the shape is checked.

Renames the macOS job to archive-tools-macos, since it now covers strip
and the archiver, both of which are Mach-O-only failure modes the Linux
job cannot catch. No required status checks are configured on main, so
the rename breaks nothing.

@martin4861 martin4861 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.

💯

@pcrumley
pcrumley merged commit c6324f9 into main Sep 14, 2026
2 checks passed
@pcrumley
pcrumley deleted the deprecate-cc-static-library branch September 14, 2026 17:11
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.

3 participants