Solution to "no software to install" PKG error

Discussion in 'Mac / Hackintosh' started by NixMix, Nov 17, 2025 at 10:34 PM.

  1. NixMix

    NixMix Newbie

    Joined:
    Jun 8, 2025
    Messages:
    1
    Likes Received:
    0
    Hi

    Here's a little Python script for anyone getting the error about "no software to install" when trying to install a multi-file PKG VST on MacOS.

    The script extracts each PKG file and merges the result into the correct file structure.

    Presuming you've called your Python script file "extract_pkg.py":

    Usage:
    python3 extract_pkg.py "/path/to/folder/with/pkg/files"

    Example:
    python3 extract_pkg.py "/Volumes/HDD/VST Libraries/SomeLibrary"

    This will create /Volumes/HDD/VST Libraries/SomeLibrary/extracted/ with all the merged payload contents.

    Here's the code. Enjoy.


    #!/usr/bin/env python3
    """
    Simple PKG Extractor and Merger
    Usage: python3 extract_pkg.py /path/to/folder/with/pkg/files
    """

    import sys
    import os
    import subprocess
    import shutil
    from pathlib import Path


    def extract_and_merge_pkgs(source_folder):
    """
    Extract all .pkg files in source_folder and merge their payloads
    into an 'extracted' subfolder.
    """
    source_path = Path(source_folder).resolve()

    if not source_path.exists():
    print(f"Error: Folder does not exist: {source_path}")
    return False

    # Find all .pkg files
    pkg_files = sorted(source_path.glob("*.pkg"))

    if not pkg_files:
    print(f"Error: No .pkg files found in {source_path}")
    return False

    print(f"Found {len(pkg_files)} .pkg file(s)")
    print("=" * 60)

    # Create temp and output directories
    temp_dir = source_path / "temp_extraction"
    output_dir = source_path / "extracted"

    # Clean up if they exist
    if temp_dir.exists():
    print("Cleaning up previous temp directory...")
    shutil.rmtree(temp_dir)

    if output_dir.exists():
    print("Cleaning up previous extracted directory...")
    shutil.rmtree(output_dir)

    temp_dir.mkdir()
    output_dir.mkdir()

    print("\nExtracting packages...")
    print("=" * 60)

    # Extract each package
    extracted_count = 0
    for i, pkg_file in enumerate(pkg_files, 1):
    print(f"[{i}/{len(pkg_files)}] {pkg_file.name}")

    # Create unique temp directory for this package
    pkg_temp = temp_dir / f"pkg_{i}"
    pkg_temp.mkdir()

    # Extract using pkgutil
    try:
    result = subprocess.run(
    ["pkgutil", "--expand-full", str(pkg_file), str(pkg_temp)],
    capture_output=True,
    text=True
    )

    # Check if extraction was successful (payload exists)
    payload_dir = pkg_temp / "Payload"
    if payload_dir.exists():
    print(f" ✓ Extracted successfully")
    extracted_count += 1
    else:
    print(f" ✗ No Payload found (probably installer)")

    except Exception as e:
    print(f" ✗ Error: {e}")

    print("\n" + "=" * 60)
    print("Merging payloads...")
    print("=" * 60)

    # Merge all payload folders
    merged_count = 0
    for pkg_temp in sorted(temp_dir.glob("pkg_*")):
    payload_dir = pkg_temp / "Payload"
    if payload_dir.exists():
    merged_count += 1
    print(f"Merging from {pkg_temp.name}...")

    # Copy contents using ditto (preserves permissions/metadata)
    for item in payload_dir.iterdir():
    try:
    subprocess.run(
    ["ditto", str(item), str(output_dir / item.name)],
    check=True,
    capture_output=True
    )
    except subprocess.CalledProcessError as e:
    print(f" Warning: Error copying {item.name}: {e}")

    # Clean up temp directory
    print("\nCleaning up temp files...")
    shutil.rmtree(temp_dir)

    print("\n" + "=" * 60)
    print("Extraction Complete!")
    print("=" * 60)
    print(f"Processed: {len(pkg_files)} packages")
    print(f"Extracted: {extracted_count} packages with payloads")
    print(f"Merged: {merged_count} payloads")
    print(f"\nOutput: {output_dir}")

    # Show contents
    print("\nExtracted contents:")
    for item in sorted(output_dir.iterdir()):
    if item.is_file():
    size = item.stat().st_size
    size_str = f"{size:,} bytes"
    if size > 1024 * 1024:
    size_str = f"{size / (1024*1024):.1f} MB"
    print(f" {item.name} ({size_str})")
    else:
    print(f" {item.name}/ (folder)")

    return True


    if __name__ == "__main__":
    if len(sys.argv) != 2:
    print("Usage: python3 extract_pkg.py /path/to/folder/with/pkg/files")
    sys.exit(1)

    source_folder = sys.argv[1]
    success = extract_and_merge_pkgs(source_folder)

    sys.exit(0 if success else 1)
     
    Last edited by a moderator: Nov 17, 2025 at 10:40 PM
  2.  
Loading...
Loading...