diff --git a/CMakeLists.txt b/CMakeLists.txt index 487ef41b285..0e73d26f9dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,6 @@ option( ON ) - list(INSERT CMAKE_MODULE_PATH 0 ${PROJECT_SOURCE_DIR}/cmake) include(compiler_setup) include(sanitizer_options) @@ -234,6 +233,12 @@ if(NOT ZLIB_FOUND) endif() +# Snappy +set(SNAPPY_BUILD_TESTS OFF CACHE BOOL "Firestore disabled") +set(SNAPPY_BUILD_BENCHMARKS OFF CACHE BOOL "Firestore disabled") +add_external_subdirectory(snappy) +firebase_ios_add_alias(Snappy::Snappy snappy) + # LevelDB set(LEVELDB_BUILD_TESTS OFF CACHE BOOL "Firestore disabled") set(LEVELDB_BUILD_BENCHMARKS OFF CACHE BOOL "Firestore disabled") diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 2d68c73ad90..f47ac895455 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 06A3926F89C847846BE4D6BE /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; 06BCEB9C65DFAA142F3D3F0B /* view_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5466E7809AD2871FFDE6C76 /* view_testing.cc */; }; 072D805A94E767DE4D371881 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; }; + 077292C9797D97D3851F15CE /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; 0794FACCB1C0C4881A76C28D /* value_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 40F9D09063A07F710811A84F /* value_util_test.cc */; }; 079E63E270F3EFCA175D2705 /* cc_compilation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1B342370EAE3AA02393E33EB /* cc_compilation_test.cc */; }; 07A64E6C4EB700E3AF3FD496 /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; @@ -735,6 +736,7 @@ 7B8D7BAC1A075DB773230505 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; 7BCC5973C4F4FCC272150E31 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; 7BCF050BA04537B0E7D44730 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; + 7C1DC1B44729381126D083AE /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; 7C5E017689012489AAB7718D /* CodableGeoPointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */; }; 7C7BA1DB0B66EB899A928283 /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 7D25D41B013BB70ADE526055 /* target_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 526D755F65AC676234F57125 /* target_test.cc */; }; @@ -762,6 +764,7 @@ 81AF02881A8D23D02FC202F6 /* bundle_loader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A853C81A6A5A51C9D0389EDA /* bundle_loader_test.cc */; }; 81B23D2D4E061074958AF12F /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; 81D1B1D2B66BD8310AC5707F /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; + 82228CD6CE4A7A9254F8E82D /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; 822E5D5EC4955393DF26BC5C /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; 82E3634FCF4A882948B81839 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 8342277EB0553492B6668877 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; @@ -851,6 +854,7 @@ 97729B53698C0E52EB165003 /* field_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E8551D6C6FB0B1BACE9E5BAD /* field_filter_test.cc */; }; 9774A6C2AA02A12D80B34C3C /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; 9783FAEA4CF758E8C4C2D76E /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; + 978D9EFDC56CC2E1FA468712 /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; 9860F493EBF43AF5AC0A88BD /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8FA60B08D59FEA0D6751E87F /* empty_credentials_provider_test.cc */; }; 98708140787A9465D883EEC9 /* leveldb_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */; }; 98FE82875A899A40A98AAC22 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; @@ -1066,6 +1070,7 @@ C4055D868A38221B332CD03D /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; C426C6E424FB2199F5C2C5BC /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D821C2DDC800EFB9CC /* document.pb.cc */; }; C43A555928CB0441096F82D2 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; + C4548D8C790387C8E64F0FC4 /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; C482E724F4B10968417C3F78 /* Pods_Firestore_FuzzTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */; }; C4C7A8D11DC394EF81B7B1FA /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; C524026444E83EEBC1773650 /* objc_type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* objc_type_traits_apple_test.mm */; }; @@ -1225,6 +1230,7 @@ EA38690795FBAA182A9AA63E /* FIRDatabaseTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */; }; EA46611779C3EEF12822508C /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; EAA1962BFBA0EBFBA53B343F /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; + EAC0914B6DCC53008483AEE3 /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; EADD28A7859FBB9BE4D913B0 /* memory_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1CA9800A53669EFBFFB824E3 /* memory_remote_document_cache_test.cc */; }; EB04FE18E5794FEC187A09E3 /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; EB2137E6FBB0DDE2DF80E3D0 /* target_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 526D755F65AC676234F57125 /* target_test.cc */; }; @@ -1701,6 +1707,7 @@ D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = leveldb_snappy_test.cc; sourceTree = ""; }; DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; DAFF0CF721E64AC30062958F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DAFF0CF821E64AC30062958F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -2074,6 +2081,7 @@ 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */, 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */, 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */, + D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */, E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */, 88CF09277CFA45EE1273E3BA /* leveldb_transaction_test.cc */, 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */, @@ -3612,6 +3620,7 @@ 1D7919CD2A05C15803F5FE05 /* leveldb_mutation_queue_test.cc in Sources */, 23EFC681986488B033C2B318 /* leveldb_opener_test.cc in Sources */, F10A3E4E164A5458DFF7EDE6 /* leveldb_remote_document_cache_test.cc in Sources */, + 7C1DC1B44729381126D083AE /* leveldb_snappy_test.cc in Sources */, 7D40C8EB7755138F85920637 /* leveldb_target_cache_test.cc in Sources */, B46E778F9E40864B5D2B2F1C /* leveldb_transaction_test.cc in Sources */, 66FAB8EAC012A3822BD4D0C9 /* leveldb_util_test.cc in Sources */, @@ -3811,6 +3820,7 @@ 1145D70555D8CDC75183A88C /* leveldb_mutation_queue_test.cc in Sources */, 1DCA68BB2EF7A9144B35411F /* leveldb_opener_test.cc in Sources */, CD1E2F356FC71D7E74FCD26C /* leveldb_remote_document_cache_test.cc in Sources */, + 077292C9797D97D3851F15CE /* leveldb_snappy_test.cc in Sources */, 06485D6DA8F64757D72636E1 /* leveldb_target_cache_test.cc in Sources */, EC62F9E29CE3598881908FB8 /* leveldb_transaction_test.cc in Sources */, 7A3BE0ED54933C234FDE23D1 /* leveldb_util_test.cc in Sources */, @@ -4024,6 +4034,7 @@ FE701C2D739A5371BCBD62B9 /* leveldb_mutation_queue_test.cc in Sources */, 98FE82875A899A40A98AAC22 /* leveldb_opener_test.cc in Sources */, 79D86DD18BB54D2D69DC457F /* leveldb_remote_document_cache_test.cc in Sources */, + 82228CD6CE4A7A9254F8E82D /* leveldb_snappy_test.cc in Sources */, 6C388B2D0967088758FF2425 /* leveldb_target_cache_test.cc in Sources */, D4572060A0FD4D448470D329 /* leveldb_transaction_test.cc in Sources */, 3ABF84FC618016CA6E1D3C03 /* leveldb_util_test.cc in Sources */, @@ -4237,6 +4248,7 @@ A478FDD7C3F48FBFDDA7D8F5 /* leveldb_mutation_queue_test.cc in Sources */, A06FBB7367CDD496887B86F8 /* leveldb_opener_test.cc in Sources */, A27096F764227BC73526FED3 /* leveldb_remote_document_cache_test.cc in Sources */, + EAC0914B6DCC53008483AEE3 /* leveldb_snappy_test.cc in Sources */, D04CBBEDB8DC16D8C201AC49 /* leveldb_target_cache_test.cc in Sources */, 29243A4BBB2E2B1530A62C59 /* leveldb_transaction_test.cc in Sources */, 08FA4102AD14452E9587A1F2 /* leveldb_util_test.cc in Sources */, @@ -4446,6 +4458,7 @@ 98708140787A9465D883EEC9 /* leveldb_mutation_queue_test.cc in Sources */, 8342277EB0553492B6668877 /* leveldb_opener_test.cc in Sources */, 8077722A6BB175D3108CDC55 /* leveldb_remote_document_cache_test.cc in Sources */, + C4548D8C790387C8E64F0FC4 /* leveldb_snappy_test.cc in Sources */, 284A5280F868B2B4B5A1C848 /* leveldb_target_cache_test.cc in Sources */, 35DB74DFB2F174865BCCC264 /* leveldb_transaction_test.cc in Sources */, BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */, @@ -4678,6 +4691,7 @@ 4FAD8823DC37B9CA24379E85 /* leveldb_mutation_queue_test.cc in Sources */, 4562CDD90F5FF0491F07C5DA /* leveldb_opener_test.cc in Sources */, EE6DBFB0874A50578CE97A7F /* leveldb_remote_document_cache_test.cc in Sources */, + 978D9EFDC56CC2E1FA468712 /* leveldb_snappy_test.cc in Sources */, 6380CACCF96A9B26900983DC /* leveldb_target_cache_test.cc in Sources */, DDD219222EEE13E3F9F2C703 /* leveldb_transaction_test.cc in Sources */, BC549E3F3F119D80741D8612 /* leveldb_util_test.cc in Sources */, diff --git a/Firestore/core/test/unit/local/leveldb_snappy_test.cc b/Firestore/core/test/unit/local/leveldb_snappy_test.cc new file mode 100644 index 00000000000..0dc907b568c --- /dev/null +++ b/Firestore/core/test/unit/local/leveldb_snappy_test.cc @@ -0,0 +1,295 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "Firestore/core/src/local/leveldb_util.h" +#include "Firestore/core/src/util/filesystem.h" +#include "Firestore/core/src/util/path.h" + +#include "gtest/gtest.h" +#include "leveldb/db.h" + +namespace { + +using ::firebase::firestore::local::ConvertStatus; +using ::firebase::firestore::util::Filesystem; +using ::firebase::firestore::util::Path; + +// Creates a LevelDb database that uses Snappy compression for at least one of +// its blocks. Attempting to iterate over this database using a LevelDb library +// that does not have Snappy compression compiled in will return a failed status +// with reason "corruption". +Path CreateLevelDbDatabaseThatUsesSnappyCompression(); + +// Creates and opens a LevelDb database that contains at least one block that +// is compressed with Snappy compression, then iterates over it, invoking the +// given callback with the status at each point in the iteration. Once the +// callback is invoked with a `status` where `status.ok()` is not true, then +// iteration will stop and the callback will not be invoked again. +void IterateOverLevelDbDatabaseThatUsesSnappyCompression( + std::function); + +#if FIREBASE_TESTS_BUILT_BY_CMAKE + +// Ensure that LevelDb is compiled with Snappy compression support. +// See https://github.com/firebase/firebase-ios-sdk/pull/9596 for details. +TEST(LevelDbSnappy, LevelDbSupportsSnappy) { + IterateOverLevelDbDatabaseThatUsesSnappyCompression( + [](const leveldb::Status& status) { + ASSERT_TRUE(status.ok()) << ConvertStatus(status); + }); +} + +#else // FIREBASE_TESTS_BUILT_BY_CMAKE + +// Ensure that LevelDb is NOT compiled with Snappy compression support. +TEST(LevelDbSnappy, LevelDbDoesNotSupportSnappy) { + bool got_failed_status = false; + IterateOverLevelDbDatabaseThatUsesSnappyCompression( + [&](const leveldb::Status& status) { + if (!status.ok()) { + got_failed_status = true; + ASSERT_TRUE(status.IsCorruption()) << ConvertStatus(status); + } + }); + + if (!HasFailure()) { + ASSERT_TRUE(got_failed_status) + << "Reading a Snappy-compressed LevelDb database was successful; " + "however, it should NOT have been successful " + "since Snappy support is expected to NOT be available."; + } +} + +#endif // FIREBASE_TESTS_BUILT_BY_CMAKE + +void IterateOverLevelDbDatabaseThatUsesSnappyCompression( + std::function callback) { + std::unique_ptr db; + { + Path leveldb_path = CreateLevelDbDatabaseThatUsesSnappyCompression(); + if (leveldb_path.empty()) { + return; + } + + leveldb::Options options; + options.create_if_missing = false; + + leveldb::DB* db_ptr; + leveldb::Status status = + leveldb::DB::Open(options, leveldb_path.ToUtf8String(), &db_ptr); + + ASSERT_TRUE(status.ok()) + << "Opening LevelDb database " << leveldb_path.ToUtf8String() + << " failed: " << ConvertStatus(status); + + db.reset(db_ptr); + } + + std::unique_ptr it( + db->NewIterator(leveldb::ReadOptions())); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + callback(it->status()); + if (!it->status().ok()) { + return; + } + } + + // Invoke the callback on the final status. + callback(it->status()); +} + +template +void WriteFile(const Path& dir, + const std::string& file_name, + const T& data_array) { + Filesystem* fs = Filesystem::Default(); + { + auto status = fs->RecursivelyCreateDir(dir); + if (!status.ok()) { + FAIL() << "Creating directory failed: " << dir.ToUtf8String() << " (" + << status.error_message() << ")"; + } + } + + Path file = dir.AppendUtf8(file_name); + std::ofstream out_file(file.native_value(), std::ios::binary); + if (!out_file) { + FAIL() << "Unable to open file for writing: " << file.ToUtf8String(); + } + + out_file.write(reinterpret_cast(data_array.data()), + data_array.size()); + out_file.close(); + if (!out_file) { + FAIL() << "Writing to file failed: " << file.ToUtf8String(); + } +} + +const std::array LevelDbSnappyFile_000005_ldb{ + 0x84, 0x03, 0x80, 0x00, 0x42, 0x00, 0x85, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x5F, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8B, 0x43, 0x6F, + 0x6C, 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, 0x6C, 0x42, + 0x01, 0x0A, 0x68, 0x42, 0x7C, 0x66, 0x3A, 0x7C, 0x6F, 0x62, 0x3A, 0x5F, + 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x5F, 0x5F, 0x61, 0x73, 0x63, 0x00, 0x01, + 0x8C, 0x82, 0x80, 0x01, 0x07, 0x00, 0x05, 0x01, 0x08, 0x01, 0x13, 0x50, + 0x11, 0x3E, 0x01, 0x16, 0x00, 0x0A, 0x05, 0x15, 0xF0, 0x3C, 0x00, 0x08, + 0x02, 0x20, 0x05, 0x32, 0x4A, 0x12, 0x48, 0x70, 0x72, 0x6F, 0x6A, 0x65, + 0x63, 0x74, 0x73, 0x2F, 0x54, 0x65, 0x73, 0x74, 0x54, 0x65, 0x72, 0x6D, + 0x69, 0x6E, 0x61, 0x74, 0x65, 0x2F, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x73, 0x2F, 0x28, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, + 0x29, 0x2F, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x01, + 0x7B, 0x3E, 0x85, 0x00, 0x0C, 0x0D, 0x07, 0x50, 0x08, 0x15, 0x5A, 0x00, + 0x03, 0xFE, 0x5A, 0x00, 0x2E, 0x5A, 0x00, 0x38, 0x07, 0x12, 0x06, 0x5F, + 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x00, 0x01, 0x80, 0x01, 0x0B, 0x11, + 0x65, 0x1C, 0x10, 0x05, 0x20, 0x01, 0x12, 0x07, 0x06, 0x09, 0x15, 0x10, + 0x00, 0x03, 0x01, 0x10, 0x04, 0x00, 0x01, 0x09, 0x10, 0x24, 0x01, 0x12, + 0x01, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x01, 0x35, 0x00, 0x06, + 0x09, 0x15, 0x10, 0x37, 0x0C, 0x07, 0x01, 0x05, 0x09, 0x0B, 0x10, 0x36, + 0x0C, 0x07, 0x01, 0x04, 0x09, 0x0B, 0x10, 0x35, 0x0C, 0x07, 0x01, 0x03, + 0x09, 0x0B, 0x4C, 0x34, 0x0C, 0x07, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x2C, 0x6E, 0xE0, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0xF2, 0xA1, 0xB0, 0x00, 0x09, 0x03, 0x86, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC2, 0x94, 0x06, 0x8C, 0x02, 0x08, + 0x99, 0x02, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x57, 0xFB, 0x80, 0x8B, 0x24, 0x75, 0x47, 0xDB, +}; +const std::array LevelDbSnappyFile_000017_ldb{ + 0x00, 0x14, 0x50, 0x85, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, + 0x8C, 0x82, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x02, 0x20, 0x0A, 0x32, 0x4A, 0x12, 0x48, 0x70, 0x72, 0x6F, 0x6A, 0x65, + 0x63, 0x74, 0x73, 0x2F, 0x54, 0x65, 0x73, 0x74, 0x54, 0x65, 0x72, 0x6D, + 0x69, 0x6E, 0x61, 0x74, 0x65, 0x2F, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x73, 0x2F, 0x28, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, + 0x29, 0x2F, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2F, + 0x43, 0x6F, 0x6C, 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, + 0x6C, 0x42, 0x2F, 0x44, 0x6F, 0x63, 0x42, 0x07, 0x12, 0x06, 0x5F, 0x67, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x00, 0x01, 0x80, 0x01, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x10, 0x0A, 0x20, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCD, 0xE0, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF2, 0xA1, 0xB0, + 0x00, 0x09, 0x03, 0x86, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x8A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xE4, 0xA7, 0x7E, 0x74, 0x8F, 0x01, 0x08, 0x9C, 0x01, 0x17, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0xFB, 0x80, 0x8B, + 0x24, 0x75, 0x47, 0xDB, +}; +const std::array LevelDbSnappyFile_000085_ldb{}; +const std::array LevelDbSnappyFile_CURRENT{ + 0x4D, 0x41, 0x4E, 0x49, 0x46, 0x45, 0x53, 0x54, + 0x2D, 0x30, 0x30, 0x30, 0x30, 0x38, 0x34, 0x0A, +}; +const std::array LevelDbSnappyFile_LOG_old{ + 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, 0x31, + 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x32, 0x35, 0x37, 0x32, + 0x35, 0x31, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, 0x33, + 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x52, 0x65, 0x63, 0x6F, 0x76, 0x65, + 0x72, 0x69, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x67, 0x20, 0x23, 0x38, 0x31, + 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, + 0x31, 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x33, 0x30, 0x34, + 0x35, 0x35, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, + 0x33, 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x33, 0x20, 0x23, 0x38, 0x30, + 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, + 0x31, 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x33, 0x30, 0x35, + 0x30, 0x36, 0x34, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, + 0x33, 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x30, 0x20, 0x23, 0x38, 0x31, + 0x0A, +}; +const std::array LevelDbSnappyFile_LOG{ + 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, 0x31, + 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x34, 0x39, 0x33, 0x31, + 0x34, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, 0x32, + 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x52, 0x65, 0x63, 0x6F, 0x76, 0x65, + 0x72, 0x69, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x67, 0x20, 0x23, 0x38, 0x33, + 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, + 0x31, 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x35, 0x33, 0x34, + 0x37, 0x34, 0x35, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, + 0x32, 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x33, 0x20, 0x23, 0x38, 0x32, + 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, + 0x31, 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x35, 0x33, 0x35, + 0x32, 0x34, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, + 0x32, 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x30, 0x20, 0x23, 0x38, 0x33, + 0x0A, +}; +const std::array LevelDbSnappyFile_MANIFEST_000084{ + 0x45, 0x63, 0x9F, 0xDD, 0xAC, 0x00, 0x01, 0x01, 0x1A, 0x6C, 0x65, 0x76, + 0x65, 0x6C, 0x64, 0x62, 0x2E, 0x42, 0x79, 0x74, 0x65, 0x77, 0x69, 0x73, + 0x65, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6F, 0x72, 0x07, + 0x00, 0x05, 0xE5, 0x02, 0x42, 0x85, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5F, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8B, 0x43, 0x6F, 0x6C, + 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, 0x6C, 0x42, 0x2F, + 0x44, 0x6F, 0x63, 0x42, 0x7C, 0x66, 0x3A, 0x7C, 0x6F, 0x62, 0x3A, 0x5F, + 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x5F, 0x5F, 0x61, 0x73, 0x63, 0x00, 0x01, + 0x8C, 0x82, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x85, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x00, 0x01, 0x80, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x11, 0xE8, 0x01, + 0x14, 0x85, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8C, 0x82, + 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x85, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x5F, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, + 0x00, 0x01, 0x80, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, + 0x03, 0xAC, 0xBA, 0x08, 0x00, 0x01, 0x02, 0x55, 0x09, 0x00, 0x03, 0x56, + 0x04, 0x0D, +}; + +Path LevelDbDir() { + Filesystem* fs = Filesystem::Default(); + Path dir = fs->TempDir().AppendUtf8("LevelDbSnappyTest"); + + // Delete the directory first to ensure isolation between runs. + auto status = fs->RecursivelyRemove(dir); + EXPECT_TRUE(status.ok()) << "Failed to clean up leveldb in directory " + << dir.ToUtf8String() << ": " << status.ToString(); + if (!status.ok()) { + return {}; + } + + return dir; +} + +Path CreateLevelDbDatabaseThatUsesSnappyCompression() { + Path leveldb_dir = LevelDbDir(); + if (leveldb_dir.empty()) { + return {}; + } + + WriteFile(leveldb_dir, "000005.ldb", LevelDbSnappyFile_000005_ldb); + WriteFile(leveldb_dir, "000017.ldb", LevelDbSnappyFile_000017_ldb); + WriteFile(leveldb_dir, "000085.ldb", LevelDbSnappyFile_000085_ldb); + WriteFile(leveldb_dir, "CURRENT", LevelDbSnappyFile_CURRENT); + WriteFile(leveldb_dir, "LOG.old", LevelDbSnappyFile_LOG_old); + WriteFile(leveldb_dir, "LOG", LevelDbSnappyFile_LOG); + WriteFile(leveldb_dir, "MANIFEST-000084", LevelDbSnappyFile_MANIFEST_000084); + + return leveldb_dir; +} + +} // namespace diff --git a/cmake/cc_rules.cmake b/cmake/cc_rules.cmake index 05239e326e5..89ba3518fa6 100644 --- a/cmake/cc_rules.cmake +++ b/cmake/cc_rules.cmake @@ -116,6 +116,14 @@ function(firebase_ios_add_test target) firebase_ios_set_common_target_options(${target} ${flag_OPTIONS}) + # Set a preprocessor define so that tests can distinguish between having been + # built by Xcode vs. cmake. + target_compile_definitions( + ${target} + PRIVATE + FIREBASE_TESTS_BUILT_BY_CMAKE + ) + target_link_libraries(${target} PRIVATE GTest::GTest GTest::Main) endfunction() diff --git a/cmake/external/CMakeLists.txt b/cmake/external/CMakeLists.txt index 33315a72beb..2deb88c32e3 100644 --- a/cmake/external/CMakeLists.txt +++ b/cmake/external/CMakeLists.txt @@ -35,6 +35,7 @@ include(c-ares) include(googletest) include(GoogleUtilities) include(grpc) +include(snappy) include(leveldb) include(libfuzzer) include(nanopb) diff --git a/cmake/external/leveldb.cmake b/cmake/external/leveldb.cmake index b71a775353a..920bf2928a3 100644 --- a/cmake/external/leveldb.cmake +++ b/cmake/external/leveldb.cmake @@ -14,15 +14,28 @@ include(ExternalProject) +include(python_setup) +FirebaseSetupPythonInterpreter( + OUTVAR MY_PYTHON_EXECUTABLE + KEY LevelDbPatch +) + if(TARGET leveldb) return() endif() set(version 1.22) +ExternalProject_Get_property(snappy SOURCE_DIR) +set(snappy_source_dir "${SOURCE_DIR}") +ExternalProject_Get_property(snappy BINARY_DIR) +set(snappy_binary_dir "${BINARY_DIR}") + ExternalProject_Add( leveldb + DEPENDS snappy + DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} DOWNLOAD_NAME leveldb-${version}.tar.gz URL https://github.com/google/leveldb/archive/${version}.tar.gz @@ -34,6 +47,7 @@ ExternalProject_Add( BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" + PATCH_COMMAND "${MY_PYTHON_EXECUTABLE}" ${CMAKE_CURRENT_LIST_DIR}/leveldb_patch.py --snappy-source-dir ${snappy_source_dir} --snappy-binary-dir ${snappy_binary_dir} HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" ) diff --git a/cmake/external/leveldb_patch.py b/cmake/external/leveldb_patch.py new file mode 100644 index 00000000000..51a62d54ad9 --- /dev/null +++ b/cmake/external/leveldb_patch.py @@ -0,0 +1,144 @@ +# 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. + +""" +Modify the CMakeLists.txt from LevelDb to staticly link Snappy compression +support. +""" + +import argparse +import dataclasses +import os +import pathlib +from typing import Iterable, Sequence + + +def main() -> None: + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("--snappy-source-dir", required=True) + arg_parser.add_argument("--snappy-binary-dir", required=True) + parsed_args = arg_parser.parse_args() + del arg_parser + snappy_source_dir = pathlib.Path(parsed_args.snappy_source_dir) + snappy_binary_dir = pathlib.Path(parsed_args.snappy_binary_dir) + del parsed_args + + cmakelists_txt_file = pathlib.Path("CMakeLists.txt") + with cmakelists_txt_file.open("rt", encoding="utf8") as f: + lines = tuple(f) + + patcher = CMakeListsPatcher( + lines, + os.path.abspath(__file__), + snappy_source_dir, + snappy_binary_dir, + ) + + patched_lines = tuple(patcher.patch()) + + with cmakelists_txt_file.open("wt", encoding="utf8") as f: + f.writelines(patched_lines) + + +@dataclasses.dataclass(frozen=True) +class LineComponents: + full: str + indent: str + line: str + eol: str + + +class CMakeListsPatcher: + + SNAPPY_DETECT_LINE = \ + """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""" + SNAPPY_INCLUDE_LINE = \ + "target_include_directories(leveldb" + SNAPPY_LINK_LINE = \ + "target_link_libraries(leveldb snappy)" + + def __init__( + self, + lines: Sequence[str], + script_name: str, + snappy_source_dir: pathlib.Path, + snappy_binary_dir: pathlib.Path) -> None: + self.i = 0 + self.lines = lines + self.script_name = script_name + self.snappy_source_dir_str = snappy_source_dir.as_posix() + self.snappy_binary_dir_str = snappy_binary_dir.as_posix() + + def patch(self) -> Iterable[str]: + while self.i < len(self.lines): + full_line = self.lines[self.i] + line = self._split_line(full_line) + self.i += 1 + + if line.line == self.SNAPPY_DETECT_LINE: + yield from self._on_snappy_detect_line(line) + elif line.line == self.SNAPPY_INCLUDE_LINE: + yield full_line + yield from self._on_leveldb_include_start() + elif line.line == self.SNAPPY_LINK_LINE: + yield from self._on_leveldb_snappy_link_line(line) + else: + yield full_line + + def _begin_mod_line(self, mod_name: str) -> str: + return f"# BEGIN: {mod_name} modification by {self.script_name}" + + def _end_mod_line(self, mod_name: str) -> str: + return f"# END: {mod_name} modification by {self.script_name}" + + def _on_snappy_detect_line(self, line: LineComponents) -> Iterable[str]: + yield self._begin_mod_line("snappy_detect_line") + line.eol + yield line.indent + "# " + line.line + line.eol + yield line.indent + """set(HAVE_SNAPPY ON CACHE BOOL "")""" + line.eol + yield self._end_mod_line("snappy_detect_line") + line.eol + + def _on_leveldb_include_start(self) -> Iterable[str]: + line1 = self._split_line(self.lines[self.i]) + line2 = self._split_line(self.lines[self.i + 1]) + begin_mod_line = self._begin_mod_line("leveldb_include_start") + + if line1.line == begin_mod_line: + return + + yield begin_mod_line + line1.eol + yield line1.indent + "PRIVATE" + line1.eol + yield line2.indent + self.snappy_source_dir_str + line2.eol + yield line2.indent + self.snappy_binary_dir_str + line2.eol + yield self._end_mod_line("leveldb_include_start") + line1.eol + + def _on_leveldb_snappy_link_line(self, line: LineComponents) -> Iterable[str]: + yield self._begin_mod_line("leveldb_snappy_link_line") + line.eol + yield line.indent + "# " + line.line + line.eol + yield line.indent + f"target_link_libraries(leveldb Snappy::Snappy)" + line.eol + yield self._end_mod_line("leveldb_snappy_link_line") + line.eol + + def _split_line(self, line: str) -> LineComponents: + line_rstripped = line.rstrip() + eol = line[len(line_rstripped):] + line_stripped = line_rstripped.strip() + indent = line_rstripped[:len(line_rstripped) - len(line_stripped)] + return LineComponents(full=line, indent=indent, line=line_stripped, eol=eol) + + +class LeveDbPatchException(Exception): + pass + + +if __name__ == "__main__": + main() diff --git a/cmake/external/leveldb_patch_test.py b/cmake/external/leveldb_patch_test.py new file mode 100644 index 00000000000..b1d62526b92 --- /dev/null +++ b/cmake/external/leveldb_patch_test.py @@ -0,0 +1,328 @@ +# 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 leveldb_patch +import pathlib +import unittest + + +class CMakeListsPatcherTest(unittest.TestCase): + + def setUp(self): + super().setUp() + self.sample_snappy_source_dir = pathlib.Path("a/b/snappy_source_dir") + self.sample_snappy_binary_dir = pathlib.Path("a/b/snappy_binary_dir") + + def test_snappy_detect_line_is_commented_and_replaced(self): + lines = ( + """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + "MyCoolScript", + self.sample_snappy_source_dir, + self.sample_snappy_binary_dir, + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "# BEGIN: snappy_detect_line modification by MyCoolScript", + """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + """set(HAVE_SNAPPY ON CACHE BOOL "")""", + "# END: snappy_detect_line modification by MyCoolScript", + ]) + + def test_snappy_detect_line_has_indent_and_eol_preserved(self): + lines = ( + """ check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) \n""", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + "MyCoolScript", + self.sample_snappy_source_dir, + self.sample_snappy_binary_dir, + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "# BEGIN: snappy_detect_line modification by MyCoolScript \n", + """ # check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) \n""", + """ set(HAVE_SNAPPY ON CACHE BOOL "") \n""", + "# END: snappy_detect_line modification by MyCoolScript \n", + ]) + + def test_snappy_detect_line_does_not_affect_surrounding_lines(self): + lines = ( + "aaa", + "bbb", + """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + "ccc", + "ddd", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + "MyCoolScript", + self.sample_snappy_source_dir, + self.sample_snappy_binary_dir, + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "aaa", + "bbb", + "# BEGIN: snappy_detect_line modification by MyCoolScript", + """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + """set(HAVE_SNAPPY ON CACHE BOOL "")""", + "# END: snappy_detect_line modification by MyCoolScript", + "ccc", + "ddd", + ]) + + def test_snappy_include_is_amended(self): + lines = ( + "target_include_directories(leveldb", + "PUBLIC", + "path1", + "path2", + ")", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "target_include_directories(leveldb", + "# BEGIN: leveldb_include_start modification by MyCoolSript", + "PRIVATE", + "a/b", + "c/d", + "# END: leveldb_include_start modification by MyCoolSript", + "PUBLIC", + "path1", + "path2", + ")", + ]) + + def test_snappy_include_lines_adopt_indenting_and_eol_convention(self): + lines = ( + "target_include_directories(leveldb\n", + " PUBLIC \n", + " path1 \n", + " path2 \n", + ")\n", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "target_include_directories(leveldb\n", + "# BEGIN: leveldb_include_start modification by MyCoolSript \n", + " PRIVATE \n", + " a/b \n", + " c/d \n", + "# END: leveldb_include_start modification by MyCoolSript \n", + " PUBLIC \n", + " path1 \n", + " path2 \n", + ")\n", + ]) + + def test_snappy_include_line_does_not_affect_surrounding_lines(self): + lines = ( + "aaa", + "bbb", + "target_include_directories(leveldb", + "PUBLIC", + "path1", + "path2", + ")", + "ccc", + "ddd", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "aaa", + "bbb", + "target_include_directories(leveldb", + "# BEGIN: leveldb_include_start modification by MyCoolSript", + "PRIVATE", + "a/b", + "c/d", + "# END: leveldb_include_start modification by MyCoolSript", + "PUBLIC", + "path1", + "path2", + ")", + "ccc", + "ddd", + ]) + + def test_leveldb_snappy_link_line_is_commented_and_replaced(self): + lines = ( + "target_link_libraries(leveldb snappy)", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", + "# target_link_libraries(leveldb snappy)", + "target_link_libraries(leveldb Snappy::Snappy)", + "# END: leveldb_snappy_link_line modification by MyCoolSript", + ]) + + def test_leveldb_snappy_link_line_has_indent_and_eol_preserved(self): + lines = ( + " target_link_libraries(leveldb snappy) \n", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript \n", + " # target_link_libraries(leveldb snappy) \n", + " target_link_libraries(leveldb Snappy::Snappy) \n", + "# END: leveldb_snappy_link_line modification by MyCoolSript \n", + ]) + + def test_leveldb_snappy_link_line_does_not_affect_surrounding_lines(self): + lines = ( + "aaa", + "bbb", + "target_link_libraries(leveldb snappy)", + "ccc", + "ddd", + ) + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "aaa", + "bbb", + "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", + "# target_link_libraries(leveldb snappy)", + "target_link_libraries(leveldb Snappy::Snappy)", + "# END: leveldb_snappy_link_line modification by MyCoolSript", + "ccc", + "ddd", + ]) + + def test_all_patches_combined(self): + lines = ( + """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + "target_include_directories(leveldb", + "PUBLIC", + "path1", + ")", + "target_link_libraries(leveldb snappy)", + ) + + patcher = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + patched_lines = tuple(patcher.patch()) + + self.assertSequenceEqual(patched_lines, [ + "# BEGIN: snappy_detect_line modification by MyCoolSript", + """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", + """set(HAVE_SNAPPY ON CACHE BOOL "")""", + "# END: snappy_detect_line modification by MyCoolSript", + "target_include_directories(leveldb", + "# BEGIN: leveldb_include_start modification by MyCoolSript", + "PRIVATE", + "a/b", + "c/d", + "# END: leveldb_include_start modification by MyCoolSript", + "PUBLIC", + "path1", + ")", + "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", + "# target_link_libraries(leveldb snappy)", + "target_link_libraries(leveldb Snappy::Snappy)", + "# END: leveldb_snappy_link_line modification by MyCoolSript", + ]) + + def test_idempotence(self): + lines = ( + """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)\n""", + "target_include_directories(leveldb", + "PUBLIC", + "path1", + ")", + "target_link_libraries(leveldb snappy)", + ) + + patcher1 = leveldb_patch.CMakeListsPatcher( + lines, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + patched_lines1 = tuple(patcher1.patch()) + patcher2 = leveldb_patch.CMakeListsPatcher( + patched_lines1, + script_name="MyCoolSript", + snappy_source_dir=pathlib.Path("a/b"), + snappy_binary_dir=pathlib.Path("c/d"), + ) + patched_lines2 = tuple(patcher2.patch()) + + self.assertSequenceEqual(patched_lines1, patched_lines2) + + +if __name__ == "__main__": + unittest.main() diff --git a/cmake/external/snappy.cmake b/cmake/external/snappy.cmake new file mode 100644 index 00000000000..7da24f127e4 --- /dev/null +++ b/cmake/external/snappy.cmake @@ -0,0 +1,40 @@ +# 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. + +include(ExternalProject) + +if(TARGET snappy) + return() +endif() + +set(version 1.1.9) + +ExternalProject_Add( + snappy + + DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} + DOWNLOAD_NAME snappy-${version}.tar.gz + URL https://github.com/google/snappy/archive/refs/tags/${version}.tar.gz + URL_HASH SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7 + + PREFIX ${PROJECT_BINARY_DIR} + + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + PATCH_COMMAND patch -Np1 -i ${CMAKE_CURRENT_LIST_DIR}/snappy.patch + + HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" +) diff --git a/cmake/external/snappy.patch b/cmake/external/snappy.patch new file mode 100644 index 00000000000..28bfb0837ad --- /dev/null +++ b/cmake/external/snappy.patch @@ -0,0 +1,12 @@ +diff -Naur snappy/snappy.cc snappy_patched/snappy.cc +--- snappy/snappy.cc 2022-04-12 20:44:55.000000000 -0400 ++++ snappy_patched/snappy.cc 2022-04-12 20:47:05.000000000 -0400 +@@ -1014,7 +1014,7 @@ + } + + SNAPPY_ATTRIBUTE_ALWAYS_INLINE +-size_t AdvanceToNextTag(const uint8_t** ip_p, size_t* tag) { ++inline size_t AdvanceToNextTag(const uint8_t** ip_p, size_t* tag) { + const uint8_t*& ip = *ip_p; + // This section is crucial for the throughput of the decompression loop. + // The latency of an iteration is fundamentally constrained by the diff --git a/scripts/check_whitespace.sh b/scripts/check_whitespace.sh index 4fbad5e6e9b..514e7113185 100755 --- a/scripts/check_whitespace.sh +++ b/scripts/check_whitespace.sh @@ -26,6 +26,7 @@ options=( ) git grep "${options[@]}" -- \ + ':(exclude)cmake/external/snappy.patch' \ ':(exclude)Crashlytics/ProtoSupport' \ ':(exclude)Crashlytics/UnitTests/Data' \ ':(exclude)Firebase/CoreDiagnostics/ProtoSupport' \