This is an automated email from the ASF dual-hosted git repository.
chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new cd3948ce Refactor Bazel build with custom proto rules and build all
test targets (#3313)
cd3948ce is described below
commit cd3948ceac996e1cee49263e12b0f6746207f24c
Author: Bright Chen <[email protected]>
AuthorDate: Sun May 31 10:48:30 2026 +0800
Refactor Bazel build with custom proto rules and build all test targets
(#3313)
---
.bazelrc | 51 ++++-
.github/workflows/ci-linux.yml | 61 +++---
BUILD.bazel | 132 ++++++------
CMakeLists.txt | 4 +-
MODULE.bazel | 12 +-
.../tools/BUILD.bazel | 19 +-
bazel/tools/brpc_proto_library.bzl | 138 ++++++++++++
bazel/tools/proto_gen.bzl | 240 +++++++++++++++++++++
cmake/CMakeLists.download_gtest.in | 2 +-
docs/cn/bthread_tracer.md | 6 +-
example/BUILD.bazel | 34 +--
.../libunwind/1.8.3.brpc-no-unwind/source.json | 2 +-
src/bthread/task_tracer.cpp | 20 +-
src/butil/endpoint.cpp | 29 ++-
src/butil/third_party/symbolize/symbolize.cc | 115 +++++++---
src/bvar/detail/percentile.h | 18 +-
test/BUILD.bazel | 171 ++++++---------
test/brpc_alpn_protocol_unittest.cpp | 6 +-
test/brpc_http_rpc_protocol_unittest.cpp | 9 +-
test/brpc_protobuf_json_unittest.cpp | 7 +-
test/brpc_streaming_rpc_unittest.cpp | 4 +-
test/bthread_mutex_unittest.cpp | 3 +-
.../generate_unittests.bzl | 31 +--
test/root_runfiles.bzl | 79 +++++++
24 files changed, 881 insertions(+), 312 deletions(-)
diff --git a/.bazelrc b/.bazelrc
index 2ee10dda..abf05fc6 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -13,12 +13,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+# Bazel doesn't need more than 200MB of memory for local build based on memory
profiling:
+#
https://docs.bazel.build/versions/master/skylark/performance.html#memory-profiling
+# The default JVM max heapsize is 1/4 of physical memory up to 32GB which
could be large
+# enough to consume all memory constrained by cgroup in large host.
+# Limiting JVM heapsize here to let it do GC more when approaching the limit to
+# leave room for compiler/linker.
+# The number 3G is chosen heuristically to both support large VM and small VM
with RBE.
+# Startup options cannot be selected via config.
+startup --host_jvm_args=-Xmx3g
+startup --host_jvm_args="-DBAZEL_TRACK_SOURCE_DIRECTORIES=1"
+
# Default build options. These are applied first and unconditionally.
-#
common --registry=https://bcr.bazel.build
common --registry=https://baidu.github.io/babylon/registry
+common --registry=https://raw.githubusercontent.com/apache/brpc/master/registry
+build --verbose_failures
+# Keep SHT_SYMTAB in built binaries so google::Symbolize can resolve
+# in-binary functions (e.g. TestBody() in test binaries) by name
+# instead of falling back to "<unknown>". Bazel's default
+# `--strip=sometimes` strips debug/symbol sections in fastbuild mode,
+# which is what `bazel test` uses unless `-c dbg` is given.
+build --strip=never
build --cxxopt="-std=c++17"
build --copt="-fno-omit-frame-pointer"
# Use gnu17 for asm keyword.
@@ -33,8 +50,16 @@ build --features=per_object_debug_info
# We already have absl in the build, define absl=1 to tell googletest to use
absl for backtrace.
build --define absl=1
-# For brpc.
-test --define=BRPC_BUILD_FOR_UNITTEST=true
+# For UT.
+build:test --define BRPC_BUILD_FOR_UNITTEST=true
+# Hide libunwind's `_Unwind_*` symbols so they don't preempt libgcc_s at
+# runtime. Without this, pthread_exit / C++ exception unwinding crashes
+# when libunwind.so appears earlier in the dynamic link chain.
+# See registry/modules/1.8.1/overlay/BUILD.bazel for details.
+build:test --define with_bthread_tracer=false
+build:test --define libunwind_hide_unwind_symbols=true
+
+test --config=test
test --test_output=streamed
# Pass PATH, CC, CXX and LLVM_CONFIG variables from the environment.
@@ -42,3 +67,21 @@ build --action_env=CC
build --action_env=CXX
build --action_env=LLVM_CONFIG
build --action_env=PATH
+
+# Basic ASAN
+build:asan --define=with_asan=true
+build:asan --copt -fsanitize=address
+build:asan --linkopt -fsanitize=address
+# ASAN needs -O1 to get reasonable performance.
+build:asan --copt -O1
+build:asan --copt -fno-optimize-sibling-calls
+
+# macOS ASAN
+build:macos-asan --config=asan
+# Workaround, see https://github.com/bazelbuild/bazel/issues/6932
+build:macos-asan --copt -Wno-macro-redefined
+build:macos-asan --copt -D_FORTIFY_SOURCE=0
+# Dynamic link cause issues like: `dyld: malformed mach-o: load commands size
(59272) > 32768`
+build:macos-asan --dynamic_mode=off
+
+test:asan
--test_env=ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=1
diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml
index f2d6d692..8a36af60 100644
--- a/.github/workflows/ci-linux.yml
+++ b/.github/workflows/ci-linux.yml
@@ -103,33 +103,28 @@ jobs:
protobuf-install-dir: /protobuf-3.21.12
config-brpc-options: --cc=gcc --cxx=g++ --werror
- gcc-compile-with-bazel:
+ gcc-unittest-with-bazel:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- - run: bazel build --verbose_failures -- //... -//example/...
-
- gcc-compile-with-boringssl:
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v2
- - run: bazel build --verbose_failures --define with_mesalink=false
--define with_glog=true --define with_thrift=true --define
BRPC_WITH_BORINGSSL=true -- //... -//example/...
+ - run: bazel test //test/...
gcc-compile-with-bazel-all-options:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- run: |
- bazel build --verbose_failures \
- --define with_mesalink=false \
+ bazel build --define with_mesalink=false \
--define with_glog=true \
--define with_thrift=true \
+ --define BRPC_WITH_BORINGSSL=true \
--define with_debug_bthread_sche_safety=true \
--define with_debug_lock=true \
--define with_asan=true \
--define with_bthread_tracer=true \
--define BRPC_WITH_NO_PTHREAD_MUTEX_HOOK=true \
- -- //... -//example/...
+ --define with_babylon_counter=true \
+ -- //:brpc
clang-compile-with-make-protobuf:
runs-on: ubuntu-22.04
@@ -161,34 +156,32 @@ jobs:
protobuf-install-dir: /protobuf-3.21.12
config-brpc-options: --cc=clang --cxx=clang++ --werror
- clang-compile-with-bazel:
+ clang-unittest-with-bazel:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- - run: bazel build --verbose_failures --action_env=CC=clang -- //...
-//example/...
-
- clang-compile-with-boringssl:
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v2
- - run: bazel build --verbose_failures --action_env=CC=clang --define
with_mesalink=false --define with_glog=true --define with_thrift=true --define
BRPC_WITH_BORINGSSL=true -- //... -//example/...
+ - run: |
+ bazel test --test_output=streamed \
+ --action_env=CC=clang \
+ //test/...
clang-compile-with-bazel-all-options:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- run: |
- bazel build --verbose_failures \
- --action_env=CC=clang \
+ bazel build --action_env=CC=clang \
--define with_mesalink=false \
--define with_glog=true \
--define with_thrift=true \
+ --define BRPC_WITH_BORINGSSL=true \
--define with_debug_bthread_sche_safety=true \
--define with_debug_lock=true \
--define with_asan=true \
--define with_bthread_tracer=true \
--define BRPC_WITH_NO_PTHREAD_MUTEX_HOOK=true \
- -- //... -//example/...
+ --define with_babylon_counter=true \
+ -- //:brpc
clang-unittest:
runs-on: ubuntu-22.04
@@ -226,11 +219,25 @@ jobs:
cd test
sh ./run_tests.sh
- bazel-bvar-unittest:
+ clang-unittest-bazel-with-babylon-and-new-pb:
runs-on: ubuntu-22.04
+ env:
+ TEST_PROTOBUF_VERSION: "34.1"
+ # protobuf >= 34.x uses new ProtoInfo fields (option_deps,
+ # extension_declarations) introduced in Bazel 8.x. The repo's
+ # .bazelversion (7.2.1) is too old. bazelisk honors USE_BAZEL_VERSION.
+ USE_BAZEL_VERSION: "8.3.1"
steps:
- uses: actions/checkout@v2
- - run: bazel test --verbose_failures //test:bvar_test
- - run: bazel test --verbose_failures --define with_babylon_counter=true
//test:bvar_test
- - run: bazel test --verbose_failures --action_env=CC=clang //test:bvar_test
- - run: bazel test --verbose_failures --action_env=CC=clang --define
with_babylon_counter=true //test:bvar_test
+ - name: Override protobuf version for testing
+ run: |
+ sed -i -E "s/(bazel_dep\(name = ['\"]protobuf['\"], version =
['\"])[^'\"]+/\1${TEST_PROTOBUF_VERSION}/" MODULE.bazel
+ echo "After override:"
+ grep -E "bazel_dep\(name = ['\"]protobuf['\"]" MODULE.bazel
+ grep -qE "bazel_dep\(name = ['\"]protobuf['\"], version =
['\"]${TEST_PROTOBUF_VERSION}['\"]" MODULE.bazel \
+ || { echo "ERROR: failed to override protobuf version in
MODULE.bazel to ${TEST_PROTOBUF_VERSION}"; exit 1; }
+ - run: |
+ bazel test --action_env=CC=clang \
+ --define with_babylon_counter=true \
+ --define with_babylon_counter=true \
+ //test:brpc_unittests
diff --git a/BUILD.bazel b/BUILD.bazel
index c763cb3a..22cb5085 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_proto_library",
"objc_library")
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "objc_library")
+load("//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library")
licenses(["notice"]) # Apache v2
@@ -22,43 +22,46 @@ exports_files(["LICENSE"])
COPTS = [
"-fno-omit-frame-pointer",
- "-DBTHREAD_USE_FAST_PTHREAD_MUTEX",
- "-D__const__=__unused__",
- "-D_GNU_SOURCE",
- "-DUSE_SYMBOLIZE",
- "-DNO_TCMALLOC",
- "-D__STDC_FORMAT_MACROS",
- "-D__STDC_LIMIT_MACROS",
- "-D__STDC_CONSTANT_MACROS",
] + select({
- "//bazel/config:brpc_with_glog": ["-DBRPC_WITH_GLOG=1"],
- "//conditions:default": ["-DBRPC_WITH_GLOG=0"],
-}) + select({
- "//bazel/config:brpc_with_mesalink": ["-DUSE_MESALINK"],
- "//conditions:default": [""],
-}) + select({
- "//bazel/config:brpc_with_thrift": ["-DENABLE_THRIFT_FRAMED_PROTOCOL=1"],
- "//conditions:default": [""],
-}) + select({
- "//bazel/config:brpc_with_thrift_legacy_version": [],
- "//conditions:default": ["-DTHRIFT_STDCXX=std"],
-}) + select({
- "//bazel/config:brpc_with_rdma": ["-DBRPC_WITH_RDMA=1"],
- "//conditions:default": [""],
-}) + select({
- "//bazel/config:brpc_with_debug_bthread_sche_safety":
["-DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=1"],
- "//conditions:default": ["-DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=0"],
-}) + select({
- "//bazel/config:brpc_with_debug_lock": ["-DBRPC_DEBUG_LOCK=1"],
- "//conditions:default": ["-DBRPC_DEBUG_LOCK=0"],
-}) + select({
"//bazel/config:brpc_with_asan": ["-fsanitize=address"],
- "//conditions:default": [""],
-}) + select({
- "//bazel/config:brpc_with_no_pthread_mutex_hook":
["-DNO_PTHREAD_MUTEX_HOOK"],
- "//conditions:default": [""],
+ "//conditions:default": [],
})
+DEFINES = [
+ "BTHREAD_USE_FAST_PTHREAD_MUTEX",
+ "__const__=__unused__",
+ "_GNU_SOURCE",
+ "USE_SYMBOLIZE",
+ "NO_TCMALLOC",
+ "__STDC_FORMAT_MACROS",
+ "__STDC_LIMIT_MACROS",
+ "__STDC_CONSTANT_MACROS",
+] + select({
+ "//bazel/config:brpc_with_glog": ["BRPC_WITH_GLOG=1"],
+ "//conditions:default": ["BRPC_WITH_GLOG=0"],
+ }) + select({
+ "//bazel/config:brpc_with_mesalink": ["USE_MESALINK"],
+ "//conditions:default": [],
+ }) + select({
+ "//bazel/config:brpc_with_thrift": ["ENABLE_THRIFT_FRAMED_PROTOCOL=1"],
+ "//conditions:default": [],
+ }) + select({
+ "//bazel/config:brpc_with_thrift_legacy_version": [],
+ "//conditions:default": ["THRIFT_STDCXX=std"],
+ }) + select({
+ "//bazel/config:brpc_with_rdma": ["BRPC_WITH_RDMA=1"],
+ "//conditions:default": [],
+ }) + select({
+ "//bazel/config:brpc_with_debug_bthread_sche_safety":
["BRPC_DEBUG_BTHREAD_SCHE_SAFETY=1"],
+ "//conditions:default": ["BRPC_DEBUG_BTHREAD_SCHE_SAFETY=0"],
+ }) + select({
+ "//bazel/config:brpc_with_debug_lock": ["BRPC_DEBUG_LOCK=1"],
+ "//conditions:default": ["BRPC_DEBUG_LOCK=0"],
+ }) + select({
+ "//bazel/config:brpc_with_no_pthread_mutex_hook":
["NO_PTHREAD_MUTEX_HOOK"],
+ "//conditions:default": [],
+ })
+
LINKOPTS = [
"-pthread",
"-ldl",
@@ -93,7 +96,7 @@ LINKOPTS = [
"//conditions:default": [],
}) + select({
"//bazel/config:brpc_with_asan": ["-fsanitize=address"],
- "//conditions:default": [""],
+ "//conditions:default": [],
})
genrule(
@@ -331,13 +334,13 @@ cc_library(
"src/butil/third_party/dmg_fp/dtoa.cc",
":config_h",
],
- copts = COPTS + select({
+ defines = DEFINES + select({
"//bazel/config:brpc_build_for_unittest": [
- "-DBVAR_NOT_LINK_DEFAULT_VARIABLES",
- "-DUNIT_TEST",
+ "UNIT_TEST",
],
"//conditions:default": [],
}),
+ copts = COPTS,
includes = [
"src/",
],
@@ -354,8 +357,14 @@ cc_library(
"@bazel_tools//src/conditions:darwin": [":macos_lib"],
"//conditions:default": [],
}) + select({
- "//bazel/config:brpc_with_boringssl": ["@boringssl//:ssl",
"@boringssl//:crypto"],
- "//conditions:default": ["@openssl//:ssl", "@openssl//:crypto"],
+ "//bazel/config:brpc_with_boringssl": [
+ "@boringssl//:ssl",
+ "@boringssl//:crypto"
+ ],
+ "//conditions:default": [
+ "@openssl//:ssl",
+ "@openssl//:crypto"
+ ],
}),
)
@@ -381,14 +390,13 @@ cc_library(
defines = [] + select({
"//bazel/config:with_babylon_counter": ["WITH_BABYLON_COUNTER=1"],
"//conditions:default": [],
- }),
- copts = COPTS + select({
+ }) + select({
"//bazel/config:brpc_build_for_unittest": [
- "-DBVAR_NOT_LINK_DEFAULT_VARIABLES",
- "-DUNIT_TEST",
+ "BVAR_NOT_LINK_DEFAULT_VARIABLES",
],
"//conditions:default": [],
}),
+ copts = COPTS,
includes = [
"src/",
],
@@ -475,7 +483,6 @@ cc_library(
deps = [
":brpc_idl_options_cc_proto",
":butil",
- "@com_google_protobuf//:protoc_lib",
],
)
@@ -487,20 +494,11 @@ filegroup(
visibility = ["//visibility:public"],
)
-proto_library(
- name = "brpc_idl_options_proto",
- srcs = [":brpc_idl_options_proto_srcs"],
- strip_import_prefix = "src",
- visibility = ["//visibility:public"],
- deps = [
- "@com_google_protobuf//:descriptor_proto",
- ],
-)
-
-cc_proto_library(
+brpc_proto_library(
name = "brpc_idl_options_cc_proto",
+ srcs = [":brpc_idl_options_proto_srcs"],
+ include = "src",
visibility = ["//visibility:public"],
- deps = [":brpc_idl_options_proto"],
)
filegroup(
@@ -512,21 +510,12 @@ filegroup(
visibility = ["//visibility:public"],
)
-proto_library(
- name = "brpc_internal_proto",
- srcs = [":brpc_internal_proto_srcs"],
- strip_import_prefix = "src",
- visibility = ["//visibility:public"],
- deps = [
- ":brpc_idl_options_proto",
- "@com_google_protobuf//:descriptor_proto",
- ],
-)
-
-cc_proto_library(
+brpc_proto_library(
name = "brpc_internal_cc_proto",
+ srcs = [":brpc_internal_proto_srcs"],
+ include = "src",
+ deps = [":brpc_idl_options_cc_proto"],
visibility = ["//visibility:public"],
- deps = [":brpc_internal_proto"],
)
cc_library(
@@ -587,5 +576,6 @@ cc_binary(
deps = [
":brpc",
":brpc_idl_options_cc_proto",
+ "@com_google_protobuf//:protoc_lib",
],
)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 77703a46..98418355 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -206,11 +206,13 @@ if(Protobuf_VERSION GREATER 4.21)
absl::hash
absl::layout
absl::log_initialize
+ absl::log_globals
absl::log_severity
absl::memory
absl::node_hash_map
absl::node_hash_set
- absl::optional
+ absl::random_distributions
+ absl::random_random
absl::span
absl::status
absl::statusor
diff --git a/MODULE.bazel b/MODULE.bazel
index 95f4e6b7..add40787 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -16,8 +16,17 @@ bazel_dep(name = "apple_support", version = "1.17.1")
bazel_dep(name = 'rules_cc', version = '0.0.1')
bazel_dep(name = 'rules_proto', version = '4.0.0')
bazel_dep(name = 'zlib', version = '1.3.1.bcr.5', repo_name =
'com_github_madler_zlib')
-bazel_dep(name = 'libunwind', version = '1.8.1', repo_name =
'com_github_libunwind_libunwind')
bazel_dep(name = 'babylon', version = '1.4.4')
+# --registry=https://raw.githubusercontent.com/apache/brpc/master/registry
+# `registry/modules/libunwind/` (see the
`--registry=https://raw.githubusercontent.com/apache/brpc/master/registry`
+# entry at the top of `.bazelrc`). The version suffix `.brpc-no-unwind` marks
+# this as brpc's self-maintained variant whose `src/unwind/*.c` (the GCC
+# `_Unwind_*` ABI compatibility layer) is gated by the
+# `--define=libunwind_hide_unwind_symbols=true` switch. Hiding those
+# `_Unwind_*` symbols is required for `--define=with_bthread_tracer=true`
+# to work without crashing in pthread_exit / exception unwinding.
+# See docs/cn/bthread_tracer.md for background.
+bazel_dep(name = 'libunwind', version = '1.8.1.brpc-no-unwind', repo_name =
'com_github_libunwind_libunwind')
# --registry=https://baidu.github.io/babylon/registry
bazel_dep(name = 'leveldb', version = '1.23', repo_name =
'com_github_google_leveldb')
@@ -34,6 +43,7 @@ single_version_override(
bazel_dep(name = 'thrift', version = '0.21.0', repo_name = 'org_apache_thrift')
# test only
+bazel_dep(name = "gperftools", version = "2.18.1", dev_dependency = True)
bazel_dep(name = 'googletest', version = '1.14.0.bcr.1', repo_name =
'com_google_googletest', dev_dependency = True)
bazel_dep(name = 'hedron_compile_commands', dev_dependency = True)
git_override(
diff --git a/cmake/CMakeLists.download_gtest.in b/bazel/tools/BUILD.bazel
similarity index 64%
copy from cmake/CMakeLists.download_gtest.in
copy to bazel/tools/BUILD.bazel
index df020c89..3b448305 100644
--- a/cmake/CMakeLists.download_gtest.in
+++ b/bazel/tools/BUILD.bazel
@@ -13,18 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-cmake_minimum_required(VERSION 2.8.10)
+# This BUILD file marks bazel/tools/ as a Bazel package so that loads
+# of the form `//bazel/tools:xxx.bzl` are valid. The directory only
+# hosts .bzl files (brpc_proto_library.bzl / proto_gen.bzl) and does
+# not define any build targets.
-project(googletest-download NONE)
+package(default_visibility = ["//visibility:public"])
-include(ExternalProject)
-ExternalProject_Add(googletest
- GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG release-1.12.0
- SOURCE_DIR "${PROJECT_BINARY_DIR}/googletest-src"
- BINARY_DIR "${PROJECT_BINARY_DIR}/googletest-build"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- TEST_COMMAND ""
-)
+exports_files(glob(["*.bzl"]))
diff --git a/bazel/tools/brpc_proto_library.bzl
b/bazel/tools/brpc_proto_library.bzl
new file mode 100644
index 00000000..22a3c00b
--- /dev/null
+++ b/bazel/tools/brpc_proto_library.bzl
@@ -0,0 +1,138 @@
+# 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.
+#
+# brpc_proto_library: a proto -> cc_library macro that is robust against
+# protobuf and Bazel version churn.
+#
+# Background:
+# - Old protobuf (< 3.21) used to expose a `cc_proto_library` macro
+# via `@com_google_protobuf//:protobuf.bzl`; newer protobuf removed
+# that macro.
+# - Newer protobuf expects callers to use Bazel's native
+# `proto_library` / `cc_proto_library` (or the `cc_proto_library`
+# re-exported from `@com_google_protobuf//bazel:cc_proto_library.bzl`),
+# but older protobuf does not surface a complete `ProtoInfo`, so
+# the native rules cannot build it.
+# - Bazel 8 additionally removed `cc_proto_library` from
+# `@rules_cc//cc:defs.bzl` (the load now resolves to a stub rule
+# that fails analysis), forcing every caller to either load it
+# from `@com_google_protobuf` or replace it.
+# - Bazel's `load()` is static and cannot be conditional on the
+# protobuf version, so a single brpc_proto_library.bzl cannot
+# directly satisfy both old and new protobuf by switching imports.
+#
+# Solution (inspired by
https://github.com/trpc-group/trpc-cpp/blob/a69d2950f4a9c16b3cb40a2750c69d8940c96820/trpc/trpc.bzl):
+# We implement the proto compilation rule from scratch (see
+# proto_gen.bzl in this directory). Dependency information is
+# propagated through Bazel's native `ProtoInfo` provider, and the
+# default well-known-proto source is
+# `@com_google_protobuf//:descriptor_proto` -- a `proto_library`
+# whose name has been stable across protobuf 3.x / 27.x / 34.x.
+load("//bazel/tools:proto_gen.bzl", "brpc_proto_gen")
+
+# `:descriptor_proto` is the protobuf repo's `proto_library` target;
+# its label has been stable since protobuf 3.x. We consume it through
+# Bazel's native `ProtoInfo` provider, which is uniform across PB
+# versions. If a caller's .proto needs additional well-known protos
+# (any.proto, timestamp.proto, ...), they can pass extra entries via
+# the `proto_deps` argument.
+_DEFAULT_PROTO_DEPS = ["@com_google_protobuf//:descriptor_proto"]
+
+def brpc_proto_library(
+ name,
+ srcs,
+ deps = [],
+ include = None,
+ proto_deps = None,
+ visibility = None,
+ testonly = 0):
+ """Generate a cc_library from a set of .proto files.
+
+ Args:
+ name: target name.
+ srcs: list of .proto files, paths relative to the current package.
+ deps: list of other `brpc_proto_library` targets this target
+ depends on. The macro internally translates these labels
+ to the underlying `<name>_genproto` rule so that .proto
+ include paths and sources propagate.
+ include: protoc `-I` root AND the resulting cc_library `includes`
+ root, relative to the current package.
+ When omitted, "" or None, the include root is the
+ current package itself (suitable for .proto files
+ sitting directly under the package root, as in `test/`
+ and `example/...`). The root `BUILD.bazel` of brpc must
+ pass `"src"` so that code can reference the protos as
+ `import "brpc/foo.proto"`.
+ proto_deps: list of native `proto_library` dependencies
+ (well-known protos or external .proto libraries).
+ Defaults to
+ `["@com_google_protobuf//:descriptor_proto"]`.
+ Pass `[]` explicitly to disable the default; pass
+ None (the default) to use it.
+ visibility: same semantics as cc_library.
+ testonly: same semantics as cc_library.
+ """
+
+ real_include = include if include != None else ""
+ real_proto_deps = proto_deps if proto_deps != None else _DEFAULT_PROTO_DEPS
+
+ gen_name = name + "_genproto"
+ brpc_proto_gen(
+ name = gen_name,
+ srcs = srcs,
+ # Rewrite each `brpc_proto_library` dep to its underlying
+ # `<dep>_genproto` rule, which exports BrpcProtoInfo.
+ deps = [d + "_genproto" for d in deps],
+ include = real_include,
+ proto_deps = real_proto_deps,
+ visibility = visibility,
+ testonly = testonly,
+ )
+
+ # Split the generated files into .pb.h / .pb.cc via OutputGroupInfo
+ # and feed them to cc_library's hdrs / srcs separately. Bazel
+ # rejects declaring the same File in both hdrs and srcs, so a
+ # plain DefaultInfo wouldn't work here.
+ native.filegroup(
+ name = gen_name + "_hdrs",
+ srcs = [":" + gen_name],
+ output_group = "hdrs",
+ visibility = visibility,
+ testonly = testonly,
+ )
+ native.filegroup(
+ name = gen_name + "_srcs",
+ srcs = [":" + gen_name],
+ output_group = "srcs",
+ visibility = visibility,
+ testonly = testonly,
+ )
+
+ native.cc_library(
+ name = name,
+ srcs = [":" + gen_name + "_srcs"],
+ hdrs = [":" + gen_name + "_hdrs"],
+ # cc_library `includes` is required, otherwise the .pb.cc
+ # files inside this cc_library cannot find the .pb.h headers
+ # they just generated (the headers live under
+ # bazel-bin/<package>/<include>/...). When include="" we pass
+ # "." to mean "the current package itself"; Bazel then exposes
+ # both `-I <package>` and `-I bazel-bin/<package>`
+ # automatically to dependents.
+ includes = [real_include if real_include else "."],
+ deps = deps + ["@com_google_protobuf//:protobuf"],
+ visibility = visibility,
+ testonly = testonly,
+ )
diff --git a/bazel/tools/proto_gen.bzl b/bazel/tools/proto_gen.bzl
new file mode 100644
index 00000000..554d24df
--- /dev/null
+++ b/bazel/tools/proto_gen.bzl
@@ -0,0 +1,240 @@
+# 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.
+#
+# brpc's self-implemented proto -> .pb.{h,cc} compilation rule.
+#
+# Cross-protobuf-version compatibility strategy (inspired by trpc.bzl):
+# 1) Do NOT load `@com_google_protobuf//:protobuf.bzl` and do NOT
+# call `native.cc_proto_library` -- this side-steps the double
+# incompatibility of newer protobuf removing the macro and older
+# protobuf missing a complete `ProtoInfo` provider.
+# 2) Do NOT hardcode any protobuf-repo-internal filegroup names
+# (such as `well_known_protos` / `descriptor_proto_srcs`) -- the
+# names of those filegroups have shifted between protobuf 3.x
+# and 27.x+.
+# 3) Propagate dependencies via Bazel's native `ProtoInfo` provider:
+# callers pass `proto_library` targets through `proto_deps`
+# (defaulting to `@com_google_protobuf//:descriptor_proto`, a
+# `proto_library` whose label has been stable across PB versions),
+# and this rule walks `transitive_sources` / `transitive_proto_path`
+# to collect .proto source files and protoc `-I` paths -- fully
+# decoupled from the PB version.
+# 4) Among `brpc_proto_gen` targets themselves, dependencies are
+# propagated via the custom `BrpcProtoInfo` provider declared
+# below.
+#
+# Note on minimum Bazel version: this rule itself does not call any
+# Bazel-version-specific API, but it still consumes `ProtoInfo` from
+# `proto_library` targets (e.g. `descriptor_proto`) defined inside
+# the protobuf repo. Protobuf >= 34 calls
+# `ProtoInfo(option_deps = ..., extension_declarations = ...)` in its
+# own `proto_library` implementation, and those fields are only
+# accepted by Bazel >= 8. In other words, the minimum Bazel version
+# brpc requires is whatever the bundled protobuf version requires --
+# we just don't add any extra requirement on top of that.
+
+BrpcProtoInfo = provider(
+ doc = "Carries transitive .proto sources and protoc -I flags between
brpc_proto_gen targets.",
+ fields = {
+ "transitive_srcs": "depset of all transitive .proto Files.",
+ "transitive_imports": "depset of strings, all -I flags needed by
protoc.",
+ },
+)
+
+def _resolve_include_dir(ctx):
+ """Compute this target's protoc include root (workspace-relative).
+
+ Examples:
+ ctx.label.package = "" + include = "src" -> "src"
+ ctx.label.package = "test" + include = "" -> "test"
+ ctx.label.package = "" + include = "" -> "."
+ """
+ pkg = ctx.label.package
+ inc = ctx.attr.include.rstrip("/")
+ if pkg and inc:
+ return pkg + "/" + inc
+ if pkg:
+ return pkg
+ if inc:
+ return inc
+ return "."
+
+def _proto_gen_impl(ctx):
+ srcs = ctx.files.srcs
+ include_dir = _resolve_include_dir(ctx)
+ bin_root = ctx.bin_dir.path
+
+ # `-I` flags for this target itself: the source-tree root plus
+ # the corresponding bin-dir root. The bin-dir entry is needed
+ # when a transitive dep generates .proto files into bazel-bin
+ # (e.g. via a custom code generator).
+ own_imports = ["-I" + include_dir]
+ if include_dir == ".":
+ own_imports.append("-I" + bin_root)
+ else:
+ own_imports.append("-I" + bin_root + "/" + include_dir)
+
+ # Collect transitive info from other `brpc_proto_gen` deps.
+ dep_srcs_list = [d[BrpcProtoInfo].transitive_srcs for d in ctx.attr.deps]
+ dep_imports_list = [d[BrpcProtoInfo].transitive_imports for d in
ctx.attr.deps]
+
+ # Collect native `proto_library` deps (well-known protos or
+ # external .proto libraries). Key design: .proto sources and
+ # `-I` paths are obtained via Bazel's native `ProtoInfo`,
+ # avoiding any reliance on protobuf-repo-internal filegroup
+ # names (descriptor_proto_srcs / well_known_protos / ...).
+ # `transitive_proto_path` already accounts for the
+ # `_virtual_imports` paths created by `strip_import_prefix`, so
+ # protoc can consume the paths directly.
+ #
+ # Important: `strip_import_prefix` causes .proto files to live at
+ # virtual paths under bazel-out/<config>/bin/<workspace_root>/<virtual>
+ # (see trpc.bzl for prior art). For every `proto_dep` repo we
+ # therefore expose FOUR candidate `-I` paths to be safe:
+ # 1) <workspace_root> -- source root (plain
proto_library)
+ # 2) <bin_dir>/<workspace_root> -- virtual root after
strip_import_prefix
+ # 3) <workspace_root>/src -- fallback for older PB
descriptor_proto without strip
+ # 4) <bin_dir>/<workspace_root>/src -- same as #3, under bin-dir
+ # protoc only warns on non-existent `-I` paths, so this is harmless.
+ proto_dep_src_depsets = []
+ proto_dep_imports = []
+ extra_pb_root_imports = []
+ # Belt-and-suspenders: also feed each `proto_dep`'s default outputs
+ # (i.e. the .proto files themselves) into `inputs`, in case the
+ # ProtoInfo's transitive_sources does not cover everything we need.
+ proto_dep_src_depsets.append(depset(direct = ctx.files.proto_deps))
+ for pd in ctx.attr.proto_deps:
+ pi = pd[ProtoInfo]
+ proto_dep_src_depsets.append(pi.transitive_sources)
+ for path in pi.transitive_proto_path.to_list():
+ proto_dep_imports.append("-I" + path)
+ wsroot = pd.label.workspace_root
+ if wsroot:
+ extra_pb_root_imports.append("-I" + wsroot)
+ extra_pb_root_imports.append("-I" + bin_root + "/" + wsroot)
+ extra_pb_root_imports.append("-I" + wsroot + "/src")
+ extra_pb_root_imports.append("-I" + bin_root + "/" + wsroot +
"/src")
+ # Deduplicate the workspace-level `-I` entries so the same repo
+ # is not listed multiple times when several proto_deps share it.
+ proto_dep_imports.extend(depset(extra_pb_root_imports).to_list())
+
+ all_imports = depset(
+ direct = own_imports + proto_dep_imports,
+ transitive = dep_imports_list,
+ )
+
+ # Output files: every .proto produces a .pb.h and a .pb.cc.
+ # NB: the `path` argument of `ctx.actions.declare_file(path)` is
+ # *relative to the current package*, while `src.short_path` is
+ # *relative to the workspace root*. The two differ by the package
+ # prefix; we must strip that prefix before calling declare_file,
+ # otherwise the outputs would land at
+ # bazel-bin/<pkg>/<pkg>/... (one level too deep).
+ pkg = ctx.label.package
+ pkg_prefix = pkg + "/" if pkg else ""
+ outs = []
+ for src in srcs:
+ if not src.short_path.endswith(".proto"):
+ fail("brpc_proto_gen srcs must be .proto, got: %s" %
src.short_path)
+
+ rel = src.short_path
+ if pkg_prefix and rel.startswith(pkg_prefix):
+ rel = rel[len(pkg_prefix):]
+ base = rel[:-len(".proto")]
+ outs.append(ctx.actions.declare_file(base + ".pb.h"))
+ outs.append(ctx.actions.declare_file(base + ".pb.cc"))
+
+ # protoc's --cpp_out points at the include root under bin_root.
+ # After protoc organizes outputs by their import-relative path,
+ # the .pb.{h,cc} files land exactly where declare_file declared
+ # them above.
+ if include_dir == ".":
+ cpp_out_dir = bin_root
+ else:
+ cpp_out_dir = bin_root + "/" + include_dir
+
+ args = ctx.actions.args()
+ args.add_all(all_imports.to_list())
+ args.add("--cpp_out=" + cpp_out_dir)
+ args.add_all([s.path for s in srcs])
+
+ ctx.actions.run(
+ executable = ctx.executable.protoc,
+ arguments = [args],
+ inputs = depset(
+ direct = srcs,
+ transitive = dep_srcs_list + proto_dep_src_depsets,
+ ),
+ outputs = outs,
+ mnemonic = "BrpcProtoCompile",
+ progress_message = "BrpcProtoCompile %s" % ctx.label,
+ )
+
+ hdr_files = [f for f in outs if f.path.endswith(".pb.h")]
+ src_files = [f for f in outs if f.path.endswith(".pb.cc")]
+
+ return [
+ DefaultInfo(files = depset(outs)),
+ # Split outputs so the brpc_proto_library macro can route
+ # them to cc_library.hdrs vs cc_library.srcs separately.
+ OutputGroupInfo(
+ hdrs = depset(hdr_files),
+ srcs = depset(src_files),
+ ),
+ BrpcProtoInfo(
+ transitive_srcs = depset(direct = srcs, transitive =
dep_srcs_list),
+ transitive_imports = all_imports,
+ ),
+ ]
+
+brpc_proto_gen = rule(
+ implementation = _proto_gen_impl,
+ attrs = {
+ "srcs": attr.label_list(
+ allow_files = [".proto"],
+ mandatory = True,
+ ),
+ # Other `brpc_proto_gen` targets; deps are propagated via the
+ # custom `BrpcProtoInfo` provider.
+ "deps": attr.label_list(
+ providers = [BrpcProtoInfo],
+ default = [],
+ ),
+ # The include root, relative to the current BUILD package
+ # (e.g. "src" for the brpc root BUILD).
+ "include": attr.string(default = ""),
+ "protoc": attr.label(
+ default = Label("@com_google_protobuf//:protoc"),
+ executable = True,
+ # `"host"` is the legacy spelling of `"exec"`. Modern
+ # Bazel (>= 6) prefers `"exec"`, but still accepts
+ # `"host"` as an alias through at least Bazel 8.x. We
+ # keep `"host"` here for the broad range of Bazel
+ # versions used to build brpc; switch to `"exec"` once
+ # `"host"` is finally removed (likely a future major
+ # Bazel release).
+ cfg = "host",
+ allow_files = True,
+ ),
+ # Native `proto_library` deps (well-known protos or external
+ # .proto libraries). .proto sources and -I paths are obtained
+ # automatically through Bazel's native `ProtoInfo`, fully
+ # decoupled from the protobuf version.
+ "proto_deps": attr.label_list(
+ providers = [ProtoInfo],
+ default = [],
+ ),
+ },
+)
diff --git a/cmake/CMakeLists.download_gtest.in
b/cmake/CMakeLists.download_gtest.in
index df020c89..78d99baf 100644
--- a/cmake/CMakeLists.download_gtest.in
+++ b/cmake/CMakeLists.download_gtest.in
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-cmake_minimum_required(VERSION 2.8.10)
+cmake_minimum_required(VERSION 2.8.12)
project(googletest-download NONE)
diff --git a/docs/cn/bthread_tracer.md b/docs/cn/bthread_tracer.md
index 275585d1..bab09ce4 100644
--- a/docs/cn/bthread_tracer.md
+++ b/docs/cn/bthread_tracer.md
@@ -122,7 +122,7 @@ libunwind 的 `src/unwind/*.c` 实现了 GCC 的 `_Unwind_*` ABI
兼容层(`_U
| `cmake` | 从源码编译并安装 libunwind,把头文件与库目录显式传给 `cmake` |
| `bazel`(Bzlmod) | 直接使用 brpc 仓库自维护的 libunwind 版本 |
-### `make`(`config_brpc.sh`)
+### make (config_brpc.sh)
源码编译并安装 libunwind 到一个独立目录(避免污染系统目录),然后让 `config_brpc.sh` 显式从该目录查找 libunwind。
@@ -153,7 +153,7 @@ nm -D /libunwind/lib/libunwind.so | grep ' T _Unwind_' \
|| echo "OK: _Unwind_* hidden"
```
-### `cmake`
+### cmake
[`CMakeLists.txt`](../../CMakeLists.txt:90-100) 通过 `find_library(... NAMES
unwind unwind-x86_64)` 查找 libunwind。同样需要先源码编译 libunwind 到独立前缀,再用
`CMAKE_PREFIX_PATH` 让 cmake 优先在该前缀下查找:
@@ -173,7 +173,7 @@ make -j$(nproc)
> `-DLIBUNWIND_LIB=/libunwind/lib/libunwind.so
> -DLIBUNWIND_X86_64_LIB=/libunwind/lib/libunwind-x86_64.so
> -DLIBUNWIND_INCLUDE_PATH=/libunwind/include`
> 强制走自编译版本,避免系统包混入。
-### `bazel`(Bzlmod)
+### bazel (Bzlmod)
bRPC 仓库已经在 [`registry/modules/libunwind/`](../../registry/modules/libunwind/)
维护了一份 libunwind 的 Bzlmod overlay,并通过 [`.bazelrc`](../../.bazelrc) 中的
`--registry=https://github.com/apache/brpc/registry` 使用 bRPC 维护的 overlay。版本号采用
`<base>.brpc-no-unwind` 后缀(例如 `1.8.3.brpc-no-unwind`),用以区别于 BCR 上的同基版本。该
overlay 增加了一个开关:
diff --git a/example/BUILD.bazel b/example/BUILD.bazel
index df2722a4..4ee7cb14 100644
--- a/example/BUILD.bazel
+++ b/example/BUILD.bazel
@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_proto_library")
+load("@rules_cc//cc:defs.bzl", "cc_binary")
+load("//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library")
COPTS = [
"-D__STDC_FORMAT_MACROS",
@@ -36,32 +36,18 @@ COPTS = [
"//conditions:default": [""],
})
-proto_library(
- name = "echo_c++_proto",
- srcs = [
- "echo_c++/echo.proto",
- ],
-)
-
-proto_library(
- name = "rdma_performance_proto",
- srcs = [
- "rdma_performance/test.proto",
- ],
-)
-
-cc_proto_library(
+brpc_proto_library(
name = "cc_echo_c++_proto",
- deps = [
- ":echo_c++_proto",
- ],
+ srcs = ["echo_c++/echo.proto"],
+ include = "echo_c++",
+ proto_deps = [],
)
-cc_proto_library(
+brpc_proto_library(
name = "cc_rdma_performance_proto",
- deps = [
- ":rdma_performance_proto",
- ],
+ srcs = ["rdma_performance/test.proto"],
+ include = "rdma_performance",
+ proto_deps = [],
)
cc_binary(
diff --git a/registry/modules/libunwind/1.8.3.brpc-no-unwind/source.json
b/registry/modules/libunwind/1.8.3.brpc-no-unwind/source.json
index 3f14a160..aa6ba905 100644
--- a/registry/modules/libunwind/1.8.3.brpc-no-unwind/source.json
+++ b/registry/modules/libunwind/1.8.3.brpc-no-unwind/source.json
@@ -3,7 +3,7 @@
"strip_prefix": "libunwind-1.8.3",
"overlay": {
"BUILD.bazel": "sha256-ozAABv0I3+tacve4z2m+lAh/lhJWh7t7L3unh8FSfp8=",
- "MODULE.bazel": "sha256-EtBEsSDMJgdmCs7ByJxVP2SSVc+E7kUVLgpI3TEh/9M="
+ "MODULE.bazel": "sha256-qimDkl8soxNM/JuqndzGOOcn5ZwGvo2J6CSsNS2/IQs="
},
"integrity": "sha256-vjDZEOZ/WNgudTIx8TV/MmoaCIrPEmsh/3fmCqsZuQs="
}
diff --git a/src/bthread/task_tracer.cpp b/src/bthread/task_tracer.cpp
index b550c03a..e6049f0d 100644
--- a/src/bthread/task_tracer.cpp
+++ b/src/bthread/task_tracer.cpp
@@ -17,6 +17,13 @@
#ifdef BRPC_BTHREAD_TRACER
+#include <inttypes.h>
+#include <signal.h>
+#include <unistd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <iomanip>
+#include <gflags/gflags.h>
#include "bthread/task_tracer.h"
#include "bthread/processor.h"
#include "bthread/task_group.h"
@@ -25,11 +32,6 @@
#include "butil/reloadable_flags.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
-#include <csignal>
-#include <gflags/gflags.h>
-#include <poll.h>
-#include <pthread.h>
-#include <unistd.h>
namespace bthread {
@@ -77,7 +79,8 @@ std::string TaskTracer::Result::OutputToString() const {
char unknown_symbol_name[] = "<unknown>";
if (frame_count > 0) {
for (size_t i = 0; i < frame_count; ++i) {
- butil::string_appendf(&str, "#%zu 0x%16p ", i, ips[i]);
+ butil::string_appendf(&str, "#%zu 0x%016" PRIxPTR " ", i,
+ reinterpret_cast<uintptr_t>(ips[i]));
if (absl::Symbolize(ips[i], symbol_name, arraysize(symbol_name))) {
str.append(symbol_name);
} else {
@@ -103,7 +106,10 @@ void TaskTracer::Result::OutputToStream(std::ostream& os)
const {
char unknown_symbol_name[] = "<unknown>";
if (frame_count > 0) {
for (size_t i = 0; i < frame_count; ++i) {
- os << "# " << i << " 0x" << std::hex << ips[i] << std::dec << " ";
+ os << "#" << i << " 0x"
+ << std::hex << std::setw(16) << std::setfill('0')
+ << reinterpret_cast<uintptr_t>(ips[i])
+ << std::dec << std::setfill(' ') << " ";
if (absl::Symbolize(ips[i], symbol_name, arraysize(symbol_name))) {
os << symbol_name;
} else {
diff --git a/src/butil/endpoint.cpp b/src/butil/endpoint.cpp
index a8d8936c..c5a888b8 100644
--- a/src/butil/endpoint.cpp
+++ b/src/butil/endpoint.cpp
@@ -394,13 +394,38 @@ int endpoint2hostname(const EndPoint& point, std::string*
host) {
#if defined(OS_LINUX)
static short epoll_to_poll_events(uint32_t epoll_events) {
- // Most POLL* and EPOLL* are same values.
+ // Most POLL* and EPOLL* share the same numeric values for the basic
+ // event bits, so a plain mask is enough to translate them.
short poll_events = (epoll_events &
(EPOLLIN | EPOLLPRI | EPOLLOUT |
EPOLLRDNORM | EPOLLRDBAND |
EPOLLWRNORM | EPOLLWRBAND |
EPOLLMSG | EPOLLERR | EPOLLHUP));
- CHECK_EQ((uint32_t)poll_events, epoll_events);
+ // epoll-only modifier bits (EPOLLET / EPOLLONESHOT / EPOLLRDHUP / ...)
+ // have no poll(2) counterpart and MUST be silently dropped here:
+ // * poll(2) is already level-triggered and reports events per call,
+ // so EPOLLET / EPOLLONESHOT degrade naturally to "no-op".
+ // * `short` cannot even represent EPOLLET (bit 31 = 0x80000000).
+ // Without this filtering, a caller invoking bthread_fd_wait(fd,
+ // EPOLLIN | EPOLLET) from a pthread context would CHECK-fail here.
+ const uint32_t epoll_modifier_bits = 0u
+#ifdef EPOLLET
+ | EPOLLET
+#endif
+#ifdef EPOLLONESHOT
+ | EPOLLONESHOT
+#endif
+#ifdef EPOLLRDHUP
+ | EPOLLRDHUP
+#endif
+#ifdef EPOLLEXCLUSIVE
+ | EPOLLEXCLUSIVE
+#endif
+#ifdef EPOLLWAKEUP
+ | EPOLLWAKEUP
+#endif
+ ;
+ CHECK_EQ((uint32_t)poll_events, epoll_events & ~epoll_modifier_bits);
return poll_events;
}
#elif defined(OS_MACOSX)
diff --git a/src/butil/third_party/symbolize/symbolize.cc
b/src/butil/third_party/symbolize/symbolize.cc
index 19649b35..0b4000ad 100644
--- a/src/butil/third_party/symbolize/symbolize.cc
+++ b/src/butil/third_party/symbolize/symbolize.cc
@@ -333,10 +333,18 @@ FindSymbol(uint64_t pc, const int fd, char *out, int
out_size,
// both regular and dynamic symbol tables if necessary. On success,
// write the symbol name to "out" and return true. Otherwise, return
// false.
+// `base_address` is the runtime VA that corresponds to ELF VA 0 of the
+// object identified by `fd`. For ET_EXEC binaries it is ignored
+// (symbols already hold absolute runtime addresses); for ET_DYN
+// objects (PIE executables and shared libraries) the caller must
+// supply a precise value (see
+// OpenObjectFileContainingPcAndGetStartAddress() below, which derives
+// it from the object's PT_LOAD program headers rather than from the
+// /proc/self/maps file_offset field).
static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
char *out, int out_size,
uint64_t *out_saddr,
- uint64_t map_start_address) {
+ uint64_t base_address) {
// Read the ELF header.
ElfW(Ehdr) elf_header;
if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
@@ -345,7 +353,7 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t
pc,
uint64_t symbol_offset = 0;
if (elf_header.e_type == ET_DYN) { // DSO needs offset adjustment.
- symbol_offset = map_start_address;
+ symbol_offset = base_address;
}
ElfW(Shdr) symtab, strtab;
@@ -528,13 +536,26 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
return -1;
}
+ // Also open /proc/self/mem so we can read ELF headers / program
+ // headers directly out of the mapped binary, which is the only
+ // reliable way to compute the runtime ELF base address for PIE
+ // executables and shared libraries (the file_offset reported by
+ // /proc/self/maps is per-mapping and does not necessarily match the
+ // ELF base when modern toolchains use `-z separate-code` or when
+ // the first PT_LOAD has a non-zero p_vaddr). Mirrors the
+ // implementation in glog upstream.
+ int mem_fd;
+ NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
+ FileDescriptor wrapped_mem_fd(mem_fd);
+ if (wrapped_mem_fd.get() < 0) {
+ return -1;
+ }
+
// Iterate over maps and look for the map containing the pc. Then
// look into the symbol tables inside.
char buf[1024]; // Big enough for line of sane /proc/self/maps
- int num_maps = 0;
LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf));
while (true) {
- num_maps++;
const char *cursor;
const char *eol;
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
@@ -563,11 +584,6 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
++cursor; // Skip ' '.
- // Check start and end addresses.
- if (!(start_address <= pc && pc < end_address)) {
- continue; // We skip this map. PC isn't in this map.
- }
-
// Read flags. Skip flags until we encounter a space or eol.
const char * const flags_start = cursor;
while (cursor < eol && *cursor != ' ') {
@@ -578,32 +594,74 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
return -1; // Malformed line.
}
+ // Determine the base address by reading ELF headers in process
+ // memory. We must do this for *every* readable map, not just the
+ // r-x map that contains PC, because a PIE binary or shared
+ // library's ELF header is typically mapped by an earlier r-- map
+ // (separate-code layout). Once we encounter the r-- map carrying
+ // the ELF magic, we record base_address for the whole object.
+ // When we later reach the r-x map containing PC, base_address is
+ // already correct.
+ if (flags_start[0] == 'r') {
+ ElfW(Ehdr) ehdr;
+ if (ReadFromOffsetExact(wrapped_mem_fd.get(), &ehdr, sizeof(ehdr),
+ start_address) &&
+ memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
+ switch (ehdr.e_type) {
+ case ET_EXEC:
+ base_address = 0;
+ break;
+ case ET_DYN:
+ // Find the PT_LOAD segment with p_offset == 0 (i.e. the
+ // segment that contains the ELF header). Its p_vaddr is
+ // the ELF VA that corresponds to the bytes we just read
+ // at `start_address`, so base_address = start_address -
+ // p_vaddr. Normally p_vaddr is 0 and base_address ==
+ // start_address, but some non-standard linker scripts
+ // place the first LOAD at a non-zero VA. Fall back to
+ // start_address if no such PT_LOAD is found.
+ base_address = start_address;
+ for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
+ ElfW(Phdr) phdr;
+ if (ReadFromOffsetExact(wrapped_mem_fd.get(), &phdr,
+ sizeof(phdr),
+ start_address + ehdr.e_phoff +
+ i * sizeof(phdr)) &&
+ phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
+ base_address = start_address - phdr.p_vaddr;
+ break;
+ }
+ }
+ break;
+ default:
+ // ET_REL or ET_CORE. Not directly executable, leave
+ // base_address untouched.
+ break;
+ }
+ }
+ }
+
+ // Check start and end addresses.
+ if (!(start_address <= pc && pc < end_address)) {
+ continue; // We skip this map. PC isn't in this map.
+ }
+
// Check flags. We are only interested in "r-x" maps.
if (memcmp(flags_start, "r-x", 3) != 0) { // Not a "r-x" map.
continue; // We skip this map.
}
++cursor; // Skip ' '.
- // Read file offset.
+ // Read file offset (parsed but no longer used for base_address;
+ // base_address is now computed from PT_LOAD program headers
+ // above. Keep the parse so the cursor advances to the file name).
uint64_t file_offset;
cursor = GetHex(cursor, eol, &file_offset);
if (cursor == eol || *cursor != ' ') {
return -1; // Malformed line.
}
++cursor; // Skip ' '.
-
- // Don't subtract 'start_address' from the first entry:
- // * If a binary is compiled w/o -pie, then the first entry in
- // process maps is likely the binary itself (all dynamic libs
- // are mapped higher in address space). For such a binary,
- // instruction offset in binary coincides with the actual
- // instruction address in virtual memory (as code section
- // is mapped to a fixed memory range).
- // * If a binary is compiled with -pie, all the modules are
- // mapped high at address space (in particular, higher than
- // shadow memory of the tool), so the module can't be the
- // first entry.
- base_address = ((num_maps == 1) ? 0U : start_address) - file_offset;
+ (void)file_offset;
// Skip to file name. "cursor" now points to dev. We need to
// skip at least two spaces for dev and inode.
@@ -794,9 +852,18 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void
*pc, char *out,
out_size -= num_bytes_written;
}
}
+ // Use `base_address` (computed by
+ // OpenObjectFileContainingPcAndGetStartAddress() via the object's
+ // PT_LOAD program headers) as the relocation offset, NOT
+ // `start_address`. For ET_DYN objects produced by modern toolchains
+ // (binutils >= 2.31, lld) using `-z separate-code`, the r-x mapping
+ // starts at a non-zero file offset and `start_address` no longer
+ // equals the ELF base; only `base_address` is the correct value
+ // such that `symbol.st_value + base_address` recovers the runtime
+ // VA of a symbol.
if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0,
out, out_size, out_saddr,
- start_address)) {
+ base_address)) {
return false;
}
diff --git a/src/bvar/detail/percentile.h b/src/bvar/detail/percentile.h
index bac729f1..e6acbf3a 100644
--- a/src/bvar/detail/percentile.h
+++ b/src/bvar/detail/percentile.h
@@ -561,7 +561,23 @@ public:
typedef VoidOp InvOp;
typedef ReducerSampler<Percentile, value_type, Op, InvOp> sampler_type;
- Percentile() = default;
+ Percentile() {
+ // babylon::ConcurrentSampler defaults every per-value-range
+ // bucket to capacity 30 (see _bucket_capacity[32] in
+ // babylon/concurrent/counter.h) and only grows it inside
+ // Percentile::reset() after observing how many samples
+ // arrived. That means the *first* collection round always
+ // observes badly under-sampled buckets, which destabilises
+ // mid-quantile estimates (e.g. p60/p70 on a uniform 1..10000
+ // stream can land at ~5482/~6284 instead of ~6000/~7000 and
+ // make PercentileTest.add fail). Pre-size every bucket to the
+ // full SAMPLE_SIZE on construction so the very first reset()
+ // already sees representative samples.
+ for (size_t i = 0; i < NUM_INTERVALS; ++i) {
+ _concurrent_sampler.set_bucket_capacity(i,
value_type::SAMPLE_SIZE);
+ }
+ }
+
DISALLOW_COPY_AND_MOVE(Percentile);
~Percentile() noexcept {
if (NULL != _sampler) {
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
index b68b3fa0..18af200d 100644
--- a/test/BUILD.bazel
+++ b/test/BUILD.bazel
@@ -13,30 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_proto_library", "cc_test")
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+load("//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library")
load("@hedron_compile_commands//:refresh_compile_commands.bzl",
"refresh_compile_commands")
+load("//test:generate_unittests.bzl", "generate_unittests")
+load("//test:root_runfiles.bzl", "root_runfiles")
COPTS = [
- "-D__STDC_FORMAT_MACROS",
- "-DBTHREAD_USE_FAST_PTHREAD_MUTEX",
- "-D__const__=__unused__",
- "-D_GNU_SOURCE",
- "-DUSE_SYMBOLIZE",
- "-DNO_TCMALLOC",
- "-D__STDC_LIMIT_MACROS",
- "-D__STDC_CONSTANT_MACROS",
"-fPIC",
"-Wno-unused-parameter",
"-fno-omit-frame-pointer",
"-fno-access-control",
- "-DBAZEL_TEST=1",
- "-DBVAR_NOT_LINK_DEFAULT_VARIABLES",
- "-DUNIT_TEST",
-] + select({
- "//bazel/config:brpc_with_glog": ["-DBRPC_WITH_GLOG=1"],
- "//conditions:default": ["-DBRPC_WITH_GLOG=0"],
-})
+]
TEST_BUTIL_SOURCES = [
"at_exit_unittest.cc",
@@ -148,26 +136,11 @@ TEST_BUTIL_SOURCES = [
],
})
-proto_library(
- name = "test_proto",
- srcs = glob(
- [
- "*.proto",
- ],
- ),
- strip_import_prefix = "/test",
- visibility = ["//visibility:public"],
- deps = [
- "//:brpc_idl_options_proto",
- ]
-)
-
-cc_proto_library(
+brpc_proto_library(
name = "cc_test_proto",
+ srcs = glob(["*.proto"]),
+ deps = ["//:brpc_idl_options_cc_proto"],
visibility = ["//visibility:public"],
- deps = [
- ":test_proto",
- ],
)
cc_library(
@@ -184,8 +157,22 @@ cc_library(
],
)
+# tcmalloc and AddressSanitizer both intercept malloc/free, so linking
+# both produces undefined behaviour at runtime (typically a hard hang
+# at process start, or random allocator errors). When `brpc_with_asan`
+# is active we drop the @gperftools//:tcmalloc dep entirely; the
+# gperftools_helper header above already no-ops gracefully when
+# BRPC_ENABLE_CPU_PROFILER is not defined.
+#
+# Activation: `--config=asan` in .bazelrc also sets
+# `--define=with_asan=true`, which flips this config_setting.
+TCMALLOC_DEP_UNLESS_ASAN = select({
+ "//bazel/config:brpc_with_asan": [],
+ "//conditions:default": ["@gperftools//:tcmalloc"],
+})
+
cc_test(
- name = "butil_test",
+ name = "butil_unittests",
srcs = TEST_BUTIL_SOURCES + [
"scoped_locale.h",
"multiprocess_func_list.h",
@@ -196,104 +183,73 @@ cc_test(
":cc_test_proto",
":sstream_workaround",
":gperftools_helper",
- "//:brpc",
+ "//:butil",
+ "//:bthread",
"@com_google_googletest//:gtest",
],
)
-
cc_test(
- name = "bvar_test",
- srcs = glob(
- [
- "bvar_*_unittest.cpp",
- ],
- exclude = [
- "bvar_lock_timer_unittest.cpp",
- "bvar_recorder_unittest.cpp",
- ],
- ),
- copts = COPTS,
+ name = "bvar_unittests",
+ srcs = glob(["bvar_*_unittest.cpp"]),
deps = [
":sstream_workaround",
":gperftools_helper",
"//:bvar",
"@com_google_googletest//:gtest",
- ],
+ "@com_google_googletest//:gtest_main",
+ ] + TCMALLOC_DEP_UNLESS_ASAN,
+ copts = COPTS,
)
-cc_test(
- name = "bthread_test",
- srcs = glob(
- [
- "bthread_*_unittest.cpp",
- ],
- exclude = [
- "bthread_cond_unittest.cpp",
- "bthread_execution_queue_unittest.cpp",
- "bthread_dispatcher_unittest.cpp",
- "bthread_fd_unittest.cpp",
- "bthread_mutex_unittest.cpp",
- "bthread_setconcurrency_unittest.cpp",
- # glog CHECK die with a fatal error
- "bthread_key_unittest.cpp",
- "bthread_butex_multi_tag_unittest.cpp",
- "bthread_rwlock_unittest.cpp",
- "bthread_semaphore_unittest.cpp",
- ],
- ),
- copts = COPTS,
+generate_unittests(
+ name = "bthread_unittests",
+ srcs = glob([
+ "bthread*_unittest.cpp",
+ ]),
deps = [
":sstream_workaround",
":gperftools_helper",
- "//:brpc",
+ "//:bthread",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "brpc_prometheus_test",
- srcs = glob(
- [
- "brpc_prometheus_*_unittest.cpp",
- ],
- ),
+ ] + TCMALLOC_DEP_UNLESS_ASAN,
copts = COPTS,
- deps = [
- ":cc_test_proto",
- ":sstream_workaround",
- "//:brpc",
- "@com_google_googletest//:gtest",
- "@com_google_googletest//:gtest_main",
- ],
)
-cc_test(
- name = "brpc_auto_concurrency_limiter_test",
+# Expose unit-test data files (cert*, jsonout) at the runfiles workspace root
+# so that tests can open them via plain relative paths like "cert1.crt".
+# See test/root_runfiles.bzl for why a genrule does NOT work here.
+root_runfiles(
+ name = "test_runfiles_root_data",
srcs = [
- "brpc_auto_concurrency_limiter_unittest.cpp",
- ],
- copts = COPTS,
- deps = [
- "//:brpc",
- "@com_google_googletest//:gtest",
- "@com_google_googletest//:gtest_main",
+ "cert1.crt",
+ "cert1.key",
+ "cert2.crt",
+ "cert2.key",
+ "jsonout",
],
)
-cc_test(
- name = "brpc_redis_cluster_test",
- srcs = [
- "brpc_redis_cluster_unittest.cpp",
- ],
- copts = COPTS,
+generate_unittests(
+ name = "brpc_unittests",
+ srcs = glob([
+ "brpc_*_unittest.cpp",
+ ]),
deps = [
":sstream_workaround",
":gperftools_helper",
"//:brpc",
+ ":cc_test_proto",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
+ ] + TCMALLOC_DEP_UNLESS_ASAN,
+ copts = COPTS,
+ # Place cert*/jsonout at <runfiles>/_main/<file> (the cwd of `bazel test`)
+ # via :test_runfiles_root_data so test code can open them with plain
+ # relative paths like "cert1.crt".
+ data = [
+ ":test_runfiles_root_data",
],
)
@@ -303,9 +259,10 @@ refresh_compile_commands(
# For example, specify a dict of targets and their arguments:
targets = {
"//:brpc": "",
- ":bvar_test": "",
- ":bthread_test": "",
- ":butil_test": "",
+ ":butil_unittests": "",
+ ":bvar_unittests": "",
+ ":bthread_unittests": "",
+ ":brpc_unittests": "",
},
# For more details, feel free to look into refresh_compile_commands.bzl if
you want.
)
diff --git a/test/brpc_alpn_protocol_unittest.cpp
b/test/brpc_alpn_protocol_unittest.cpp
index 21aada70..9c150671 100644
--- a/test/brpc_alpn_protocol_unittest.cpp
+++ b/test/brpc_alpn_protocol_unittest.cpp
@@ -48,7 +48,7 @@ public:
response->set_message(request->message());
brpc::Controller* cntl = static_cast<brpc::Controller*>(controller);
- LOG(NOTICE) << "protocol:" << cntl->request_protocol();
+ LOG(INFO) << "protocol:" << cntl->request_protocol();
}
};
@@ -65,9 +65,9 @@ public:
ssl_options->default_cert.private_key = "cert1.key";
ssl_options->alpns = "http, h2, baidu_std";
- EXPECT_EQ(0, _server.AddService(&_echo_server_impl,
+ ASSERT_EQ(0, _server.AddService(&_echo_server_impl,
brpc::SERVER_DOESNT_OWN_SERVICE));
- EXPECT_EQ(0, _server.Start(FLAGS_listen_addr.data(), &server_options));
+ ASSERT_EQ(0, _server.Start(FLAGS_listen_addr.data(), &server_options));
}
virtual void TearDown() override {
diff --git a/test/brpc_http_rpc_protocol_unittest.cpp
b/test/brpc_http_rpc_protocol_unittest.cpp
index e39ac39d..c7022ed2 100644
--- a/test/brpc_http_rpc_protocol_unittest.cpp
+++ b/test/brpc_http_rpc_protocol_unittest.cpp
@@ -20,7 +20,6 @@
// Date: Sun Jul 13 15:04:18 CST 2014
#include <cstddef>
-#include <google/protobuf/stubs/logging.h>
#include <string>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -1278,7 +1277,7 @@ public:
private:
void check_header(brpc::Controller* cntl) {
const std::string* test_header =
cntl->http_request().GetHeader(TEST_PROGRESSIVE_HEADER);
- GOOGLE_CHECK_NOTNULL(test_header);
+ CHECK(test_header != NULL);
CHECK_EQ(*test_header, TEST_PROGRESSIVE_HEADER_VAL);
}
};
@@ -1958,9 +1957,11 @@ TEST_F(HttpTest, proto_text_content_type) {
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
cntl.http_request().uri() = "/EchoService/Echo";
cntl.http_request().set_content_type("application/proto-text");
- cntl.request_attachment().append(req.Utf8DebugString());
+ std::string req_text;
+ ASSERT_TRUE(google::protobuf::TextFormat::PrintToString(req, &req_text));
+ cntl.request_attachment().append(req_text);
channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr);
- ASSERT_FALSE(cntl.Failed());
+ ASSERT_FALSE(cntl.Failed()) << req_text;
ASSERT_EQ("application/proto-text", cntl.http_response().content_type());
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
cntl.response_attachment().to_string(), &res));
diff --git a/test/brpc_protobuf_json_unittest.cpp
b/test/brpc_protobuf_json_unittest.cpp
index b9289b20..aa73fbd8 100644
--- a/test/brpc_protobuf_json_unittest.cpp
+++ b/test/brpc_protobuf_json_unittest.cpp
@@ -526,7 +526,12 @@ TEST_F(ProtobufJsonTest, json_to_pb_unbounded_recursion) {
std::string error;
bool ret = json2pb::ProtoJsonToProtoMessage(nested_json, &msg,
options, &error);
ASSERT_FALSE(ret);
- ASSERT_EQ("INVALID_ARGUMENT:Message too deep. Max recursion depth
reached for key 'child'", error);
+ ASSERT_NE(std::string::npos, error.find("INVALID_ARGUMENT"))
+ << "error=" << error;
+ ASSERT_TRUE(error.find("recursion") != std::string::npos ||
+ error.find("nested") != std::string::npos ||
+ error.find("too deep") != std::string::npos)
+ << "error=" << error;
}
}
diff --git a/test/brpc_streaming_rpc_unittest.cpp
b/test/brpc_streaming_rpc_unittest.cpp
index 0f8a3e56..6a3b7b9e 100644
--- a/test/brpc_streaming_rpc_unittest.cpp
+++ b/test/brpc_streaming_rpc_unittest.cpp
@@ -633,7 +633,9 @@ TEST_F(StreamingRpcTest, failed_when_rst) {
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
- usleep(1000 * 10);
+ while (handler._expected_next_value != N) {
+ usleep(100);
+ }
{
brpc::SocketUniquePtr ptr;
ASSERT_EQ(0, brpc::Socket::Address(request_stream, &ptr));
diff --git a/test/bthread_mutex_unittest.cpp b/test/bthread_mutex_unittest.cpp
index f839d063..121f1ebb 100644
--- a/test/bthread_mutex_unittest.cpp
+++ b/test/bthread_mutex_unittest.cpp
@@ -328,7 +328,6 @@ TEST(MutexTest, fast_pthread_mutex) {
void* do_pthread_timedlock(void *arg) {
struct timespec t = { -2, 0 };
EXPECT_EQ(ETIMEDOUT, pthread_mutex_timedlock((pthread_mutex_t*)arg, &t));
- EXPECT_EQ(ETIMEDOUT, errno);
return NULL;
}
#endif
@@ -347,7 +346,7 @@ TEST(MutexTest, pthread_mutex) {
struct timespec t = { -2, 0 };
ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&mutex, &t));
pthread_t th;
- ASSERT_EQ(0, pthread_create(&th, NULL, do_fast_pthread_timedlock,
&mutex));
+ ASSERT_EQ(0, pthread_create(&th, NULL, do_pthread_timedlock, &mutex));
ASSERT_EQ(0, pthread_join(th, NULL));
#endif
}
diff --git a/cmake/CMakeLists.download_gtest.in b/test/generate_unittests.bzl
similarity index 63%
copy from cmake/CMakeLists.download_gtest.in
copy to test/generate_unittests.bzl
index df020c89..4b37dd6f 100644
--- a/cmake/CMakeLists.download_gtest.in
+++ b/test/generate_unittests.bzl
@@ -13,18 +13,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-cmake_minimum_required(VERSION 2.8.10)
+def generate_unittests(name, srcs, deps, copts, linkopts = [], data = []):
+ tests = []
+ for s in srcs:
+ ut_name = s.replace(".cpp", "")
+ native.cc_test(
+ name = ut_name,
+ srcs = [s],
+ copts = copts,
+ deps = deps,
+ linkopts = linkopts,
+ data = data,
+ )
+ tests.append(":" + ut_name)
-project(googletest-download NONE)
-
-include(ExternalProject)
-ExternalProject_Add(googletest
- GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG release-1.12.0
- SOURCE_DIR "${PROJECT_BINARY_DIR}/googletest-src"
- BINARY_DIR "${PROJECT_BINARY_DIR}/googletest-build"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- TEST_COMMAND ""
-)
+ native.test_suite(
+ name = name,
+ tests = tests,
+ )
\ No newline at end of file
diff --git a/test/root_runfiles.bzl b/test/root_runfiles.bzl
new file mode 100644
index 00000000..7d38b87d
--- /dev/null
+++ b/test/root_runfiles.bzl
@@ -0,0 +1,79 @@
+# 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.
+
+"""A small Starlark rule that exposes files at the *runfiles workspace root*.
+
+Why this exists
+---------------
+`bazel test` sets the test process cwd to `${TEST_SRCDIR}/${TEST_WORKSPACE}`,
+which under bzlmod is `<test.runfiles>/_main/` -- the workspace root inside
+the runfiles tree. Files declared via the standard `data` attribute on a
+target in `//test` are placed under `_main/test/<file>` (the rule's package
+path), which is NOT the cwd.
+
+Several brpc tests (e.g. brpc_alpn_protocol_unittest, brpc_ssl_unittest,
+brpc_protobuf_json_unittest) open data files such as `cert1.crt`, `cert1.key`,
+`jsonout` by *plain relative paths* (this works under the CMake build because
+file(COPY ...) places them next to the test binary). To make those same
+relative paths work under Bazel without rewriting the test code, we need the
+files to appear at `_main/<file>`, i.e. directly at the cwd.
+
+A genrule cannot help here, because its `outs` must live in the genrule's own
+package. The clean way is `ctx.runfiles(symlinks = {...})`, which places the
+given files at arbitrary paths *relative to the workspace root inside the
+runfiles tree* -- which is exactly the cwd of a Bazel-launched test.
+
+Beware: `ctx.runfiles` has TWO related dict parameters:
+ * symlinks -> paths are relative to <runfiles>/<workspace>/ (the cwd)
+ * root_symlinks -> paths are relative to <runfiles>/ (one level above cwd)
+We want the first one.
+
+Usage
+-----
+ load("//test:root_runfiles.bzl", "root_runfiles")
+
+ root_runfiles(
+ name = "test_runfiles_root_data",
+ srcs = [
+ "cert1.crt",
+ "cert1.key",
+ "jsonout",
+ ],
+ )
+
+Then add `:test_runfiles_root_data` to a cc_test's `data` attribute.
+"""
+
+def _root_runfiles_impl(ctx):
+ # Map each input file to its basename, placed at the workspace root inside
+ # the runfiles tree. That root is the cwd of a `bazel test` process, so
+ # tests can open the files via plain relative paths like "cert1.crt".
+ symlinks = {f.basename: f for f in ctx.files.srcs}
+ runfiles = ctx.runfiles(symlinks = symlinks)
+ return [DefaultInfo(runfiles = runfiles)]
+
+root_runfiles = rule(
+ implementation = _root_runfiles_impl,
+ attrs = {
+ "srcs": attr.label_list(
+ allow_files = True,
+ doc = "Files to expose at the workspace root inside the runfiles "
+
+ "tree (i.e. the cwd of `bazel test`), keyed by basename.",
+ ),
+ },
+ doc = "Exposes data files at the workspace root inside the runfiles tree "
+
+ "(the cwd of `bazel test`), so tests can open them via plain " +
+ "relative paths.",
+)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]