mirror of
https://github.com/apache/doris.git
synced 2026-11-04 07:31:17 +00:00
### What problem does this PR solve? Related PR: #66510 (the BE build-time series, #66615..#66826, that this PR locks in) Problem Summary: The build-time series cut BE cold builds by ~49% and took `olap_common.h` / `uid_util.h` off the everything-rebuilds line. Every one of those wins can regress **silently**: a stray include, a lost `extern template`, a header added to the PCH — the code still compiles, only every build afterwards is slower, and nothing reports it. `#66052` already showed the failure mode: the layering rules in `check-header-deps.py` only worked because the author happened to maintain them by hand; nothing in CI runs them. This PR turns each known regression vector into a **fail-loud, seconds-level gate that runs at configure time**, so a violation is a build error on every path that builds the BE (CI, `build.sh`, the UT build) with zero extra CI wiring — the same mounting pattern the FE side already uses for its architecture gates (`exec-maven-plugin` running `check-fe-core-metadata-funnel.sh` at `validate`). **Checks** (pure text scans, ~1.3s combined, zero build dependency, zero false positives on current master): | Gate | Regression it stops | |---|---| | Header layering rules (30, +6 new) | a hub header (e.g. `exec_env.h`, `function.h`, `pch.h`) re-growing an include edge into a subsystem that was deliberately cut | | Third-party ban table (new) | `fmt`/`boost`/`concurrentqueue.h`/`<ranges>` returning to headers whose bodies were moved out of line to contain template-instantiation cost (`<ranges>` in a src header also breaks the `-fno-access-control` UT build on libc++) | | Two-axis budgets (new) | the edges nobody names: light hubs must stay light (include-closure budget, zero slack), heavy payloads must not spread (transitive-reach baseline +10%); `--closure` / `--reach` subcommands rank the offending edges for the fix | | pch whitelist (new) | any quoted include added to `pch/pch.h` — the single most leveraged regression in the repo (touch one such header and the whole backend plus the PCH rebuild) | | `check-extern-template-pairing.py` (new) | the one **silent** extern-template failure: a `.cpp` keeps the explicit instantiation but the header loses the `extern` declaration, so every TU quietly re-instantiates; also the loud direction (declaration with no definition), reported at once instead of at link | | `check-unity-skip-coverage.py` (new) | a test `#include`-ing a src `.cpp` that is not opted out of unity batching — reported at configure with the exact skip entry to add, instead of as duplicate symbols at the end of the BE UT link | `build-support/check-build-hygiene.sh` runs all three scripts; `be/CMakeLists.txt` executes it at configure time. Escape hatches, by design: - `-DENABLE_BUILD_HYGIENE=OFF` (default ON; a missing `python3` is a loud FATAL naming this switch, never a silent skip); - every budget/whitelist/rule table lives in the scripts themselves, so the legal way past a gate is a **one-line reviewed diff in the same PR** — the gate is never a dead end. Every failure message carries the mechanism (why this edge is expensive) and the concrete fix path. **Found by the new gates on day one, fixed here:** `core/types.h` has carried five `extern template struct fmt::formatter<Decimal*>;` declarations with **no matching definition anywhere** since #53483 — every member is defined in-class, so the linker never noticed; deleted. Also made the FE funnel self-test's `sed -i` invocation portable to BSD/macOS. Blind spot, by design: instantiations expanded from macros (e.g. `DECLARE_OPERATOR` in `operator.cpp`) are invisible to the pairing scan on both sides — macro bodies are skipped; a comment at the site says so. `be/README.md` (new) documents the gates, what to do when each fires, and the `*_fwd.h` convention as the sanctioned way through a layering barrier. The same rules are encoded for AI-assisted development and review: `be/AGENTS.md` (new — include discipline while writing BE code + review checkpoints), `build-support/AGENTS.md` (new — maintenance discipline for the gate scripts themselves), an include-src-cpp checkpoint in `be/test/AGENTS.md`, and a Header Hygiene entry in the root `AGENTS.md`, so coding agents and the review pipeline apply them automatically. ### Release note None ### Check List (For Author) - Test - [x] Manual test (add detailed scripts or steps below) - `build-support/check-build-hygiene.sh` on master: all green, ~1.3s, zero false positives. - Self-tests `bash build-support/tests/run.sh` (7/7 PASS): red/green injection per gate family — each seeded violation turns red with the mechanism + fix in the message, restoring the file turns it green again. - Real configure on macOS arm64: green path passes the gate section; with an injected violation, configure dies at the gate in under a second with the full message; `-DENABLE_BUILD_HYGIENE=OFF` bypasses; a missing `python3` is a loud exit-2 FATAL naming the switch. - This PR's own Linux CI exercises the configure mount on the second platform. - Behavior changed: - [x] Yes. BE configure now fails fast (with mechanism + fix in the message) on header-hygiene violations; emergency bypass via `-DENABLE_BUILD_HYGIENE=OFF`. No runtime behavior change — the only source-code changes are comments and the deletion of five never-defined `extern template` declarations. - Does this need documentation? - [x] No. Developer-facing docs are included in-repo (`be/README.md`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01EyZL631YTeuNXa9WLjvk7s --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
119 lines
5.1 KiB
Bash
Executable File
119 lines
5.1 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
# Unified entry for the BE build-hygiene gates. Runs every seconds-level,
|
|
# zero-build-dependency check that protects the backend's compile-time
|
|
# invariants:
|
|
#
|
|
# check-header-deps.py header layering rules, third-party bans,
|
|
# the two-axis closure/reach budgets and the
|
|
# pch include lock
|
|
# check-extern-template-pairing.py extern template declarations and explicit
|
|
# instantiation definitions stay paired in
|
|
# both directions
|
|
# check-unity-skip-coverage.py every src .cpp a test #includes is opted
|
|
# out of unity batching
|
|
#
|
|
# All checks are pure text scans and run in about a second combined. Every
|
|
# failure message carries the mechanism (why the edge/entry is expensive) and
|
|
# the fix path. All checks run even when an early one fails, so one pass shows
|
|
# everything there is to fix.
|
|
#
|
|
# Mounted at configure time by be/CMakeLists.txt (ENABLE_BUILD_HYGIENE, default
|
|
# ON), which is what makes a violation a build error rather than advice. To run
|
|
# by hand:
|
|
#
|
|
# build-support/check-build-hygiene.sh
|
|
#
|
|
# Exit code: 0 -- all checks passed; 1 -- at least one violation (details on
|
|
# stderr); 2 -- environment problem (no Python 3 interpreter).
|
|
#
|
|
# Self-tests: build-support/tests/test-build-hygiene-*.sh (run.sh runs them).
|
|
|
|
set -u
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# The gates are python3 sources, so they need a Python 3 interpreter -- which is
|
|
# not the interpreter the build itself uses. env.sh exports PYTHON as
|
|
# DORIS_BUILD_PYTHON_VERSION, defaulting to `python`; on the official build-env
|
|
# image (FROM almalinux:8) that is python2.7, under which every gate dies with a
|
|
# SyntaxError on its first f-string. So resolve an interpreter here and never
|
|
# read PYTHON.
|
|
#
|
|
# The probe order covers the two shapes that exist in practice: `python3` on
|
|
# developer boxes and most distros, and /usr/libexec/platform-python -- the
|
|
# RHEL8/AlmaLinux8 system interpreter (3.6), which the build-env image ships
|
|
# while carrying no /usr/bin/python3 at all.
|
|
#
|
|
# BUILD_HYGIENE_PYTHON names an interpreter explicitly (a venv, a non-standard
|
|
# prefix). It selects which python runs the gates; it cannot switch them off --
|
|
# that is -DENABLE_BUILD_HYGIENE=OFF.
|
|
is_python3() {
|
|
"$1" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 6) else 1)' \
|
|
>/dev/null 2>&1
|
|
}
|
|
|
|
PYTHON=""
|
|
if [ -n "${BUILD_HYGIENE_PYTHON:-}" ]; then
|
|
# An explicit choice is honoured strictly: if it is unusable, say so rather
|
|
# than quietly running the gates under some other interpreter.
|
|
if is_python3 "${BUILD_HYGIENE_PYTHON}"; then
|
|
PYTHON="${BUILD_HYGIENE_PYTHON}"
|
|
else
|
|
echo "check-build-hygiene: BUILD_HYGIENE_PYTHON=${BUILD_HYGIENE_PYTHON} is not found, or is not Python >= 3.6." >&2
|
|
echo "Point it at a Python 3 interpreter, or re-run with -DENABLE_BUILD_HYGIENE=OFF to skip the gates." >&2
|
|
exit 2
|
|
fi
|
|
else
|
|
for candidate in python3 python /usr/libexec/platform-python; do
|
|
if is_python3 "${candidate}"; then
|
|
PYTHON="${candidate}"
|
|
break
|
|
fi
|
|
done
|
|
if [ -z "${PYTHON}" ]; then
|
|
echo "check-build-hygiene: no Python >= 3.6 found (tried python3, python, /usr/libexec/platform-python); the build-hygiene gates need one." >&2
|
|
echo "Install python3 or set BUILD_HYGIENE_PYTHON, or re-run with -DENABLE_BUILD_HYGIENE=OFF to skip the gates." >&2
|
|
exit 2
|
|
fi
|
|
fi
|
|
|
|
FAILURES=0
|
|
for check in \
|
|
check-header-deps.py \
|
|
check-extern-template-pairing.py \
|
|
check-unity-skip-coverage.py; do
|
|
if ! "${PYTHON}" "${SCRIPT_DIR}/${check}"; then
|
|
FAILURES=$((FAILURES + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "${FAILURES}" -ne 0 ]; then
|
|
echo "" >&2
|
|
echo "build hygiene: ${FAILURES} check(s) failed (details above)." >&2
|
|
echo "Each message ends with its fix path; the budget/whitelist tables live in the" >&2
|
|
echo "check scripts themselves, so a deliberate change is a one-line, reviewed diff." >&2
|
|
exit 1
|
|
fi
|
|
# Name the resolved interpreter: it is the one thing about this run that varies
|
|
# per environment, and the configure log is where you look when a gate behaves
|
|
# differently in CI than it did locally.
|
|
echo "build hygiene: all checks passed (python: ${PYTHON} $("${PYTHON}" -c 'import sys; print("%d.%d.%d" % sys.version_info[:3])'))"
|