diff --git a/Firestore/README.md b/Firestore/README.md index 9c7d01a3531..13a79d68606 100644 --- a/Firestore/README.md +++ b/Firestore/README.md @@ -48,6 +48,9 @@ scripts/build.sh Firestore iOS spm This is rarely necessary for primary development and is done automatically by CI. +For a detailed explanation of the Firestore target hierarchy in the +`Package.swift` manifest, see [FirestoreSPM.md](../docs/FirestoreSPM.md). + ### Improving the debugger experience You can install a set of type formatters to improve the presentation of diff --git a/docs/FirestoreSPM.md b/docs/FirestoreSPM.md new file mode 100644 index 00000000000..e473a986983 --- /dev/null +++ b/docs/FirestoreSPM.md @@ -0,0 +1,196 @@ +# Firestore Swift Package Manager Target Hierarchy + +This document outlines the hierarchy of the Firestore-related targets in the +`Package.swift` manifest. The setup is designed to support three different +build options for Firestore: from a pre-compiled binary (the default), from +source (via the `FIREBASE_SOURCE_FIRESTORE` environment variable), or from a +local binary for CI purposes (via the `FIREBASECI_USE_LOCAL_FIRESTORE_ZIP` +environment variable). + +--- + +## 1. Binary-based build (Default) + +When the `FIREBASE_SOURCE_FIRESTORE` environment variable is **not** set, SPM +will use pre-compiled binaries for Firestore and its heavy dependencies. This +is the default and recommended approach for most users. + +### Dependency hierarchy + +The dependency tree for a binary-based build is as follows: + +``` +FirebaseFirestore (Library Product) +└── FirebaseFirestoreTarget (Wrapper Target) + └── FirebaseFirestore (Swift Target) + ├── FirebaseAppCheckInterop + ├── FirebaseCore + ├── FirebaseCoreExtension + ├── FirebaseSharedSwift + ├── leveldb + ├── nanopb + ├── abseil (binary) (from https://github.com/google/abseil-cpp-binary.git) + ├── gRPC-C++ (binary) (from https://github.com/google/grpc-binary.git, contains BoringSSL-GRPC target) + └── FirebaseFirestoreInternalWrapper (Wrapper Target) + └── FirebaseFirestoreInternal (Binary Target) +``` + +### Target breakdown + +* **`FirebaseFirestore`**: The Swift target containing the public API. In this + configuration, it depends on the binary versions of abseil and gRPC, as + well as the `FirebaseFirestoreInternalWrapper`. +* **`FirebaseFirestoreInternalWrapper`**: A thin wrapper target that exists to + expose the headers from the underlying binary target. +* **`FirebaseFirestoreInternal`**: This is a `binaryTarget` that downloads and + links the pre-compiled `FirebaseFirestoreInternal.xcframework`. This + framework contains the compiled C++ core of Firestore. + +--- + +## 2. Source-based build + +When the `FIREBASE_SOURCE_FIRESTORE` environment variable is set, Firestore and +its dependencies (like abseil and gRPC) are compiled from source. + +### How to build Firestore from source + +To build Firestore from source, set the `FIREBASE_SOURCE_FIRESTORE` environment +variable before building the project. + +#### Building with Xcode + +A direct method for building within Xcode is to pass the environment variable +when opening it from the command line. This approach scopes the variable to the +Xcode instance. To enable an env var within Xcode, first quit any running Xcode +instance, and then open the project from the command line: + +```console +open --env FIREBASE_SOURCE_FIRESTORE Package.swift +``` + +To unset the env var, quit the running Xcode instance. If you need to pass +multiple variables, repeat the `--env` argument for each: +```console +open --env FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT \ +--env FIREBASE_SOURCE_FIRESTORE Package.swift +``` + +#### Command-line builds + +For command-line builds using `xcodebuild` or `swift build`, the recommended +approach is to prefix the build command with the environment variable. This sets +the variable only for that specific command, avoiding unintended side effects. + +```bash +FIREBASE_SOURCE_FIRESTORE=1 xcodebuild -scheme FirebaseFirestore \ +-destination 'generic/platform=iOS' +``` + +Alternatively, if you plan to run multiple commands that require the variable +to be set, you can `export` it. This will apply the variable to all subsequent +commands in that terminal session. + +```bash +export FIREBASE_SOURCE_FIRESTORE=1 +xcodebuild -scheme FirebaseFirestore -destination 'generic/platform=iOS' +# Any other commands here will also have the variable set +``` + +Once the project is built with the variable set, SPM will clone and build +Firestore and its C++ dependencies (like abseil and gRPC) from source. + +### Dependency hierarchy + +The dependency tree for a source-based build looks like this: + +``` +FirebaseFirestore (Library Product) +└── FirebaseFirestoreTarget (Wrapper Target) + └── FirebaseFirestore (Swift Target) + ├── FirebaseCore + ├── FirebaseCoreExtension + ├── FirebaseSharedSwift + └── FirebaseFirestoreInternalWrapper (C++ Target) + ├── FirebaseAppCheckInterop + ├── FirebaseCore + ├── leveldb + ├── nanopb + ├── abseil (source) (from https://github.com/firebase/abseil-cpp-SwiftPM.git) + └── gRPC-cpp (source) (from https://github.com/grpc/grpc-ios.git) + └── BoringSSL (source) (from https://github.com/firebase/boringSSL-SwiftPM.git) +``` + +### Target breakdown + +* **`FirebaseFirestore`**: The main Swift target containing the public Swift + API for Firestore. It acts as a bridge to the underlying C++ + implementation. +* **`FirebaseFirestoreInternalWrapper`**: This target compiles the core C++ + source code of Firestore. It depends on other low-level libraries and C++ + dependencies, which are also built from source. + +--- + +## 3. Local binary build (CI only) + +A third, less common build option is available for CI environments. When the +`FIREBASECI_USE_LOCAL_FIRESTORE_ZIP` environment variable is set, the build +system will use a local `FirebaseFirestoreInternal.xcframework` instead of +downloading the pre-compiled binary. This option assumes the xcframework is +located at the root of the repository. + +This option is primarily used by internal scripts, such as +`scripts/check_firestore_symbols.sh`, to perform validation against a locally +built version of the Firestore binary. It is not intended for general consumer +use. + +--- + +## Core target explanations + +### `FirebaseFirestore` (Library product) + +The main entry point for integrating Firestore via SPM is the +`FirebaseFirestore` library product. + +```swift +.library( + name: "FirebaseFirestore", + targets: ["FirebaseFirestoreTarget"]) +``` + +This product points to a wrapper target, `FirebaseFirestoreTarget`, which then +depends on the appropriate Firestore targets based on the chosen build option. + +### `FirebaseFirestoreTarget` (Wrapper target) + +The `FirebaseFirestoreTarget` is a thin wrapper that exists to work around a +limitation in SPM where a single target cannot conditionally depend on +different sets of targets (source vs. binary). + +By having clients depend on the wrapper, the `Package.swift` can internally +manage the complexity of switching between source and binary builds. This +provides a stable entry point for all clients and avoids pushing conditional +logic into their own package manifests. + +--- + +## Test targets + +The testing infrastructure for Firestore in SPM is designed to be independent +of the build choice (source vs. binary). + +* **`FirebaseFirestoreTestingSupport`**: This is a library target, not a test + target. It provides public testing utilities that consumers can use to + write unit tests for their Firestore-dependent code. It has a dependency on + `FirebaseFirestoreTarget`, which means it will link against whichever + version of Firestore (source or binary) is being used in the build. + +* **`FirestoreTestingSupportTests`**: This is a test target that contains the + unit tests for the `FirebaseFirestoreTestingSupport` library itself. Its + purpose is to validate the testing utilities. + +Because both of these targets depend on the `FirebaseFirestoreTarget` wrapper, +they seamlessly adapt to either the source-based or binary-based build path +without any conditional logic.