build_profile.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import json
  2. import sys
  3. def parse_build_profile(profile_filepath, api):
  4. if profile_filepath == "":
  5. return {}
  6. with open(profile_filepath, encoding="utf-8") as profile_file:
  7. profile = json.load(profile_file)
  8. api_dict = {}
  9. parents = {}
  10. children = {}
  11. for engine_class in api["classes"]:
  12. api_dict[engine_class["name"]] = engine_class
  13. parent = engine_class.get("inherits", "")
  14. child = engine_class["name"]
  15. parents[child] = parent
  16. if parent == "":
  17. continue
  18. children[parent] = children.get(parent, [])
  19. children[parent].append(child)
  20. included = []
  21. front = list(profile.get("enabled_classes", []))
  22. if front:
  23. # These must always be included
  24. front.append("WorkerThreadPool")
  25. front.append("ClassDB")
  26. front.append("ClassDBSingleton")
  27. # In src/classes/low_level.cpp
  28. front.append("FileAccess")
  29. front.append("Image")
  30. front.append("XMLParser")
  31. # In include/godot_cpp/templates/thread_work_pool.hpp
  32. front.append("Semaphore")
  33. while front:
  34. cls = front.pop()
  35. if cls in included:
  36. continue
  37. included.append(cls)
  38. parent = parents.get(cls, "")
  39. if parent:
  40. front.append(parent)
  41. excluded = []
  42. front = list(profile.get("disabled_classes", []))
  43. while front:
  44. cls = front.pop()
  45. if cls in excluded:
  46. continue
  47. excluded.append(cls)
  48. front += children.get(cls, [])
  49. if included and excluded:
  50. print(
  51. "WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
  52. )
  53. return {
  54. "enabled_classes": included,
  55. "disabled_classes": excluded,
  56. }
  57. def generate_trimmed_api(source_api_filepath, profile_filepath):
  58. with open(source_api_filepath, encoding="utf-8") as api_file:
  59. api = json.load(api_file)
  60. if profile_filepath == "":
  61. return api
  62. build_profile = parse_build_profile(profile_filepath, api)
  63. engine_classes = {}
  64. for class_api in api["classes"]:
  65. engine_classes[class_api["name"]] = class_api["is_refcounted"]
  66. for native_struct in api["native_structures"]:
  67. if native_struct["name"] == "ObjectID":
  68. continue
  69. engine_classes[native_struct["name"]] = False
  70. classes = []
  71. for class_api in api["classes"]:
  72. if not is_class_included(class_api["name"], build_profile):
  73. continue
  74. if "methods" in class_api:
  75. methods = []
  76. for method in class_api["methods"]:
  77. if not is_method_included(method, build_profile, engine_classes):
  78. continue
  79. methods.append(method)
  80. class_api["methods"] = methods
  81. classes.append(class_api)
  82. api["classes"] = classes
  83. return api
  84. def is_class_included(class_name, build_profile):
  85. """
  86. Check if an engine class should be included.
  87. This removes classes according to a build profile of enabled or disabled classes.
  88. """
  89. included = build_profile.get("enabled_classes", [])
  90. excluded = build_profile.get("disabled_classes", [])
  91. if included:
  92. return class_name in included
  93. if excluded:
  94. return class_name not in excluded
  95. return True
  96. def is_method_included(method, build_profile, engine_classes):
  97. """
  98. Check if an engine class method should be included.
  99. This removes methods according to a build profile of enabled or disabled classes.
  100. """
  101. included = build_profile.get("enabled_classes", [])
  102. excluded = build_profile.get("disabled_classes", [])
  103. ref_cls = set()
  104. rtype = get_base_type(method.get("return_value", {}).get("type", ""))
  105. args = [get_base_type(a["type"]) for a in method.get("arguments", [])]
  106. if rtype in engine_classes:
  107. ref_cls.add(rtype)
  108. elif is_enum(rtype) and get_enum_class(rtype) in engine_classes:
  109. ref_cls.add(get_enum_class(rtype))
  110. for arg in args:
  111. if arg in engine_classes:
  112. ref_cls.add(arg)
  113. elif is_enum(arg) and get_enum_class(arg) in engine_classes:
  114. ref_cls.add(get_enum_class(arg))
  115. for acls in ref_cls:
  116. if len(included) > 0 and acls not in included:
  117. return False
  118. elif len(excluded) > 0 and acls in excluded:
  119. return False
  120. return True
  121. def is_enum(type_name):
  122. return type_name.startswith("enum::") or type_name.startswith("bitfield::")
  123. def get_enum_class(enum_name: str):
  124. if "." in enum_name:
  125. if is_bitfield(enum_name):
  126. return enum_name.replace("bitfield::", "").split(".")[0]
  127. else:
  128. return enum_name.replace("enum::", "").split(".")[0]
  129. else:
  130. return "GlobalConstants"
  131. def get_base_type(type_name):
  132. if type_name.startswith("const "):
  133. type_name = type_name[6:]
  134. if type_name.endswith("*"):
  135. type_name = type_name[:-1]
  136. if type_name.startswith("typedarray::"):
  137. type_name = type_name.replace("typedarray::", "")
  138. return type_name
  139. def is_bitfield(type_name):
  140. return type_name.startswith("bitfield::")
  141. if __name__ == "__main__":
  142. if len(sys.argv) < 3 or len(sys.argv) > 4:
  143. print("Usage: %s BUILD_PROFILE INPUT_JSON [OUTPUT_JSON]" % (sys.argv[0]))
  144. sys.exit(1)
  145. profile = sys.argv[1]
  146. infile = sys.argv[2]
  147. outfile = sys.argv[3] if len(sys.argv) > 3 else ""
  148. api = generate_trimmed_api(infile, profile)
  149. if outfile:
  150. with open(outfile, "w", encoding="utf-8") as f:
  151. json.dump(api, f)
  152. else:
  153. json.dump(api, sys.stdout)