import devenver import pprint import subprocess import sys import pathlib import os import shutil import tempfile import argparse import urllib.request import app_manifest_dev import app_manifest_user def git_clone(install_dir, git_exe, url, commit_hash): devenver.lprint(f"Git clone {url} to {install_dir}", level=0) # Clone repository if it does not exist if not os.path.exists(install_dir): clone_cmd = [git_exe, "clone", url, install_dir] devenver.lprint(f"Cloning command: {clone_cmd}") subprocess.run(clone_cmd) # Determine current git hash result = subprocess.run(f"{git_exe} rev-parse --short HEAD", cwd=install_dir, capture_output=True) curr_commit_hash = result.stdout.decode("utf-8").strip() # Checkout correct target of Odin if not curr_commit_hash.startswith(commit_hash): subprocess.run(f"{git_exe} checkout master", cwd=install_dir) subprocess.run(f"{git_exe} pull origin master", cwd=install_dir) subprocess.run(f"{git_exe} checkout {commit_hash}", cwd=install_dir) # Arguments # ------------------------------------------------------------------------------ arg_parser = argparse.ArgumentParser() arg_parser.add_argument('--download-dir', help=f'Set the directory where downloaded files are cached (default: ./(Win|Linux))', default="", type=pathlib.Path) arg_parser.add_argument('--install-dir', help=f'Set the directory where downloaded files are installed (default: ./Downloads)', default="", type=pathlib.Path) arg_parser.add_argument('--with-dev-apps', help=f'Download and install apps from the developer manifest', const=True, action="store_const") arg_parser.add_argument('--with-user-apps', help=f'Download and install apps from the user manifest', const=True, action="store_const") arg_parser.add_argument('operating_system', choices=['win', 'linux'], help=f'Download and install apps for the specified operating system') args = arg_parser.parse_args() download_dir = args.download_dir install_dir = args.install_dir is_windows = args.operating_system == 'win' if download_dir == pathlib.Path(""): download_dir = devenver.script_dir / 'Downloads' if install_dir == pathlib.Path(""): if is_windows: install_dir = devenver.script_dir / 'Win' else: install_dir = devenver.script_dir / 'Linux' # Install development apps # ------------------------------------------------------------------------------ if args.with_dev_apps: # Run DEVenver, installing the portable apps # -------------------------------------------------------------------------- dev_env_script_name = "dev_env" app_list = app_manifest_dev.get_manifest(is_windows=is_windows) installed_dev_apps = devenver.run(user_app_list=app_list, download_dir=download_dir, install_dir=install_dir, devenv_script_name=dev_env_script_name, is_windows=is_windows) install_script_path = pathlib.Path(devenver.script_dir, "install.py") if is_windows: # Install apps dependent on Git # -------------------------------------------------------------------------- devenver.print_header("Install apps that rely on Git") git_exe = installed_dev_apps["Git"][0]['exe_path'] # Clink # -------------------------------------------------------------------------- clink_install_dir = installed_dev_apps["Clink"][0]['install_dir'] clink_base_dir = clink_install_dir.parent # Gizmos clink_gizmo_git_hash = "fb2edd9" clink_gizmo_install_dir = clink_base_dir / "clink-gizmos" git_clone(install_dir=clink_gizmo_install_dir, git_exe=git_exe, url="https://github.com/chrisant996/clink-gizmos", commit_hash=clink_gizmo_git_hash) # Completions clink_completions_git_hash = "86b6f07" clink_completions_install_dir = clink_base_dir / "clink-completions" git_clone(install_dir=clink_completions_install_dir, git_exe=git_exe, url="https://github.com/vladimir-kotikov/clink-completions", commit_hash=clink_completions_git_hash) # Odin # -------------------------------------------------------------------------- # odin_git_hash = "9ae1bfb6" # odin_install_dir = install_dir / "Odin" # git_clone(install_dir=odin_install_dir, # git_exe=git_exe, # url="https://github.com/odin-lang/odin.git", # commit_hash=odin_git_hash) # TODO: We can't do this yet because the odin build requires a registry hack so # that it knows where to find MSVC. # Build Odin # subprocess.run(f"{git_exe} checkout {odin_git_hash}", # cwd=odin_install_dir) # Install clink configuration # -------------------------------------------------------------------------- clink_profile_dir = clink_base_dir / "profile" clink_settings_path = clink_profile_dir / "clink_settings" devenver.lprint(f"Installing clink_settings to: {clink_settings_path}") clink_settings_path.parent.mkdir(exist_ok=True) clink_settings_path.write_text(f"""# When this file is named "default_settings" and is in the binaries # directory or profile directory, it provides enhanced default settings. # Override built-in default settings with ones that provide a more # enhanced Clink experience. autosuggest.enable = True clink.default_bindings = windows cmd.ctrld_exits = False color.arginfo = sgr 38;5;172 color.argmatcher = sgr 1;38;5;40 color.cmd = sgr 1;38;5;231 color.cmdredir = sgr 38;5;172 color.cmdsep = sgr 38;5;214 color.comment_row = sgr 38;5;87;48;5;18 color.description = sgr 38;5;39 color.doskey = sgr 1;38;5;75 color.executable = sgr 1;38;5;33 color.filtered = sgr 38;5;231 color.flag = sgr 38;5;117 color.hidden = sgr 38;5;160 color.histexpand = sgr 97;48;5;55 color.horizscroll = sgr 38;5;16;48;5;30 color.input = sgr 38;5;222 color.readonly = sgr 38;5;28 color.selected_completion = sgr 38;5;16;48;5;254 color.selection = sgr 38;5;16;48;5;179 color.suggestion = sgr 38;5;239 color.unrecognized = sgr 38;5;203 history.max_lines = 25000 history.time_stamp = show match.expand_envvars = True match.substring = True clink.path = {clink_completions_install_dir};{clink_gizmo_install_dir} fzf.default_bindings = True """) # Install wezterm configuration # -------------------------------------------------------------------------- wezterm_config_dest_path = installed_dev_apps["WezTerm"][0]["install_dir"] / "wezterm.lua" devenver.lprint(f"Installing WezTerm config to {wezterm_config_dest_path}") clink_exe_path = clink_install_dir.relative_to(install_dir) / "clink_x64.exe" clink_exe_path_for_wezterm = str(clink_exe_path).replace("\\", "\\\\") clink_profile_path_for_wezterm = str(clink_profile_dir.relative_to(install_dir)).replace("\\", "\\\\") wezterm_config_dest_path.write_text(f"""local wezterm = require 'wezterm'; local default_prog local set_environment_variables = {{}} if wezterm.target_triple == "x86_64-pc-windows-msvc" then clink_exe = string.format("%s\\\\..\\\\..\\\\{clink_exe_path_for_wezterm}", wezterm.executable_dir) devenv_bat = string.format("%s\\\\..\\\\..\\\\{dev_env_script_name}.bat", wezterm.executable_dir) clink_profile = string.format("%s\\\\..\\\\..\\\\{clink_profile_path_for_wezterm}", wezterm.executable_dir) -- Taken from: https://wezfurlong.org/wezterm/shell-integration.html -- Use OSC 7 as per the above example set_environment_variables['prompt'] = '$E]7;file://localhost/$P$E\\\\$E[32m$T$E[0m $E[35m$P$E[36m$_$G$E[0m ' default_prog = {{"cmd.exe", "/s", "/k", clink_exe, "inject", "--profile", clink_profile, "-q", "&&", "call", devenv_bat}} end return {{ font_size = 10.0, color_scheme = "Peppermint", default_prog = default_prog, set_environment_variables = set_environment_variables, }} """) # Wezterm super terminal # -------------------------------------------------------------------------- wezterm_terminal_script_path = install_dir / "dev_terminal.bat" devenver.lprint(f"Installing WezTerm terminal script to {wezterm_terminal_script_path}") wezterm_terminal_script_path.write_text(f"""@echo off setlocal EnableDelayedExpansion set working_dir= if "%~1" neq "" ( set working_dir=start --cwd "%~1" set working_dir=!working_dir:\=/! ) start "" /MAX "%~dp0{installed_dev_apps["WezTerm"][0]["exe_path"].relative_to(install_dir)}" !working_dir! """) # Python # -------------------------------------------------------------------------- # TODO: If I'm using the terminal that this script generates it will lock the # executable and Python cannot open the file for verifying the SHA256. python_exe_path = pathlib.Path(installed_dev_apps["Python"][0]['exe_path']) # PyNvim devenver.lprint(f"Installing PyNVIM") subprocess.run(f"{python_exe_path} -m pip install pynvim") # Add update script python_rel_exe_path = pathlib.Path(python_exe_path).relative_to(install_dir) python_install_dir = pathlib.Path(python_exe_path).parent.relative_to(install_dir) (install_dir / "dev_env_update.bat").write_text(f"""@echo off setlocal EnableDelayedExpansion set PYTHONHOME=%~dp0{python_install_dir} %~dp0{python_rel_exe_path} {install_script_path} --with-dev-apps win pause """) (install_dir / "user_env_update.bat").write_text(f"""@echo off setlocal EnableDelayedExpansion set PYTHONHOME=%~dp0{python_install_dir} %~dp0{python_rel_exe_path} {install_script_path} --with-user-apps win pause """) else: dev_env_script_path = (install_dir / "dev_env_update.sh") user_env_script_path = (install_dir / "user_env_update.sh") dev_env_script_path.write_text(f"{sys.executable} {install_script_path} --with-dev-apps linux\n") user_env_script_path.write_text(f"{sys.executable} {install_script_path} --with-user-apps linux\n") subprocess.run(args=["chmod", "+x", dev_env_script_path]) subprocess.run(args=["chmod", "+x", user_env_script_path]) # Use LLVM script to fix up bloated installation # -------------------------------------------------------------------------- # See: https://github.com/zufuliu/llvm-utils/blob/main/llvm/llvm-link.bat internal_dir = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) / "Internal" if is_windows: devenver.print_header("Use LLVM utils script to slim installation size") llvm_install_dir_set = set() for entry in installed_dev_apps["LLVM"]: llvm_install_dir_set.add(entry['install_dir']) llvm_script_src_path = internal_dir / "win_llvm-link-ad01970-2022-08-29.bat" for llvm_install_dir in llvm_install_dir_set: llvm_script_dest_path = llvm_install_dir / "llvm-link.bat" shutil.copy(llvm_script_src_path, llvm_script_dest_path) subprocess.run(llvm_script_dest_path, cwd=llvm_install_dir) os.remove(llvm_script_dest_path) # Install fzf scripts # -------------------------------------------------------------------------- if not is_windows: shutil.copy(internal_dir / "unix_fzf-completion.bash", install_dir) shutil.copy(internal_dir / "unix_fzf-key-bindings.bash", install_dir) # Install left-overs # -------------------------------------------------------------------------- devenver.print_header("Install configuration files") # ClangFormat clang_format_src_path = internal_dir / "os_clang_format_style_file" clang_format_dest_path = install_dir / "_clang-format" devenver.lprint(f"Copying clang-format file from {clang_format_src_path} to {clang_format_dest_path}") shutil.copy(clang_format_src_path, clang_format_dest_path) # Copy init.vim to NVIM directory nvim_init_dir = "" if is_windows: nvim_init_dir = pathlib.Path(os.path.expanduser("~")) / "AppData" / "Local" / "nvim" else: nvim_init_dir = pathlib.Path(os.path.expanduser("~")) / ".config" / "nvim" nvim_config_dest_path = nvim_init_dir / "init.vim" nvim_config_src_path = internal_dir / "os_nvim_init.vim" devenver.lprint(f"Installing NVIM config to {nvim_config_dest_path}") nvim_init_dir.mkdir(parents=True, exist_ok=True) shutil.copy(nvim_config_src_path, nvim_config_dest_path) # Download vim.plug to NVIM init directory nvim_plug_vim_dir = nvim_init_dir / "autoload" nvim_plug_vim_path = nvim_plug_vim_dir / "plug.vim" nvim_plug_vim_dir.mkdir(parents=True, exist_ok=True) if not os.path.exists(nvim_plug_vim_path): devenver.lprint(f"Installing NVIM plugin manager to {nvim_plug_vim_path}") urllib.request.urlretrieve("https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim", nvim_plug_vim_path) # Install user apps # ------------------------------------------------------------------------------ if args.with_user_apps: app_list = app_manifest_user.get_manifest(is_windows=is_windows) installed_user_apps = devenver.run(user_app_list=app_list, download_dir=download_dir, install_dir=install_dir, devenv_script_name="user_env", is_windows=is_windows)