1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00
JUCE/doxygen/process_source_files.py

181 lines
7 KiB
Python

#!/usr/bin/env python
import os
import shutil
import re
import argparse
def get_curly_brace_scope_end(string, start_pos):
"""Given a string and the position of an opening curly brace, find the
position of the closing brace.
"""
if string[start_pos] != "{":
raise ValueError("string must have \"{\" at start pos")
string_end = len(string)
bracket_counter = 1
start_pos += 1
while start_pos < string_end:
if string[start_pos] == "{":
bracket_counter += 1
elif string[start_pos] == "}":
bracket_counter -= 1
if bracket_counter == 0:
return start_pos
start_pos += 1
return -1
def add_doxygen_group(path, group_name):
"""Add a Doxygen group to the file at 'path'.
Namespaces cause all kinds of problems, and we need to ensure that if
the classes in a source file are contained within a namespace then we
also put the @weakgroup inside the namespace.
"""
filename = os.path.basename(path)
if re.match(r"^juce_.*\.(h|dox)", filename):
group_definition_start = ("\r\n/** @weakgroup "
+ group_name
+ "\r\n * @{\r\n */\r\n")
group_definition_end = "\r\n/** @}*/\r\n"
with open(path, "r") as f:
content = f.read()
# Put the group definitions inside all namespaces.
namespace_regex = re.compile(r"\s+namespace\s+\S+\s+{")
match = namespace_regex.search(content)
while (match is not None):
namespace_end = get_curly_brace_scope_end(content, match.end() - 1)
if namespace_end == -1:
raise ValueError("error finding end of namespace "
+ match.group()
+ " in "
+ path)
content = (content[:match.end()]
+ group_definition_start
+ content[match.end():namespace_end]
+ group_definition_end
+ content[namespace_end:])
search_start = (namespace_end
+ len(group_definition_start)
+ len(group_definition_end))
match = namespace_regex.search(content, search_start)
with open(path, "w") as f:
f.write(group_definition_start)
f.write(content)
f.write(group_definition_end)
###############################################################################
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("source_dir",
help="the directory to search for source files")
parser.add_argument("dest_dir",
help="the directory in which to place processed files")
parser.add_argument("--subdirs",
help="if specified, only include these comma separated"
"subdirectories")
args = parser.parse_args()
# Get the list of JUCE modules to include.
if args.subdirs:
juce_modules = args.subdirs.split(",")
else:
juce_modules = []
for item in os.listdir(args.source_dir):
if os.path.isdir(os.path.join(args.source_dir, item)):
juce_modules.append(item)
# Copy the JUCE modules to the temporary directory, and process the source
# files.
module_definitions = []
for module_name in juce_modules:
# Copy the required modules.
original_module_dir = os.path.join(args.source_dir, module_name)
module_path = os.path.join(args.dest_dir, module_name)
shutil.copytree(original_module_dir, module_path)
# Parse the module header to get module information.
module_header = os.path.join(module_path, module_name + ".h")
with open(module_header, "r") as f:
content = f.read()
block_info_result = re.match(r".*BEGIN_JUCE_MODULE_DECLARATION"
"(.*)"
"END_JUCE_MODULE_DECLARATION.*",
content,
re.DOTALL)
detail_lines = []
for line in block_info_result.group(1).split("\n"):
stripped_line = line.strip()
if stripped_line:
result = re.match(r"^.*?description:\s*(.*)$", stripped_line)
if result:
short_description = result.group(1)
else:
detail_lines.append(stripped_line)
# The module header causes problems for Doxygen, so delete it.
os.remove(module_header)
# Create a Doxygen group definition for the module.
module_definiton = []
module_definiton.append("/** @defgroup {n} {n}".format(n=module_name))
module_definiton.append(" {d}".format(d=short_description))
module_definiton.append("")
for line in detail_lines:
module_definiton.append(" - {l}".format(l=line))
module_definiton.append("")
module_definiton.append(" @{")
module_definiton.append("*/")
# Create a list of the directories in the module that we can use as
# subgroups and create the Doxygen group hierarchy string.
dir_contents = os.listdir(module_path)
# Ignore "native" folders as these are excluded by doxygen.
try:
dir_contents.remove("native")
except ValueError:
pass
subdirs = []
for item in dir_contents:
if (os.path.isdir(os.path.join(module_path, item))):
subdirs.append(item)
module_groups = {}
for subdir in subdirs:
subgroup_name = "{n}-{s}".format(n=module_name, s=subdir)
module_groups[subgroup_name] = os.path.join(module_path, subdir)
module_definiton.append("")
module_definiton.append(
"/** @defgroup {tag} {n} */".format(tag=subgroup_name, n=subdir)
)
module_definiton.append("")
module_definiton.append("/** @} */")
module_definitions.append("\r\n".join(module_definiton))
# Put the top level files into the main group.
for filename in (set(dir_contents) - set(subdirs)):
add_doxygen_group(os.path.join(module_path, filename), module_name)
# Put subdirectory files into their respective groups.
for group_name in module_groups:
for dirpath, dirnames, filenames in os.walk(module_groups[group_name]):
for filename in filenames:
try:
add_doxygen_group(os.path.join(dirpath, filename), group_name)
except:
print("Error preprocessing " + filename)
continue
# Create an extra header file containing the module hierarchy.
with open(os.path.join(args.dest_dir, "juce_modules.dox"), "w") as f:
f.write("\r\n\r\n".join(module_definitions))