GP-6252: pyghidra_launcher.py fixes

pyghidra_launcher.py can now use an existing pyghidra
installation in an externally managed environment. Upgrading is disabled
in this scenario.

Fixed an issue with getting the package version from an arbitrary
environment
This commit is contained in:
Ryan Kurtz
2025-12-19 05:56:36 -05:00
parent 8890f90d8b
commit 56252685c0

View File

@@ -21,7 +21,6 @@ import subprocess
import sysconfig import sysconfig
from pathlib import Path from pathlib import Path
from itertools import chain from itertools import chain
from importlib import metadata
from typing import List, Dict, Tuple, Optional from typing import List, Dict, Tuple, Optional
def get_application_properties(install_dir: Path) -> Dict[str, str]: def get_application_properties(install_dir: Path) -> Dict[str, str]:
@@ -140,14 +139,10 @@ def version_tuple(v: str) -> Tuple[str, ...]:
filled.append(point.zfill(8)) filled.append(point.zfill(8))
return tuple(filled) return tuple(filled)
def get_package_version(package: str) -> Optional[str]: def get_package_version(python_cmd: List[str], package: str) -> Optional[str]:
""" source = f'import importlib.metadata as m; print(m.version("{package}"))'
Checks for an installed package version. result = subprocess.run(python_cmd + ['-c', source], capture_output=True, text=True)
""" return result.stdout.strip() if result.returncode == 0 else None
try:
return metadata.version(package)
except metadata.PackageNotFoundError:
return None
def get_saved_python_cmd(install_dir: Path, dev: bool) -> List[str]: def get_saved_python_cmd(install_dir: Path, dev: bool) -> List[str]:
user_settings_dir: Path = get_user_settings_dir(install_dir, dev) user_settings_dir: Path = get_user_settings_dir(install_dir, dev)
@@ -200,6 +195,10 @@ def upgrade(python_cmd: List[str], pip_args: List[str], dist_dir: Path, current_
included_version = included_pyghidra.name.split('-')[1] included_version = included_pyghidra.name.split('-')[1]
current_version = current_pyghidra_version current_version = current_pyghidra_version
if version_tuple(included_version) > version_tuple(current_version): if version_tuple(included_version) > version_tuple(current_version):
print(f'PyGhidra upgrade available: {current_version} -> {included_version}')
if is_externally_managed():
print(f'Automated upgrade is not supported in an externally managed environment')
return False
choice: str = input(f'Do you wish to upgrade PyGhidra {current_version} to {included_version} (y/n)? ') choice: str = input(f'Do you wish to upgrade PyGhidra {current_version} to {included_version} (y/n)? ')
if choice.lower() in ('y', 'yes'): if choice.lower() in ('y', 'yes'):
pip_args.append('-U') pip_args.append('-U')
@@ -238,11 +237,11 @@ def main() -> None:
if args.dev: if args.dev:
# If in dev mode, launch PyGhidra from the source tree using the development virtual environment # If in dev mode, launch PyGhidra from the source tree using the development virtual environment
if not venv_dir.is_dir(): if not venv_dir.is_dir():
print('Virtual environment not found!') print('Development virtual environment not found!')
print('Run "gradle prepPyGhidra" and try again.') print('Run "gradle prepPyGhidra" and try again.')
sys.exit(1) sys.exit(1)
python_cmd = get_venv_exe(venv_dir) python_cmd = get_venv_exe(venv_dir)
print(f'Switching to Ghidra virtual environment: {venv_dir}') print(f'Switching to Ghidra development virtual environment: {venv_dir}')
else: else:
# If in release mode, offer to install or upgrade PyGhidra before launching from user-controlled environment # If in release mode, offer to install or upgrade PyGhidra before launching from user-controlled environment
pip_args: List[str] = ['-m', 'pip', 'install', '--no-index', '-f', str(dist_dir), 'pyghidra'] pip_args: List[str] = ['-m', 'pip', 'install', '--no-index', '-f', str(dist_dir), 'pyghidra']
@@ -251,7 +250,7 @@ def main() -> None:
# 1) If we are already in a virtual environment, use that # 1) If we are already in a virtual environment, use that
# 2) If the Ghidra user settings virtual environment exists, use that # 2) If the Ghidra user settings virtual environment exists, use that
# 3) If we are "externally managed", automatically create/use the Ghidra user settings virtual environment # 3) If we are "externally managed", automatically create/use the Ghidra user settings virtual environment
offer_venv: bool = False offer_venv = False
if in_venv(): if in_venv():
# If we are already in a virtual environment, assume that's where the user wants to be # If we are already in a virtual environment, assume that's where the user wants to be
python_cmd = get_venv_exe(Path(sys.prefix)) python_cmd = get_venv_exe(Path(sys.prefix))
@@ -261,16 +260,21 @@ def main() -> None:
python_cmd = get_venv_exe(venv_dir) python_cmd = get_venv_exe(venv_dir)
print(f'Switching to Ghidra virtual environment: {venv_dir}') print(f'Switching to Ghidra virtual environment: {venv_dir}')
elif is_externally_managed(): elif is_externally_managed():
print('Externally managed environment detected!') print('Externally managed environment detected')
create_ghidra_venv(python_cmd, venv_dir) current_pyghidra_version = get_package_version(python_cmd, 'pyghidra')
python_cmd = get_venv_exe(venv_dir) if current_pyghidra_version is None:
print(f'Switching to Ghidra virtual environment: {venv_dir}') create_ghidra_venv(python_cmd, venv_dir)
python_cmd = get_venv_exe(venv_dir)
print(f'Switching to Ghidra virtual environment: {venv_dir}')
else:
print(f'Using externally managed PyGhidra {current_pyghidra_version}')
else: else:
offer_venv = True offer_venv = True
# If PyGhidra is not installed in the execution environment, offer to install it # If PyGhidra is not installed in the execution environment, offer to install it
# If it's already installed, offer to upgrade (if applicable) # If it's already installed, offer to upgrade (if applicable)
current_pyghidra_version = get_package_version('pyghidra') current_pyghidra_version = get_package_version(python_cmd, 'pyghidra')
if current_pyghidra_version is None: if current_pyghidra_version is None:
python_cmd = install(install_dir, python_cmd, pip_args, offer_venv) python_cmd = install(install_dir, python_cmd, pip_args, offer_venv)
if not python_cmd: if not python_cmd: