Skip to content

Instantly share code, notes, and snippets.

@Ryex
Last active February 21, 2025 00:14
Show Gist options
  • Save Ryex/c50ef490043259da76f3cc9bfe78a34e to your computer and use it in GitHub Desktop.
Save Ryex/c50ef490043259da76f3cc9bfe78a34e to your computer and use it in GitHub Desktop.
import sys
import pathlib
import re
import shlex
def main():
path = pathlib.Path(__file__).resolve().parent.parent
ignore_paths = [path.joinpath(p) for p in ["libraries", "program_info"]]
if len(sys.argv) > 1:
path = pathlib.Path(sys.argv[1]).resolve()
paths_to_process = [path]
while paths_to_process:
cur_path = paths_to_process.pop()
if (
cur_path not in ignore_paths
and cur_path.parent not in ignore_paths
and cur_path.parent.parent not in ignore_paths
):
new_paths = update_sources(cur_path)
if len(new_paths) > 0:
paths_to_process.extend(new_paths)
UPDATE_SOURCES_COMMENT = re.compile(
r"^(?P<indent>[ \t]*)#[ \t]*UPDATE SOURCES( (?P<recurse>RECURSE))?[ \t]*(?P<globs>.*)$"
)
SET_START = re.compile(
r"^(?P<indent>[ \t]*)[Ss][Ee][Tt]\((?P<var_name>[A-Za-z0-9_-]+)[ \t]*$"
)
SET_CMD = re.compile(
r"^(?P<indent>[ \t]*)[Ss][Ee][Tt]\((?P<var_name>[A-Za-z0-9_-]+)[ \t]*(?P<sources>.*)\)$"
)
CMD_END_LINE = re.compile(r"^(?P<indent>[ \t]*)\)[ \t]*$")
COMMENT_LINE = re.compile(r"^(?P<indent>[ \t]*)#")
ADD_SUBDIRECTORY_CMD = re.compile(
r"^[Aa][Dd][Dd]_[Ss][Uu][Bb][Dd][Ii][Rr][Ee][Cc][Tt][Oo][Rr][Yy]\((?P<path>.*)( EXCLUDE_FROM_ALL)?\)$"
)
def update_sources(path: pathlib.Path) -> list[pathlib.Path]:
addtional_paths: list[pathlib.Path] = []
file = path.joinpath("CMakeLists.txt")
if not file.exists():
return []
print(f"Processing: {file}")
content = file.read_text()
lines = content.splitlines()
output_lines: list[str] = []
in_sources = False
found: list[str] = []
updating_var: str = ""
sources: list[str] = []
have_sources = False
def process_line(line: str) -> str | None:
nonlocal in_sources
nonlocal have_sources
nonlocal sources
nonlocal found
nonlocal updating_var
nonlocal path
nonlocal addtional_paths
indent: str = ""
if (not in_sources) and (match := UPDATE_SOURCES_COMMENT.match(line)):
recurse = False
if match.group("recurse") is not None and len(match.group("recurse")) > 0:
recurse = True
globs = shlex.split(match.group("globs"))
glob_sources: list[pathlib.Path] = []
for glob in globs:
if not recurse:
glob_sources.extend(path.glob(glob))
else:
glob_sources.extend(path.rglob(glob))
sources = [
str(pathlib.PurePosixPath(source.relative_to(path)))
for source in glob_sources
]
have_sources = True
return line
elif have_sources and (match := SET_START.match(line)):
in_sources = True
updating_var = match.group("var_name")
found.clear()
return line
elif have_sources and (match := SET_CMD.match(line)):
updating_var = match.group("var_name")
indent = match.group("indent")
found.clear()
output_sources: list[str] = []
line_sources = shlex.split(match.group("sources"))
for line_source in line_sources:
if line_source in sources:
found.append(line_source)
output_sources.append(line_source)
for source in sources:
if source not in found:
output_sources.append(source)
output_source_lines = "\n".join(
[f"{indent} {source}" for source in output_sources]
)
return f"{indent}set({updating_var}\n{output_source_lines}\n)"
elif in_sources and (match := CMD_END_LINE.match(line)):
in_sources = False
indent = match.group("indent")
updating_var = ""
missing: list[str] = []
for source in sources:
if source not in found:
missing.append(source)
output_source_lines = "".join(
[f"{indent} {source}\n" for source in missing]
)
return f"{output_source_lines}{line}"
elif in_sources and (not COMMENT_LINE.match(line)) and not len(line) == 0:
source = line.strip()
if source in sources:
found.append(source)
return line
else:
return None
else:
if have_sources and not in_sources:
sources.clear()
have_sources = False
if match := ADD_SUBDIRECTORY_CMD.match(line):
addtional_paths.append(path.joinpath(match.group("path")))
return line
for line in lines:
res = process_line(line)
if res is not None:
output_lines.append(res)
output_lines.append("") # newline at file end
output = "\n".join(output_lines)
if output != content:
file.write_text(output, newline="\n")
print(f"UPDATE: {file} has changed")
return addtional_paths
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment