Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ build-tsan
build-coverage
build-coverage-svconstructor

# Submodule checkouts kept for CMake (see internal/bazel-migration-plan.md's
# Phase 1 "Bazel-only" scope decision) -- Bazel fetches its own separate
# copies of these instead (git_repository/bazel_dep in MODULE.bazel), so
# these on-disk checkouts should never be scanned as Bazel packages (some
# ship their own unrelated BUILD files with unmet dev dependencies, e.g.
# thirdparty/googletest's own test suite needs rules_python we don't have).
# Submodule checkouts kept for CMake -- Bazel takes these as modules
# (bazel_dep in MODULE.bazel) instead, so these on-disk checkouts should
# never be scanned as Bazel packages (some ship their own unrelated BUILD
# files with unmet dev dependencies).
thirdparty/googletest
thirdparty/naja-verilog
thirdparty/cpptrace
Expand Down
15 changes: 14 additions & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
#
# SPDX-License-Identifier: Apache-2.0

# Modules not yet on the Bazel Central Registry come from their open
# bazel-central-registry pull requests, each pinned to a commit; drop a
# line once its PR has merged. Everything else comes from BCR.
# bison 3.8.2.bcr.10 bazelbuild/bazel-central-registry#10879
# sv-lang 11.0.0-20260701-b60d729d.bcr.1 bazelbuild/bazel-central-registry#10882
# naja-if 0.0.0-20260723-099677d9 bazelbuild/bazel-central-registry#10881
# naja-verilog 0.0.0-20260909-be6544b1 bazelbuild/bazel-central-registry#10885
common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-registry/bb42d7dc14939aaf578926cf1c93bd987ca9fac7/
common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-registry/5888fdb942da7cf4a6eecd8d5bc07c777e82c4ee/
common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-registry/377d10e631e9461794687b9ff1d4bbbde0c9ad1d/
common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-registry/649d563703219943c013c9e5af8884ea81b2ab19/
common --registry=https://bcr.bazel.build/

# Matches CMakeLists.txt:59's `set(CMAKE_CXX_STANDARD 20)` -- applied
# globally here rather than per-target, same as the CMake build.
build --cxxopt=-std=c++20
Expand All @@ -18,7 +31,7 @@ build -c opt
# Force fully static linking on every platform. Found via live Ubuntu
# CI (internal/bazel-migration-plan.md, Phase 8, task #26): Bazel's
# default --dynamic_mode on Linux can auto-split a large cc_library
# (here, @slang//:slang's libsvlang.a) that's shared across multiple
# (here, @sv-lang//:libsvlang) that's shared across multiple
# test binaries into an intermediate `_solib_k8/*.so`, built with only
# what that specific linker invocation resolved at the time -- which can
# come up short on symbols (`slang::SourceLocation::NoLocation`, a
Expand Down
2 changes: 1 addition & 1 deletion .bazelversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.4.2
8.6.0
20 changes: 20 additions & 0 deletions .bcr/metadata.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"homepage": "https://github.com/najaeda/naja",
"maintainers": [
{
"github": "xtofalex",
"github_user_id": 3635601,
"name": "Christophe Alexandre"
},
{
"github": "nanocoh",
"github_user_id": 115838389,
"name": "Noam Cohen"
}
],
"repository": [
"github:najaeda/naja"
],
"versions": [],
"yanked_versions": {}
}
17 changes: 17 additions & 0 deletions .bcr/presubmit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
matrix:
platform: ["ubuntu2204", "ubuntu2404", "macos_arm64"]
bazel: ["8.x", "9.x"]
tasks:
verify_targets:
name: Verify build targets
platform: ${{ platform }}
bazel: ${{ bazel }}
build_flags:
- "--cxxopt=-std=c++20"
build_targets:
- "@naja//src/dnl:naja_dnl"
- "@naja//src/nl/formats/systemverilog:naja_snl_systemverilog"
- "@naja//src/nl/formats/verilog:naja_snl_verilog"
- "@naja//src/nl/netlist/serialization/capnp:naja_nl_dump"
- "@naja//src/nl/python/pyloader:naja_snl_pyloader"
- "@naja//src/optimization:naja_opt"
5 changes: 5 additions & 0 deletions .bcr/source.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"integrity": "",
"strip_prefix": "{REPO}-{VERSION}",
"url": "https://github.com/{OWNER}/{REPO}/archive/refs/tags/{TAG}.tar.gz"
}
39 changes: 7 additions & 32 deletions .github/workflows/macos-bazel.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
name: macOS Bazel Build

# CMake is naja's primary build system (see macos-build.yml) -- this
# workflow keeps the Bazel side (MODULE.bazel, BUILD.bazel throughout
# the tree) validated as a smoke test rather than as CI's main gate. See
# CLAUDE.md's "Build systems: CMake (primary) + Bazel (validated smoke
# test)" section, and internal/bazel-migration-plan.md for the full
# history of how this was built out and why it's not CI's primary path.
# CMake is naja's primary build system -- this workflow keeps the Bazel
# side (MODULE.bazel, BUILD.bazel throughout the tree) validated. The
# Bazel build needs no host packages: every library and code generator
# comes from a Bazel module (BCR, or an open BCR pull request listed in
# .bazelrc for the few that are not on BCR yet).

on:
push:
Expand All @@ -23,34 +22,10 @@ jobs:

steps:
- uses: actions/checkout@v7
# No `submodules: true` -- Bazel fetches naja-if/naja-verilog/slang
# itself (MODULE.bazel), doesn't use the thirdparty/ checkouts.
# dependency-sync-check.yml is what keeps these two build systems'
# No `submodules: true` -- Bazel doesn't use the thirdparty/
# checkouts. dependency-sync-check.yml keeps the two build systems'
# pins from silently drifting apart.

- name: Install dependencies
# tomlplusplus here is for slang's *own* internal find_package()
# (bazel/thirdparty/slang_host_prefixes.bzl queries `brew --prefix
# tomlplusplus` for slang's CMAKE_PREFIX_PATH) -- unrelated to this
# repo's own @tomlplusplus (vendored via git_repository since
# libtomlplusplus-dev isn't apt-installable on Ubuntu; still needed
# here regardless of that).
run: |
brew install capnp tbb bison flex boost fmt tomlplusplus pkg-config

- name: Set Homebrew paths
# Xcode's own toolchain ships an ancient BSD bison/flex
# (/Applications/Xcode*/.../usr/bin) that otherwise shadows
# Homebrew's -- e.g. that bison doesn't understand GNU bison's
# -Wconflicts-sr flag (bazel/lefdef_bison.bzl,
# bazel/thirdparty/*/system_tool.bzl's `which("bison")` picks
# whichever comes first on PATH).
run: |
echo "/usr/local/opt/flex/bin" >> $GITHUB_PATH
echo "/usr/local/opt/bison/bin" >> $GITHUB_PATH
echo "/opt/homebrew/opt/flex/bin" >> $GITHUB_PATH
echo "/opt/homebrew/opt/bison/bin" >> $GITHUB_PATH

- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
Expand Down
50 changes: 7 additions & 43 deletions .github/workflows/ubuntu-bazel.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
name: Ubuntu Bazel Build

# CMake is naja's primary build system (see ubuntu-build.yml) -- this
# workflow keeps the Bazel side (MODULE.bazel, BUILD.bazel throughout
# the tree) validated as a smoke test rather than as CI's main gate. See
# CLAUDE.md's "Build systems: CMake (primary) + Bazel (validated smoke
# test)" section, and internal/bazel-migration-plan.md for the full
# history of how this was built out and why it's not CI's primary path.
#
# @fmt//:fmt and @tomlplusplus//:tomlplusplus are vendored (git_repository
# + bazel/thirdparty/{fmt,tomlplusplus}_overlay.BUILD in MODULE.bazel,
# each pinned to the exact version slang itself requires) rather than
# pkg-config-wrapped like tbb/boost -- not installed via apt here at all.
# Found the hard way on live CI: tomlplusplus isn't apt-installable on
# either Ubuntu version at all; fmt *is* installable, but too old
# (libfmt-dev is 8.1.1/22.04, 9.1.0/24.04, both below slang's `fmt>=12.2`
# requirement), and pkg-config-wrapping that stale apt fmt caused a real
# `fmt::v12::...` undefined-reference link failure (libsvlang.a links
# against the real fmt 12.2.0 slang's own build fetches over the network
# when its `find_package()` rejects the old apt one -- `requires-network`
# on slang's cmake() rule permits that fetch). Vendoring both removes
# this whole category of platform-dependent version mismatch.
#
# Single default toolchain for now (no gcc/clang matrix) -- kept simple
# until there's a concrete reason to add it back.
# CMake is naja's primary build system -- this workflow keeps the Bazel
# side (MODULE.bazel, BUILD.bazel throughout the tree) validated. The
# Bazel build needs no host packages: every library and code generator
# comes from a Bazel module (BCR, or an open BCR pull request listed in
# .bazelrc for the few that are not on BCR yet).

on:
push:
Expand All @@ -40,28 +22,10 @@ jobs:

steps:
- uses: actions/checkout@v7
# No `submodules: true` -- Bazel fetches naja-if/naja-verilog/slang
# itself (MODULE.bazel), doesn't use the thirdparty/ checkouts.
# dependency-sync-check.yml is what keeps these two build systems'
# No `submodules: true` -- Bazel doesn't use the thirdparty/
# checkouts. dependency-sync-check.yml keeps the two build systems'
# pins from silently drifting apart.

- name: Install dependencies
# libboost-dev: not needed for naja's own code (Boost is vendored
# via a pinned http_archive tarball, task #11) -- needed because
# slang is wrapped via rules_foreign_cc's cmake() rule, and slang's
# own nested CMake build does a real find_package(Boost) against
# the system install. libtbb-dev: no BCR module exists for oneTBB;
# bazel/thirdparty/tbb/repo.bzl locates it via pkg-config, so the
# real library has to already be on the host. See
# internal/bazel-migration-plan.md's CI apt dependency audit for
# the full per-package reasoning (also covers capnproto/
# libcapnp-dev, dropped, and libfl-dev, kept).
run: |
sudo apt-get update
sudo apt-get install -yq \
libboost-dev libfl-dev libtbb-dev \
bison flex m4 pkg-config python3-dev git

- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
Expand Down
5 changes: 5 additions & 0 deletions .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Files: CLAUDE.md AGENTS.md
Copyright: 2026 The Naja Authors.
License: Apache-2.0

Files: .bcr/*
Copyright: 2026 The Naja Authors.
License: Apache-2.0

Files: src/nl/formats/hdl/BUILD.bazel src/nl/formats/hdl/CMakeLists.txt
src/nl/formats/vhdl/BUILD.bazel src/nl/formats/vhdl/CMakeLists.txt
src/vhdl/BUILD.bazel src/vhdl/CMakeLists.txt src/vhdl/README.md
Expand Down Expand Up @@ -95,3 +99,4 @@ Files: tutorials/README.md tutorials/images/*
tutorials/notebooks/06_fanout_analysis.ipynb
Copyright: 2024 The Naja Authors <https://github.com/najaeda/naja/blob/main/AUTHORS>
License: Apache-2.0

97 changes: 79 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,38 +85,99 @@ Docker images), and most workflows use, and it's the one to reach for by
default. Bazel (`MODULE.bazel`, `BUILD.bazel` throughout the tree) is
kept in parallel as a validated smoke test only, via `ubuntu-bazel.yml`/
`macos-bazel.yml` (`bazel build //... && bazel test //...`, no
submodules — bzlmod fetches its own copies of shared dependencies). It
submodules, no host packages — every dependency is a `bazel_dep`). It
is **not** CI's primary gate and doesn't need to track every workflow's
behavior (sanitizer suppressions, coverage flags, etc.) — just prove the
Bazel side keeps compiling and passing tests.

**Keep submodule pins and Bazel pins in sync.** CMake pins shared
upstream dependencies via git submodules (`.gitmodules`, `thirdparty/*`);
Bazel pins its own copies of the *same* dependencies via
`git_override()`/`git_repository()` commits in `MODULE.bazel`. Nothing
forces these to move together — bumping one without the other silently
makes the two build systems test different upstream code. When you bump
a submodule commit (or vice versa), update the matching `MODULE.bazel`
pin in the same change:

- `cpptrace`, `slang`: must be an **exact** commit match.
- `naja-if`, `naja-verilog`: these are the project's own forks, and
Bazel tracks a separate `bazel-support` branch (native Bazel BUILD
files added on top) rather than the branch CMake tracks — so an exact
match isn't meaningful. Instead, the submodule's pinned commit must be
an **ancestor of (or equal to)** the `bazel-support` pin, i.e.
`bazel-support` must never fall behind main.
Bazel takes the *same* dependencies as `bazel_dep`s, and those not on
BCR are pinned to commits by their registry entries
(`modules/<name>/<version>/source.json` in the registries `.bazelrc`
lists). Nothing forces these to move together —
bumping one without the other silently makes the two build systems test
different upstream code. When you bump a submodule commit (or vice
versa), submit the matching BCR version and point `MODULE.bazel` at it
in the same change:

- `slang` (Bazel module `sv-lang`), `naja-verilog`: must be an
**exact** commit match.
- `naja-if`: Bazel tracks a separate `bazel-support` branch (native
Bazel BUILD files added on top) rather than the branch CMake tracks —
so an exact match isn't meaningful. Instead, the submodule's pinned
commit must be an **ancestor of (or equal to)** the `bazel-support`
pin, i.e. `bazel-support` must never fall behind main.
- `googletest`: deliberately excluded — CMake pins an old submodule dev
commit, Bazel takes a BCR release (`1.17.0.bcr.2`). Different
dependency-sourcing mechanisms entirely; not meant to track in
lockstep.
commit, Bazel takes a BCR release. Different dependency-sourcing
mechanisms entirely; not meant to track in lockstep.

This is enforced automatically: `ci/check_submodule_bazel_sync.py`
(run by `.github/workflows/dependency-sync-check.yml` on every push/PR)
checks exactly this and fails CI if a pin has drifted. Run it locally
after bumping any of these dependencies: `python3
ci/check_submodule_bazel_sync.py`.

## Bazel: a BCR-ready module

naja's Bazel build is meant to be published to the Bazel Central
Registry (BCR) and consumed by other modules (kepler-formal does) with a
plain `bazel_dep`. Keep it that way. Invariants:

- `MODULE.bazel` contains only `bazel_dep`s. No `http_archive`,
`git_override`, `archive_override`, module extensions or repository
rules of our own; overrides are ignored for non-root modules, so they
would only hide breakage that consumers then hit.
- Nothing runs cmake/make inside Bazel (no `rules_foreign_cc`), and
nothing is found on the host (`PATH`, pkg-config, `python3-config`,
system headers). Tools and libraries come from Bazel modules: bison
and flex rules from BCR `bison`/`flex`, Python from `rules_python`.
- Load every rule (`@rules_cc//cc:cc_library.bzl`,
`@rules_shell//shell:sh_test.bzl`, …); Bazel 9 has no native ones.
- Fix a dependency in its own module (registry entry or upstream), never
by patching its BUILD files from this repository, and never by asking
consumers to patch naja.

**Where dependencies come from.** Everything comes from BCR. A module
version that is not on BCR yet comes from its open
bazel-central-registry pull request: `.bazelrc` lists the PR's commit
(`https://raw.githubusercontent.com/<fork>/bazel-central-registry/<sha>/`)
ahead of BCR, and Bazel takes each `name@version` from the first
registry that has it. Drop the line once the PR merges. Unreleased
commits use `<release>-<YYYYMMDD>-<commit>` versions. Never change a
version's contents once something depends on it; add a new version.
When a PR needs a fix, push a new commit (don't force-push, so pinned
commits stay reachable) and move the pin.

**Publishing to BCR**: one module per bazel-central-registry pull
request, each based on BCR main, so each goes in on its own. naja itself is
published from a release tag by the publish-to-bcr app, using the
templates in `.bcr/` (maintainers: xtofalex, nanocoh), once its
dependencies are on BCR. BCR rejects symlinks in entries, so
`overlay/MODULE.bazel` is a copy.

**Known traps** (each already fixed; don't reintroduce):

- hermetic toolchains (BCR `llvm`, used by kepler-formal) pass libc
headers as early `-isystem` flags. Libraries whose own headers must
shadow libc's (gnulib in bison) need `-I`, not `includes = [...]`;
hence the registry's `bison 3.8.2.bcr.10`. Building only with the host
toolchain hides this class of bug, and also hides headers leaking in
from `/usr/include` (slang's `boost/regex.hpp` did).
- `cc_shared_library` (`naja_runtime`) drops linker inputs its graph
aspect cannot see: rules must advertise `CcInfo` and own their linker
inputs. That's why `src/nl/python/pyloader/python_libs.bzl` re-owns
libpython instead of using `current_py_cc_libs` directly. Libraries
linked into `naja_runtime` that a binary also uses (TBB, zlib) must be
in its `exports_filter`, or the binary links a second copy.
- `NAJA_GIT_HASH` comes from the module version
(`src/core/naja_version.bzl`): "unknown" when naja is the root module.

**Before merging Bazel changes**, besides `bazel test //...` here, build
naja as a dependency with a hermetic toolchain: kepler-formal's tests
(`bazel test //... --override_module=naja=<path to this checkout>`) are
the reference consumer.

## Conventions

- Whenever the `najaeda` version is incremented, update the pinned `najaeda` version in all Colab tutorials under `tutorials/notebooks/` and the local installation command in `tutorials/README.md` in the same change.
Expand Down
Loading
Loading