diff --git a/.github/workflows/cpp-packaging.yml b/.github/workflows/cpp-packaging.yml index 6d74da9102..32becc6435 100644 --- a/.github/workflows/cpp-packaging.yml +++ b/.github/workflows/cpp-packaging.yml @@ -28,8 +28,8 @@ on: env: # Packaging prerequisites - # Demumble 1.1.0 released Nov 13, 2018 - demumbleVer: "1.1.0" + # Demumble version from March 22, 2022 + demumbleVer: "df938e45c2b0e064fb5323d88b692d03b451d271" # Use SHA256 for hashing files. hashCommand: "sha256sum" # Xcode version 13.3.1 is the version we build the SDK with. @@ -94,6 +94,11 @@ jobs: if: runner.os == 'macOS' run: sudo xcode-select -s /Applications/Xcode_${{ env.xcodeVersion }}.app/Contents/Developer + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Fetch and build binutils run: | set +e @@ -154,7 +159,7 @@ jobs: with: repository: nico/demumble path: demumble-src - ref: v${{ env.demumbleVer }} + ref: ${{ env.demumbleVer }} - name: build demumble run: | @@ -544,6 +549,9 @@ jobs: cd sdk-src python scripts/gha/install_prereqs_desktop.py --ssl boringssl cd .. + if [[ $(uname) == "Darwin"* ]]; then + brew install parallel + fi - name: postprocess and package built SDK run: | @@ -605,6 +613,48 @@ jobs: name: firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix}}-package path: firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix}}-package.tgz + - name: Check for unrenamed namespaces + shell: bash + run: | + set +e + if [[ "${{ matrix.sdk_platform }}" == "darwin" ]]; then + nm=bin/llvm-nm + elif [[ "${{ matrix.sdk_platform }}" == "windows" ]]; then + if [[ "${{ matrix.sdk_platform }}" == *"x64"* ]]; then + nm="bin/nm --target pe-x86-64" + else + nm="bin/nm --target pe-i386" + fi + else # sdk_platform = linux + nm=bin/nm + fi + ${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 + if [[ "${{ matrix.sdk_platform }}" == "windows" ]]; then + cat raw_symbols.txt | sort | uniq | bin/demumble | grep '^[a-zA-Z_]*::' > cpp_symbols.txt + elif [[ "${{ matrix.sdk_platform }}" == "darwin" ]]; then + # remove leading _ on mach-o symbols + cat raw_symbols.txt | sort | uniq | sed 's/^_//' | bin/c++filt | grep '^[a-zA-Z_]*::' > cpp_symbols.txt + else # linux + cat raw_symbols.txt | sort | uniq | bin/c++filt | grep '^[a-zA-Z_]*::' > cpp_symbols.txt + fi + # cpp_symbols.txt contains a list of all C++ symbols, sorted and deduped + # get a list of all top-level namespaces (except system namespaces) + cat cpp_symbols.txt | cut -d: -f1 | sort | uniq > cpp_namespaces.txt + echo "List of all namespaces found:" + cat cpp_namespaces.txt + # Filter out renamed namespaces (f_b_*), standard namespaces, and the public namespace (firebase::). + # These are all specified in the respective scripts, package.sh and merge_libraries.py + echo $(sdk-src/build_scripts/desktop/package.sh -R)'.*' > allow_namespaces.txt + sdk-src/build_scripts/desktop/package.sh -N >> allow_namespaces.txt + python sdk-src/scripts/print_allowed_namespaces.py >> allow_namespaces.txt + allow_namespaces=$(cat allow_namespaces.txt | tr '\n' '|') + cat cpp_namespaces.txt | grep -vE "^(${allow_namespaces})$" > extra_namespaces.txt + # If there are any namespaces in this file, print an error. + if [[ -s extra_namespaces.txt ]]; then + echo '::error ::Unrenamed C++ namespaces in ${{ matrix.sdk_platform }}${{ matrix.suffix }} package:%0A'$(cat extra_namespaces.txt | tr '\n' ' ' | sed 's/ /%0A/g') + exit 1 + fi + - name: cleanup build artifacts if: | ( diff --git a/build_scripts/desktop/package.sh b/build_scripts/desktop/package.sh index 41bd3c0b00..cef0a16787 100755 --- a/build_scripts/desktop/package.sh +++ b/build_scripts/desktop/package.sh @@ -19,6 +19,8 @@ options: -j, run merge_libraries jobs in parallel -v, enable verbose mode -L, use LLVM binutils + -R, print rename prefix and exit + -N, print allowed namespaces and exit example: build_scripts/desktop/package.sh -b firebase-cpp-sdk-linux -p linux -o package_out -v x86 -j" } @@ -42,6 +44,11 @@ use_llvm_binutils=0 readonly SUPPORTED_PLATFORMS=(linux windows darwin) +# String to prepend to all hidden symbols. +readonly rename_string=f_b_ +# Comma-separated list of c++ namespaces to allow. +readonly allow_cpp_namespaces=firebase + abspath(){ if [[ -d $1 ]]; then echo "$(cd "$1"; pwd -P)" @@ -50,7 +57,7 @@ abspath(){ fi } -while getopts "f:b:o:p:d:m:P:t:hjLv" opt; do +while getopts "f:b:o:p:d:m:P:t:NRhjLv" opt; do case $opt in f) binutils_format=$OPTARG @@ -94,6 +101,14 @@ while getopts "f:b:o:p:d:m:P:t:hjLv" opt; do t) tools_path=$OPTARG ;; + N) + echo "${allow_cpp_namespaces}" | tr ',' '\n' + exit 0 + ;; + R) + echo "${rename_string}" + exit 0 + ;; h) usage exit 0 @@ -208,32 +223,6 @@ readonly deps_hidden_firebase_firestore=" */firestore-build/*/grpc-build/third_party/re2/*${subdir}${prefix}re2.${ext} " -# List of C++ namespaces to be renamed, so as to not conflict with the -# developer's own dependencies. -readonly -a rename_namespaces=(flatbuffers flexbuffers reflection ZLib bssl uWS absl google - base_raw_logging ConnectivityWatcher grpc - grpc_access_token_credentials grpc_alts_credentials - grpc_alts_server_credentials grpc_auth_context - grpc_channel_credentials grpc_channel_security_connector - grpc_chttp2_hpack_compressor grpc_chttp2_stream grpc_chttp2_transport - grpc_client_security_context grpc_composite_call_credentials - grpc_composite_channel_credentials grpc_core grpc_deadline_state - grpc_google_default_channel_credentials grpc_google_iam_credentials - grpc_google_refresh_token_credentials grpc_impl grpc_local_credentials - grpc_local_server_credentials grpc_md_only_test_credentials - grpc_message_compression_algorithm_for_level - grpc_oauth2_token_fetcher_credentials grpc_plugin_credentials - grpc_server_credentials grpc_server_security_connector - grpc_server_security_context - grpc_service_account_jwt_access_credentials grpc_ssl_credentials - grpc_ssl_server_credentials grpc_tls_credential_reload_config - grpc_tls_server_authorization_check_config GrpcUdpListener leveldb - leveldb_filterpolicy_create_bloom leveldb_writebatch_iterate strings - TlsCredentials TlsServerCredentials tsi snappy re2) - -# String to prepend to all hidden symbols. -readonly rename_string=f_b_ - readonly demangle_cmds=${tools_path}/c++filt,${tools_path}/demumble if [[ ${use_llvm_binutils} -eq 1 ]]; then readonly binutils_objcopy=${tools_path}/llvm-objcopy @@ -260,14 +249,11 @@ merge_libraries_params=( --binutils_objcopy_cmd=${binutils_objcopy} --demangle_cmds=${demangle_cmds} --platform=${platform} - --hide_cpp_namespaces=$(echo "${rename_namespaces[*]}" | sed 's| |,|g') + --auto_hide_cpp_namespaces + --ignore_cpp_namespaces="${allow_cpp_namespaces}" ) cache_param=--cache=${cache_file} -if [[ ${platform} == "windows" ]]; then - # Windows has a hard time with strict C++ demangling. - merge_libraries_params+=(--nostrict_cpp) -fi if [[ ${verbose} -eq 1 ]]; then merge_libraries_params+=(--verbosity=3) fi diff --git a/release_build_files/readme.md b/release_build_files/readme.md index 82ac5296b6..1c731be668 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -634,6 +634,12 @@ workflow use only during the development of your app, not for publicly shipping code. ## Release Notes +### Upcoming Release +- Changes + - General (Desktop): Fixed an issue with embedded dependencies that could + cause duplicate symbol linker errors in conjunction with other libraries + ([#989](https://github.com/firebase/firebase-cpp-sdk/issues/989)). + ### 9.3.0 - Changes - General (Android,Linux): Fixed a concurrency bug where waiting for an diff --git a/scripts/merge_libraries.py b/scripts/merge_libraries.py index b91015299d..43dd3ce31a 100644 --- a/scripts/merge_libraries.py +++ b/scripts/merge_libraries.py @@ -104,8 +104,10 @@ "will autodetect target format. If you want to specify " "different input and output formats, separate them with a comma.") -# Never rename 'std::' by default when --auto_hide_cpp_namespaces is enabled. -IMPLICIT_CPP_NAMESPACES_TO_IGNORE = {"std"} +# Never rename system namespaces by default when --auto_hide_cpp_namespaces is enabled. +IMPLICIT_CPP_NAMESPACES_TO_IGNORE = {"std", "type_info", + "__gnu_cxx", "stdext", + "cxxabiv1", "__cxxabiv1"} DEFAULT_ENCODING = "ascii" @@ -416,7 +418,7 @@ def get_top_level_namespaces(demangled_symbols): # It will specifically not match second-level or deeper namespaces: # matched_namespace::unmatched_namespace::Class::Method() regex_top_level_namespaces = re.compile( - r"(?()": {"tparam"}, + "outer_ns::inner::templatefn(Fparam::type)": { + "outer_ns", "t2Param", "Fparam" + }, + "nswithclass::ClassName::ClassName()": {"nswithclass"}, + "onlydestructor::DestructorClass::~DestructorClass()": {"onlydestructor"}, + "topleveldestructor::~topleveldestructor()": {"topleveldestructor"}, + "rtti::RttiClass::`RTTI Class Hierarchy Descriptor'": {"rtti"}, + "toplevelrtti::`RTTI Class Hierarchy Descriptor'": {"toplevelrtti"}, + "public: virtual enum grpc_security_level __cdecl grpc_call_credentials::min_security_level(void) const": { + "grpc_call_credentials" + }, + # std namespaces are used by get_top_level_namespaces but not by add_automatic_namespaces + "std::dont_use_this()": {"std"}, + "std::vector()": { "std", "inside_std_template_param" } + } + all_namespaces = set() + for (symbol, namespaces) in test_symbols.items(): + self.assertEqual( + merge_libraries.get_top_level_namespaces(set({symbol})), + set(namespaces)) + # Gather all the namespaces for later. + all_namespaces.update(namespaces) + # Check that if we pass in all the symbols, we get all the namespaces. + self.assertEqual( + merge_libraries.get_top_level_namespaces(set(test_symbols.keys())), + all_namespaces) + + # std:: should not be added as an automatic namespace + all_namespaces.remove("std") + prev_flag = FLAGS.hide_cpp_namespaces + merge_libraries.add_automatic_namespaces(set(test_symbols.keys())) + self.assertEqual(set(FLAGS.hide_cpp_namespaces), set(all_namespaces)) + FLAGS.hide_cpp_namespaces = prev_flag + + def test_demanglers(self): + """Verify that Demangler works in both streaming and non-streaming mode.""" + + merge_libraries.FLAGS.streaming_demanglers = False + demangler_list = merge_libraries.init_demanglers() + for demangler in demangler_list: + logging.info("Testing %s", os.path.basename(demangler.cmdline[0])) + self.assertEqual( + demangler.demangle("regular_c_symbol_nonstreaming"), + "regular_c_symbol_nonstreaming") + self.assertEqual( + demangler.demangle("_ZN12my_namespace19MyClassNonStreamingC2Ev"), + "my_namespace::MyClassNonStreaming::MyClassNonStreaming()") + merge_libraries.shutdown_demanglers() + + merge_libraries.FLAGS.streaming_demanglers = True + demangler_list = merge_libraries.init_demanglers() + for demangler in demangler_list: + logging.info("Testing %s streaming", os.path.basename(demangler.cmdline[0])) + self.assertEqual( + demangler.demangle("regular_c_symbol_streaming"), + "regular_c_symbol_streaming") + self.assertEqual( + demangler.demangle("_ZN12my_namespace16MyClassStreamingC2Ev"), + "my_namespace::MyClassStreaming::MyClassStreaming()") + merge_libraries.shutdown_demanglers() + + def test_extracting_archive(self): + tempdir = tempfile.mkdtemp() + cwd = os.getcwd() + os.chdir(tempfile.mkdtemp()) + try: + archive = _TEST_FILE + (obj_file_list, errors) = merge_libraries.extract_archive(archive) + self.assertEmpty(errors) + self.assertLen(obj_file_list, 1) + self.assertTrue(os.path.isfile(obj_file_list[0])) + finally: + os.chdir(cwd) + if os.path.exists(tempdir): + shutil.rmtree(tempdir) + + def test_error_extracting_archive(self): + v = logging.get_verbosity() + # Temporarily turn off warnings so loading a bad filename doesn't log a bunch + logging.set_verbosity(logging.ERROR) + (obj_file_list, errors) = merge_libraries.extract_archive("bad_filename.a") + logging.set_verbosity(v) + self.assertEmpty(obj_file_list) + self.assertNotEmpty(errors) + + def test_renaming_c_symbols(self): + """Verify that C symbols can be renamed.""" + tempdir = tempfile.mkdtemp() + cwd = os.getcwd() + os.chdir(tempdir) + prefix = '_' if merge_libraries.FLAGS.platform == 'darwin' else '' + try: + archive = _TEST_FILE + + all_expected_c_symbols = { + prefix+"global_c_symbol", + prefix+"test_another_symbol", + prefix+"test_symbol", + prefix+"test_yet_one_more_symbol", + } + rename_symbols = {prefix+"test_symbol": prefix+"renamed_test_symbol"} + redefinition_file = merge_libraries.create_symbol_redefinition_file( + rename_symbols) + + # Extract the .o file from the library. + (obj_file_list, errors) = merge_libraries.extract_archive(archive) + self.assertEmpty(errors) + self.assertLen(obj_file_list, 1) + self.assertTrue(os.path.isfile(obj_file_list[0])) + library_file = obj_file_list[0] + before_symbols = merge_libraries.read_symbols_from_archive( + library_file)[0] + before_c_symbols = {s for s in before_symbols + if not merge_libraries.is_cpp_symbol(s)} + + self.assertTrue(all_expected_c_symbols.issubset(before_c_symbols)) + + expect_c_symbols_after_rename = all_expected_c_symbols + expect_c_symbols_after_rename.remove(prefix+"test_symbol") + expect_c_symbols_after_rename.add(prefix+"renamed_test_symbol") + + # Rename the symbols. + merge_libraries.move_object_file(library_file, + "%s_renamed.o" % library_file, + redefinition_file.name) + after_symbols = merge_libraries.read_symbols_from_archive("%s_renamed.o" % + library_file)[0] + after_c_symbols = {s for s in after_symbols + if not merge_libraries.is_cpp_symbol(s)} + self.assertTrue(expect_c_symbols_after_rename.issubset(after_c_symbols)) + finally: + os.chdir(cwd) + if os.path.exists(tempdir): + shutil.rmtree(tempdir) + + def test_renaming_cpp_symbols(self): + """Verify that C++ symbols can be renamed.""" + + merge_libraries.init_cache() # cache is required for demangling + merge_libraries.init_demanglers() + # Make sure the demangler is operating correctly. + self.assertEqual( + merge_libraries.demangle_symbol("_Znwm"), "operator new(unsigned long)") + tempdir = tempfile.mkdtemp() + cwd = os.getcwd() + os.chdir(tempdir) + try: + archive = _TEST_FILE + merge_libraries.FLAGS.hide_cpp_namespaces = ["test_namespace"] + merge_libraries.FLAGS.rename_string = "renamed_" + + # Unlike the c test above, we can't make an exhaustive list of all + # symbols, so instead, grab all the symbols and make sure that + # test_namespace is changed to renamed_test_namespace. + + # Extract the .o file from the library. + (obj_file_list, errors) = merge_libraries.extract_archive(archive) + self.assertEmpty(errors) + self.assertLen(obj_file_list, 1) + self.assertTrue(os.path.isfile(obj_file_list[0])) + library_file = obj_file_list[0] + before_symbols = merge_libraries.read_symbols_from_archive( + library_file)[1] # read all symbols, not just defined ones + before_cpp_symbols_raw = { + s for s in before_symbols if merge_libraries.is_cpp_symbol(s) + } + # demangle the symbols for human consumption (and so we can check if the + # namespace was modified) + before_cpp_symbols = { + merge_libraries.demangle_symbol(s) for s in before_cpp_symbols_raw + } + + # Ensure a few known symbols are present. + self.assertIn("test_namespace::TestClass::TestClass()", + before_cpp_symbols) + self.assertIn("test_namespace::TestClass::~TestClass()", + before_cpp_symbols) + self.assertIn( + "test_namespace::TestClass::TestClass(test_namespace::TestClass const&)", + before_cpp_symbols) + self.assertIn("test_namespace::TestClass::TestStaticMethod()", + before_cpp_symbols) + self.assertIn( + "test_namespace::TestClass::TestStaticMethodNotInThisFile()", + before_cpp_symbols) + + rename_symbols = {} + for sym in before_cpp_symbols_raw: + rename_symbols.update(merge_libraries.rename_symbol(sym)) + + redefinition_file = merge_libraries.create_symbol_redefinition_file( + rename_symbols) + + # If no file is written, no symbols were set to be renamed. + self.assertIsNotNone(redefinition_file) + + # To get the expected list of symbols, simply change test_namespace:: to + # renamed_test_namespace:: + expect_cpp_symbols_after_rename = set() + + for s in before_cpp_symbols: + if not s.startswith("another_namespace"): + s = s.replace("test_namespace::", "renamed_test_namespace::") + expect_cpp_symbols_after_rename.add(s) + + # Rename the symbols. + merge_libraries.move_object_file(library_file, + "%s_renamed.o" % library_file, + redefinition_file.name) + after_symbols = merge_libraries.read_symbols_from_archive("%s_renamed.o" % + library_file)[1] + after_cpp_symbols_raw = { + s for s in after_symbols if merge_libraries.is_cpp_symbol(s) + } + after_cpp_symbols = { + merge_libraries.demangle_symbol(s) for s in after_cpp_symbols_raw + } + self.assertEqual(after_cpp_symbols, expect_cpp_symbols_after_rename) + + # Ensure the DontRenameThis class isn't renamed, as it's inside an inner namespace. + self.assertTrue("another_namespace::test_namespace::DontRenameThis::DontRenameThisMethod()" in before_cpp_symbols) + self.assertTrue("another_namespace::test_namespace::DontRenameThis::DontRenameThisMethod()" in after_cpp_symbols) + finally: + merge_libraries.shutdown_demanglers() + merge_libraries.shutdown_cache() + os.chdir(cwd) + if os.path.exists(tempdir): + shutil.rmtree(tempdir) + + +if __name__ == "__main__": + absltest.main() diff --git a/scripts/merge_libraries_test.sh b/scripts/merge_libraries_test.sh new file mode 100755 index 0000000000..fedb10bf67 --- /dev/null +++ b/scripts/merge_libraries_test.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Script to test merge_libraries script via merge_libraries_test. + +set -e + +usage(){ + echo "Usage: $0 [options] +options: + -t, packaging tools directory default: ~/bin + -P, python command default: python3 + -L, use LLVM binutils +example: + scripts/merge_libraries_test.sh -t ~/llvm-binutils -L" +} + +use_llvm_binutils=0 +python_cmd=python3 +tools_path=~/bin +script_dir=$(cd $(dirname $0); pwd -P) + +while getopts "t:P:Lh" opt; do + case $opt in + L) + use_llvm_binutils=1 + ;; + P) + python_cmd=$OPTARG + ;; + t) + tools_path=$OPTARG + ;; + h) + usage + exit 0 + ;; + *) + usage + exit 2 + ;; + esac +done + +readonly demangle_cmds=${tools_path}/c++filt,${tools_path}/demumble +if [[ ${use_llvm_binutils} -eq 1 ]]; then + readonly binutils_objcopy=${tools_path}/llvm-objcopy + readonly binutils_nm=${tools_path}/llvm-nm + readonly binutils_ar=${tools_path}/llvm-ar +else + readonly binutils_objcopy=${tools_path}/objcopy + if [[ -x ${tools_path}/nm-new ]] ; then + readonly binutils_nm=${tools_path}/nm-new + else + readonly binutils_nm=${tools_path}/nm + fi + readonly binutils_ar=${tools_path}/ar +fi + +declare -a merge_libraries_params +merge_libraries_params=( + --binutils_nm_cmd=${binutils_nm} + --binutils_ar_cmd=${binutils_ar} + --binutils_objcopy_cmd=${binutils_objcopy} + --demangle_cmds=${demangle_cmds} +) + +if [[ $(uname) == "Darwin" ]]; then + platform=darwin +else + platform=linux +fi + +rm -f merge_libraries_test_file.a test_file.o +set -x +"${python_cmd}" "${script_dir}/merge_libraries_test.py" --platform ${platform} ${merge_libraries_params[*]} diff --git a/scripts/merge_libraries_test_file.cc b/scripts/merge_libraries_test_file.cc new file mode 100644 index 0000000000..e8518bf3c0 --- /dev/null +++ b/scripts/merge_libraries_test_file.cc @@ -0,0 +1,101 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed 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. + */ + +/* This is a test file for merge_libraries.py tests. It contains some C and C++ + * symbols that merge_libraries can rename. */ + +#include +#include +#include +#include + +extern "C" { +int test_symbol(void) { return 1; } // NOLINT + +int test_another_symbol(void) { return 2; } // NOLINT + +int test_yet_one_more_symbol(void) { return 3; } // NOLINT + +int global_c_symbol = 789; // NOLINT + +extern int not_in_this_file(); // NOLINT +} + +namespace test_namespace { + +class TestClass { + public: + TestClass(); + TestClass(const TestClass&); // not in this file + TestClass(const TestClass&&); // not in this file + ~TestClass(); // not in this file + int TestMethod(); + int TestMethodNotInThisfile(); + static int TestStaticMethod(); + static int TestStaticMethodNotInThisFile(); + static int test_static_field; // NOLINT + static int test_static_field_not_in_this_file; // NOLINT +}; + +int global_cpp_symbol = 12345; // NOLINT + +int TestClass::test_static_field; + +TestClass::TestClass() {} + +int TestClass::TestMethod() { + return TestMethodNotInThisfile() + not_in_this_file(); +} + +int TestClass::TestStaticMethod() { return TestStaticMethodNotInThisFile(); } + +} // namespace test_namespace + +void GlobalFunctionWithParameter(test_namespace::TestClass const&, int) {} + +void GlobalFunctionWithMultipleParameters( + test_namespace::TestClass* p1, std::vector p2, + std::unique_ptr p3, + std::vector> p4, std::string) { + p2.push_back(*p1); + p2.pop_back(); + p4.push_back(std::move(p3)); + p4.pop_back(); +} + +extern void ExternFunctionWithParameter(test_namespace::TestClass&&, int); + +extern void ExternFunctionWithMultipleParameters( + const test_namespace::TestClass&, + std::unique_ptr, std::string); + +namespace another_namespace { + +extern void ExternFunctionNotUsingNamespace(std::string); + +// Not at the top level, don't rename this class's methods. +namespace test_namespace { + +class DontRenameThis { + public: + int DontRenameThisMethod(); +}; + +int DontRenameThis::DontRenameThisMethod() { return 0; } + +} // namespace test_namespace + +} // namespace another_namespace diff --git a/scripts/print_allowed_namespaces.py b/scripts/print_allowed_namespaces.py new file mode 100644 index 0000000000..d41b4193ae --- /dev/null +++ b/scripts/print_allowed_namespaces.py @@ -0,0 +1,17 @@ +# Copyright 2022 Google LLC +# +# Licensed 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. + +import merge_libraries + +print('\n'.join(merge_libraries.IMPLICIT_CPP_NAMESPACES_TO_IGNORE), end='')