Skip to content

Commit 57f303d

Browse files
Updated Analytics Windows to allow multiple DLL hashes, and add usage of Options struct. (#1744)
* Update script and code to handle known hashes for multiple DLLs. * Fixed issues on Windows with multi-dll-hash code. * Fix: Link Crypt32.lib for firebase_analytics on Windows Add Crypt32.lib to the linked libraries for the firebase_analytics target on Windows. This resolves a linker error LNK2019 for _CryptBinaryToStringA@20, which was occurring in builds that included analytics_windows.obj (e.g., firebase_analytics_test). While CryptBinaryToStringA is not directly called in analytics_windows.cc, the linker indicates that analytics_windows.obj requires this symbol, possibly due to an indirect dependency or other build system interactions. Linking Crypt32.lib provides the necessary symbol. * Fix: Link Crypt32.lib for firebase_analytics_test on Windows I've added Crypt32.lib to the dependencies of the firebase_analytics_test target in `analytics/tests/CMakeLists.txt` when building on Windows. This is to resolve a persistent LNK2019 error for _CryptBinaryToStringA@20, which occurs during the linking phase of firebase_analytics_test.obj. This change directly links the required system library at the test executable level. * Update DLL. * Remove unused file. * Remove extra file. * Initialize Analytics C SDK with AppOptions on desktop This change updates the desktop analytics initialization to use the newly required Options struct for the Windows C API. - In `analytics/src/analytics_desktop.cc`: - `firebase::analytics::Initialize(const App& app)` now retrieves `app_id` and `package_name` from `app.options()`. - It calls `GoogleAnalytics_Options_Create()` to create the options struct. - Populates `app_id`, `package_name`, and sets a default for `analytics_collection_enabled_at_first_launch`. - Calls `GoogleAnalytics_Initialize()` with the populated options struct. - String lifetimes for `app_id` and `package_name` are handled by creating local `std::string` copies before passing their `c_str()` to the C API. * Update stub generation. * Format code. * Fix build issues. * Update Analytics to use new options struct. (#1745) * Initialize Analytics C SDK with AppOptions on desktop This change updates the desktop analytics initialization to use the newly required Options struct for the Windows C API. - In `analytics/src/analytics_desktop.cc`: - `firebase::analytics::Initialize(const App& app)` now retrieves `app_id` and `package_name` from `app.options()`. - It calls `GoogleAnalytics_Options_Create()` to create the options struct. - Populates `app_id`, `package_name`, and sets a default for `analytics_collection_enabled_at_first_launch`. - Calls `GoogleAnalytics_Initialize()` with the populated options struct. - String lifetimes for `app_id` and `package_name` are handled by creating local `std::string` copies before passing their `c_str()` to the C API. * Format code. * Fix build issues. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * Format code. * Only display Analytics warnings if we are not in stub mode. * Format code. * Address review comments and add missing copyright notices. * Remove extraneous commented out code. * Add another known hash to the list. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 56de502 commit 57f303d

16 files changed

+534
-528
lines changed

analytics/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ set_property(TARGET firebase_analytics PROPERTY FOLDER "Firebase Cpp")
104104
# Set up the dependency on Firebase App.
105105
target_link_libraries(firebase_analytics
106106
PUBLIC firebase_app)
107+
if(WIN32)
108+
target_link_libraries(firebase_analytics PRIVATE Crypt32.lib)
109+
endif()
107110
# Public headers all refer to each other relative to the src/include directory,
108111
# while private headers are relative to the entire C++ SDK directory.
109112
target_include_directories(firebase_analytics

analytics/generate_windows_stubs.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,34 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
5555
initialized pointers, and a dynamic loading function for Windows.
5656
5757
Args:
58-
header_file_path (str): The path to the DLL file.
58+
dll_file_path (str): The path to the DLL file.
5959
header_file_path (str): The path to the input C header file.
6060
output_h_path (str): The path for the generated C header output file.
6161
output_c_path (str): The path for the generated C source output file.
6262
"""
6363
print(f"Reading DLL file: {dll_file_path}")
64-
dll_hash = hash_file(dll_file_path)
64+
dll_hash = hash_file(dll_file_path) # This is binary
65+
66+
# --- Manage known hashes ---
67+
hash_file_path = os.path.join(os.path.dirname(dll_file_path), "known_dll_hashes.txt")
68+
known_hex_hashes = []
69+
try:
70+
with open(hash_file_path, 'r') as f:
71+
for line in f:
72+
known_hex_hashes.append(line.strip())
73+
except FileNotFoundError:
74+
print(f"Info: '{hash_file_path}' not found, will be created.")
75+
pass # File doesn't exist, list remains empty
76+
77+
current_dll_hex_hash = dll_hash.hex()
78+
if current_dll_hex_hash not in known_hex_hashes:
79+
known_hex_hashes.append(current_dll_hex_hash)
80+
81+
with open(hash_file_path, 'w') as f:
82+
for hex_hash in known_hex_hashes:
83+
f.write(hex_hash + '\n')
84+
print(f"Updated known hashes in: {hash_file_path}")
85+
# --- End of manage known hashes ---
6586

6687
print(f"Reading header file: {header_file_path}")
6788
try:
@@ -77,7 +98,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
7798
includes = re.findall(r"#include\s+<.*?>", header_content)
7899

79100
# Find all typedefs, including their documentation comments
80-
typedefs = re.findall(r"/\*\*(?:[\s\S]*?)\*/\s*typedef[\s\S]*?;\s*", header_content)
101+
typedefs = re.findall(r"(?:/\*\*(?:[\s\S]*?)\*/\s*)?typedef[\s\S]*?;\s*", header_content)
81102

82103
# --- Extract function prototypes ---
83104
function_pattern = re.compile(
@@ -104,7 +125,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
104125
if "void" in return_type:
105126
return_statement = " // No return value."
106127
elif "*" in return_type:
107-
return_statement = f' return ({return_type})(&g_stub_memory);'
128+
return_statement = f' return ({return_type})(&g_stub_memory[0]);'
108129
else: # bool, int64_t, etc.
109130
return_statement = " return 1;"
110131

@@ -136,6 +157,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
136157
f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n")
137158
f.write(f"#ifndef {header_guard}\n")
138159
f.write(f"#define {header_guard}\n\n")
160+
f.write(f"#define ANALYTICS_API // filter out from header copy\n\n")
139161
f.write("#include <stdbool.h> // needed for bool type in pure C\n\n")
140162

141163
f.write("// --- Copied from original header ---\n")
@@ -157,8 +179,10 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
157179
f.write("\n// --- Dynamic Loader Declaration for Windows ---\n")
158180
f.write("#if defined(_WIN32)\n\n")
159181
f.write('#include <windows.h>\n')
160-
f.write(f'\n// Google Analytics Windows DLL SHA256 hash, to be verified before loading.')
161-
f.write(f'\nextern const unsigned char FirebaseAnalytics_WindowsDllHash[{len(dll_hash)}];\n\n');
182+
f.write('\n// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).\n')
183+
f.write('extern const char* FirebaseAnalytics_KnownWindowsDllHashes[];\n')
184+
f.write('// Count of known Google Analytics Windows DLL SHA256 hashes.\n')
185+
f.write('extern const int FirebaseAnalytics_KnownWindowsDllHashCount;\n\n')
162186
f.write('// Load Analytics functions from the given DLL handle into function pointers.\n')
163187
f.write(f'// Returns the number of functions successfully loaded.\n')
164188
f.write("int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle);\n\n")
@@ -178,15 +202,23 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
178202
f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n")
179203
f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n\n')
180204
f.write('#include <stddef.h>\n\n')
181-
f.write("static void* g_stub_memory = NULL;\n\n")
205+
f.write("// A nice big chunk of stub memory that can be returned by stubbed Create methods.\n")
206+
f.write("static char g_stub_memory[256] = {0};\n\n")
182207
f.write("// clang-format off\n")
183208
f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.')
184209
f.write(f'\nconst int FirebaseAnalytics_DynamicFunctionCount = {len(function_details_for_loader)};\n\n');
185210
f.write("#if defined(_WIN32)\n")
186-
f.write('// Google Analytics Windows DLL SHA256 hash, to be verified before loading.\n')
187-
f.write('const unsigned char FirebaseAnalytics_WindowsDllHash[] = {\n ')
188-
f.write(', '.join(["0x%02x" % s for s in dll_hash]))
189-
f.write('\n};\n')
211+
f.write('// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).\n')
212+
f.write('const char* FirebaseAnalytics_KnownWindowsDllHashes[] = {\n')
213+
if known_hex_hashes:
214+
for i, hex_hash in enumerate(known_hex_hashes):
215+
f.write(f' "{hex_hash}"')
216+
if i < len(known_hex_hashes) - 1:
217+
f.write(',')
218+
f.write('\n')
219+
f.write('};\n\n')
220+
f.write('// Count of known Google Analytics Windows DLL SHA256 hashes.\n')
221+
f.write(f'const int FirebaseAnalytics_KnownWindowsDllHashCount = {len(known_hex_hashes)};\n')
190222
f.write("#endif // defined(_WIN32)\n")
191223
f.write("\n// --- Stub Function Definitions ---\n")
192224
f.write("\n\n".join(stub_functions))
@@ -231,25 +263,21 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
231263
parser.add_argument(
232264
"--windows_dll",
233265
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
234-
#required=True,
235266
help="Path to the DLL file to calculate a hash."
236267
)
237268
parser.add_argument(
238269
"--windows_header",
239270
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/include/public/c/analytics.h"),
240-
#required=True,
241271
help="Path to the input C header file."
242272
)
243273
parser.add_argument(
244274
"--output_header",
245275
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.h"),
246-
#required=True,
247276
help="Path for the generated output header file."
248277
)
249278
parser.add_argument(
250279
"--output_source",
251280
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.c"),
252-
#required=True,
253281
help="Path for the generated output source file."
254282
)
255283
args = parser.parse_args()

0 commit comments

Comments
 (0)