This is an automated email from the ASF dual-hosted git repository.

airborne12 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 1cd8d296d69 [improvement](be) Stop exec_env.h from pulling in the 
inverted index writer stack (#66262)
1cd8d296d69 is described below

commit 1cd8d296d69f1358c08156a2fd3f1fdf356be9a0
Author: Jack <[email protected]>
AuthorDate: Fri Jul 31 14:58:55 2026 +0800

    [improvement](be) Stop exec_env.h from pulling in the inverted index writer 
stack (#66262)
    
    ### What problem does this PR solve?
    
    Issue Number: close #xxx
    
    Related PR: #xxx
    
    Problem Summary:
    
    `runtime/exec_env.h` included
    `storage/index/inverted/inverted_index_writer.h`, which nothing in that
    header actually needed: the two types it names,
    `InvertedIndexSearcherCache` and `InvertedIndexQueryCache`, are already
    forward declared a few lines below. The include was dead, but not free —
    `exec_env.h` is reached by most of the backend, so it put the whole
    inverted index writer stack (and through it CLucene) in front of ~1750
    translation units. Editing any index or index-writer header therefore
    rebuilt essentially all of BE.
    
    Measured on this tree from the reverse include graph, before → after:
    
    | header | TUs rebuilt before | after |
    | --- | ---: | ---: |
    | `storage/index/inverted/inverted_index_writer.h` | 1752 | **142** |
    | `storage/index/index_file_writer.h` | 1754 | **254** |
    
    Deleting the include alone does not compile, which is why it had
    survived. The `#ifdef BE_TEST` accessor `set_tmp_file_dir` was defined
    inline in the header:
    
    ```cpp
    void set_tmp_file_dir(std::unique_ptr<segment_v2::TmpFileDirs> 
tmp_file_dirs) {
        this->_tmp_file_dirs = std::move(tmp_file_dirs);
    }
    ```
    
    Assigning the `unique_ptr` destroys the old pointee, so `TmpFileDirs`
    had to be **complete** in every translation unit that includes
    `exec_env.h` — and that completeness was being supplied, by accident,
    through the dead include. Removing the include without this fix breaks
    every translation unit that instantiates the setter (29 of them on the
    tree where this was first tried), all with the same `invalid application
    of 'sizeof' to an incomplete type 'doris::segment_v2::TmpFileDirs'`.
    
    Worth noting: the inline body only exists under `BE_TEST`, so production
    builds were always clean and only the unit-test build exposed the
    requirement. That is part of why the dead include went unnoticed.
    
    The fix keeps the header free of the dependency — the accessor is
    declared in the header and defined in `exec_env.cpp`, which includes
    `storage/index/index_writer.h` for the complete type. The member
    `std::unique_ptr<TmpFileDirs>` needs nothing further, because
    `~ExecEnv()` is already out of line.
    
    **Guard against regression.** Explaining the rule does not hold the
    line; the include compiled fine for as long as it sat there. So
    `build-support/check-header-deps.py` turns it into a build error. Rules
    are declarative — per hub header, a subtree that must not be reachable
    through any chain of includes, plus explicit exceptions and the reason
    for each:
    
    ```python
    RULES = [(
        "runtime/exec_env.h",
        "storage/index/",
        {"storage/index/inverted/inverted_index_stats.h"},  # leaf POD struct 
via storage/olap_common.h
        "ExecEnv only names index types as pointers and already 
forward-declares them; ...",
    )]
    ```
    
    On a violation it prints the offending include chain and the fix, so the
    next person can see how they got there:
    
    ```
    error: runtime/exec_env.h must not reach storage/index/*
      chain:  runtime/exec_env.h
           -> storage/index/inverted/inverted_index_writer.h
           -> storage/index/index_file_writer.h
      fix:    forward-declare the type in the header and include the real 
header in the
              .cpp, or route it through a *_fwd.h
    ```
    
    `--report` ranks headers by how many translation units they can force a
    rebuild of, for finding the next one of these. Exceptions are listed
    explicitly rather than inferred, so widening the barrier stays a
    deliberate, reviewable act.
---
 be/src/runtime/exec_env.cpp                        |   4 +
 be/src/runtime/exec_env.h                          |  14 +-
 be/src/storage/index/index_file_writer.cpp         |   1 +
 .../inverted/query_v2/prefix_query/prefix_weight.h |   4 +
 be/src/storage/segment/encoding_info.h             |   1 +
 be/src/storage/tablet/tablet_schema.cpp            |   1 +
 be/src/storage/task/index_builder.cpp              |   1 +
 be/test/exprs/function/function_is_null_test.cpp   |   1 +
 be/test/io/fs/s3_file_writer_test.cpp              |   1 +
 .../compaction/ordered_data_compaction_test.cpp    |   1 +
 be/test/storage/index/index_builder_test.cpp       |   1 +
 .../common/inverted_index_gc_binlogs_test.cpp      |   1 +
 .../index_compaction_performance_test.cpp          |   1 +
 .../inverted/compaction/index_compaction_test.cpp  |   1 +
 .../index/inverted/empty_index_file_test.cpp       |   1 +
 .../inverted_index_compound_reader_test.cpp        |   1 +
 .../segment/inverted_index_file_reader_test.cpp    |   1 +
 .../segment/inverted_index_file_writer_test.cpp    |   1 +
 build-support/check-header-deps.py                 | 193 +++++++++++++++++++++
 19 files changed, 224 insertions(+), 6 deletions(-)

diff --git a/be/src/runtime/exec_env.cpp b/be/src/runtime/exec_env.cpp
index 0ce4544ddc8..3177c73cdc0 100644
--- a/be/src/runtime/exec_env.cpp
+++ b/be/src/runtime/exec_env.cpp
@@ -31,6 +31,7 @@
 #include "load/channel/load_stream_mgr.h"
 #include "runtime/fragment_mgr.h"
 #include "runtime/frontend_info.h"
+#include "storage/index/index_writer.h" // TmpFileDirs, completed here rather 
than in the header
 #include "storage/olap_define.h"
 #include "storage/storage_engine.h"
 #include "storage/tablet/tablet_manager.h"
@@ -44,6 +45,9 @@ void ExecEnv::set_inverted_index_searcher_cache(
         segment_v2::InvertedIndexSearcherCache* inverted_index_searcher_cache) 
{
     _inverted_index_searcher_cache = inverted_index_searcher_cache;
 }
+void ExecEnv::set_tmp_file_dir(std::unique_ptr<segment_v2::TmpFileDirs> 
tmp_file_dirs) {
+    _tmp_file_dirs = std::move(tmp_file_dirs);
+}
 void ExecEnv::set_storage_engine(std::unique_ptr<BaseStorageEngine>&& engine) {
     _storage_engine = std::move(engine);
 }
diff --git a/be/src/runtime/exec_env.h b/be/src/runtime/exec_env.h
index 3198be77337..affa6a0f048 100644
--- a/be/src/runtime/exec_env.h
+++ b/be/src/runtime/exec_env.h
@@ -35,7 +35,6 @@
 #include "load/memtable/memtable_memory_limiter.h"
 #include "runtime/cluster_info.h"
 #include "runtime/frontend_info.h" // TODO(zhiqiang): find a way to remove 
this include header
-#include "storage/index/inverted/inverted_index_writer.h"
 #include "storage/options.h"
 #include "storage/tablet/tablet_fwd.h"
 #include "util/threadpool.h"
@@ -318,11 +317,14 @@ public:
     }
 
 #ifdef BE_TEST
-    void set_tmp_file_dir(std::unique_ptr<segment_v2::TmpFileDirs> 
tmp_file_dirs) {
-        this->_tmp_file_dirs = std::move(tmp_file_dirs);
-    }
-    void set_ready() { this->_s_ready = true; }
-    void set_not_ready() { this->_s_ready = false; }
+    // Defined out of line on purpose: assigning the unique_ptr destroys the 
old
+    // pointee, which would require TmpFileDirs to be COMPLETE in every 
translation
+    // unit that includes this header -- and this header is included by most 
of the
+    // backend. Keeping the body in the .cpp lets the forward declaration above
+    // suffice.
+    void set_tmp_file_dir(std::unique_ptr<segment_v2::TmpFileDirs> 
tmp_file_dirs);
+    void set_ready() { _s_ready = true; }
+    void set_not_ready() { _s_ready = false; }
     void set_memtable_memory_limiter(MemTableMemoryLimiter* limiter) {
         _memtable_memory_limiter.reset(limiter);
     }
diff --git a/be/src/storage/index/index_file_writer.cpp 
b/be/src/storage/index/index_file_writer.cpp
index afd09c84620..fcaded4bd7a 100644
--- a/be/src/storage/index/index_file_writer.cpp
+++ b/be/src/storage/index/index_file_writer.cpp
@@ -30,6 +30,7 @@
 #include "storage/index/index_file_reader.h"
 #include "storage/index/index_storage_format_v1.h"
 #include "storage/index/index_storage_format_v2.h"
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/inverted_index_compound_reader.h"
 #include "storage/index/inverted/inverted_index_desc.h"
 #include "storage/index/inverted/inverted_index_fs_directory.h"
diff --git 
a/be/src/storage/index/inverted/query_v2/prefix_query/prefix_weight.h 
b/be/src/storage/index/inverted/query_v2/prefix_query/prefix_weight.h
index 5e863fd49ce..c62f69a94b3 100644
--- a/be/src/storage/index/inverted/query_v2/prefix_query/prefix_weight.h
+++ b/be/src/storage/index/inverted/query_v2/prefix_query/prefix_weight.h
@@ -24,6 +24,10 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
+// StdHeader.h must precede repl_wchar.h: it defines the CLUCENE_SHARED_EXPORT 
/
+// TCHAR macros repl_wchar.h uses but does not pull in itself (same pairing as
+// exprs/function/function_tokenize.cpp).
+#include <CLucene/StdHeader.h>
 #include <CLucene/config/repl_wchar.h>
 #include <CLucene/index/IndexReader.h>
 #include <CLucene/index/Term.h>
diff --git a/be/src/storage/segment/encoding_info.h 
b/be/src/storage/segment/encoding_info.h
index de670da0964..dfbf7b1d7cf 100644
--- a/be/src/storage/segment/encoding_info.h
+++ b/be/src/storage/segment/encoding_info.h
@@ -27,6 +27,7 @@
 #include "common/status.h"
 #include "storage/cache/page_cache.h"
 #include "storage/segment/options.h"
+#include "storage/types.h" // CppTypeTraits, used by the EncodingTraits base 
below
 #include "util/slice.h"
 
 namespace doris {
diff --git a/be/src/storage/tablet/tablet_schema.cpp 
b/be/src/storage/tablet/tablet_schema.cpp
index 8dec9913f88..44928c0b169 100644
--- a/be/src/storage/tablet/tablet_schema.cpp
+++ b/be/src/storage/tablet/tablet_schema.cpp
@@ -43,6 +43,7 @@
 #include "exec/common/hex.h"
 #include "exprs/aggregate/aggregate_function_simple_factory.h"
 #include "exprs/aggregate/aggregate_function_state_union.h"
+#include "storage/index/index_writer.h" // 
IndexColumnWriter::check_support_*_index
 #include "storage/index/inverted/analyzer/analyzer.h"
 #include "storage/index/inverted/inverted_index_parser.h"
 #include "storage/olap_common.h"
diff --git a/be/src/storage/task/index_builder.cpp 
b/be/src/storage/task/index_builder.cpp
index ef49626e143..f85fcee9ddf 100644
--- a/be/src/storage/task/index_builder.cpp
+++ b/be/src/storage/task/index_builder.cpp
@@ -23,6 +23,7 @@
 #include "common/status.h"
 #include "storage/index/index_file_reader.h"
 #include "storage/index/index_file_writer.h"
+#include "storage/index/index_writer.h" // IndexColumnWriter, complete type 
for member calls
 #include "storage/index/inverted/inverted_index_desc.h"
 #include "storage/index/inverted/inverted_index_fs_directory.h"
 #include "storage/olap_define.h"
diff --git a/be/test/exprs/function/function_is_null_test.cpp 
b/be/test/exprs/function/function_is_null_test.cpp
index c92c8964571..b10e7d76f31 100644
--- a/be/test/exprs/function/function_is_null_test.cpp
+++ b/be/test/exprs/function/function_is_null_test.cpp
@@ -20,6 +20,7 @@
 #include "exprs/function/is_not_null.h"
 #include "exprs/function/is_null.h"
 #include "storage/index/index_file_reader.h"
+#include "storage/index/index_writer.h"
 #include "storage/rowset/rowset_factory.h"
 #include "storage/storage_engine.h"
 
diff --git a/be/test/io/fs/s3_file_writer_test.cpp 
b/be/test/io/fs/s3_file_writer_test.cpp
index 13f9d12c501..3937d6e3856 100644
--- a/be/test/io/fs/s3_file_writer_test.cpp
+++ b/be/test/io/fs/s3_file_writer_test.cpp
@@ -59,6 +59,7 @@
 #include "io/io_common.h"
 #include "runtime/exec_env.h"
 #include "storage/index/index_file_writer.h"
+#include "storage/index/index_writer.h"
 #include "util/slice.h"
 #include "util/threadpool.h"
 #include "util/uuid_generator.h"
diff --git a/be/test/storage/compaction/ordered_data_compaction_test.cpp 
b/be/test/storage/compaction/ordered_data_compaction_test.cpp
index 43efedc13c1..9cc991de654 100644
--- a/be/test/storage/compaction/ordered_data_compaction_test.cpp
+++ b/be/test/storage/compaction/ordered_data_compaction_test.cpp
@@ -49,6 +49,7 @@
 #include "storage/compaction/cumulative_compaction.h"
 #include "storage/data_dir.h"
 #include "storage/delete/delete_handler.h"
+#include "storage/index/index_writer.h"
 #include "storage/olap_common.h"
 #include "storage/options.h"
 #include "storage/rowset/beta_rowset.h"
diff --git a/be/test/storage/index/index_builder_test.cpp 
b/be/test/storage/index/index_builder_test.cpp
index dd36ba3ab33..4d5d890c887 100644
--- a/be/test/storage/index/index_builder_test.cpp
+++ b/be/test/storage/index/index_builder_test.cpp
@@ -20,6 +20,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "storage/index/index_writer.h"
 #include "storage/olap_common.h"
 #include "storage/rowset/beta_rowset.h"
 #include "storage/rowset/rowset_factory.h"
diff --git 
a/be/test/storage/index/inverted/common/inverted_index_gc_binlogs_test.cpp 
b/be/test/storage/index/inverted/common/inverted_index_gc_binlogs_test.cpp
index ea7a71510f9..4ae64d8da7d 100644
--- a/be/test/storage/index/inverted/common/inverted_index_gc_binlogs_test.cpp
+++ b/be/test/storage/index/inverted/common/inverted_index_gc_binlogs_test.cpp
@@ -17,6 +17,7 @@
 
 #include <gtest/gtest.h>
 
+#include "storage/index/index_writer.h"
 #include "storage/rowset/beta_rowset_writer.h"
 #include "storage/rowset/rowset_factory.h"
 #include "storage/rowset/rowset_meta_manager.h"
diff --git 
a/be/test/storage/index/inverted/compaction/index_compaction_performance_test.cpp
 
b/be/test/storage/index/inverted/compaction/index_compaction_performance_test.cpp
index 083c1c8f42a..24c8ca78127 100644
--- 
a/be/test/storage/index/inverted/compaction/index_compaction_performance_test.cpp
+++ 
b/be/test/storage/index/inverted/compaction/index_compaction_performance_test.cpp
@@ -21,6 +21,7 @@
 #include <map>
 #include <string>
 
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/compaction/util/index_compaction_utils.cpp"
 #include "storage/utils.h"
 
diff --git 
a/be/test/storage/index/inverted/compaction/index_compaction_test.cpp 
b/be/test/storage/index/inverted/compaction/index_compaction_test.cpp
index 0f96372a5b6..9f8eb7070dc 100644
--- a/be/test/storage/index/inverted/compaction/index_compaction_test.cpp
+++ b/be/test/storage/index/inverted/compaction/index_compaction_test.cpp
@@ -17,6 +17,7 @@
 
 #include <gmock/gmock.h>
 
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/compaction/util/index_compaction_utils.cpp"
 #include "storage/utils.h"
 
diff --git a/be/test/storage/index/inverted/empty_index_file_test.cpp 
b/be/test/storage/index/inverted/empty_index_file_test.cpp
index 94f1a649320..af1e7f0630f 100644
--- a/be/test/storage/index/inverted/empty_index_file_test.cpp
+++ b/be/test/storage/index/inverted/empty_index_file_test.cpp
@@ -22,6 +22,7 @@
 #include "gtest/gtest_pred_impl.h"
 #include "io/fs/stream_sink_file_writer.h"
 #include "storage/index/index_file_writer.h"
+#include "storage/index/index_writer.h"
 #include "storage/olap_common.h"
 
 namespace doris {
diff --git a/be/test/storage/segment/inverted_index_compound_reader_test.cpp 
b/be/test/storage/segment/inverted_index_compound_reader_test.cpp
index eaef09a6ba2..040b1f2e370 100644
--- a/be/test/storage/segment/inverted_index_compound_reader_test.cpp
+++ b/be/test/storage/segment/inverted_index_compound_reader_test.cpp
@@ -39,6 +39,7 @@
 #include "runtime/exec_env.h"
 #include "storage/index/index_file_reader.h"
 #include "storage/index/index_file_writer.h"
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/inverted_index_desc.h"
 #include "storage/index/inverted/inverted_index_fs_directory.h"
 #include "storage/tablet/tablet_schema.h"
diff --git a/be/test/storage/segment/inverted_index_file_reader_test.cpp 
b/be/test/storage/segment/inverted_index_file_reader_test.cpp
index ba14fbb8359..76b25891277 100644
--- a/be/test/storage/segment/inverted_index_file_reader_test.cpp
+++ b/be/test/storage/segment/inverted_index_file_reader_test.cpp
@@ -28,6 +28,7 @@
 #include "storage/data_dir.h"
 #include "storage/index/index_file_reader.h"
 #include "storage/index/index_file_writer.h"
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/inverted_index_cache.h"
 #include "storage/index/inverted/inverted_index_desc.h"
 #include "storage/index/inverted/inverted_index_fs_directory.h"
diff --git a/be/test/storage/segment/inverted_index_file_writer_test.cpp 
b/be/test/storage/segment/inverted_index_file_writer_test.cpp
index f0a7680d251..a7a64c28732 100644
--- a/be/test/storage/segment/inverted_index_file_writer_test.cpp
+++ b/be/test/storage/segment/inverted_index_file_writer_test.cpp
@@ -23,6 +23,7 @@
 #include "storage/index/index_file_writer.h"
 #include "storage/index/index_storage_format_v1.h"
 #include "storage/index/index_storage_format_v2.h"
+#include "storage/index/index_writer.h"
 #include "storage/index/inverted/inverted_index_fs_directory.h"
 #include "storage/storage_engine.h"
 
diff --git a/build-support/check-header-deps.py 
b/build-support/check-header-deps.py
new file mode 100755
index 00000000000..315f63eaa3b
--- /dev/null
+++ b/build-support/check-header-deps.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# 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.
+
+"""Layering guard for BE headers.
+
+A handful of headers -- runtime/exec_env.h above all -- are included, directly 
or
+not, by most of the backend. Anything they pull in becomes a dependency of 
nearly
+every translation unit, so one stray include can multiply rebuild cost by an 
order
+of magnitude and does so silently: the code still compiles, only the build 
slows
+down. This script turns that into a build error instead.
+
+Each rule names a hub header and a directory prefix it must not reach. Keeping 
a
+hub out of a subsystem is what lets that subsystem's headers be edited cheaply.
+
+Usage:
+    build-support/check-header-deps.py            # enforce the rules
+    build-support/check-header-deps.py --report   # rank headers by rebuild 
cost
+"""
+
+import argparse
+import collections
+import os
+import re
+import sys
+
+INCLUDE = re.compile(r'^\s*#\s*include\s+"([^"]+)"')
+SOURCE_ROOTS = ("be/src", "be/test")
+INCLUDE_ROOT = "be/src"
+
+# (hub header, forbidden prefix, allowed exceptions, why).
+# A hub must not reach the forbidden subtree through ANY chain of includes, 
except
+# through the listed headers. An exception is only appropriate for a header 
that
+# carries declarations or plain data types and pulls in nothing of its own -- 
adding
+# one should be a deliberate decision, which is exactly why they are listed 
here
+# instead of being inferred.
+RULES = [
+    (
+        "runtime/exec_env.h",
+        "storage/index/",
+        {
+            # A leaf statistics struct with no project includes of its own, 
which
+            # storage/olap_common.h carries as a plain data type.
+            "storage/index/inverted/inverted_index_stats.h",
+        },
+        "ExecEnv only ever names index types as pointers and already 
forward-declares "
+        "them; reaching the index implementation headers from here puts the 
whole "
+        "index writer stack (and CLucene) in front of most of the backend",
+    ),
+]
+
+# Forward-declaration headers are the sanctioned way through a barrier: they 
carry
+# declarations only, so they cost nothing to include.
+FWD_SUFFIX = "_fwd.h"
+
+
+def load_includes():
+    """Maps each repo file to the list of project headers it includes."""
+    includes = {}
+    for root in SOURCE_ROOTS:
+        for directory, _, names in os.walk(root):
+            for name in names:
+                if not name.endswith((".h", ".hpp", ".cpp", ".cc")):
+                    continue
+                path = os.path.join(directory, name)
+                with open(path, encoding="utf-8", errors="ignore") as handle:
+                    includes[path] = [
+                        match.group(1)
+                        for match in (INCLUDE.match(line) for line in handle)
+                        if match
+                    ]
+    return includes
+
+
+def resolve(header, includes):
+    """Maps an include spelling to a repo path, or None when it is external."""
+    path = os.path.join(INCLUDE_ROOT, header)
+    return path if path in includes else None
+
+
+def reachable(start, includes):
+    """Every header reachable from `start`, with the chain that got there."""
+    chains = {start: [start]}
+    frontier = [start]
+    while frontier:
+        current = frontier.pop()
+        path = resolve(current, includes)
+        if path is None:
+            continue
+        for nxt in includes[path]:
+            if nxt in chains:
+                continue
+            chains[nxt] = chains[current] + [nxt]
+            frontier.append(nxt)
+    return chains
+
+
+def translation_units_affected(includes):
+    """How many translation units each header can force a rebuild of."""
+    users = collections.defaultdict(set)
+    for path, headers in includes.items():
+        for header in headers:
+            users[header].add(path)
+    counts = {}
+    for header in users:
+        seen, frontier = set(), [header]
+        while frontier:
+            current = frontier.pop()
+            for user in users.get(current, ()):
+                if user in seen:
+                    continue
+                seen.add(user)
+                if user.startswith(INCLUDE_ROOT + "/"):
+                    frontier.append(user[len(INCLUDE_ROOT) + 1:])
+        counts[header] = sum(1 for f in seen if f.endswith((".cpp", ".cc")))
+    return counts
+
+
+def enforce(includes):
+    failures = 0
+    for hub, forbidden, allowed, why in RULES:
+        if resolve(hub, includes) is None:
+            print(f"error: rule names a missing header: {hub}", 
file=sys.stderr)
+            failures += 1
+            continue
+        chains = reachable(hub, includes)
+        for header, chain in sorted(chains.items()):
+            if not header.startswith(forbidden) or header.endswith(FWD_SUFFIX):
+                continue
+            if header in allowed:
+                continue
+            failures += 1
+            print(f"error: {hub} must not reach {forbidden}*", file=sys.stderr)
+            print(f"  reason: {why}", file=sys.stderr)
+            print("  chain:  " + "\n       -> ".join(chain), file=sys.stderr)
+            print(
+                "  fix:    forward-declare the type in the header and include 
the "
+                "real header in the .cpp, or route it through a *_fwd.h",
+                file=sys.stderr,
+            )
+            break
+    return failures
+
+
+def report(includes):
+    counts = translation_units_affected(includes)
+    ranked = sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:30]
+    print(f"{'TUs rebuilt':>11}  header")
+    for header, count in ranked:
+        print(f"{count:>11}  {header}")
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+        "--report",
+        action="store_true",
+        help="rank headers by how many translation units they force a rebuild 
of",
+    )
+    args = parser.parse_args()
+
+    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+    os.chdir(root)
+    includes = load_includes()
+
+    if args.report:
+        report(includes)
+        return 0
+
+    failures = enforce(includes)
+    if failures:
+        print(f"\n{failures} header layering violation(s)", file=sys.stderr)
+        return 1
+    print(f"header layering: {len(RULES)} rule(s) satisfied")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to