import os import re def strip_single_line_comments(code): # Удалим // ... до конца строки return re.sub(r'//.*?$', '', code, flags=re.MULTILINE) def read_file_try_encodings(filepath): if not os.path.isfile(filepath): # Файл не существует — просто вернуть пустую строку или None return "", None for enc in ['utf-8', 'cp1251']: try: with open(filepath, 'r', encoding=enc) as f: content = f.read() content = strip_single_line_comments(content) return content, enc except UnicodeDecodeError: continue raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251") def find_all_includes_recursive(c_files, include_dirs, processed_files=None): """ Рекурсивно ищет все include-файлы начиная с заданных c_files. Возвращает множество ПОЛНЫХ ПУТЕЙ к найденным include-файлам. include_dirs — список директорий, в которых ищем include-файлы. processed_files — множество уже обработанных файлов (для избежания циклов). """ if processed_files is None: processed_files = set() include_files = set() include_pattern = re.compile(r'#include\s+"([^"]+)"') for cfile in c_files: norm_path = os.path.normpath(cfile) if norm_path in processed_files: continue processed_files.add(norm_path) content, _ = read_file_try_encodings(cfile) if content is None: continue includes = include_pattern.findall(content) for inc in includes: # Ищем полный путь к include-файлу в include_dirs inc_full_path = None for dir_ in include_dirs: candidate = os.path.normpath(os.path.join(dir_, inc)) if os.path.isfile(candidate): inc_full_path = os.path.abspath(candidate) break if inc_full_path: include_files.add(inc_full_path) # Рекурсивный обход вложенных includes if inc_full_path not in processed_files: nested_includes = find_all_includes_recursive( [inc_full_path], include_dirs, processed_files ) include_files.update(nested_includes) return include_files def parse_makefile(makefile_path, proj_path): makefile_dir = os.path.dirname(makefile_path) project_root = proj_path with open(makefile_path, 'r', encoding='utf-8') as f: lines = f.readlines() objs_lines = [] collecting = False for line in lines: stripped = line.strip() if stripped.startswith("ORDERED_OBJS") and "+=" in stripped: parts = stripped.split("\\") first_part = parts[0] idx = first_part.find("+=") tail = first_part[idx+2:].strip() if tail: objs_lines.append(tail) collecting = True if len(parts) > 1: for p in parts[1:]: p = p.strip() if p: objs_lines.append(p) continue if collecting: if stripped.endswith("\\"): objs_lines.append(stripped[:-1].strip()) else: objs_lines.append(stripped) collecting = False objs_str = ' '.join(objs_lines) objs_str = re.sub(r"\$\([^)]+\)", "", objs_str) objs = [] for part in objs_str.split(): part = part.strip() if part.startswith('"') and part.endswith('"'): part = part[1:-1] if part: objs.append(part) c_files = [] include_dirs = set() for obj_path in objs: if "DebugTools" in obj_path: continue if "v120" in obj_path: continue if "v100" in obj_path: continue if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"): rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/") else: rel_path = obj_path abs_path = os.path.normpath(os.path.join(project_root, rel_path)) root, ext = os.path.splitext(abs_path) if ext.lower() == ".obj": c_path = root + ".c" else: c_path = abs_path # Проверяем существование файла, если нет — пропускаем if not os.path.isfile(c_path): continue # Сохраняем только .c файлы if c_path.lower().endswith(".c"): c_files.append(c_path) dir_path = os.path.dirname(c_path) if dir_path and "DebugTools" not in dir_path: include_dirs.add(dir_path) h_files = find_all_includes_recursive(c_files, include_dirs) return sorted(c_files), sorted(h_files), sorted(include_dirs)