如何让Bazel正确链接ICU外部依赖?构建测试遇符号缺失问题
M1 Mac下Bazel集成ICU依赖时符号缺失问题
环境
- M1 Mac
- Homebrew安装的Bazel
- Xcode clang编译器
- ICU 73.1版本,通过
http_archive引入,使用其自带BUILD文件
现象
- 可正常构建
//:utils和//:utils_test目标 - 运行
bazel test //:utils_test时出现动态链接符号缺失错误 - 构建
//:app时出现链接阶段符号未定义错误
错误详情
测试执行错误(bazel test //:utils_test)
INFO: From Testing //:utils_test: ==================== Test output for //:utils_test: dyld[15553]: symbol not found in flat namespace '__ZN6icu_738ByteSink15GetAppendBufferEiiPciPi' ================================================================================ Target //:utils_test up-to-date: bazel-bin/utils_test INFO: Elapsed time: 0.275s, Critical Path: 0.20s INFO: 2 processes: 2 darwin-sandbox. INFO: Build completed, 1 test FAILED, 2 total actions //:utils_test FAILED in 0.1s
构建app错误(bazel build //:app)
ERROR: /Users/default/Projects/icu-demo/BUILD:10:10: Linking app failed: (Exit 1): cc_wrapper.sh failed: error executing command (from target //:app) external/local_config_cc/cc_wrapper.sh @bazel-out/darwin_arm64-fastbuild/bin/app-2.params Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging Undefined symbols for architecture arm64: "icu_73::UnicodeString::toLower()", referenced from: ToLower(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) in libutils.a(utils.o) "icu_73::ByteSink::GetAppendBuffer(int, int, char*, int, int*)", referenced from: vtable for icu_73::StringByteSink<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> in libutils.a(utils.o) "icu_73::ByteSink::Flush()", referenced from: vtable for icu_73::StringByteSink<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> in libutils.a(utils.o) "icu_73::ByteSink::~ByteSink()", referenced from: icu_73::StringByteSink<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>::StringByteSink(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, int) in libutils.a(utils.o) icu_73::StringByteSink<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>::~StringByteSink() in libutils.a(utils.o) "typeinfo for icu_73::ByteSink", referenced from: typeinfo for icu_73::StringByteSink<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> in libutils.a(utils.o) "vtable for icu_73::ByteSink", referenced from: icu_73::ByteSink::ByteSink() in libutils.a(utils.o) NOTE: a missing vtable usually means the first non-inline virtual member function has no definition. ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) Error in child process '/usr/bin/xcrun'. 1 Target //:app failed to build Use --verbose_failures to see the command lines of failed build steps. INFO: Elapsed time: 0.168s, Critical Path: 0.11s INFO: 2 processes: 2 internal. FAILED: Build did NOT complete successfully
相关配置文件
WORKSPACE
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "icu", sha256 = "a457431de164b4aa7eca00ed134d00dfbf88a77c6986a10ae7774fc076bb8c45", strip_prefix = "icu", urls = ["https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.tgz"], ) http_archive( name = "tinytest", sha256 = "71f366e680606e18268e6b9673a65c44c9e672f7356a61ffbcd3502c6a8eea0b", strip_prefix = "TinyTest-460c9492d927689b9db7f28d8742705dc0bbee62", urls = ["https://github.com/headhunter45/TinyTest/archive/460c9492d927689b9db7f28d8742705dc0bbee62.zip"], )
BUILD
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") cc_library( name = "utils", srcs = ["utils.cpp"], hdrs = ["utils.h"], deps = ["@icu//source/common:platform"], ) cc_binary( name = "app", srcs = ["app.cpp"], deps = ["utils"], ) cc_test( name = "utils_test", srcs = ["utils_test.cpp"], deps = [ "utils", "@tinytest", ], )
utils.h
#include <string> std::string ToLower(const std::string& text);
utils.cpp
#include "utils.h" #include <string> #include "unicode/unistr.h" using icu::UnicodeString; using std::string; string ToLower(const string& text) { UnicodeString ustr = UnicodeString::fromUTF8(text); string result; ustr.toLower(); ustr.toUTF8String(result); return result; }
utils_test.cpp
#include "utils.h" #include <string> #include <tuple> #include "tinytest.h" using namespace TinyTest; using namespace std; int main(int argc, char* argv[]) { TestResults results; results += execute_suite(make_test_suite( "ToLower(const std::string&)", ToLower, { make_test("should get an empty string", (string) "", make_tuple("")), make_test("should get the same string", (string) "the same string", make_tuple("the same string")), make_test("should get a lowercase string", (string) "a lowercase string", make_tuple("A lOwErCaSe StRiNg")), })); return results.failed() + results.errors(); }
app.cpp
#include <iostream> #include <string> #include "utils.h" using namespace std; int main(int argc, char* argv[]) { string original = "ThIs Is A nOrMaL sTrInG."; cout << "original: " << original << endl; cout << "lowercase: " << ToLower(original) << endl; return 0; }
解决方案
问题根源在于ICU依赖目标选择错误:当前utils仅依赖@icu//source/common:platform,该目标仅包含平台基础代码,未涵盖UnicodeString、ByteSink等核心功能的实现库。
修改BUILD文件中的utils库定义,替换为ICU核心字符处理库:
cc_library( name = "utils", srcs = ["utils.cpp"], hdrs = ["utils.h"], deps = [ "@icu//source/common:icuuc", # ICU核心字符处理库,包含所有核心功能实现 ], )
替换后验证:
- 执行
bazel build //:app,构建成功 - 执行
bazel test //:utils_test,测试运行成功
内容的提问来源于stack exchange,提问作者majinnaibu
相关产品推荐
相关产品推荐

