import os import sys from pathlib import Path def _is_path_ignored(path, root_dir, ignored_folders=None): """ Checks if a given path should be ignored. `path` should be an absolute path. `root_dir` should be the absolute path to the project root. `ignored_folders` is a list of paths relative to `root_dir`. """ if ignored_folders is None: return False try: relative_path = Path(path).relative_to(root_dir).as_posix() except ValueError: # The path is not within the root_dir, so we don't ignore it based on project rules. return False for ignored in ignored_folders: if relative_path.startswith(ignored): return True return False def find_files_by_extension(root_dir, extension, ignored_folders=None, project_root=None): """ Locates all files with a specific extension, excluding specified folders. """ if not extension.startswith('.'): print("Error: Extension must start with a dot (e.g., '.txt').", file=sys.stderr) return [] # If project_root isn't specified, assume it's the same as the search root. if project_root is None: project_root = root_dir found_files = [] for root, dirs, files in os.walk(root_dir): # Prune ignored directories to prevent descending into them dirs[:] = [d for d in dirs if not _is_path_ignored(os.path.join(root, d), project_root, ignored_folders)] for file in files: if file.endswith(extension): full_path = os.path.join(root, file) if not _is_path_ignored(full_path, project_root, ignored_folders): found_files.append(full_path) return found_files def replicate_directory_structure(source_root, target_root, ignored_folders=None, project_root=None): """ Copies a directory tree, excluding specified folders. """ source_root_path = Path(source_root) target_root_path = Path(target_root) if not source_root_path.is_dir(): print(f"Error: Source path '{source_root_path}' is not a valid directory.", file=sys.stderr) return if project_root is None: project_root = source_root for dirpath, dirnames, _ in os.walk(source_root): # Prune ignored directories dirnames[:] = [d for d in dirnames if not _is_path_ignored(os.path.join(dirpath, d), project_root, ignored_folders)] relative_path = Path(dirpath).relative_to(source_root_path) target_path = target_root_path / relative_path target_path.mkdir(parents=True, exist_ok=True) def create_guid_to_path_map(root_dir, ignored_folders=None): """ Creates a dictionary mapping GUIDs to their corresponding asset file paths, respecting an ignore list. """ guid_map = {} # Define scan directories relative to the project root relative_scan_dirs = ['Assets', 'Packages', 'Library/PackageCache'] for rel_dir in relative_scan_dirs: scan_dir = os.path.join(root_dir, rel_dir) if not os.path.isdir(scan_dir): continue meta_files = find_files_by_extension(scan_dir, '.meta', ignored_folders, project_root=root_dir) for meta_file_path in meta_files: asset_path = meta_file_path[:-5] guid = None is_folder = False try: with open(meta_file_path, 'r', encoding='utf-8') as f: for line in f: stripped_line = line.strip() if stripped_line.startswith('guid:'): guid = stripped_line.split(':')[1].strip() if stripped_line == 'folderAsset: yes': is_folder = True break if is_folder: continue if guid and guid not in guid_map: # Store path relative to the root_dir for consistency guid_map[guid] = Path(asset_path).relative_to(root_dir).as_posix() except Exception as e: print(f"Warning: Could not process meta file {meta_file_path}. {e}", file=sys.stderr) return guid_map if __name__ == '__main__': # Example usage for testing the module directly. import shutil test_root = Path('./tmp_test_root') test_source_dir = test_root / 'Assets' test_target_dir = Path('./tmp_target_for_testing') ignored_list = ['Assets/ThirdParty'] try: print("Setting up test directory structure...") (test_source_dir / 'scenes').mkdir(parents=True, exist_ok=True) (test_source_dir / 'prefabs').mkdir(parents=True, exist_ok=True) (test_source_dir / 'ThirdParty' / 'ignored').mkdir(parents=True, exist_ok=True) (test_source_dir / 'scenes' / 'level1.unity').touch() (test_source_dir / 'prefabs' / 'player.prefab').touch() (test_source_dir / 'ThirdParty' / 'ignored' / 'plugin.cs').touch() print(f"\n--- Testing find_files_by_extension with ignore list: {ignored_list} ---") # Test finding files (should ignore the .cs file) all_files = find_files_by_extension(str(test_source_dir), '.cs', ignored_folders=ignored_list, project_root=test_root) print(f"Found .cs files: {all_files}") assert len(all_files) == 0 unity_files = find_files_by_extension(str(test_source_dir), '.unity', ignored_folders=ignored_list, project_root=test_root) print(f"Found .unity files: {unity_files}") assert len(unity_files) == 1 print("\n--- Testing replicate_directory_structure with ignore list ---") replicate_directory_structure(str(test_source_dir), str(test_target_dir), ignored_folders=ignored_list, project_root=test_root) print("Checking replicated structure:") assert (test_target_dir / 'scenes').is_dir() assert not (test_target_dir / 'ThirdParty').exists() print("Replicated structure seems correct (ignored folder was skipped).") finally: if test_root.exists(): shutil.rmtree(test_root) if test_target_dir.exists(): shutil.rmtree(test_target_dir) print("\nCleaned up test directories.")