icore_module.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # Copyright 2023 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the 'License');
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an 'AS IS' BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import os
  15. import logging
  16. import json
  17. import subprocess
  18. SWIFT = 'Swift'
  19. OBJECTIVE_C = 'Objective-C'
  20. # List of Swift and Objective-C modules
  21. MODULE_LIST = [
  22. 'FirebaseABTesting',
  23. 'FirebaseAnalytics', # Not buildable from source
  24. 'FirebaseAnalyticsOnDeviceConversion', # Not buildable.
  25. 'FirebaseAppCheck',
  26. 'FirebaseAppDistribution',
  27. 'FirebaseAuth',
  28. 'FirebaseCore',
  29. 'FirebaseCrashlytics',
  30. 'FirebaseDatabase',
  31. 'FirebaseDynamicLinks',
  32. 'FirebaseFirestoreInternal',
  33. 'FirebaseFirestore',
  34. 'FirebaseFunctions',
  35. 'FirebaseInAppMessaging'
  36. 'FirebaseInstallations',
  37. 'FirebaseMessaging',
  38. 'FirebaseMLModelDownloader',
  39. 'FirebasePerformance',
  40. 'FirebaseRemoteConfig',
  41. # Not buildable. No scheme named "FirebaseSharedSwift"
  42. 'FirebaseSharedSwift',
  43. 'FirebaseStorage',
  44. # Not buildable. NO "source_files"
  45. 'GoogleAppMeasurement',
  46. # Not buildable. NO "source_files"
  47. 'GoogleAppMeasurementOnDeviceConversion'
  48. ]
  49. def main():
  50. module_info()
  51. def detect_changed_modules(changed_api_files):
  52. """Detect changed modules based on changed API files."""
  53. all_modules = module_info()
  54. changed_modules = {}
  55. for file_path in changed_api_files:
  56. for k, v in all_modules.items():
  57. if v['root_dir'] and v['root_dir'] in file_path:
  58. changed_modules[k] = v
  59. break
  60. logging.info(f'changed_modules:\n{json.dumps(changed_modules, indent=4)}')
  61. return changed_modules
  62. def module_info():
  63. """retrieve module info in MODULE_LIST from `.podspecs`
  64. The module info helps to build Jazzy
  65. includes: module name, source_files, public_header_files,
  66. language, umbrella_header, framework_root
  67. """
  68. module_from_podspecs = module_info_from_podspecs()
  69. module_list = {}
  70. for k, v in module_from_podspecs.items():
  71. if k in MODULE_LIST:
  72. if k not in module_list:
  73. module_list[k] = v
  74. module_list[k]['language'] = OBJECTIVE_C if v.get(
  75. 'public_header_files') else SWIFT
  76. module_list[k]['scheme'] = get_scheme(k)
  77. module_list[k]['umbrella_header'] = get_umbrella_header(
  78. k, v.get('public_header_files'))
  79. module_list[k]['root_dir'] = get_root_dir(k, v.get('source_files'))
  80. logging.info(f'all_module:\n{json.dumps(module_list, indent=4)}')
  81. return module_list
  82. def get_scheme(module_name):
  83. """Jazzy documentation Info SWIFT only.
  84. Get scheme from module name in .podspecs Assume the scheme is the
  85. same as the module name:
  86. """
  87. MODULE_SCHEME_PATCH = {}
  88. if module_name in MODULE_SCHEME_PATCH:
  89. return MODULE_SCHEME_PATCH[module_name]
  90. return module_name
  91. def get_umbrella_header(module_name, public_header_files):
  92. """Jazzy documentation Info OBJC only Get umbrella_header from
  93. public_header_files in .podspecs Assume the umbrella_header is with the
  94. format:
  95. {module_name}/Sources/Public/{module_name}/{module_name}.h
  96. """
  97. if public_header_files:
  98. if isinstance(public_header_files, list):
  99. return public_header_files[0].replace('*', module_name)
  100. elif isinstance(public_header_files, str):
  101. return public_header_files.replace('*', module_name)
  102. return ''
  103. def get_root_dir(module_name, source_files):
  104. """Get source code root_dir from source_files in .podspecs Assume the
  105. root_dir is with the format:
  106. {module_name}/Sources or {module_name}/Source
  107. """
  108. MODULE_ROOT_PATCH = {
  109. 'FirebaseFirestoreInternal': 'Firestore/Source',
  110. 'FirebaseFirestore': 'Firestore/Swift/Source',
  111. 'FirebaseCrashlytics': 'Crashlytics/Crashlytics',
  112. }
  113. if module_name in MODULE_ROOT_PATCH:
  114. return MODULE_ROOT_PATCH[module_name]
  115. if source_files:
  116. for source_file in source_files:
  117. if f'{module_name}/Sources' in source_file:
  118. return f'{module_name}/Sources'
  119. if f'{module_name}/Source' in source_file:
  120. return f'{module_name}/Source'
  121. return ''
  122. def module_info_from_podspecs(root_dir=os.getcwd()):
  123. result = {}
  124. for filename in os.listdir(root_dir):
  125. if filename.endswith('.podspec'):
  126. podspec_data = parse_podspec(filename)
  127. source_files = podspec_data.get('source_files')
  128. if not podspec_data.get('source_files') and podspec_data.get('ios'):
  129. source_files = podspec_data.get('ios').get('source_files')
  130. result[podspec_data['name']] = {
  131. 'name': podspec_data['name'],
  132. 'source_files': source_files,
  133. 'public_header_files': podspec_data.get('public_header_files')
  134. }
  135. return result
  136. def parse_podspec(podspec_file):
  137. result = subprocess.run(f'pod ipc spec {podspec_file}',
  138. stdout=subprocess.PIPE,
  139. stderr=subprocess.PIPE,
  140. text=True,
  141. shell=True)
  142. if result.returncode != 0:
  143. logging.info(f'Error: {result.stderr}')
  144. return None
  145. # Parse the JSON output
  146. podspec_data = json.loads(result.stdout)
  147. return podspec_data
  148. if __name__ == '__main__':
  149. main()