|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Copyright 2025 Google LLC |
| 3 | +# |
| 4 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +# you may not use this file except in compliance with the License. |
| 6 | +# You may obtain a copy of the License at |
| 7 | +# |
| 8 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +# |
| 10 | +# Unless required by applicable law or agreed to in writing, software |
| 11 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +# See the License for the specific language governing permissions and |
| 14 | +# limitations under the License. |
| 15 | + |
| 16 | +"""Generate stubs and function pointers for Windows SDK""" |
| 17 | + |
| 18 | +import argparse |
| 19 | +import os |
| 20 | +import re |
| 21 | +import sys |
| 22 | + |
| 23 | +HEADER_GUARD_PREFIX = "FIREBASE_ANALYTICS_SRC_WINDOWS_" |
| 24 | +INCLUDE_PATH = "src/windows/" |
| 25 | +INCLUDE_PREFIX = "analytics/" + INCLUDE_PATH |
| 26 | +COPYRIGHT_NOTICE = """// Copyright 2025 Google LLC |
| 27 | +// |
| 28 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 29 | +// you may not use this file except in compliance with the License. |
| 30 | +// You may obtain a copy of the License at |
| 31 | +// |
| 32 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 33 | +// |
| 34 | +// Unless required by applicable law or agreed to in writing, software |
| 35 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 36 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 37 | +// See the License for the specific language governing permissions and |
| 38 | +// limitations under the License. |
| 39 | +
|
| 40 | +""" |
| 41 | + |
| 42 | +def generate_function_pointers(header_file_path, output_h_path, output_c_path): |
| 43 | + """ |
| 44 | + Parses a C header file to generate a self-contained header with typedefs, |
| 45 | + extern function pointer declarations, and a source file with stub functions, |
| 46 | + initialized pointers, and a dynamic loading function for Windows. |
| 47 | +
|
| 48 | + Args: |
| 49 | + header_file_path (str): The path to the input C header file. |
| 50 | + output_h_path (str): The path for the generated C header output file. |
| 51 | + output_c_path (str): The path for the generated C source output file. |
| 52 | + """ |
| 53 | + print(f"Reading header file: {header_file_path}") |
| 54 | + try: |
| 55 | + with open(header_file_path, 'r', encoding='utf-8') as f: |
| 56 | + header_content = f.read() |
| 57 | + except FileNotFoundError: |
| 58 | + print(f"Error: Header file not found at '{header_file_path}'") |
| 59 | + return |
| 60 | + |
| 61 | + # --- Extract necessary definitions from the original header --- |
| 62 | + |
| 63 | + # Find all standard includes (e.g., <stdint.h>) |
| 64 | + includes = re.findall(r"#include\s+<.*?>", header_content) |
| 65 | + |
| 66 | + # Find all typedefs, including their documentation comments |
| 67 | + typedefs = re.findall(r"/\*\*(?:[\s\S]*?)\*/\s*typedef[\s\S]*?;\s*", header_content) |
| 68 | + |
| 69 | + # --- Extract function prototypes --- |
| 70 | + function_pattern = re.compile( |
| 71 | + r"ANALYTICS_API\s+([\w\s\*]+?)\s+(\w+)\s*\((.*?)\);", |
| 72 | + re.DOTALL |
| 73 | + ) |
| 74 | + matches = function_pattern.finditer(header_content) |
| 75 | + |
| 76 | + extern_declarations = [] |
| 77 | + stub_functions = [] |
| 78 | + pointer_initializations = [] |
| 79 | + function_details_for_loader = [] |
| 80 | + |
| 81 | + for match in matches: |
| 82 | + return_type = match.group(1).strip() |
| 83 | + function_name = match.group(2).strip() |
| 84 | + params_str = match.group(3).strip() |
| 85 | + |
| 86 | + cleaned_params_for_decl = re.sub(r'\s+', ' ', params_str) if params_str else "" |
| 87 | + stub_name = f"Stub_{function_name}" |
| 88 | + |
| 89 | + # Generate return statement for the stub |
| 90 | + if "void" in return_type: |
| 91 | + return_statement = " // No return value." |
| 92 | + elif "*" in return_type: |
| 93 | + return_statement = " return NULL;" |
| 94 | + else: # bool, int64_t, etc. |
| 95 | + return_statement = " return 0;" |
| 96 | + |
| 97 | + stub_function = ( |
| 98 | + f"// Stub for {function_name}\n" |
| 99 | + f"static {return_type} {stub_name}({params_str}) {{\n" |
| 100 | + f"{return_statement}\n" |
| 101 | + f"}}" |
| 102 | + ) |
| 103 | + stub_functions.append(stub_function) |
| 104 | + |
| 105 | + declaration = f"extern {return_type} (*ptr_{function_name})({cleaned_params_for_decl});" |
| 106 | + extern_declarations.append(declaration) |
| 107 | + |
| 108 | + pointer_init = f"{return_type} (*ptr_{function_name})({cleaned_params_for_decl}) = &{stub_name};" |
| 109 | + pointer_initializations.append(pointer_init) |
| 110 | + |
| 111 | + function_details_for_loader.append((function_name, return_type, cleaned_params_for_decl)) |
| 112 | + |
| 113 | + print(f"Found {len(pointer_initializations)} functions. Generating output files...") |
| 114 | + |
| 115 | + # --- Write the self-contained Header File (.h) --- |
| 116 | + header_guard = f"{HEADER_GUARD_PREFIX}{os.path.basename(output_h_path).upper().replace('.', '_')}_" |
| 117 | + with open(output_h_path, 'w', encoding='utf-8') as f: |
| 118 | + f.write(f"{COPYRIGHT_NOTICE}") |
| 119 | + f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n") |
| 120 | + f.write(f"#ifndef {header_guard}\n") |
| 121 | + f.write(f"#define {header_guard}\n\n") |
| 122 | + |
| 123 | + f.write("// --- Copied from original header ---\n") |
| 124 | + f.write("\n".join(includes) + "\n\n") |
| 125 | + f.write("".join(typedefs)) |
| 126 | + f.write("// --- End of copied section ---\n\n") |
| 127 | + |
| 128 | + f.write("#ifdef __cplusplus\n") |
| 129 | + f.write('extern "C" {\n') |
| 130 | + f.write("#endif\n\n") |
| 131 | + f.write("// --- Function Pointer Declarations ---\n") |
| 132 | + f.write("// clang-format off\n") |
| 133 | + f.write("\n".join(extern_declarations)) |
| 134 | + f.write("\n// clang-format on\n") |
| 135 | + f.write("\n\n// --- Dynamic Loader Declaration for Windows ---\n") |
| 136 | + f.write("#if defined(_WIN32)\n") |
| 137 | + f.write('#include <windows.h> // For HMODULE\n') |
| 138 | + f.write('// Load Google Analytics functions from the given DLL handle into function pointers.\n') |
| 139 | + f.write(f'// Returns the number of functions successfully loaded (out of {len(function_details_for_loader)}).\n') |
| 140 | + f.write("int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle);\n\n") |
| 141 | + f.write('// Reset all function pointers back to stubs.\n') |
| 142 | + f.write("void FirebaseAnalytics_UnloadAnalyticsFunctions(void);\n\n") |
| 143 | + f.write("#endif // defined(_WIN32)\n") |
| 144 | + f.write("\n#ifdef __cplusplus\n") |
| 145 | + f.write("}\n") |
| 146 | + f.write("#endif\n\n") |
| 147 | + f.write(f"#endif // {header_guard}\n") |
| 148 | + |
| 149 | + print(f"Successfully generated header file: {output_h_path}") |
| 150 | + |
| 151 | + # --- Write the Source File (.c) --- |
| 152 | + with open(output_c_path, 'w', encoding='utf-8') as f: |
| 153 | + f.write(f"{COPYRIGHT_NOTICE}") |
| 154 | + f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n") |
| 155 | + f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n') |
| 156 | + f.write('#include <stddef.h>\n\n') |
| 157 | + f.write("// clang-format off\n\n") |
| 158 | + f.write("// --- Stub Function Definitions ---\n") |
| 159 | + f.write("\n\n".join(stub_functions)) |
| 160 | + f.write("\n\n\n// --- Function Pointer Initializations ---\n") |
| 161 | + f.write("\n".join(pointer_initializations)) |
| 162 | + f.write("\n\n// --- Dynamic Loader Function for Windows ---\n") |
| 163 | + loader_lines = [ |
| 164 | + '#if defined(_WIN32)', |
| 165 | + 'int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle) {', |
| 166 | + ' int count = 0;\n', |
| 167 | + ' if (!dll_handle) {', |
| 168 | + ' return count;', |
| 169 | + ' }\n' |
| 170 | + ] |
| 171 | + for name, ret_type, params in function_details_for_loader: |
| 172 | + pointer_type_cast = f"({ret_type} (*)({params}))" |
| 173 | + proc_check = [ |
| 174 | + f' FARPROC proc_{name} = GetProcAddress(dll_handle, "{name}");', |
| 175 | + f' if (proc_{name}) {{', |
| 176 | + f' ptr_{name} = {pointer_type_cast}proc_{name};', |
| 177 | + f' count++;', |
| 178 | + f' }}' |
| 179 | + ] |
| 180 | + loader_lines.extend(proc_check) |
| 181 | + loader_lines.append('\n return count;') |
| 182 | + loader_lines.append('}\n') |
| 183 | + loader_lines.append('void FirebaseAnalytics_UnloadAnalyticsFunctions(void) {') |
| 184 | + for name, ret_type, params in function_details_for_loader: |
| 185 | + loader_lines.append(f' ptr_{name} = &Stub_{name};'); |
| 186 | + loader_lines.append('}\n') |
| 187 | + loader_lines.append('#endif // defined(_WIN32)\n') |
| 188 | + f.write('\n'.join(loader_lines)) |
| 189 | + f.write("// clang-format on\n") |
| 190 | + |
| 191 | + print(f"Successfully generated C source file: {output_c_path}") |
| 192 | + |
| 193 | + |
| 194 | +if __name__ == '__main__': |
| 195 | + parser = argparse.ArgumentParser( |
| 196 | + description="Generate C stubs and function pointers from a header file." |
| 197 | + ) |
| 198 | + parser.add_argument( |
| 199 | + "--windows_header", |
| 200 | + default = os.path.join(os.path.dirname(sys.argv[0]), "windows/include/public/c/analytics.h"), |
| 201 | + #required=True, |
| 202 | + help="Path to the input C header file." |
| 203 | + ) |
| 204 | + parser.add_argument( |
| 205 | + "--output_header", |
| 206 | + default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.h"), |
| 207 | + #required=True, |
| 208 | + help="Path for the generated output header file." |
| 209 | + ) |
| 210 | + parser.add_argument( |
| 211 | + "--output_source", |
| 212 | + default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.c"), |
| 213 | + #required=True, |
| 214 | + help="Path for the generated output source file." |
| 215 | + ) |
| 216 | + |
| 217 | + args = parser.parse_args() |
| 218 | + |
| 219 | + generate_function_pointers( |
| 220 | + args.windows_header, |
| 221 | + args.output_header, |
| 222 | + args.output_source |
| 223 | + ) |
0 commit comments