file_utils.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import os
  2. import sys
  3. from pathlib import Path
  4. def _is_path_ignored(path, root_dir, ignored_folders=None):
  5. """
  6. Checks if a given path should be ignored.
  7. `path` should be an absolute path.
  8. `root_dir` should be the absolute path to the project root.
  9. `ignored_folders` is a list of paths relative to `root_dir`.
  10. """
  11. if ignored_folders is None:
  12. return False
  13. try:
  14. relative_path = Path(path).relative_to(root_dir).as_posix()
  15. except ValueError:
  16. # The path is not within the root_dir, so we don't ignore it based on project rules.
  17. return False
  18. for ignored in ignored_folders:
  19. if relative_path.startswith(ignored):
  20. return True
  21. return False
  22. def find_files_by_extension(root_dir, extension, ignored_folders=None, project_root=None):
  23. """
  24. Locates all files with a specific extension, excluding specified folders.
  25. """
  26. if not extension.startswith('.'):
  27. print("Error: Extension must start with a dot (e.g., '.txt').", file=sys.stderr)
  28. return []
  29. # If project_root isn't specified, assume it's the same as the search root.
  30. if project_root is None:
  31. project_root = root_dir
  32. found_files = []
  33. for root, dirs, files in os.walk(root_dir):
  34. # Prune ignored directories to prevent descending into them
  35. dirs[:] = [d for d in dirs if not _is_path_ignored(os.path.join(root, d), project_root, ignored_folders)]
  36. for file in files:
  37. if file.endswith(extension):
  38. full_path = os.path.join(root, file)
  39. if not _is_path_ignored(full_path, project_root, ignored_folders):
  40. found_files.append(full_path)
  41. return found_files
  42. def replicate_directory_structure(source_root, target_root, ignored_folders=None, project_root=None):
  43. """
  44. Copies a directory tree, excluding specified folders.
  45. """
  46. source_root_path = Path(source_root)
  47. target_root_path = Path(target_root)
  48. if not source_root_path.is_dir():
  49. print(f"Error: Source path '{source_root_path}' is not a valid directory.", file=sys.stderr)
  50. return
  51. if project_root is None:
  52. project_root = source_root
  53. for dirpath, dirnames, _ in os.walk(source_root):
  54. # Prune ignored directories
  55. dirnames[:] = [d for d in dirnames if not _is_path_ignored(os.path.join(dirpath, d), project_root, ignored_folders)]
  56. relative_path = Path(dirpath).relative_to(source_root_path)
  57. target_path = target_root_path / relative_path
  58. target_path.mkdir(parents=True, exist_ok=True)
  59. def create_guid_to_path_map(root_dir, ignored_folders=None):
  60. """
  61. Creates a dictionary mapping GUIDs to their corresponding asset file paths,
  62. respecting an ignore list.
  63. """
  64. guid_map = {}
  65. # Define scan directories relative to the project root
  66. relative_scan_dirs = ['Assets', 'Packages', 'Library/PackageCache']
  67. for rel_dir in relative_scan_dirs:
  68. scan_dir = os.path.join(root_dir, rel_dir)
  69. if not os.path.isdir(scan_dir):
  70. continue
  71. meta_files = find_files_by_extension(scan_dir, '.meta', ignored_folders, project_root=root_dir)
  72. for meta_file_path in meta_files:
  73. asset_path = meta_file_path[:-5]
  74. guid = None
  75. is_folder = False
  76. try:
  77. with open(meta_file_path, 'r', encoding='utf-8') as f:
  78. for line in f:
  79. stripped_line = line.strip()
  80. if stripped_line.startswith('guid:'):
  81. guid = stripped_line.split(':')[1].strip()
  82. if stripped_line == 'folderAsset: yes':
  83. is_folder = True
  84. break
  85. if is_folder:
  86. continue
  87. if guid and guid not in guid_map:
  88. # Store path relative to the root_dir for consistency
  89. guid_map[guid] = Path(asset_path).relative_to(root_dir).as_posix()
  90. except Exception as e:
  91. print(f"Warning: Could not process meta file {meta_file_path}. {e}", file=sys.stderr)
  92. return guid_map
  93. if __name__ == '__main__':
  94. # Example usage for testing the module directly.
  95. import shutil
  96. test_root = Path('./tmp_test_root')
  97. test_source_dir = test_root / 'Assets'
  98. test_target_dir = Path('./tmp_target_for_testing')
  99. ignored_list = ['Assets/ThirdParty']
  100. try:
  101. print("Setting up test directory structure...")
  102. (test_source_dir / 'scenes').mkdir(parents=True, exist_ok=True)
  103. (test_source_dir / 'prefabs').mkdir(parents=True, exist_ok=True)
  104. (test_source_dir / 'ThirdParty' / 'ignored').mkdir(parents=True, exist_ok=True)
  105. (test_source_dir / 'scenes' / 'level1.unity').touch()
  106. (test_source_dir / 'prefabs' / 'player.prefab').touch()
  107. (test_source_dir / 'ThirdParty' / 'ignored' / 'plugin.cs').touch()
  108. print(f"\n--- Testing find_files_by_extension with ignore list: {ignored_list} ---")
  109. # Test finding files (should ignore the .cs file)
  110. all_files = find_files_by_extension(str(test_source_dir), '.cs', ignored_folders=ignored_list, project_root=test_root)
  111. print(f"Found .cs files: {all_files}")
  112. assert len(all_files) == 0
  113. unity_files = find_files_by_extension(str(test_source_dir), '.unity', ignored_folders=ignored_list, project_root=test_root)
  114. print(f"Found .unity files: {unity_files}")
  115. assert len(unity_files) == 1
  116. print("\n--- Testing replicate_directory_structure with ignore list ---")
  117. replicate_directory_structure(str(test_source_dir), str(test_target_dir), ignored_folders=ignored_list, project_root=test_root)
  118. print("Checking replicated structure:")
  119. assert (test_target_dir / 'scenes').is_dir()
  120. assert not (test_target_dir / 'ThirdParty').exists()
  121. print("Replicated structure seems correct (ignored folder was skipped).")
  122. finally:
  123. if test_root.exists():
  124. shutil.rmtree(test_root)
  125. if test_target_dir.exists():
  126. shutil.rmtree(test_target_dir)
  127. print("\nCleaned up test directories.")