From 360184b5d6a1b11cd8212575e6554f3be3a12cef Mon Sep 17 00:00:00 2001 From: MentatBot <160964065+MentatBot@users.noreply.github.com> Date: Sun, 13 Apr 2025 04:32:46 +0000 Subject: [PATCH 1/5] Fix dependency script to handle project file structure variations --- dep-bdg.json | 9 +++ scripts/add_dependency.py | 112 +++++++++++++++++++++++++++++---- scripts/analyze_project.py | 125 +++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 12 deletions(-) create mode 100644 scripts/analyze_project.py diff --git a/dep-bdg.json b/dep-bdg.json index 4050ac4..6487ff8 100644 --- a/dep-bdg.json +++ b/dep-bdg.json @@ -16,5 +16,14 @@ "version": "4.2.2" }, "products": ["KeychainAccess"] + }, + { + "name": "Lottie", + "url": "https://github.com/airbnb/lottie-ios.git", + "requirement": { + "kind": "upToNextMajorVersion", + "minimumVersion": "4.3.3" + }, + "products": ["Lottie"] } ] diff --git a/scripts/add_dependency.py b/scripts/add_dependency.py index 9d94e0c..98d31be 100644 --- a/scripts/add_dependency.py +++ b/scripts/add_dependency.py @@ -153,13 +153,42 @@ def add_to_project_file(project_content, dependency): insert_position = product_dependency_section_end.start() project_content = project_content[:insert_position] + product_dependencies + project_content[insert_position:] - # Find the PBXFrameworksBuildPhase section - frameworks_section = re.search(r'\/\* Frameworks \*\/,\s*\{\s*isa = PBXFrameworksBuildPhase;[^;]*files = \(\s*([^;]*)\s*\);', project_content) - if not frameworks_section: - raise Exception("Could not find PBXFrameworksBuildPhase section") + # Find the PBXFrameworksBuildPhase section using a more robust approach + # First, find the begin marker for the PBXFrameworksBuildPhase section + frameworks_begin = re.search(r'\/\* Begin PBXFrameworksBuildPhase section \*\/', project_content) + frameworks_end = re.search(r'\/\* End PBXFrameworksBuildPhase section \*\/', project_content) + + if not frameworks_begin or not frameworks_end: + print("Project structure diagnostic information:") + print(f"Found Begin PBXFrameworksBuildPhase: {frameworks_begin is not None}") + print(f"Found End PBXFrameworksBuildPhase: {frameworks_end is not None}") + # Look for similar sections to help with debugging + other_sections = re.findall(r'\/\* Begin ([A-Za-z]+) section \*\/', project_content) + print(f"Found these sections: {', '.join(other_sections)}") + raise Exception("Could not find PBXFrameworksBuildPhase section markers") + + # Extract the frameworks section content + frameworks_section_content = project_content[frameworks_begin.end():frameworks_end.start()] + + # Find the build phase for the main target + # This is more flexible than the previous approach + build_phase_match = re.search(r'([A-F0-9]+)\s+\/\*\s*Frameworks\s*\*\/\s+=\s+\{\s*isa\s+=\s+PBXFrameworksBuildPhase;[^{]*files\s+=\s+\(\s*(.*?)\s*\);', + frameworks_section_content, re.DOTALL) + + if not build_phase_match: + # Alternative pattern, try a more general match + build_phase_match = re.search(r'([A-F0-9]+).*?isa\s+=\s+PBXFrameworksBuildPhase;.*?files\s+=\s+\(\s*(.*?)\s*\);', + frameworks_section_content, re.DOTALL) + + if not build_phase_match: + print("Failed to find framework build phase. Section content:") + print(frameworks_section_content[:500] + "..." if len(frameworks_section_content) > 500 else frameworks_section_content) + raise Exception("Could not find PBXFrameworksBuildPhase files section") + + build_phase_id = build_phase_match.group(1) + frameworks_list = build_phase_match.group(2) # Add the products to the frameworks build phase - frameworks_list = frameworks_section.group(1) frameworks_entries = "" for product_id, product in product_ids: @@ -173,11 +202,27 @@ def add_to_project_file(project_content, dependency): # If no frameworks yet, just add the new ones new_frameworks_list = frameworks_entries.lstrip() - project_content = re.sub( - r'(\/\* Frameworks \*\/,\s*\{\s*isa = PBXFrameworksBuildPhase;[^;]*files = \()\s*([^;]*)\s*(\);)', - lambda m: f"{m.group(1)}{new_frameworks_list}{m.group(3)}", - project_content - ) + # Replace the old files list with the new one + # This is more robust as it matches the exact pattern we found + old_files_section = f"files = (\n\t\t\t\t{frameworks_list}\n\t\t\t);" + new_files_section = f"files = (\n\t\t\t\t{new_frameworks_list}\n\t\t\t);" + + project_content = project_content.replace(old_files_section, new_files_section) + + # If the exact replacement failed, try a more flexible approach + if old_files_section not in project_content: + # Create a more flexible pattern with a regex + pattern = re.compile(r'(files\s+=\s+\()(\s*.*?\s*)(\);)', re.DOTALL) + # Replace within the PBXFrameworksBuildPhase section + section_start = project_content.find("/* Begin PBXFrameworksBuildPhase section */") + section_end = project_content.find("/* End PBXFrameworksBuildPhase section */", section_start) + + if section_start != -1 and section_end != -1: + section = project_content[section_start:section_end] + updated_section = pattern.sub(lambda m: f"{m.group(1)}\n\t\t\t\t{new_frameworks_list}\n\t\t\t{m.group(3)}", section) + project_content = project_content[:section_start] + updated_section + project_content[section_end:] + else: + raise Exception("Could not find PBXFrameworksBuildPhase section boundaries for flexible replacement") # Add PBXBuildFile entries for the products build_file_section_end = re.search(r'\/\* End PBXBuildFile section \*\/', project_content) @@ -291,6 +336,8 @@ def main(): parser = argparse.ArgumentParser(description='Add Swift Package Manager dependencies to Xcode project') parser.add_argument('dependency_file', nargs='?', default=DEFAULT_DEPENDENCY_FILE, help=f'JSON file containing dependencies to add (defaults to {DEFAULT_DEPENDENCY_FILE})') + parser.add_argument('--debug', action='store_true', + help='Enable debug mode with more detailed output') args = parser.parse_args() try: @@ -317,16 +364,46 @@ def main(): # Read the current project file and package resolved project_content = read_project_file() + + # Debug: Print project file structure if debug mode is enabled + if args.debug: + print("\n--- Project Structure Analysis ---") + sections = re.findall(r'\/\* Begin ([A-Za-z]+) section \*\/', project_content) + print(f"Detected sections: {', '.join(sections)}") + + # Look for PBXFrameworksBuildPhase specifically + framework_section = re.search(r'\/\* Begin PBXFrameworksBuildPhase section \*\/(.*?)\/\* End PBXFrameworksBuildPhase section \*\/', + project_content, re.DOTALL) + if framework_section: + print("\nPBXFrameworksBuildPhase section found:") + print(framework_section.group(1)[:300] + "..." if len(framework_section.group(1)) > 300 else framework_section.group(1)) + else: + print("\nPBXFrameworksBuildPhase section NOT found!") + package_data = read_package_resolved() # Process each dependency for dependency in dependencies: print(f"Adding dependency: {dependency['name']}") # Update the project file - project_content = add_to_project_file(project_content, dependency) + try: + project_content = add_to_project_file(project_content, dependency) + except Exception as e: + print(f"Error adding {dependency['name']} to project file: {str(e)}") + if args.debug: + import traceback + traceback.print_exc() + raise # Update the package resolved - package_data = add_to_package_resolved(package_data, dependency) + try: + package_data = add_to_package_resolved(package_data, dependency) + except Exception as e: + print(f"Error adding {dependency['name']} to Package.resolved: {str(e)}") + if args.debug: + import traceback + traceback.print_exc() + raise # Write the updated files write_project_file(project_content) @@ -339,6 +416,17 @@ def main(): print(f"Error adding dependencies: {e}") import traceback traceback.print_exc() + + # Print additional diagnostic info in case of error + print("\n--- Diagnostic Information ---") + print(f"Python version: {sys.version}") + print(f"Operating system: {os.name} - {sys.platform}") + print(f"Working directory: {os.getcwd()}") + print(f"File exists - project.pbxproj: {os.path.exists(PROJECT_FILE)}") + print(f"File exists - Package.resolved: {os.path.exists(PACKAGE_RESOLVED_FILE)}") + print(f"File exists - {args.dependency_file}: {os.path.exists(args.dependency_file)}") + + sys.exit(1) # Exit with error code if __name__ == '__main__': main() diff --git a/scripts/analyze_project.py b/scripts/analyze_project.py new file mode 100644 index 0000000..0a1f3c9 --- /dev/null +++ b/scripts/analyze_project.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +A diagnostic tool to analyze the structure of an Xcode project file. +This tool helps identify the correct patterns for the dependency scripts. + +Usage: + python3 analyze_project.py [project_file] +""" + +import argparse +import re +import os +import sys +import json + +DEFAULT_PROJECT_FILE = 'backdoor.xcodeproj/project.pbxproj' + +def analyze_project(project_file): + """Analyze the structure of the Xcode project file.""" + try: + with open(project_file, 'r') as f: + content = f.read() + except Exception as e: + print(f"Error reading project file: {e}") + return 1 + + print(f"\n=== Project File Analysis: {project_file} ===") + print(f"File size: {len(content)} bytes") + + # Find all section markers + begin_sections = re.findall(r'\/\* Begin ([A-Za-z]+) section \*\/', content) + end_sections = re.findall(r'\/\* End ([A-Za-z]+) section \*\/', content) + + print(f"\n=== Detected {len(begin_sections)} section types ===") + for section in sorted(set(begin_sections)): + print(f"- {section}") + + if set(begin_sections) != set(end_sections): + print("\nWARNING: Mismatched begin/end section markers!") + only_begin = set(begin_sections) - set(end_sections) + only_end = set(end_sections) - set(begin_sections) + + if only_begin: + print(f"Sections with only BEGIN marker: {', '.join(only_begin)}") + if only_end: + print(f"Sections with only END marker: {', '.join(only_end)}") + + # Analyze PBXFrameworksBuildPhase section specifically + print("\n=== PBXFrameworksBuildPhase Analysis ===") + frameworks_match = re.search(r'\/\* Begin PBXFrameworksBuildPhase section \*\/(.*?)\/\* End PBXFrameworksBuildPhase section \*\/', + content, re.DOTALL) + + if frameworks_match: + frameworks_section = frameworks_match.group(1) + print(f"Section length: {len(frameworks_section)} bytes") + + # Find all framework build phases + build_phases = re.findall(r'([A-F0-9]+)\s+\/\*\s*([^*]+)\s*\*\/\s+=\s+\{\s*isa\s+=\s+PBXFrameworksBuildPhase;(.*?)};', + frameworks_section, re.DOTALL) + + print(f"Found {len(build_phases)} framework build phases:") + + for i, (phase_id, phase_name, phase_content) in enumerate(build_phases): + print(f"\n--- Build Phase {i+1}: {phase_name.strip()} ({phase_id}) ---") + + # Extract attributes + attributes = re.findall(r'(\w+)\s*=\s*([^;]+);', phase_content) + for attr_name, attr_value in attributes: + if attr_name == "files": + file_entries = re.findall(r'([A-F0-9]+)\s+\/\*\s*([^*]+)\*\/', attr_value) + print(f" {attr_name} = ({len(file_entries)} entries)") + for file_id, file_name in file_entries[:5]: # Show first 5 + print(f" - {file_name.strip()} ({file_id})") + if len(file_entries) > 5: + print(f" ... and {len(file_entries) - 5} more") + else: + print(f" {attr_name} = {attr_value.strip()}") + else: + print("WARNING: Could not find PBXFrameworksBuildPhase section!") + + # Try to find similar sections + similar_sections = [section for section in begin_sections if "Phase" in section] + if similar_sections: + print(f"Found these similar phase sections: {', '.join(similar_sections)}") + + # Sample the first one + for section in similar_sections: + section_match = re.search(f'\/\* Begin {section} section \*\/(.*?)\/\* End {section} section \*\/', + content, re.DOTALL) + if section_match: + print(f"\nSample from {section} section (first 200 chars):") + print(section_match.group(1)[:200] + "...") + break + + # Look at PBXBuildFile section for framework entries + print("\n=== Framework Build File Entries ===") + buildfile_match = re.search(r'\/\* Begin PBXBuildFile section \*\/(.*?)\/\* End PBXBuildFile section \*\/', + content, re.DOTALL) + + if buildfile_match: + buildfile_section = buildfile_match.group(1) + framework_entries = re.findall(r'([A-F0-9]+)\s+\/\*\s*([^*]+in Frameworks)\s*\*\/\s+=\s+\{(.*?)\};', + buildfile_section, re.DOTALL) + + print(f"Found {len(framework_entries)} framework build file entries:") + for i, (file_id, file_name, file_content) in enumerate(framework_entries[:10]): # Show first 10 + print(f" - {file_name.strip()} ({file_id})") + + if len(framework_entries) > 10: + print(f" ... and {len(framework_entries) - 10} more") + else: + print("WARNING: Could not find PBXBuildFile section!") + + return 0 + +def main(): + parser = argparse.ArgumentParser(description='Analyze Xcode project structure for diagnostics') + parser.add_argument('project_file', nargs='?', default=DEFAULT_PROJECT_FILE, + help=f'Path to project.pbxproj file (defaults to {DEFAULT_PROJECT_FILE})') + args = parser.parse_args() + + return analyze_project(args.project_file) + +if __name__ == '__main__': + sys.exit(main()) From e267775ff4ce033e542ea2c558f899ddcbd6d463 Mon Sep 17 00:00:00 2001 From: MentatBot <160964065+MentatBot@users.noreply.github.com> Date: Sun, 13 Apr 2025 04:33:25 +0000 Subject: [PATCH 2/5] Enable debug mode in GitHub workflow for better error reporting --- .github/workflows/update-dependencies.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 836f4cb..9d4b753 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -43,7 +43,8 @@ jobs: - name: Update dependencies run: | # Run the dependency script with the default dep-bdg.json file - python scripts/add_dependency.py + # Include debug flag for detailed output if there are errors + python scripts/add_dependency.py --debug - name: Commit changes if any run: | From 154f0dfc05110e9c882a50473cf8d914305a3a9f Mon Sep 17 00:00:00 2001 From: stayaway245 Date: Sun, 13 Apr 2025 00:56:24 -0400 Subject: [PATCH 3/5] Update dep-bdg.json --- dep-bdg.json | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/dep-bdg.json b/dep-bdg.json index 6487ff8..992239e 100644 --- a/dep-bdg.json +++ b/dep-bdg.json @@ -1,21 +1,3 @@ -[ - { - "name": "SDWebImage", - "url": "https://github.com/SDWebImage/SDWebImage.git", - "requirement": { - "kind": "upToNextMajorVersion", - "minimumVersion": "5.18.3" - }, - "products": ["SDWebImage", "SDWebImageMapKit"] - }, - { - "name": "KeychainAccess", - "url": "https://github.com/kishikawakatsumi/KeychainAccess.git", - "requirement": { - "kind": "exactVersion", - "version": "4.2.2" - }, - "products": ["KeychainAccess"] }, { "name": "Lottie", From 518138b841ceeb9c48bfd1b077cd53cea9cde22c Mon Sep 17 00:00:00 2001 From: MentatBot <160964065+MentatBot@users.noreply.github.com> Date: Sun, 13 Apr 2025 04:58:27 +0000 Subject: [PATCH 4/5] Fix missing sys import in add_dependency.py --- scripts/add_dependency.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/add_dependency.py b/scripts/add_dependency.py index 98d31be..d9430a5 100644 --- a/scripts/add_dependency.py +++ b/scripts/add_dependency.py @@ -25,6 +25,7 @@ import json import os import re +import sys import uuid import subprocess from datetime import datetime From 050cd9957772765e429e05d1fbc52b1952dbe801 Mon Sep 17 00:00:00 2001 From: MentatBot <160964065+MentatBot@users.noreply.github.com> Date: Sun, 13 Apr 2025 05:00:36 +0000 Subject: [PATCH 5/5] Remove unwanted dependencies, keep only Lottie --- dep-bdg.json | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/dep-bdg.json b/dep-bdg.json index d3c1294..df678e2 100644 --- a/dep-bdg.json +++ b/dep-bdg.json @@ -1,22 +1,4 @@ [ - { - "name": "SDWebImage", - "url": "https://github.com/SDWebImage/SDWebImage.git", - "requirement": { - "kind": "upToNextMajorVersion", - "minimumVersion": "5.18.3" - }, - "products": ["SDWebImage", "SDWebImageMapKit"] - }, - { - "name": "KeychainAccess", - "url": "https://github.com/kishikawakatsumi/KeychainAccess.git", - "requirement": { - "kind": "exactVersion", - "version": "4.2.2" - }, - "products": ["KeychainAccess"] - }, { "name": "Lottie", "url": "https://github.com/airbnb/lottie-spm.git",