#! /usr/bin/python3 # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. """Setup and maintain a development build of PVSC. You must have git, node, and npm installed. """ # Downloading the development build of the ``.vsix` was considered, but it actually # takes longer to install due to the number of files to unzip compared to the # incremental updates working from a git clone. import argparse import enum import os import pathlib import shutil import subprocess import sys REPO_URL = "https://github.com/Microsoft/vscode-python.git" @enum.unique class VSCode(enum.Enum): """Enum representing the install types of VS Code.""" stable = ".vscode" insiders = ".vscode-insiders" def run_command(command, cwd=None): """Run the specified command in a subprocess shell.""" cmd = subprocess.run(command, cwd=cwd, shell=True) cmd.check_returncode() def checkout_directory(install_type, dir_name="vscode-python"): return pathlib.Path.home() / install_type.value / "extensions" / dir_name def clone_repo(clone_to): """Clone the repository to the appropriate location.""" # https://code.visualstudio.com/docs/editor/extension-gallery#_where-are-extensions-installed cmd = ["git", "clone", "-q", "--single-branch", "--branch", "master", REPO_URL, os.fspath(clone_to)] run_command(cmd) def update_checkout(checkout): """Update the code the latest version.""" run_command(["git", "pull", "-q", "origin", "master"], cwd=checkout) def install_npm_dependencies(checkout): """Install packages from npm.""" run_command(["npm", "--silent", "--no-progress", "install", "--no-save"], cwd=checkout) def build_typescript(checkout): """Compile all TypeScript code in the extension.""" tsc_path = pathlib.Path("node_modules") / "typescript" / "bin" / "tsc" run_command(["node", os.fspath(tsc_path), "-p", os.fspath(checkout)], cwd=checkout) def install_ptvsd(checkout): """Install ptvsd from PyPI.""" ptvsd_path = checkout / "pythonFiles" / "experimental" / "ptvsd" cmd = [sys.executable, "-m", "pip", "-q", "--disable-pip-version-check", "install", "--target", os.fspath(ptvsd_path), "--upgrade", "ptvsd"] run_command(cmd) def cleanup(checkout): """Delete files downloaded by the extension.""" pls_path = checkout / "languageServer" if pls_path.exists(): shutil.rmtree(pls_path) def build(checkout): """Install dependencies and build the extension.""" print("Installing npm dependencies ...") install_npm_dependencies(checkout) print("Building TypeScript files ...") build_typescript(checkout) print("Installing ptvsd ...") install_ptvsd(checkout) def setup(install_type): """Set up a clone of PVSC.""" checkout = checkout_directory(install_type) print("Cloning {REPO_URL} ...") clone_repo(checkout) build(checkout) def update(): """Update development installs of PVSC.""" for install_type in VSCode: checkout = checkout_directory(install_type) if not checkout.exists(): continue print(f"UPDATING {checkout}") print("Deleting files downloaded by the extension ...") cleanup(checkout) print("Updating clone ...") update_checkout(checkout) build(checkout) def parse_args(args=sys.argv[1:]): """Parse CLI arguments.""" parser = argparse.ArgumentParser(description="Setup and maintain a development build of PVSC (requires git, node, and npm)") subparsers = parser.add_subparsers(dest="cmd") setup_parser = subparsers.add_parser("setup") setup_parser.add_argument("install_type", choices=[install_type.name for install_type in VSCode]) update_parser = subparsers.add_parser("update") return parser.parse_args(args) if __name__ == "__main__": args = parse_args() if args.cmd == "setup": setup(VSCode[args.install_type]) elif args.cmd == "update": update() else: raise RuntimeError(f"unrecognized sub-command: {args.cmd!r}")