Skip to content

Unrevert "Revert "gRPC: replace Objective-C implementation with the new C++ implementation (#1968)" #2065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ set(
CACHE PATH "Where to store downloaded files"
)

set(
FIREBASE_EXTERNAL_SOURCE_DIR
${FIREBASE_BINARY_DIR}/external/src
CACHE PATH "Root directory of source code of the external dependencies"
)

download_external_sources()


Expand Down Expand Up @@ -103,8 +109,6 @@ set(gRPC_BUILD_TESTS OFF CACHE BOOL "Disable gRPC tests")
add_external_subdirectory(grpc)

# Fix up targets included by gRPC's build
set(external_src_dir ${FIREBASE_BINARY_DIR}/external/src)

if(OPENSSL_FOUND)
# gRPC's CMakeLists.txt does not account for finding OpenSSL in a directory
# that's not in the default search path.
Expand All @@ -127,14 +131,14 @@ else()
target_include_directories(
crypto
INTERFACE
$<BUILD_INTERFACE:${external_src_dir}/grpc/third_party/boringssl/include>
$<BUILD_INTERFACE:${FIREBASE_EXTERNAL_SOURCE_DIR}/grpc/third_party/boringssl/include>
)

add_alias(OpenSSL::SSL ssl)
target_include_directories(
ssl
INTERFACE
$<BUILD_INTERFACE:${external_src_dir}/grpc/third_party/boringssl/include>
$<BUILD_INTERFACE:${FIREBASE_EXTERNAL_SOURCE_DIR}/grpc/third_party/boringssl/include>
)
endif()

Expand All @@ -156,7 +160,7 @@ endif()
if(NOT ZLIB_FOUND)
target_include_directories(
zlibstatic
INTERFACE $<BUILD_INTERFACE:${external_src_dir}/grpc/third_party/zlib>
INTERFACE $<BUILD_INTERFACE:${FIREBASE_EXTERNAL_SOURCE_DIR}/grpc/third_party/zlib>
)
endif()

Expand Down Expand Up @@ -186,7 +190,7 @@ target_compile_definitions(

target_include_directories(
protobuf-nanopb
INTERFACE $<BUILD_INTERFACE:${external_src_dir}/nanopb>
INTERFACE $<BUILD_INTERFACE:${FIREBASE_EXTERNAL_SOURCE_DIR}/nanopb>
)


Expand Down
6 changes: 5 additions & 1 deletion FirebaseFirestore.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,20 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling,

# Exclude alternate implementations for other platforms
'Firestore/core/src/firebase/firestore/remote/connectivity_monitor_noop.cc',
'Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_generated.cc',
'Firestore/core/src/firebase/firestore/util/filesystem_win.cc',
'Firestore/core/src/firebase/firestore/util/hard_assert_stdio.cc',
'Firestore/core/src/firebase/firestore/util/log_stdio.cc',
'Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc'
]
s.public_header_files = 'Firestore/Source/Public/*.h'

# TODO(varconst): remove once https://github.com/grpc/grpc/pull/16962 makes it
# into a release.
s.resource_bundles = { 'gRPCCertificates' => ['Firestore/etc/roots.pem'] }

s.dependency 'FirebaseAuthInterop', '~> 1.0'
s.dependency 'FirebaseCore', '~> 5.1'
s.dependency 'gRPC-ProtoRPC', '~> 1.0'
s.dependency 'gRPC-C++', '~> 0.0.3'
s.dependency 'leveldb-library', '~> 1.20'
s.dependency 'Protobuf', '~> 3.1'
Expand Down
86 changes: 38 additions & 48 deletions Firestore/Example/Firestore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
5492E07A202154D600B64F25 /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; };
5492E07F202154EC00B64F25 /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; };
5492E080202154EC00B64F25 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; };
5492E081202154EC00B64F25 /* FSTStreamTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07D202154EB00B64F25 /* FSTStreamTests.mm */; };
5492E082202154EC00B64F25 /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; };
5492E09D2021552D00B64F25 /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; };
5492E09F2021552D00B64F25 /* FSTLevelDBMigrationsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */; };
Expand Down Expand Up @@ -361,7 +360,6 @@
5492E071202154D600B64F25 /* FIRTypeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRTypeTests.mm; sourceTree = "<group>"; };
5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTTransactionTests.mm; sourceTree = "<group>"; };
5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSmokeTests.mm; sourceTree = "<group>"; };
5492E07D202154EB00B64F25 /* FSTStreamTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTStreamTests.mm; sourceTree = "<group>"; };
5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatastoreTests.mm; sourceTree = "<group>"; };
5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLocalStoreTests.mm; sourceTree = "<group>"; };
5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTRemoteDocumentCacheTests.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1234,7 +1232,6 @@
DE03B3621F215E1600A30B9C /* CAcert.pem */,
5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */,
5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */,
5492E07D202154EB00B64F25 /* FSTStreamTests.mm */,
5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */,
);
path = Integration;
Expand Down Expand Up @@ -1270,6 +1267,7 @@
54C9EDEE2040E16300A969CD /* Frameworks */,
54C9EDEF2040E16300A969CD /* Resources */,
EA424838F4A5DD7B337F57AB /* [CP] Embed Pods Frameworks */,
DE5C36328822481F6EB6EF16 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -1310,6 +1308,7 @@
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
1EE692C7509A98D7EB03CA51 /* [CP] Embed Pods Frameworks */,
1E7DAED3207D01F1744EA227 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -1510,6 +1509,24 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
1E7DAED3207D01F1744EA227 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/gRPCCertificates.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-resources.sh\"\n";
showEnvVarsInLog = 0;
};
1EE692C7509A98D7EB03CA51 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -1521,11 +1538,8 @@
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/Protobuf-iOS8.0/Protobuf.framework",
"${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework",
"${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
);
Expand All @@ -1535,11 +1549,8 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
);
Expand Down Expand Up @@ -1757,6 +1768,24 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DE5C36328822481F6EB6EF16 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/gRPCCertificates.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-resources.sh\"\n";
showEnvVarsInLog = 0;
};
EA424838F4A5DD7B337F57AB /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -1768,11 +1797,8 @@
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/Protobuf-iOS8.0/Protobuf.framework",
"${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework",
"${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
);
Expand All @@ -1782,11 +1808,8 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
);
Expand Down Expand Up @@ -2006,7 +2029,6 @@
5492E0422021440500B64F25 /* FSTHelpers.mm in Sources */,
5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */,
5492E080202154EC00B64F25 /* FSTSmokeTests.mm in Sources */,
5492E081202154EC00B64F25 /* FSTStreamTests.mm in Sources */,
5492E07F202154EC00B64F25 /* FSTTransactionTests.mm in Sources */,
5492E0442021457E00B64F25 /* XCTestCase+Await.mm in Sources */,
B67BF44A216EB43000CA9097 /* create_noop_connectivity_monitor.cc in Sources */,
Expand Down Expand Up @@ -2219,14 +2241,6 @@
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"",
Expand Down Expand Up @@ -2303,14 +2317,6 @@
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"",
Expand Down Expand Up @@ -2512,14 +2518,6 @@
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"",
Expand Down Expand Up @@ -2597,14 +2595,6 @@
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"",
"-iquote",
"\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES"
enableThreadSanitizer = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES"
enableThreadSanitizer = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand Down
8 changes: 5 additions & 3 deletions Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#import <FirebaseFirestore/FirebaseFirestore.h>

#import <FirebaseFirestore/FIRTimestamp.h>
#import <GRPCClient/GRPCCall+ChannelCredentials.h>
#import <GRPCClient/GRPCCall+Tests.h>
#import <XCTest/XCTest.h>

#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
Expand Down Expand Up @@ -52,6 +50,7 @@
using firebase::firestore::model::DocumentKeySet;
using firebase::firestore::model::Precondition;
using firebase::firestore::model::TargetId;
using firebase::firestore::remote::GrpcConnection;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -162,7 +161,7 @@ - (void)setUp {
NSString *projectID = [FSTIntegrationTestCase projectID];
FIRFirestoreSettings *settings = [FSTIntegrationTestCase settings];
if (!settings.sslEnabled) {
[GRPCCall useInsecureConnectionsForHost:settings.host];
GrpcConnection::UseInsecureChannel(util::MakeString(settings.host));
}

DatabaseId database_id(util::MakeString(projectID), DatabaseId::kDefault);
Expand Down Expand Up @@ -222,6 +221,9 @@ - (void)testStreamingWrite {
mutations:@[ mutation ]];
[_testWorkerQueue dispatchAsync:^{
[_remoteStore addBatchToWritePipeline:batch];
// The added batch won't be written immediately because write stream wasn't yet open --
// trigger its opening.
[_remoteStore fillWritePipeline];
}];

[self awaitExpectations];
Expand Down
Loading