177 lines
6.3 KiB
Python
177 lines
6.3 KiB
Python
"""
|
||||
|
|
install_dependencies.py
|
|||
|
|
========================
|
|||
|
|
Installs all Python dependencies for the PST Email Extractor and
|
|||
|
|
verifies that pypff (libpff) is importable.
|
|||
|
|
|
|||
|
|
Run once before first use:
|
|||
|
|
python install_dependencies.py
|
|||
|
|
|
|||
|
|
Platform notes
|
|||
|
|
--------------
|
|||
|
|
* Linux / macOS – pypff is usually available via pip (libpff-python) or a
|
|||
|
|
system package manager.
|
|||
|
|
* Windows – pypff typically requires a pre-built wheel or compiling
|
|||
|
|
libpff from source. See the README for details.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import subprocess
|
|||
|
|
import platform
|
|||
|
|
from importlib.util import find_spec
|
|||
|
|
|
|||
|
|
# ── package lists ────────────────────────────────────────────────
|
|||
|
|
# Packages that install cleanly via pip on all platforms
|
|||
|
|
PIP_PACKAGES: list[tuple[str, str]] = [
|
|||
|
|
("tqdm", "tqdm>=4.66"), # Progress bars
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# pypff can be installed with 'libpff-python' on Linux/macOS
|
|||
|
|
PYPFF_PIP_NAME = "libpff-python"
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── helpers ──────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def run(cmd: list[str]) -> subprocess.CompletedProcess:
|
|||
|
|
"""Print and run a command, returning the result without raising."""
|
|||
|
|
print(f"\n>> {' '.join(cmd)}")
|
|||
|
|
return subprocess.run(cmd, check=False)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def is_importable(module: str) -> bool:
|
|||
|
|
"""Return True if *module* can be found in the current environment."""
|
|||
|
|
return find_spec(module) is not None
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── steps ────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def ensure_pip() -> None:
|
|||
|
|
"""Bootstrap / upgrade pip, setuptools, and wheel."""
|
|||
|
|
print("\n[1/3] Ensuring pip is up-to-date …")
|
|||
|
|
run([sys.executable, "-m", "ensurepip", "--upgrade"])
|
|||
|
|
run([sys.executable, "-m", "pip", "install", "--upgrade",
|
|||
|
|
"pip", "setuptools", "wheel"])
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_pip_packages() -> None:
|
|||
|
|
"""Install the pip-only dependencies."""
|
|||
|
|
print("\n[2/3] Installing pip packages …")
|
|||
|
|
specs = [spec for _, spec in PIP_PACKAGES]
|
|||
|
|
result = run([sys.executable, "-m", "pip", "install", "--upgrade"] + specs)
|
|||
|
|
if result.returncode != 0:
|
|||
|
|
print("\n❌ ERROR: Failed to install pip packages.")
|
|||
|
|
sys.exit(result.returncode)
|
|||
|
|
|
|||
|
|
# Verify each import
|
|||
|
|
for module, spec in PIP_PACKAGES:
|
|||
|
|
ok = is_importable(module)
|
|||
|
|
status = "✓" if ok else "✗"
|
|||
|
|
print(f" {status} {spec} ({'ok' if ok else 'MISSING'})")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_pypff() -> bool:
|
|||
|
|
"""
|
|||
|
|
Try to install pypff via pip (works on Linux/macOS).
|
|||
|
|
On Windows this usually fails; we give targeted guidance instead.
|
|||
|
|
"""
|
|||
|
|
print("\n[3/3] Installing pypff (libpff-python) …")
|
|||
|
|
|
|||
|
|
if is_importable("pypff"):
|
|||
|
|
print(" ✓ pypff is already importable – skipping install.")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
result = run([sys.executable, "-m", "pip", "install", "--upgrade", PYPFF_PIP_NAME])
|
|||
|
|
|
|||
|
|
if result.returncode == 0 and is_importable("pypff"):
|
|||
|
|
print(" ✓ pypff installed and importable.")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
# ── second attempt: some distros ship it as 'pypff' directly ──
|
|||
|
|
result2 = run([sys.executable, "-m", "pip", "install", "--upgrade", "pypff"])
|
|||
|
|
if result2.returncode == 0 and is_importable("pypff"):
|
|||
|
|
print(" ✓ pypff installed via 'pypff' package name.")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
print(" ✗ Could not install pypff automatically.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── platform guidance ─────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def print_pypff_guidance() -> None:
|
|||
|
|
system = platform.system()
|
|||
|
|
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 72)
|
|||
|
|
print("MANUAL INSTALLATION REQUIRED: pypff / libpff")
|
|||
|
|
print("=" * 72)
|
|||
|
|
|
|||
|
|
if system == "Linux":
|
|||
|
|
print(
|
|||
|
|
"On Debian / Ubuntu:\n"
|
|||
|
|
" sudo apt-get install python3-libpff\n"
|
|||
|
|
" # or\n"
|
|||
|
|
" pip install libpff-python\n\n"
|
|||
|
|
"On Fedora / RHEL:\n"
|
|||
|
|
" sudo dnf install libpff-devel python3-libpff\n"
|
|||
|
|
)
|
|||
|
|
elif system == "Darwin":
|
|||
|
|
print(
|
|||
|
|
"On macOS (Homebrew):\n"
|
|||
|
|
" brew install libpff\n"
|
|||
|
|
" pip install libpff-python\n"
|
|||
|
|
)
|
|||
|
|
elif system == "Windows":
|
|||
|
|
print(
|
|||
|
|
f"On Windows (Python {py_ver}):\n"
|
|||
|
|
" Option 1 – Pre-built wheel (if available for your Python version):\n"
|
|||
|
|
" pip install libpff-python\n\n"
|
|||
|
|
" Option 2 – Build from source:\n"
|
|||
|
|
" https://github.com/libyal/libpff/blob/main/documentation/Building.md\n\n"
|
|||
|
|
" Option 3 – Use WSL (Windows Subsystem for Linux) and follow the Linux steps.\n"
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
print(
|
|||
|
|
" Please visit https://github.com/libyal/libpff for build instructions\n"
|
|||
|
|
f" appropriate for your platform ({system}).\n"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
print("=" * 72)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── main ──────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def main() -> None:
|
|||
|
|
print("PST Email Extractor – Dependency Installer")
|
|||
|
|
print("=" * 72)
|
|||
|
|
print(f" Python: {sys.version.replace(chr(10), ' ')}")
|
|||
|
|
print(f" Executable: {sys.executable}")
|
|||
|
|
print(f" Platform: {platform.platform()}")
|
|||
|
|
print(f" CWD: {os.getcwd()}")
|
|||
|
|
|
|||
|
|
ensure_pip()
|
|||
|
|
install_pip_packages()
|
|||
|
|
has_pypff = install_pypff()
|
|||
|
|
|
|||
|
|
# ── summary ──────────────────────────────────────────────────
|
|||
|
|
print("\n" + "=" * 72)
|
|||
|
|
print("Installation summary")
|
|||
|
|
print("=" * 72)
|
|||
|
|
for module, spec in PIP_PACKAGES:
|
|||
|
|
ok = is_importable(module)
|
|||
|
|
print(f" {'✓' if ok else '✗'} {spec}")
|
|||
|
|
print(f" {'✓' if has_pypff else '✗'} pypff (libpff)")
|
|||
|
|
print("=" * 72)
|
|||
|
|
|
|||
|
|
if not has_pypff:
|
|||
|
|
print_pypff_guidance()
|
|||
|
|
sys.exit(2)
|
|||
|
|
|
|||
|
|
print("\n✅ All dependencies are installed.")
|
|||
|
|
print(" You can now run: python Extract.py [directory]")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|