From fb9a89f6fabf8da93456a7cb51bdd82f5414e690 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Fri, 17 Jan 2025 19:22:45 +0000 Subject: [PATCH] Install Git and ifcmerge (revised) The ZIP installer now contains a `libs/bin` folder that contains `ifcmerge`. Bonsai on `register()` adds this folder to the system `PATH` for the current Blender session. Advantage of this is that we don't have to fiddle with the registry on windows, or install files to `~/.local/bin` on Linux, it _should_ work on Darwin, updates are automatic, and we have a mechanism to ship other executables if required. (Note that ifcmerge.exe increases the size of the Windows Bonsai ZIP download by about 7MB) If Git isn't installed on Windows, the Git panel now offers to install it from the Windows Package Manager Community Repository using `winget`. --- src/bonsai/Makefile | 9 +++-- src/bonsai/bonsai/bim/__init__.py | 3 ++ .../bonsai/bim/module/ifcgit/__init__.py | 1 - .../bonsai/bim/module/ifcgit/operator.py | 17 ++------- src/bonsai/bonsai/bim/module/ifcgit/ui.py | 11 +----- src/bonsai/bonsai/core/ifcgit.py | 9 ----- src/bonsai/bonsai/tool/blender.py | 13 +++++++ src/bonsai/bonsai/tool/ifcgit.py | 37 +------------------ 8 files changed, 27 insertions(+), 73 deletions(-) diff --git a/src/bonsai/Makefile b/src/bonsai/Makefile index b937a932d3..53c135a2e6 100644 --- a/src/bonsai/Makefile +++ b/src/bonsai/Makefile @@ -229,12 +229,15 @@ endif # Required for Desktop icon and file association cp -r bonsai/libs/desktop build/bonsai/libs/ + # folder for executable files + mkdir -p build/bonsai/libs/bin + # required for three-way git merging ifeq ($(PLATFORM), win) - cd build/bonsai/libs/desktop && wget https://github.com/brunopostle/ifcmerge/releases/download/2024-06-24/ifcmerge.zip - cd build/bonsai/libs/desktop && unzip ifcmerge.zip && rm ifcmerge.zip + cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/2024-06-24/ifcmerge.zip + cd build/bonsai/libs/bin && unzip ifcmerge.zip && rm ifcmerge.zip else - cd build/bonsai/libs/desktop && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/main/ifcmerge + cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/main/ifcmerge && chmod +x ifcmerge endif # Generate translations module for Bonsai build diff --git a/src/bonsai/bonsai/bim/__init__.py b/src/bonsai/bonsai/bim/__init__.py index 170fba0ae6..b3655fcd32 100644 --- a/src/bonsai/bonsai/bim/__init__.py +++ b/src/bonsai/bonsai/bim/__init__.py @@ -258,6 +258,9 @@ def register(): icons = icon_preview bpy.app.translations.register("bonsai", translations_dict) + import bonsai.tool as tool + tool.Blender.ensure_bin_in_path() + def unregister(): global icons diff --git a/src/bonsai/bonsai/bim/module/ifcgit/__init__.py b/src/bonsai/bonsai/bim/module/ifcgit/__init__.py index 6c87a659e8..d48f6e285d 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/__init__.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/__init__.py @@ -38,7 +38,6 @@ classes = ( operator.RefreshGit, operator.SwitchRevision, operator.InstallGit, - operator.InstallIfcmerge, prop.IfcGitTag, prop.IfcGitListItem, prop.IfcGitProperties, diff --git a/src/bonsai/bonsai/bim/module/ifcgit/operator.py b/src/bonsai/bonsai/bim/module/ifcgit/operator.py index 4555a94190..266b85bc92 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/operator.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/operator.py @@ -389,7 +389,9 @@ class ObjectLog(bpy.types.Operator): class InstallGit(bpy.types.Operator): - """Installs Git if possible""" + """Install Git Version Control System from the +Windows Package Manager Community Repository, +requires restarting Blender after installation""" bl_label = "Install Git" bl_idname = "ifcgit.install_git" @@ -406,16 +408,3 @@ class InstallGit(bpy.types.Operator): core.install_git(tool.IfcGit, self) refresh() return {"FINISHED"} - - -class InstallIfcmerge(bpy.types.Operator): - """Installs ifcmerge if possible""" - - bl_label = "Install ifcmerge" - bl_idname = "ifcgit.install_ifcmerge" - bl_options = {"REGISTER"} - - def execute(self, context): - core.install_ifcmerge(tool.IfcGit, self) - refresh() - return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/ifcgit/ui.py b/src/bonsai/bonsai/bim/module/ifcgit/ui.py index 25665d362c..b8d288c77b 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/ui.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/ui.py @@ -30,19 +30,10 @@ class IFCGIT_PT_panel(bpy.types.Panel): if platform.system() == "Windows": row.operator( "ifcgit.install_git", - text="Install Git from Microsoft Store", + text="Install Git", icon="PACKAGE", ) return - elif not IfcGitData.data["ifcmerge_exe"]: - # TODO check if ifcmerge is up-to-date - row = layout.row() - row.label(text="ifcmerge is not installed", icon="ERROR") - row.operator( - "ifcgit.install_ifcmerge", - text="Install ifcmerge", - icon="PACKAGE", - ) props = context.scene.IfcGitProperties diff --git a/src/bonsai/bonsai/core/ifcgit.py b/src/bonsai/bonsai/core/ifcgit.py index 6e4ac90ac9..731e3751f2 100644 --- a/src/bonsai/bonsai/core/ifcgit.py +++ b/src/bonsai/bonsai/core/ifcgit.py @@ -138,12 +138,3 @@ def install_git(ifcgit: tool.IfcGit, operator: bpy.types.Operator) -> None: ifcgit.install_git_windows(operator=operator) else: print("install_git() not implemented") - - -def install_ifcmerge(ifcgit: tool.IfcGit, operator: bpy.types.Operator) -> None: - if platform.system() == "Windows": - ifcgit.install_ifcmerge_windows(name_exe="ifcmerge.exe", operator=operator) - elif platform.system() == "Linux": - ifcgit.install_ifcmerge_linux(name_exe="ifcmerge", operator=operator) - elif platform.system() == "Darwin": - print("install_ifcmerge() not implemented") diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index 051dfa9dc3..e1c4852387 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -21,6 +21,7 @@ import bpy import bmesh import json import os +import platform from ifcopenshell import entity_instance import ifcopenshell.api import ifcopenshell.util.element @@ -430,6 +431,18 @@ class Blender(bonsai.core.tool.Blender): return blender_path return bpy.path.abspath("//") / blender_path + @classmethod + def ensure_bin_in_path(cls) -> None: + """Check 'bin' folder is in PATH, if not add for this session""" + bin_dir = str(Path(__file__).parent.parent.resolve() / "libs" / "bin") + current_path = os.environ["PATH"] + if bin_dir not in current_path: + if platform.system() == "Windows": + path_separator = ";" + else: + path_separator = ":" + os.environ["PATH"] = current_path + path_separator + bin_dir + @classmethod def get_default_selection_keypmap(cls) -> tuple: """keymap to replicate default blender selection behaviour with click and box selection""" diff --git a/src/bonsai/bonsai/tool/ifcgit.py b/src/bonsai/bonsai/tool/ifcgit.py index 011d5800a9..5b0f9b88e5 100644 --- a/src/bonsai/bonsai/tool/ifcgit.py +++ b/src/bonsai/bonsai/tool/ifcgit.py @@ -20,7 +20,6 @@ from __future__ import annotations import os import re import subprocess -import shutil import bpy import logging from bonsai.bim import import_ifc @@ -551,7 +550,7 @@ class IfcGit: @classmethod def install_git_windows(cls, operator: bpy.types.Operator) -> None: """Command to install Git on Windows using winget""" - command = ["winget", "install", "--id", "Git.Git", "-e", "--source", "msstore"] + command = ["winget", "install", "--id", "Git.Git", "-e", "--source", "winget"] try: subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except subprocess.CalledProcessError as e: @@ -559,40 +558,6 @@ class IfcGit: except FileNotFoundError: operator.report({"ERROR"}, "Winget is not available. Make sure Windows Package Manager is installed.") - @classmethod - def install_ifcmerge_linux(cls, name_exe: str, operator: bpy.types.Operator) -> None: - """Command to install ifcmerge on Linux""" - src_dir = os.path.join(os.path.dirname(__file__), "../libs/desktop") - destdir = os.path.join(os.environ["HOME"], ".local", "bin") - try: - os.makedirs(destdir, exist_ok=True) - shutil.copy(os.path.join(src_dir, name_exe), destdir) - os.chmod(os.path.join(destdir, name_exe), 0o755) - except Exception as e: - operator.report({"ERROR"}, f"Error installing file: {e}") - - @classmethod - def install_ifcmerge_windows(cls, name_exe: str, operator: bpy.types.Operator) -> None: - """Command to install ifcmerge on Windows""" - src_dir = os.path.join(os.path.dirname(__file__), "..\\libs\\desktop") - destdir = os.path.join(os.environ["USERPROFILE"], "AppData", "Local", "Bonsai", "bin") - try: - os.makedirs(destdir, exist_ok=True) - shutil.copy(os.path.join(src_dir, name_exe), destdir) - except Exception as e: - operator.report({"ERROR"}, f"Error installing file: {e}") - - current_path = os.environ["PATH"] - - if destdir not in current_path: - os.environ["PATH"] = current_path + ";" + destdir - command = f'setx PATH "%PATH%;{destdir}"' - try: - subprocess.run(command, check=True, shell=True) - print(f"User %PATH% updated permanently with: {destdir}") - except subprocess.CalledProcessError as e: - operator.report({"ERROR"}, f"Error permanently updating %PATH%: {e}") - class IfcGitRepo: repo: git.Repo = None