Skip to content

Commit fa8fab0

Browse files
authored
Merge e75fb8c into 5d44914
2 parents 5d44914 + e75fb8c commit fa8fab0

File tree

10 files changed

+1350
-221
lines changed

10 files changed

+1350
-221
lines changed

analytics/CMakeLists.txt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,15 @@ set(android_SRCS
7474
set(ios_SRCS
7575
src/analytics_ios.mm)
7676

77-
# Source files used by the stub implementation.
78-
set(stub_SRCS
79-
src/analytics_stub.cc)
77+
# Source files used by the desktop / stub implementation.
78+
set(desktop_SRCS
79+
src/analytics_desktop.cc
80+
src/analytics_desktop_dynamic.c)
81+
82+
if(WIN32)
83+
# Add Windows-specific sources for desktop builds.
84+
list(APPEND desktop_SRCS src/analytics_windows.cc)
85+
endif()
8086

8187
if(ANDROID)
8288
set(analytics_platform_SRCS
@@ -86,7 +92,7 @@ elseif(IOS)
8692
"${ios_SRCS}")
8793
else()
8894
set(analytics_platform_SRCS
89-
"${stub_SRCS}")
95+
"${desktop_SRCS}")
9096
endif()
9197

9298
add_library(firebase_analytics STATIC
@@ -98,6 +104,9 @@ set_property(TARGET firebase_analytics PROPERTY FOLDER "Firebase Cpp")
98104
# Set up the dependency on Firebase App.
99105
target_link_libraries(firebase_analytics
100106
PUBLIC firebase_app)
107+
if(WIN32)
108+
target_link_libraries(firebase_analytics PUBLIC Crypt32.lib)
109+
endif()
101110
# Public headers all refer to each other relative to the src/include directory,
102111
# while private headers are relative to the entire C++ SDK directory.
103112
target_include_directories(firebase_analytics

analytics/generate_windows_stubs.py

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
"""Generate stubs and function pointers for Windows SDK"""
1717

1818
import argparse
19+
import hashlib
1920
import os
2021
import re
2122
import sys
2223

2324
HEADER_GUARD_PREFIX = "FIREBASE_ANALYTICS_SRC_WINDOWS_"
24-
INCLUDE_PATH = "src/windows/"
25+
INCLUDE_PATH = "src/"
2526
INCLUDE_PREFIX = "analytics/" + INCLUDE_PATH
2627
COPYRIGHT_NOTICE = """// Copyright 2025 Google LLC
2728
//
@@ -39,17 +40,29 @@
3940
4041
"""
4142

42-
def generate_function_pointers(header_file_path, output_h_path, output_c_path):
43+
44+
def hash_file(filename):
45+
sha256_hash = hashlib.sha256()
46+
with open(filename, "rb") as file:
47+
while chunk := file.read(4096):
48+
sha256_hash.update(chunk)
49+
return sha256_hash.digest()
50+
51+
def generate_function_pointers(dll_file_path, header_file_path, output_h_path, output_c_path):
4352
"""
4453
Parses a C header file to generate a self-contained header with typedefs,
4554
extern function pointer declarations, and a source file with stub functions,
4655
initialized pointers, and a dynamic loading function for Windows.
4756
4857
Args:
58+
header_file_path (str): The path to the DLL file.
4959
header_file_path (str): The path to the input C header file.
5060
output_h_path (str): The path for the generated C header output file.
5161
output_c_path (str): The path for the generated C source output file.
5262
"""
63+
print(f"Reading DLL file: {dll_file_path}")
64+
dll_hash = hash_file(dll_file_path)
65+
5366
print(f"Reading header file: {header_file_path}")
5467
try:
5568
with open(header_file_path, 'r', encoding='utf-8') as f:
@@ -83,7 +96,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
8396
return_type = match.group(1).strip()
8497
function_name = match.group(2).strip()
8598
params_str = match.group(3).strip()
86-
99+
87100
cleaned_params_for_decl = re.sub(r'\s+', ' ', params_str) if params_str else ""
88101
stub_name = f"Stub_{function_name}"
89102

@@ -94,7 +107,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
94107
return_statement = f' return ({return_type})(&g_stub_memory);'
95108
else: # bool, int64_t, etc.
96109
return_statement = " return 1;"
97-
110+
98111
stub_function = (
99112
f"// Stub for {function_name}\n"
100113
f"static {return_type} {stub_name}({params_str}) {{\n"
@@ -111,7 +124,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
111124

112125
pointer_init = f"{return_type} (*ptr_{function_name})({cleaned_params_for_decl}) = &{stub_name};"
113126
pointer_initializations.append(pointer_init)
114-
127+
115128
function_details_for_loader.append((function_name, return_type, cleaned_params_for_decl))
116129

117130
print(f"Found {len(pointer_initializations)} functions. Generating output files...")
@@ -124,12 +137,12 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
124137
f.write(f"#ifndef {header_guard}\n")
125138
f.write(f"#define {header_guard}\n\n")
126139
f.write("#include <stdbool.h> // needed for bool type in pure C\n\n")
127-
140+
128141
f.write("// --- Copied from original header ---\n")
129142
f.write("\n".join(includes) + "\n\n")
130143
f.write("".join(typedefs))
131144
f.write("// --- End of copied section ---\n\n")
132-
145+
133146
f.write("#ifdef __cplusplus\n")
134147
f.write('extern "C" {\n')
135148
f.write("#endif\n\n")
@@ -139,15 +152,19 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
139152
f.write("\n\n")
140153
f.write("\n".join(macro_definitions))
141154
f.write("\n// clang-format on\n")
142-
f.write("\n\n// --- Dynamic Loader Declaration for Windows ---\n")
143-
f.write("#if defined(_WIN32)\n")
144-
f.write('#include <windows.h> // For HMODULE\n')
145-
f.write('// Load Google Analytics functions from the given DLL handle into function pointers.\n')
146-
f.write(f'// Returns the number of functions successfully loaded (out of {len(function_details_for_loader)}).\n')
147-
f.write("int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle);\n\n")
155+
f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.')
156+
f.write('\nextern const int FirebaseAnalytics_DynamicFunctionCount;\n');
157+
f.write("\n// --- Dynamic Loader Declaration for Windows ---\n")
158+
f.write("#if defined(_WIN32)\n\n")
159+
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');
162+
f.write('// Load Analytics functions from the given DLL handle into function pointers.\n')
163+
f.write(f'// Returns the number of functions successfully loaded.\n')
164+
f.write("int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle);\n\n")
148165
f.write('// Reset all function pointers back to stubs.\n')
149-
f.write("void FirebaseAnalytics_UnloadAnalyticsFunctions(void);\n\n")
150-
f.write("#endif // defined(_WIN32)\n")
166+
f.write("void FirebaseAnalytics_UnloadDynamicFunctions(void);\n\n")
167+
f.write("#endif // defined(_WIN32)\n")
151168
f.write("\n#ifdef __cplusplus\n")
152169
f.write("}\n")
153170
f.write("#endif\n\n")
@@ -159,18 +176,26 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
159176
with open(output_c_path, 'w', encoding='utf-8') as f:
160177
f.write(f"{COPYRIGHT_NOTICE}")
161178
f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n")
162-
f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n')
179+
f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n\n')
163180
f.write('#include <stddef.h>\n\n')
164-
f.write("// clang-format off\n\n")
165181
f.write("static void* g_stub_memory = NULL;\n\n")
166-
f.write("// --- Stub Function Definitions ---\n")
182+
f.write("// clang-format off\n")
183+
f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.')
184+
f.write(f'\nconst int FirebaseAnalytics_DynamicFunctionCount = {len(function_details_for_loader)};\n\n');
185+
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')
190+
f.write("#endif // defined(_WIN32)\n")
191+
f.write("\n// --- Stub Function Definitions ---\n")
167192
f.write("\n\n".join(stub_functions))
168193
f.write("\n\n\n// --- Function Pointer Initializations ---\n")
169194
f.write("\n".join(pointer_initializations))
170195
f.write("\n\n// --- Dynamic Loader Function for Windows ---\n")
171196
loader_lines = [
172197
'#if defined(_WIN32)',
173-
'int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle) {',
198+
'int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle) {',
174199
' int count = 0;\n',
175200
' if (!dll_handle) {',
176201
' return count;',
@@ -188,7 +213,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
188213
loader_lines.extend(proc_check)
189214
loader_lines.append('\n return count;')
190215
loader_lines.append('}\n')
191-
loader_lines.append('void FirebaseAnalytics_UnloadAnalyticsFunctions(void) {')
216+
loader_lines.append('void FirebaseAnalytics_UnloadDynamicFunctions(void) {')
192217
for name, ret_type, params in function_details_for_loader:
193218
loader_lines.append(f' ptr_{name} = &Stub_{name};');
194219
loader_lines.append('}\n')
@@ -203,6 +228,12 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
203228
parser = argparse.ArgumentParser(
204229
description="Generate C stubs and function pointers from a header file."
205230
)
231+
parser.add_argument(
232+
"--windows_dll",
233+
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
234+
#required=True,
235+
help="Path to the DLL file to calculate a hash."
236+
)
206237
parser.add_argument(
207238
"--windows_header",
208239
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/include/public/c/analytics.h"),
@@ -211,21 +242,21 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path):
211242
)
212243
parser.add_argument(
213244
"--output_header",
214-
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.h"),
245+
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.h"),
215246
#required=True,
216247
help="Path for the generated output header file."
217248
)
218249
parser.add_argument(
219250
"--output_source",
220-
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.c"),
251+
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.c"),
221252
#required=True,
222253
help="Path for the generated output source file."
223254
)
224-
225255
args = parser.parse_args()
226-
256+
227257
generate_function_pointers(
228-
args.windows_header,
229-
args.output_header,
258+
args.windows_dll,
259+
args.windows_header,
260+
args.output_header,
230261
args.output_source
231262
)

analytics/integration_test/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ else()
211211
)
212212
elseif(MSVC)
213213
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32)
214+
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/analytics_win.dll")
215+
216+
# For Windows, check if the Analytics DLL exists, and copy it in if so.
217+
if (EXISTS "${ANALYTICS_WINDOWS_DLL}")
218+
add_custom_command(
219+
TARGET ${integration_test_target_name} POST_BUILD
220+
COMMAND ${CMAKE_COMMAND} -E copy
221+
"${ANALYTICS_WINDOWS_DLL}"
222+
$<TARGET_FILE_DIR:${integration_test_target_name}>)
223+
endif()
214224
else()
215225
set(ADDITIONAL_LIBS pthread)
216226
endif()

0 commit comments

Comments
 (0)