1616"""Generate stubs and function pointers for Windows SDK"""
1717
1818import argparse
19+ import hashlib
1920import os
2021import re
2122import sys
2223
2324HEADER_GUARD_PREFIX = "FIREBASE_ANALYTICS_SRC_WINDOWS_"
24- INCLUDE_PATH = "src/windows/ "
25+ INCLUDE_PATH = "src/"
2526INCLUDE_PREFIX = "analytics/" + INCLUDE_PATH
2627COPYRIGHT_NOTICE = """// Copyright 2025 Google LLC
2728//
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 ('\n extern 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'\n extern 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'\n const 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 )
0 commit comments