Skip to content

Commit c046c8a

Browse files
authored
Auto populate the namespace list to rename in desktop packaging (#1000)
* Change desktop packaging to automatically get the list of C++ namespaces to rename. This prevents some namespaces from accidentally being omitted. Instead, the script will rename all namespaces except for std:: and __gnu_cxx. * Add additional namespaces to ignore. * Add readme note * After packaging, scan the libraries to ensure no leaking namespaces. Also upgrade demumble to 1.2.2 which handles more MSVC demangling cases. * Add missing parallel install * Fix a bug in merge_libraries that did not rename all symbols. * Fix a bug that failed to find namespaces with tildes. * Also parse `RTTI Class Heirarchy Descriptor` on windows. Fix windows BFD target. * Add '$' to top-level namespace search for Darwin. This fixes grpc_resource_quota_arg_vtable::$_0::__invoke * Fix renaming of Windows symbols with RTTI info, etc. * Add tests for merge_libraries script. * Fix Windows symbol renaming for odd C++ symbols like RTTI descriptors * Ignore underscores as well to fix a Windows packging issue * Don't detect namespaces from external symbols. * Don't auto-add namespaces with underscores. * Fix namespace detection to only rename top-level; add test. Remove strict_cpp as it should no longer be required for Windows. * Add test for add_automatic_namespaces * Allow the list of allowed namespaces to be determined programatically.
1 parent ef7dff1 commit c046c8a

File tree

8 files changed

+572
-47
lines changed

8 files changed

+572
-47
lines changed

.github/workflows/cpp-packaging.yml

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ on:
2828

2929
env:
3030
# Packaging prerequisites
31-
# Demumble 1.1.0 released Nov 13, 2018
32-
demumbleVer: "1.1.0"
31+
# Demumble version from March 22, 2022
32+
demumbleVer: "df938e45c2b0e064fb5323d88b692d03b451d271"
3333
# Use SHA256 for hashing files.
3434
hashCommand: "sha256sum"
3535
# Xcode version 13.3.1 is the version we build the SDK with.
@@ -94,6 +94,11 @@ jobs:
9494
if: runner.os == 'macOS'
9595
run: sudo xcode-select -s /Applications/Xcode_${{ env.xcodeVersion }}.app/Contents/Developer
9696

97+
- name: Setup python
98+
uses: actions/setup-python@v2
99+
with:
100+
python-version: 3.7
101+
97102
- name: Fetch and build binutils
98103
run: |
99104
set +e
@@ -154,7 +159,7 @@ jobs:
154159
with:
155160
repository: nico/demumble
156161
path: demumble-src
157-
ref: v${{ env.demumbleVer }}
162+
ref: ${{ env.demumbleVer }}
158163

159164
- name: build demumble
160165
run: |
@@ -544,6 +549,9 @@ jobs:
544549
cd sdk-src
545550
python scripts/gha/install_prereqs_desktop.py --ssl boringssl
546551
cd ..
552+
if [[ $(uname) == "Darwin"* ]]; then
553+
brew install parallel
554+
fi
547555
548556
- name: postprocess and package built SDK
549557
run: |
@@ -605,6 +613,48 @@ jobs:
605613
name: firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix}}-package
606614
path: firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix}}-package.tgz
607615

616+
- name: Check for unrenamed namespaces
617+
shell: bash
618+
run: |
619+
set +e
620+
if [[ "${{ matrix.sdk_platform }}" == "darwin" ]]; then
621+
nm=bin/llvm-nm
622+
elif [[ "${{ matrix.sdk_platform }}" == "windows" ]]; then
623+
if [[ "${{ matrix.sdk_platform }}" == *"x64"* ]]; then
624+
nm="bin/nm --target pe-x86-64"
625+
else
626+
nm="bin/nm --target pe-i386"
627+
fi
628+
else # sdk_platform = linux
629+
nm=bin/nm
630+
fi
631+
${nm} `find firebase-cpp-sdk-*-package/libs/${{ matrix.sdk_platform }} -type f` | grep '^[0-9a-fA-F][0-9a-fA-F]* ' | cut -d' ' -f3- > raw_symbols.txt
632+
if [[ "${{ matrix.sdk_platform }}" == "windows" ]]; then
633+
cat raw_symbols.txt | sort | uniq | bin/demumble | grep '^[a-zA-Z_]*::' > cpp_symbols.txt
634+
elif [[ "${{ matrix.sdk_platform }}" == "darwin" ]]; then
635+
# remove leading _ on mach-o symbols
636+
cat raw_symbols.txt | sort | uniq | sed 's/^_//' | bin/c++filt | grep '^[a-zA-Z_]*::' > cpp_symbols.txt
637+
else # linux
638+
cat raw_symbols.txt | sort | uniq | bin/c++filt | grep '^[a-zA-Z_]*::' > cpp_symbols.txt
639+
fi
640+
# cpp_symbols.txt contains a list of all C++ symbols, sorted and deduped
641+
# get a list of all top-level namespaces (except system namespaces)
642+
cat cpp_symbols.txt | cut -d: -f1 | sort | uniq > cpp_namespaces.txt
643+
echo "List of all namespaces found:"
644+
cat cpp_namespaces.txt
645+
# Filter out renamed namespaces (f_b_*), standard namespaces, and the public namespace (firebase::).
646+
# These are all specified in the respective scripts, package.sh and merge_libraries.py
647+
echo $(sdk-src/build_scripts/desktop/package.sh -R)'.*' > allow_namespaces.txt
648+
sdk-src/build_scripts/desktop/package.sh -N >> allow_namespaces.txt
649+
python sdk-src/scripts/print_allowed_namespaces.py >> allow_namespaces.txt
650+
allow_namespaces=$(cat allow_namespaces.txt | tr '\n' '|')
651+
cat cpp_namespaces.txt | grep -vE "^(${allow_namespaces})$" > extra_namespaces.txt
652+
# If there are any namespaces in this file, print an error.
653+
if [[ -s extra_namespaces.txt ]]; then
654+
echo '::error ::Unrenamed C++ namespaces in ${{ matrix.sdk_platform }}${{ matrix.suffix }} package:%0A'$(cat extra_namespaces.txt | tr '\n' ' ' | sed 's/ /%0A/g')
655+
exit 1
656+
fi
657+
608658
- name: cleanup build artifacts
609659
if: |
610660
(

build_scripts/desktop/package.sh

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ options:
1919
-j, run merge_libraries jobs in parallel
2020
-v, enable verbose mode
2121
-L, use LLVM binutils
22+
-R, print rename prefix and exit
23+
-N, print allowed namespaces and exit
2224
example:
2325
build_scripts/desktop/package.sh -b firebase-cpp-sdk-linux -p linux -o package_out -v x86 -j"
2426
}
@@ -42,6 +44,11 @@ use_llvm_binutils=0
4244

4345
readonly SUPPORTED_PLATFORMS=(linux windows darwin)
4446

47+
# String to prepend to all hidden symbols.
48+
readonly rename_string=f_b_
49+
# Comma-separated list of c++ namespaces to allow.
50+
readonly allow_cpp_namespaces=firebase
51+
4552
abspath(){
4653
if [[ -d $1 ]]; then
4754
echo "$(cd "$1"; pwd -P)"
@@ -50,7 +57,7 @@ abspath(){
5057
fi
5158
}
5259

53-
while getopts "f:b:o:p:d:m:P:t:hjLv" opt; do
60+
while getopts "f:b:o:p:d:m:P:t:NRhjLv" opt; do
5461
case $opt in
5562
f)
5663
binutils_format=$OPTARG
@@ -94,6 +101,14 @@ while getopts "f:b:o:p:d:m:P:t:hjLv" opt; do
94101
t)
95102
tools_path=$OPTARG
96103
;;
104+
N)
105+
echo "${allow_cpp_namespaces}" | tr ',' '\n'
106+
exit 0
107+
;;
108+
R)
109+
echo "${rename_string}"
110+
exit 0
111+
;;
97112
h)
98113
usage
99114
exit 0
@@ -208,32 +223,6 @@ readonly deps_hidden_firebase_firestore="
208223
*/firestore-build/*/grpc-build/third_party/re2/*${subdir}${prefix}re2.${ext}
209224
"
210225

211-
# List of C++ namespaces to be renamed, so as to not conflict with the
212-
# developer's own dependencies.
213-
readonly -a rename_namespaces=(flatbuffers flexbuffers reflection ZLib bssl uWS absl google
214-
base_raw_logging ConnectivityWatcher grpc
215-
grpc_access_token_credentials grpc_alts_credentials
216-
grpc_alts_server_credentials grpc_auth_context
217-
grpc_channel_credentials grpc_channel_security_connector
218-
grpc_chttp2_hpack_compressor grpc_chttp2_stream grpc_chttp2_transport
219-
grpc_client_security_context grpc_composite_call_credentials
220-
grpc_composite_channel_credentials grpc_core grpc_deadline_state
221-
grpc_google_default_channel_credentials grpc_google_iam_credentials
222-
grpc_google_refresh_token_credentials grpc_impl grpc_local_credentials
223-
grpc_local_server_credentials grpc_md_only_test_credentials
224-
grpc_message_compression_algorithm_for_level
225-
grpc_oauth2_token_fetcher_credentials grpc_plugin_credentials
226-
grpc_server_credentials grpc_server_security_connector
227-
grpc_server_security_context
228-
grpc_service_account_jwt_access_credentials grpc_ssl_credentials
229-
grpc_ssl_server_credentials grpc_tls_credential_reload_config
230-
grpc_tls_server_authorization_check_config GrpcUdpListener leveldb
231-
leveldb_filterpolicy_create_bloom leveldb_writebatch_iterate strings
232-
TlsCredentials TlsServerCredentials tsi snappy re2)
233-
234-
# String to prepend to all hidden symbols.
235-
readonly rename_string=f_b_
236-
237226
readonly demangle_cmds=${tools_path}/c++filt,${tools_path}/demumble
238227
if [[ ${use_llvm_binutils} -eq 1 ]]; then
239228
readonly binutils_objcopy=${tools_path}/llvm-objcopy
@@ -260,14 +249,11 @@ merge_libraries_params=(
260249
--binutils_objcopy_cmd=${binutils_objcopy}
261250
--demangle_cmds=${demangle_cmds}
262251
--platform=${platform}
263-
--hide_cpp_namespaces=$(echo "${rename_namespaces[*]}" | sed 's| |,|g')
252+
--auto_hide_cpp_namespaces
253+
--ignore_cpp_namespaces="${allow_cpp_namespaces}"
264254
)
265255
cache_param=--cache=${cache_file}
266256

267-
if [[ ${platform} == "windows" ]]; then
268-
# Windows has a hard time with strict C++ demangling.
269-
merge_libraries_params+=(--nostrict_cpp)
270-
fi
271257
if [[ ${verbose} -eq 1 ]]; then
272258
merge_libraries_params+=(--verbosity=3)
273259
fi

release_build_files/readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,12 @@ workflow use only during the development of your app, not for publicly shipping
634634
code.
635635

636636
## Release Notes
637+
### Upcoming Release
638+
- Changes
639+
- General (Desktop): Fixed an issue with embedded dependencies that could
640+
cause duplicate symbol linker errors in conjunction with other libraries
641+
([#989](https://github.com/firebase/firebase-cpp-sdk/issues/989)).
642+
637643
### 9.3.0
638644
- Changes
639645
- General (Android,Linux): Fixed a concurrency bug where waiting for an

scripts/merge_libraries.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,10 @@
104104
"will autodetect target format. If you want to specify "
105105
"different input and output formats, separate them with a comma.")
106106

107-
# Never rename 'std::' by default when --auto_hide_cpp_namespaces is enabled.
108-
IMPLICIT_CPP_NAMESPACES_TO_IGNORE = {"std"}
107+
# Never rename system namespaces by default when --auto_hide_cpp_namespaces is enabled.
108+
IMPLICIT_CPP_NAMESPACES_TO_IGNORE = {"std", "type_info",
109+
"__gnu_cxx", "stdext",
110+
"cxxabiv1", "__cxxabiv1"}
109111

110112
DEFAULT_ENCODING = "ascii"
111113

@@ -416,7 +418,7 @@ def get_top_level_namespaces(demangled_symbols):
416418
# It will specifically not match second-level or deeper namespaces:
417419
# matched_namespace::unmatched_namespace::Class::Method()
418420
regex_top_level_namespaces = re.compile(
419-
r"(?<![a-zA-Z0-9_])(?<!::)([a-zA-Z_][a-zA-Z0-9_]*)::(?=[a-zA-Z_])")
421+
r"(?<![a-zA-Z0-9_])(?<!::)([a-zA-Z_~\$][a-zA-Z0-9_]*)::(?=[a-zA-Z_~` \$])")
420422
found_namespaces = set()
421423
for cppsymbol in demangled_symbols:
422424
m = regex_top_level_namespaces.findall(cppsymbol)
@@ -441,6 +443,8 @@ def add_automatic_namespaces(symbols):
441443
logging.debug("Scanning for top-level namespaces.")
442444
demangled_symbols = [demangle_symbol(symbol) for symbol in symbols]
443445
hide_namespaces = get_top_level_namespaces(demangled_symbols)
446+
# strip out top-level namespaces beginning with an underscore
447+
hide_namespaces = set([n for n in hide_namespaces if n[0] != "_"])
444448
ignore_namespaces = set(FLAGS.ignore_cpp_namespaces)
445449
ignore_namespaces.update(IMPLICIT_CPP_NAMESPACES_TO_IGNORE)
446450
logging.debug("Ignoring namespaces: %s", " ".join(
@@ -533,25 +537,29 @@ def read_symbols_from_archive(archive_file):
533537
return (defined_symbols, all_symbols)
534538

535539

536-
def symbol_includes_cpp_namespace(cpp_symbol, namespace):
540+
def symbol_includes_top_level_cpp_namespace(cpp_symbol, namespace):
537541
"""Returns true if the C++ symbol contains the namespace in it.
538542
539543
This means the symbol is within the namespace, or the namespace as
540544
an argument type, return type, template type, etc.
541545
546+
If FLAGS.strict_cpp == True, this will only return true if the namespace
547+
is at the top level of the symbol.
548+
542549
Args:
543550
cpp_symbol: C++ symbol to check.
544551
namespace: Namespace to look for in the C++ symbol.
545552
Returns:
546-
True if the symbol includes the C++ namespace in some way, False otherwise.
553+
True if the symbol includes the C++ namespace at the top level (or anywhere,
554+
if FLAGS.strict_cpp == False), False otherwise.
547555
"""
548556
# Early out if the namespace isn't in the mangled symbol.
549557
if namespace not in cpp_symbol:
550558
return False
551559
if not FLAGS.strict_cpp:
552560
# If we aren't being fully strict about C++ symbol renaming,
553561
# we can use this placeholder method.
554-
if FLAGS.platform == "windows" and ("@%s@@" % namespace) in cpp_symbol:
562+
if FLAGS.platform == "windows" and re.search(r"[^a-z_]%s@@" % namespace, cpp_symbol):
555563
return True
556564
elif (FLAGS.platform != "windows" and
557565
("%d%s" % (len(namespace), namespace)) in cpp_symbol):
@@ -566,7 +574,8 @@ def symbol_includes_cpp_namespace(cpp_symbol, namespace):
566574
return True
567575
# Or, check if the demangled symbol has "namespace::" preceded by a non-
568576
# alphanumeric character. This avoids a false positive on "notmynamespace::".
569-
regex = re.compile("[^0-9a-zA-Z_]%s::" % namespace)
577+
# Also don't allow a namespace :: right before the name.
578+
regex = re.compile("[^0-9a-zA-Z_:]%s::" % namespace)
570579
if re.search(regex, demangled):
571580
return True
572581

@@ -626,7 +635,7 @@ def rename_symbol(symbol):
626635
new_symbol = symbol
627636
if FLAGS.platform in ["linux", "android", "darwin", "ios"]:
628637
for ns in FLAGS.hide_cpp_namespaces:
629-
if symbol_includes_cpp_namespace(symbol, ns):
638+
if symbol_includes_top_level_cpp_namespace(symbol, ns):
630639
# Linux and Darwin: To rename "namespace" to "prefixnamespace",
631640
# change all instances of "9namespace" to "15prefixnamespace".
632641
# (the number is the length of the namespace name)
@@ -637,12 +646,12 @@ def rename_symbol(symbol):
637646
new_renames[symbol] = new_symbol
638647
elif FLAGS.platform == "windows":
639648
for ns in FLAGS.hide_cpp_namespaces:
640-
if symbol_includes_cpp_namespace(symbol, ns):
649+
if symbol_includes_top_level_cpp_namespace(symbol, ns):
641650
# Windows: To rename "namespace" to "prefixnamespace",
642-
# change all instances of "@namespace@@" to "@prefixnamespace@@".
651+
# change all instances of "[^a-z_]namespace@@" to "[^a-z]prefixnamespace@@",
643652
# See https://msdn.microsoft.com/en-us/library/56h2zst2.aspx
644653
new_ns = FLAGS.rename_string + ns
645-
new_symbol = re.sub("@%s@@" % ns, "@%s@@" % new_ns, new_symbol)
654+
new_symbol = re.sub(r"(?<=[^a-z_])%s@@" % ns, r"%s@@" % new_ns, new_symbol)
646655
new_renames[symbol] = new_symbol
647656
else:
648657
if FLAGS.platform == "windows" and symbol.startswith("$LN"):
@@ -949,6 +958,7 @@ def main(argv):
949958
# Scan through all input libraries for C++ symbols matching any of the
950959
# hide_cpp_namespaces.
951960
cpp_symbols = set()
961+
all_defined_symbols = set()
952962
for input_path in input_paths + additional_input_paths:
953963
logging.debug("Scanning for C++ symbols in %s", input_path[1])
954964
if os.path.abspath(input_path[1]) in _cache["symbols"]:
@@ -962,10 +972,11 @@ def main(argv):
962972
cpp_symbols.update(all_symbols
963973
if FLAGS.rename_external_cpp_symbols
964974
else defined_symbols)
975+
all_defined_symbols.update(defined_symbols)
965976

966977
# If we are set to scan for namespaces, do that now.
967978
if FLAGS.auto_hide_cpp_namespaces:
968-
add_automatic_namespaces(defined_symbols)
979+
add_automatic_namespaces(all_defined_symbols)
969980

970981
for symbol in cpp_symbols:
971982
if is_cpp_symbol(symbol):

0 commit comments

Comments
 (0)