"""Dependency-free user installer for the msdev client and daemon."""
from __future__ import annotations
import argparse
import os
import shutil
import sys
from pathlib import Path
def install(prefix: Path) -> None:
source = Path(__file__).resolve().parent / "src" / "msdev"
if not source.is_dir():
raise RuntimeError(f"msdev source package not found: {source}")
prefix = prefix.expanduser().resolve()
package_parent = prefix / "share" / "msdev" / "package"
destination = package_parent / "msdev"
temporary = package_parent / f".msdev.tmp.{os.getpid()}"
package_parent.mkdir(parents=True, exist_ok=True)
shutil.rmtree(temporary, ignore_errors=True)
shutil.copytree(source, temporary, ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
backup = package_parent / ".msdev.old"
shutil.rmtree(backup, ignore_errors=True)
if destination.exists():
destination.rename(backup)
temporary.rename(destination)
shutil.rmtree(backup, ignore_errors=True)
bin_dir = prefix / "bin"
bin_dir.mkdir(parents=True, exist_ok=True)
python = Path(sys.executable).resolve()
helpers = Path(__file__).resolve().parent / "src" / "msdev" / "helpers"
arch_map = {
"x86_64": "amd64",
"amd64": "amd64",
"aarch64": "arm64",
"arm64": "arm64",
}
daemon_src = helpers / f"msdevd-linux-{arch_map.get(os.uname().machine, '')}"
if sys.platform != "win32" and daemon_src.is_file():
shutil.copy2(daemon_src, bin_dir / "msdevd")
os.chmod(bin_dir / "msdevd", 0o755)
for command, module in (("msdev", "msdev.cli"),):
if sys.platform == "win32":
wrapper = bin_dir / f"{command}.cmd"
wrapper.write_text(
"@echo off\r\n"
f"set \"PYTHONPATH={package_parent};%PYTHONPATH%\"\r\n"
f"\"{python}\" -m {module} %*\r\n",
encoding="utf-8",
)
else:
wrapper = bin_dir / command
wrapper.write_text(
"#!/usr/bin/env bash\n"
"set -e\n"
f"export PYTHONPATH={str(package_parent)!r}"
'"${PYTHONPATH:+:${PYTHONPATH}}"\n'
f"exec {str(python)!r} -m {module} \"$@\"\n",
encoding="utf-8",
)
os.chmod(wrapper, 0o755)
if sys.platform == "win32":
wrapper = bin_dir / "msdevd.cmd"
wrapper.write_text(
"@echo off\r\n"
f"set \"PYTHONPATH={package_parent};%PYTHONPATH%\"\r\n"
f"\"{python}\" -m msdev.daemon %*\r\n",
encoding="utf-8",
)
elif not (bin_dir / "msdevd").exists():
raise RuntimeError(
f"Go msdevd binary not found: {daemon_src}. "
"Run bash msdevd/build-static.sh before install-user.py"
)
print(f"installed msdev package to {package_parent}")
print(f"installed commands to {bin_dir}")
path_sep = ";" if sys.platform == "win32" else ":"
path_hint = (
f"set PATH={bin_dir}{path_sep}%PATH%"
if sys.platform == "win32"
else f"export PATH={bin_dir!s}:$PATH"
)
if str(bin_dir) not in os.environ.get("PATH", "").split(os.pathsep):
print(f"add this directory to PATH: {path_hint}")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--prefix",
type=Path,
default=Path.home() / ".local",
help="installation prefix (default: ~/.local)",
)
args = parser.parse_args()
install(args.prefix)
if __name__ == "__main__":
main()