icore_module.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. 'FirebaseFirestoreInternal',
  32. 'FirebaseFirestore',
  33. 'FirebaseFunctions',
  34. 'FirebaseInAppMessaging'
  35. 'FirebaseInstallations',
  36. 'FirebaseMessaging',
  37. 'FirebaseMLModelDownloader',
  38. 'FirebasePerformance',
  39. 'FirebaseRemoteConfig',
  40. # Not buildable. No scheme named "FirebaseSharedSwift"
  41. 'FirebaseSharedSwift',
  42. 'FirebaseStorage',
  43. # Not buildable. NO "source_files"
  44. 'GoogleAppMeasurement',
  45. # Not buildable. NO "source_files"
  46. 'GoogleAppMeasurementOnDeviceConversion'
  47. ]
  48. def main():
  49. module_info()
  50. def detect_changed_modules(changed_api_files):
  51. """Detect changed modules based on changed API files."""
  52. all_modules = module_info()
  53. changed_modules = {}
  54. for file_path in changed_api_files:
  55. for k, v in all_modules.items():
  56. if v['root_dir'] and v['root_dir'] in file_path:
  57. changed_modules[k] = v
  58. break
  59. logging.info(f'changed_modules:\n{json.dumps(changed_modules, indent=4)}')
  60. return changed_modules
  61. def module_info():
  62. """retrieve module info in MODULE_LIST from `.podspecs`
  63. The module info helps to build Jazzy
  64. includes: module name, source_files, public_header_files,
  65. language, umbrella_header, framework_root
  66. """
  67. module_from_podspecs = module_info_from_podspecs()
  68. module_list = {}
  69. for k, v in module_from_podspecs.items():
  70. if k in MODULE_LIST:
  71. if k not in module_list:
  72. module_list[k] = v
  73. module_list[k]['language'] = OBJECTIVE_C if v.get(
  74. 'public_header_files') else SWIFT
  75. module_list[k]['scheme'] = get_scheme(k)
  76. module_list[k]['umbrella_header'] = get_umbrella_header(
  77. k, v.get('public_header_files'))
  78. module_list[k]['root_dir'] = get_root_dir(k, v.get('source_files'))
  79. logging.info(f'all_module:\n{json.dumps(module_list, indent=4)}')
  80. return module_list
  81. def get_scheme(module_name):
  82. """Jazzy documentation Info SWIFT only.
  83. Get scheme from module name in .podspecs Assume the scheme is the
  84. same as the module name:
  85. """
  86. MODULE_SCHEME_PATCH = {}
  87. if module_name in MODULE_SCHEME_PATCH:
  88. return MODULE_SCHEME_PATCH[module_name]
  89. return module_name
  90. def get_umbrella_header(module_name, public_header_files):
  91. """Jazzy documentation Info OBJC only Get umbrella_header from
  92. public_header_files in .podspecs Assume the umbrella_header is with the
  93. format:
  94. {module_name}/Sources/Public/{module_name}/{module_name}.h
  95. """
  96. if public_header_files:
  97. if isinstance(public_header_files, list):
  98. return public_header_files[0].replace('*', module_name)
  99. elif isinstance(public_header_files, str):
  100. return public_header_files.replace('*', module_name)
  101. return ''
  102. def get_root_dir(module_name, source_files):
  103. """Get source code root_dir from source_files in .podspecs Assume the
  104. root_dir is with the format:
  105. {module_name}/Sources or {module_name}/Source
  106. """
  107. MODULE_ROOT_PATCH = {
  108. 'FirebaseFirestoreInternal': 'Firestore/Source',
  109. 'FirebaseFirestore': 'Firestore/Swift/Source',
  110. 'FirebaseCrashlytics': 'Crashlytics/Crashlytics',
  111. }
  112. if module_name in MODULE_ROOT_PATCH:
  113. return MODULE_ROOT_PATCH[module_name]
  114. if source_files:
  115. for source_file in source_files:
  116. if f'{module_name}/Sources' in source_file:
  117. return f'{module_name}/Sources'
  118. if f'{module_name}/Source' in source_file:
  119. return f'{module_name}/Source'
  120. return ''
  121. def module_info_from_podspecs(root_dir=os.getcwd()):
  122. result = {}
  123. for filename in os.listdir(root_dir):
  124. if filename.endswith('.podspec'):
  125. podspec_data = parse_podspec(filename)
  126. source_files = podspec_data.get('source_files')
  127. if not podspec_data.get('source_files') and podspec_data.get('ios'):
  128. source_files = podspec_data.get('ios').get('source_files')
  129. result[podspec_data['name']] = {
  130. 'name': podspec_data['name'],
  131. 'source_files': source_files,
  132. 'public_header_files': podspec_data.get('public_header_files')
  133. }
  134. return result
  135. def parse_podspec(podspec_file):
  136. result = subprocess.run(f'pod ipc spec {podspec_file}',
  137. stdout=subprocess.PIPE,
  138. stderr=subprocess.PIPE,
  139. text=True,
  140. shell=True)
  141. if result.returncode != 0:
  142. logging.info(f'Error: {result.stderr}')
  143. return None
  144. # Parse the JSON output
  145. podspec_data = json.loads(result.stdout)
  146. return podspec_data
  147. if __name__ == '__main__':
  148. main()