The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
7.0KB

  1. #!/usr/bin/env python
  2. import os
  3. import shutil
  4. import re
  5. import argparse
  6. def get_curly_brace_scope_end(string, start_pos):
  7. """Given a string and the position of an opening curly brace, find the
  8. position of the closing brace.
  9. """
  10. if string[start_pos] != "{":
  11. raise ValueError("string must have \"{\" at start pos")
  12. string_end = len(string)
  13. bracket_counter = 1
  14. start_pos += 1
  15. while start_pos < string_end:
  16. if string[start_pos] == "{":
  17. bracket_counter += 1
  18. elif string[start_pos] == "}":
  19. bracket_counter -= 1
  20. if bracket_counter == 0:
  21. return start_pos
  22. start_pos += 1
  23. return -1
  24. def add_doxygen_group(path, group_name):
  25. """Add a Doxygen group to the file at 'path'.
  26. Namespaces cause all kinds of problems, and we need to ensure that if
  27. the classes in a source file are contained within a namespace then we
  28. also put the @weakgroup inside the namespace.
  29. """
  30. filename = os.path.basename(path)
  31. if re.match(r"^juce_.*\.(h|dox)", filename):
  32. group_definition_start = ("\r\n/** @weakgroup "
  33. + group_name
  34. + "\r\n * @{\r\n */\r\n")
  35. group_definition_end = "\r\n/** @}*/\r\n"
  36. with open(path, "r") as f:
  37. content = f.read()
  38. # Put the group definitions inside all namespaces.
  39. namespace_regex = re.compile(r"\s+namespace\s+\S+\s+{")
  40. match = namespace_regex.search(content)
  41. while (match is not None):
  42. namespace_end = get_curly_brace_scope_end(content, match.end() - 1)
  43. if namespace_end == -1:
  44. raise ValueError("error finding end of namespace "
  45. + match.group()
  46. + " in "
  47. + path)
  48. content = (content[:match.end()]
  49. + group_definition_start
  50. + content[match.end():namespace_end]
  51. + group_definition_end
  52. + content[namespace_end:])
  53. search_start = (namespace_end
  54. + len(group_definition_start)
  55. + len(group_definition_end))
  56. match = namespace_regex.search(content, search_start)
  57. with open(path, "w") as f:
  58. f.write(group_definition_start)
  59. f.write(content)
  60. f.write(group_definition_end)
  61. ###############################################################################
  62. if __name__ == "__main__":
  63. parser = argparse.ArgumentParser()
  64. parser.add_argument("source_dir",
  65. help="the directory to search for source files")
  66. parser.add_argument("dest_dir",
  67. help="the directory in which to place processed files")
  68. parser.add_argument("--subdirs",
  69. help="if specified, only include these comma separated"
  70. "subdirectories")
  71. args = parser.parse_args()
  72. # Get the list of JUCE modules to include.
  73. if args.subdirs:
  74. juce_modules = args.subdirs.split(",")
  75. else:
  76. juce_modules = []
  77. for item in os.listdir(args.source_dir):
  78. if os.path.isdir(os.path.join(args.source_dir, item)):
  79. juce_modules.append(item)
  80. # Copy the JUCE modules to the temporary directory, and process the source
  81. # files.
  82. module_definitions = []
  83. for module_name in juce_modules:
  84. # Copy the required modules.
  85. original_module_dir = os.path.join(args.source_dir, module_name)
  86. module_path = os.path.join(args.dest_dir, module_name)
  87. shutil.copytree(original_module_dir, module_path)
  88. # Parse the module header to get module information.
  89. module_header = os.path.join(module_path, module_name + ".h")
  90. with open(module_header, "r") as f:
  91. content = f.read()
  92. block_info_result = re.match(r".*BEGIN_JUCE_MODULE_DECLARATION"
  93. "(.*)"
  94. "END_JUCE_MODULE_DECLARATION.*",
  95. content,
  96. re.DOTALL)
  97. detail_lines = []
  98. for line in block_info_result.group(1).split("\n"):
  99. stripped_line = line.strip()
  100. if stripped_line:
  101. result = re.match(r"^.*?description:\s*(.*)$", stripped_line)
  102. if result:
  103. short_description = result.group(1)
  104. else:
  105. detail_lines.append(stripped_line)
  106. # The module header causes problems for Doxygen, so delete it.
  107. os.remove(module_header)
  108. # Create a Doxygen group definition for the module.
  109. module_definiton = []
  110. module_definiton.append("/** @defgroup {n} {n}".format(n=module_name))
  111. module_definiton.append(" {d}".format(d=short_description))
  112. module_definiton.append("")
  113. for line in detail_lines:
  114. module_definiton.append(" - {l}".format(l=line))
  115. module_definiton.append("")
  116. module_definiton.append(" @{")
  117. module_definiton.append("*/")
  118. # Create a list of the directories in the module that we can use as
  119. # subgroups and create the Doxygen group hierarchy string.
  120. dir_contents = os.listdir(module_path)
  121. # Ignore "native" folders as these are excluded by doxygen.
  122. try:
  123. dir_contents.remove("native")
  124. except ValueError:
  125. pass
  126. subdirs = []
  127. for item in dir_contents:
  128. if (os.path.isdir(os.path.join(module_path, item))):
  129. subdirs.append(item)
  130. module_groups = {}
  131. for subdir in subdirs:
  132. subgroup_name = "{n}-{s}".format(n=module_name, s=subdir)
  133. module_groups[subgroup_name] = os.path.join(module_path, subdir)
  134. module_definiton.append("")
  135. module_definiton.append(
  136. "/** @defgroup {tag} {n} */".format(tag=subgroup_name, n=subdir)
  137. )
  138. module_definiton.append("")
  139. module_definiton.append("/** @} */")
  140. module_definitions.append("\r\n".join(module_definiton))
  141. # Put the top level files into the main group.
  142. for filename in (set(dir_contents) - set(subdirs)):
  143. add_doxygen_group(os.path.join(module_path, filename), module_name)
  144. # Put subdirectory files into their respective groups.
  145. for group_name in module_groups:
  146. for dirpath, dirnames, filenames in os.walk(module_groups[group_name]):
  147. for filename in filenames:
  148. try:
  149. add_doxygen_group(os.path.join(dirpath, filename), group_name)
  150. except:
  151. print("Error preprocessing " + filename)
  152. continue
  153. # Create an extra header file containing the module hierarchy.
  154. with open(os.path.join(args.dest_dir, "juce_modules.dox"), "w") as f:
  155. f.write("\r\n\r\n".join(module_definitions))