diff --git a/igniter/__init__.py b/igniter/__init__.py index 16ffb940f6e..a80ff68a7df 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -22,9 +22,11 @@ def _get_qt_app(): from qtpy import QtWidgets, QtCore + is_event_loop_running = True + app = QtWidgets.QApplication.instance() if app is not None: - return app + return app, is_event_loop_running for attr_name in ( "AA_EnableHighDpiScaling", @@ -39,7 +41,10 @@ def _get_qt_app(): QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) - return QtWidgets.QApplication(sys.argv) + # Since it's a new QApplication the event loop isn't running yet + is_event_loop_running = False + + return QtWidgets.QApplication(sys.argv), is_event_loop_running def open_dialog(): @@ -49,29 +54,39 @@ def open_dialog(): sys.exit(1) from .install_dialog import InstallDialog - app = _get_qt_app() + app, is_event_loop_running = _get_qt_app() d = InstallDialog() d.open() - app.exec_() + if not is_event_loop_running: + app.exec_() + else: + d.exec_() + return d.result() -def open_update_window(openpype_version): +def open_update_window(openpype_version, zxp_hosts=None): """Open update window.""" + if zxp_hosts is None: + zxp_hosts = [] if os.getenv("OPENPYPE_HEADLESS_MODE"): print("!!! Can't open dialog in headless mode. Exiting.") sys.exit(1) from .update_window import UpdateWindow - app = _get_qt_app() + app, is_event_loop_running = _get_qt_app() - d = UpdateWindow(version=openpype_version) + d = UpdateWindow(version=openpype_version, zxp_hosts=zxp_hosts) d.open() - app.exec_() + if not is_event_loop_running: + app.exec_() + else: + d.exec_() + version_path = d.get_version_path() return version_path @@ -84,12 +99,15 @@ def show_message_dialog(title, message): from .message_dialog import MessageDialog - app = _get_qt_app() + app, is_event_loop_running = _get_qt_app() dialog = MessageDialog(title, message) dialog.open() - app.exec_() + if not is_event_loop_running: + app.exec_() + else: + dialog.exec_() __all__ = [ diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 8656e452290..52a45bda9d5 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -7,6 +7,7 @@ import shutil import sys import tempfile +import subprocess from pathlib import Path from typing import Union, Callable, List, Tuple import hashlib @@ -29,7 +30,6 @@ get_local_openpype_path_from_settings ) - LOG_INFO = 0 LOG_WARNING = 1 LOG_ERROR = 3 @@ -482,8 +482,8 @@ def get_installed_version(cls): @staticmethod def get_latest_version( - local: bool = None, - remote: bool = None + local: bool = None, + remote: bool = None ) -> Union[OpenPypeVersion, None]: """Get the latest available version. @@ -560,6 +560,15 @@ def is_compatible(self, version: OpenPypeVersion): return self.major == version.major and self.minor == version.minor +class ZXPExtensionData: + + def __init__(self, host_id: str, ext_id: str, installed_version: semver.VersionInfo, shipped_version: semver.VersionInfo): + self.host_id = host_id + self.id = ext_id + self.installed_version = installed_version + self.shipped_version = shipped_version + + class BootstrapRepos: """Class for bootstrapping local OpenPype installation. @@ -572,17 +581,18 @@ class BootstrapRepos: """ - def __init__(self, progress_callback: Callable = None, message=None): + def __init__(self, progress_callback: Callable = None, log_signal=None, step_text_signal=None): """Constructor. Args: progress_callback (callable): Optional callback method to report progress. - message (QtCore.Signal, optional): Signal to report messages back. + log_signal (QtCore.Signal, optional): Signal to report messages back. """ # vendor and app used to construct user data dir - self._message = message + self._log_signal = log_signal + self._step_text_signal = step_text_signal self._log = log.getLogger(str(__class__)) self.data_dir = None self.set_data_dir(None) @@ -628,7 +638,7 @@ def get_version_path_from_list( return None @staticmethod - def get_version(repo_dir: Path) -> Union[str, None]: + def get_version(repo_dir: Path) -> Union[OpenPypeVersion, None]: """Get version of OpenPype in given directory. Note: in frozen OpenPype installed in user data dir, this must point @@ -652,7 +662,7 @@ def get_version(repo_dir: Path) -> Union[str, None]: with version_file.open("r") as fp: exec(fp.read(), version) - return version['__version__'] + return OpenPypeVersion(version=version['__version__'], path=repo_dir) def create_version_from_live_code( self, repo_dir: Path = None, data_dir: Path = None) -> Union[OpenPypeVersion, None]: @@ -665,22 +675,24 @@ def create_version_from_live_code( Args: repo_dir (Path, optional): Path to OpenPype repository. + data_dir (Path, optional): Path to the user data directory. Returns: - Path: path of installed repository file. + version (OpenPypeVersion): Info of the version created. """ - # if repo dir is not set, we detect local "live" OpenPype repository + # If repo dir is not set, we detect local "live" OpenPype repository # version and use it as a source. Otherwise, repo_dir is user # entered location. if repo_dir: version = self.get_version(repo_dir) + version_str = str(version) else: installed_version = OpenPypeVersion.get_installed_version() - version = str(installed_version) + version_str = str(installed_version) repo_dir = installed_version.path - if not version: + if not version_str: self._print("OpenPype not found.", LOG_ERROR) return @@ -692,7 +704,7 @@ def create_version_from_live_code( # create zip inside temporary directory. with tempfile.TemporaryDirectory() as temp_dir: temp_zip = \ - Path(temp_dir) / f"openpype-v{version}.zip" + Path(temp_dir) / f"openpype-v{version_str}.zip" self._print(f"creating zip: {temp_zip}") self._create_openpype_zip(temp_zip, repo_dir) @@ -702,7 +714,7 @@ def create_version_from_live_code( destination = self._move_zip_to_data_dir(temp_zip) - return OpenPypeVersion(version=version, path=Path(destination)) + return OpenPypeVersion(version=version_str, path=Path(destination)) def _move_zip_to_data_dir(self, zip_file) -> Union[None, Path]: """Move zip with OpenPype version to user data directory. @@ -782,11 +794,12 @@ def create_version_from_frozen_code(self) -> Union[None, OpenPypeVersion]: openpype_list.append(frozen_root / f) version = self.get_version(frozen_root) + version_str = str(version) # create zip inside temporary directory. with tempfile.TemporaryDirectory() as temp_dir: temp_zip = \ - Path(temp_dir) / f"openpype-v{version}.zip" + Path(temp_dir) / f"openpype-v{version_str}.zip" self._print(f"creating zip: {temp_zip}") with ZipFile(temp_zip, "w") as zip_file: @@ -806,7 +819,7 @@ def create_version_from_frozen_code(self) -> Union[None, OpenPypeVersion]: destination = self._move_zip_to_data_dir(temp_zip) - return OpenPypeVersion(version=version, path=destination) + return OpenPypeVersion(version=version_str, path=destination) def _create_openpype_zip(self, zip_path: Path, openpype_path: Path) -> None: """Pack repositories and OpenPype into zip. @@ -1291,8 +1304,8 @@ def _print(self, exc_info (bool, optional): Exception info object to pass to logger. """ - if self._message: - self._message.emit(message, level == LOG_ERROR) + if self._log_signal: + self._log_signal.emit(message, level == LOG_ERROR) if level == LOG_WARNING: self._log.warning(message, exc_info=exc_info) @@ -1457,6 +1470,151 @@ def install_version(self, return destination + @staticmethod + def _get_zxp_handler_program_path(platform_name_lowercase): + if platform_name_lowercase == "linux": + # No host in array or user is on Linux, the platform doesn't support Adobe softwares + return None + path_prog_folder = Path(os.environ["OPENPYPE_ROOT"]).resolve().joinpath( + "vendor", "bin", "ex_man_cmd", platform_name_lowercase) + if platform_name_lowercase == "windows": + return path_prog_folder.joinpath("ExManCmd.exe") + + return path_prog_folder.joinpath("MacOS", "ExManCmd") + + def extract_zxp_info_from_manifest(self, openpype_version: OpenPypeVersion, host_id: str): + version_path = openpype_version.path + path_manifest = version_path.joinpath("openpype", "hosts", host_id, "api", "extension", "CSXS", "manifest.xml") + pattern_regex_extension_id = r"ExtensionBundleId=\"(?P[\w.]+)\"" + pattern_regex_extension_version = r"ExtensionBundleVersion=\"(?P[\d.]+)\"" + + extension_id = "" + extension_version = "" + try: + with open(path_manifest, mode="r") as f: + content = f.read() + match_extension_id = re.search(pattern_regex_extension_id, content) + match_extension_version = re.search(pattern_regex_extension_version, content) + if match_extension_id: + extension_id = match_extension_id.group("extension_id") + if match_extension_version: + extension_version = semver.VersionInfo.parse(match_extension_version.group("extension_version")) + except IOError as e: + if self._log_signal: + self._log_signal.emit("I/O error({}): {}".format(e.errno, e.strerror), True) + except Exception as e: # handle other exceptions such as attribute errors + if self._log_signal: + self._log_signal.emit("Unexpected error: {}".format(e), True) + + return extension_id, extension_version + + def update_zxp_extensions(self, openpype_version: OpenPypeVersion, extensions: [ZXPExtensionData]): + # Check the current OS + low_platform = platform.system().lower() + if not extensions or platform.system().lower() == "linux": + # No host in array or user is on Linux, the platform doesn't support Adobe softwares + return + + version_path = openpype_version.path + + path_prog = self._get_zxp_handler_program_path(low_platform) + if low_platform == "windows": + cmd_arg_prefix = "/" + else: + cmd_arg_prefix = "--" + + for extension in extensions: + # Remove installed ZXP extension + if self._step_text_signal: + self._step_text_signal.emit("Removing installed ZXP extension for " + "{} ...".format(extension.host_id)) + subprocess.run([str(path_prog), "{}remove".format(cmd_arg_prefix), extension.id], + stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) + + # Install ZXP shipped in the current version folder + fullpath_curr_zxp_extension = version_path.joinpath("openpype", + "hosts", + extension.host_id, + "api", + "extension.zxp") + if not fullpath_curr_zxp_extension.exists(): + if self._log_signal: + self._log_signal.emit("Cannot find ZXP extension for {}, looked at: {}".format( + extension.host_id, str(fullpath_curr_zxp_extension)), True) + continue + + if self._step_text_signal: + self._step_text_signal.emit("Install ZXP extension for {} ...".format(extension.host_id)) + completed_process = subprocess.run([str(path_prog), "{}install".format(cmd_arg_prefix), + str(fullpath_curr_zxp_extension)], capture_output=True) + if completed_process.returncode != 0 or completed_process.stderr: + if self._log_signal: + self._log_signal.emit("Couldn't install the ZXP extension for {} " + "due to an error: full log: {}\n{}".format(extension.host_id, + completed_process.stdout, + completed_process.stderr), True) + + def get_zxp_extensions_to_update(self, openpype_version, system_settings, force=False) -> [ZXPExtensionData]: + # List of all Adobe software ids (named hosts) handled by OpenPype + # TODO: where and how to store the list of Adobe software ids + zxp_host_ids = ["photoshop", "aftereffects"] + + zxp_hosts_to_update = [] + + # Check the current OS + low_platform = platform.system().lower() + if low_platform == "linux": + # The platform doesn't support Adobe softwares + return zxp_hosts_to_update + + path_prog = self._get_zxp_handler_program_path(low_platform) + if low_platform == "windows": + cmd_arg_prefix = "/" + else: + cmd_arg_prefix = "--" + + # Get installed extensions + completed_process = subprocess.run([str(path_prog), "{}list".format(cmd_arg_prefix), "all"], + capture_output=True) + installed_extensions_info = completed_process.stdout + + zxp_hosts_to_update = [] + for zxp_host_id in zxp_host_ids: + extension_id, extension_new_version = self.extract_zxp_info_from_manifest(openpype_version, zxp_host_id) + if not extension_id or not extension_new_version: + # ZXP extension seems invalid or doesn't exists for this software, skipping + continue + + extension_curr_version = "" + + # Get the installed version + escaped_extension_id_str = extension_id.replace(".", "\.") # noqa + pattern_regex_extension_version = fr"{escaped_extension_id_str}\s+(?P[\d.]+)" + + match_extension = re.search(pattern_regex_extension_version, str(installed_extensions_info)) + if match_extension: + extension_curr_version = semver.VersionInfo.parse(match_extension.group("version")) + + if not force: + # Is the update required? + + # Check if the software is enabled in the current system settings + if system_settings and not system_settings["applications"][zxp_host_id]["enabled"]: + # The update isn't necessary if the soft is disabled for the studio, skipping + continue + + # Compare the installed version with the new version + if extension_curr_version and extension_curr_version == extension_new_version: + # The two extensions have the same version number, skipping + continue + + zxp_hosts_to_update.append(ZXPExtensionData(zxp_host_id, + extension_id, + extension_curr_version, + extension_new_version)) + + return zxp_hosts_to_update + def _copy_zip(self, source: Path, destination: Path) -> Path: try: # copy file to destination @@ -1495,8 +1653,7 @@ def _is_openpype_in_dir(self, try: # add one 'openpype' level as inside dir there should # be many other repositories. - version_str = BootstrapRepos.get_version(dir_item) - version_check = OpenPypeVersion(version=version_str) + version_check = BootstrapRepos.get_version(dir_item) except ValueError: self._print( f"cannot determine version from {dir_item}", True) @@ -1600,12 +1757,12 @@ def get_openpype_versions(self, openpype_dir: Path) -> list: detected_version = result if item.is_dir() and not self._is_openpype_in_dir( - item, detected_version + item, detected_version ): continue if item.is_file() and not self._is_openpype_in_zip( - item, detected_version + item, detected_version ): continue diff --git a/igniter/update_thread.py b/igniter/update_thread.py index 0223477d0a5..783397b2aec 100644 --- a/igniter/update_thread.py +++ b/igniter/update_thread.py @@ -4,7 +4,8 @@ from .bootstrap_repos import ( BootstrapRepos, - OpenPypeVersion + OpenPypeVersion, + ZXPExtensionData ) @@ -19,17 +20,22 @@ class UpdateThread(QtCore.QThread): user data dir. """ - progress = QtCore.Signal(int) - message = QtCore.Signal((str, bool)) + progress_signal = QtCore.Signal(int) + log_signal = QtCore.Signal((str, bool)) + step_text_signal = QtCore.Signal(str) def __init__(self, parent=None): self._result = None self._openpype_version = None + self._zxp_hosts = [] super().__init__(parent) def set_version(self, openpype_version: OpenPypeVersion): self._openpype_version = openpype_version + def set_zxp_hosts(self, zxp_hosts: [ZXPExtensionData]): + self._zxp_hosts = zxp_hosts + def result(self): """Result of finished installation.""" return self._result @@ -46,11 +52,21 @@ def run(self): or copy them from location specified by user or retrieved from database. """ - bs = BootstrapRepos( - progress_callback=self.set_progress, message=self.message) + bs = BootstrapRepos(progress_callback=self.set_progress, + log_signal=self.log_signal, + step_text_signal=self.step_text_signal) bs.set_data_dir(OpenPypeVersion.get_local_openpype_path()) - version_path = bs.install_version(self._openpype_version) + + # Adding the conditions to be able to show this window to update the ZXP extensions + # without needing to install an OP version + if not bs.is_inside_user_data(self._openpype_version.path) and self._openpype_version.path.is_file(): + version_path = bs.install_version(self._openpype_version) + else: + version_path = self._openpype_version.path + + bs.update_zxp_extensions(self._openpype_version, self._zxp_hosts) + self._set_result(version_path) def set_progress(self, progress: int) -> None: @@ -60,4 +76,4 @@ def set_progress(self, progress: int) -> None: progress (int): Progress in percents. """ - self.progress.emit(progress) + self.progress_signal.emit(progress) diff --git a/igniter/update_window.py b/igniter/update_window.py index d51ae18cd0f..b3a9db241d4 100644 --- a/igniter/update_window.py +++ b/igniter/update_window.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- """Progress window to show when OpenPype is updating/installing locally.""" import os +import logging as log from qtpy import QtCore, QtGui, QtWidgets from .update_thread import UpdateThread -from .bootstrap_repos import OpenPypeVersion +from .bootstrap_repos import OpenPypeVersion, ZXPExtensionData from .nice_progress_bar import NiceProgressBar from .tools import load_stylesheet @@ -16,10 +17,12 @@ class UpdateWindow(QtWidgets.QDialog): _width = 500 _height = 100 - def __init__(self, version: OpenPypeVersion, parent=None): + def __init__(self, version: OpenPypeVersion, zxp_hosts: [ZXPExtensionData], parent=None): super(UpdateWindow, self).__init__(parent) self._openpype_version = version + self._zxp_hosts = zxp_hosts self._result_version_path = None + self._log = log.getLogger(str(__class__)) self.setWindowTitle( f"OpenPype is updating ..." @@ -42,7 +45,7 @@ def __init__(self, version: OpenPypeVersion, parent=None): # Load logo pixmap_openpype_logo = QtGui.QPixmap(icon_path) - # Set logo as icon of window + # Set logo as icon of the window self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo)) self._pixmap_openpype_logo = pixmap_openpype_logo @@ -78,6 +81,7 @@ def _init_ui(self): main.addWidget(progress_bar, 0) main.addSpacing(15) + self._main_label = main_label self._progress_bar = progress_bar def showEvent(self, event): @@ -96,14 +100,13 @@ def _run_update(self): This will once again validate entered path and mongo if ok, start working thread that will do actual job. """ - # Check if install thread is not already running - if self._update_thread and self._update_thread.isRunning(): - return self._progress_bar.setRange(0, 0) update_thread = UpdateThread(self) update_thread.set_version(self._openpype_version) - update_thread.message.connect(self.update_console) - update_thread.progress.connect(self._update_progress) + update_thread.set_zxp_hosts(self._zxp_hosts) + update_thread.log_signal.connect(self._print) + update_thread.step_text_signal.connect(self.update_step_text) + update_thread.progress_signal.connect(self._update_progress) update_thread.finished.connect(self._installation_finished) self._update_thread = update_thread @@ -119,7 +122,15 @@ def _installation_finished(self): self._progress_bar.setRange(0, 1) self._update_progress(100) QtWidgets.QApplication.processEvents() - self.done(0) + self.done(int(QtWidgets.QDialog.Accepted)) + if self._update_thread.isRunning(): + self._update_thread.quit() + self.close() + + + + + def _update_progress(self, progress: int): # not updating progress as we are not able to determine it @@ -137,11 +148,22 @@ def _update_progress(self, progress: int): """ return - def update_console(self, msg: str, error: bool = False) -> None: - """Display message in console. + def _print(self, message: str, error: bool = False) -> None: + """Print the message in the console. Args: - msg (str): message. + message (str): message. error (bool): if True, print it red. """ - print(msg) + if error: + self._log.error(message) + else: + self._log.info(message) + + def update_step_text(self, text: str) -> None: + """Print the message in the console. + + Args: + text (str): Text describing the current step. + """ + self._main_label.setText(text) diff --git a/openpype/hosts/aftereffects/api/extension.zxp b/openpype/hosts/aftereffects/api/extension.zxp index 64d3c0318d3..ae799a15e23 100644 Binary files a/openpype/hosts/aftereffects/api/extension.zxp and b/openpype/hosts/aftereffects/api/extension.zxp differ diff --git a/openpype/hosts/photoshop/api/extension.zxp b/openpype/hosts/photoshop/api/extension.zxp index 108b0ce1922..dcfc5744751 100644 Binary files a/openpype/hosts/photoshop/api/extension.zxp and b/openpype/hosts/photoshop/api/extension.zxp differ diff --git a/openpype/hosts/standalonepublisher/addon.py b/openpype/hosts/standalonepublisher/addon.py index 67204b581b2..ad471cdfcfe 100644 --- a/openpype/hosts/standalonepublisher/addon.py +++ b/openpype/hosts/standalonepublisher/addon.py @@ -11,7 +11,7 @@ class StandAlonePublishAddon(OpenPypeModule, ITrayAction, IHostAddon): label = "Publisher (legacy)" - name = "standalonepublisher" + name = "standalone_publisher" host_name = "standalonepublisher" def initialize(self, modules_settings): diff --git a/openpype/hosts/traypublisher/addon.py b/openpype/hosts/traypublisher/addon.py index 3b34f9e6e8b..261e0be1db6 100644 --- a/openpype/hosts/traypublisher/addon.py +++ b/openpype/hosts/traypublisher/addon.py @@ -11,7 +11,7 @@ class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction): label = "Publisher" - name = "traypublisher" + name = "tray_publisher" host_name = "traypublisher" def initialize(self, modules_settings): diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 2d6dccd22df..039358e3f4d 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -1201,18 +1201,27 @@ class name and value is time delta of it's processing. class TrayModulesManager(ModulesManager): # Define order of modules in menu modules_menu_order = ( - "user", - "ftrack", - "kitsu", - "muster", - "launcher_tool", - "avalon", - "clockify", - "standalonepublish_tool", - "traypublish_tool", - "log_viewer", - "local_settings", - "settings" + # Menu ------------------------------------------ + # "user", # {USERNAME} + "ftrack", # ftrack + "kitsu", # Kitsu + "muster", # Muster + "launcher_tool", # Launcher + "avalon", # Loader + "tray_publisher", # Publisher + "standalone_publisher", # Publisher (legacy) + "clockify", # Clockify + "local_settings", # Local Settings + # More Tools Submenu ----------------------------- + "sync_server", # Sync Queue + "update_zxp_extensions", # Update ZXP Extensions + "log_viewer", # Show Logs + "python_interpreter", # Console + # Admin Submenu ---------------------------------- + "settings", # Studio Settings + "project_manager", # Project Manager (beta) + # Services Submenu ------------------------------- + # Order currently not defined (this could be done) ) def __init__(self): diff --git a/openpype/modules/interfaces.py b/openpype/modules/interfaces.py index ad5a18eafc5..770a72fd3b0 100644 --- a/openpype/modules/interfaces.py +++ b/openpype/modules/interfaces.py @@ -274,8 +274,8 @@ class ITrayAction(ITrayModule): necessary. """ - admin_action = False - _admin_submenu = None + submenu = None + _submenus = {} _action_item = None @property @@ -292,13 +292,12 @@ def on_action_trigger(self): def tray_menu(self, tray_menu): from qtpy import QtWidgets - if self.admin_action: - menu = self.admin_submenu(tray_menu) + if self.submenu: + menu = self.get_submenu(tray_menu, self.submenu) action = QtWidgets.QAction(self.label, menu) menu.addAction(action) if not menu.menuAction().isVisible(): menu.menuAction().setVisible(True) - else: action = QtWidgets.QAction(self.label, tray_menu) tray_menu.addAction(action) @@ -313,14 +312,15 @@ def tray_exit(self): return @staticmethod - def admin_submenu(tray_menu): - if ITrayAction._admin_submenu is None: + def get_submenu(tray_menu, submenu_name): + if submenu_name not in ITrayAction._submenus: from qtpy import QtWidgets - admin_submenu = QtWidgets.QMenu("Admin", tray_menu) - admin_submenu.menuAction().setVisible(False) - ITrayAction._admin_submenu = admin_submenu - return ITrayAction._admin_submenu + submenu = QtWidgets.QMenu(submenu_name, tray_menu) + submenu.menuAction().setVisible(False) + tray_menu.addMenu(submenu) + ITrayAction._submenus[submenu_name] = submenu + return ITrayAction._submenus[submenu_name] class ITrayService(ITrayModule): diff --git a/openpype/modules/log_viewer/log_view_module.py b/openpype/modules/log_viewer/log_view_module.py index 1cafbe4fbde..3462435e43d 100644 --- a/openpype/modules/log_viewer/log_view_module.py +++ b/openpype/modules/log_viewer/log_view_module.py @@ -1,12 +1,19 @@ from openpype import AYON_SERVER_ENABLED -from openpype.modules import OpenPypeModule, ITrayModule +from openpype.modules import OpenPypeModule, ITrayAction -class LogViewModule(OpenPypeModule, ITrayModule): +class LogViewModule(OpenPypeModule, ITrayAction): name = "log_viewer" + label = "Show Logs" + submenu = "More Tools" - def initialize(self, modules_settings): - logging_settings = modules_settings[self.name] + def __init__(self, manager, settings): + self.window = None + + super().__init__(manager, settings) + + def initialize(self, _modules_settings): + logging_settings = _modules_settings[self.name] self.enabled = logging_settings["enabled"] if AYON_SERVER_ENABLED: self.enabled = False @@ -23,24 +30,14 @@ def tray_init(self): "Couldn't set Logging GUI due to error.", exc_info=True ) - # Definition of Tray menu - def tray_menu(self, tray_menu): - from qtpy import QtWidgets - # Menu for Tray App - menu = QtWidgets.QMenu('Logging', tray_menu) - - show_action = QtWidgets.QAction("Show Logs", menu) - show_action.triggered.connect(self._show_logs_gui) - menu.addAction(show_action) - - tray_menu.addMenu(menu) - def tray_start(self): return def tray_exit(self): - return + # Close window UI + if self.window: + self.window.close() - def _show_logs_gui(self): + def on_action_trigger(self): if self.window: self.window.show() diff --git a/openpype/modules/project_manager_action.py b/openpype/modules/project_manager_action.py index bf55e1544df..058ee8f087b 100644 --- a/openpype/modules/project_manager_action.py +++ b/openpype/modules/project_manager_action.py @@ -5,7 +5,7 @@ class ProjectManagerAction(OpenPypeModule, ITrayAction): label = "Project Manager (beta)" name = "project_manager" - admin_action = True + submenu = "Admin" def initialize(self, modules_settings): enabled = False diff --git a/openpype/modules/python_console_interpreter/module.py b/openpype/modules/python_console_interpreter/module.py index cb99c05e37d..e6e7394cf8d 100644 --- a/openpype/modules/python_console_interpreter/module.py +++ b/openpype/modules/python_console_interpreter/module.py @@ -4,7 +4,7 @@ class PythonInterpreterAction(OpenPypeModule, ITrayAction): label = "Console" name = "python_interpreter" - admin_action = True + submenu = "More Tools" def initialize(self, modules_settings): self.enabled = True diff --git a/openpype/modules/python_console_interpreter/window/widgets.py b/openpype/modules/python_console_interpreter/window/widgets.py index b670352f44d..0a90c497edc 100644 --- a/openpype/modules/python_console_interpreter/window/widgets.py +++ b/openpype/modules/python_console_interpreter/window/widgets.py @@ -43,7 +43,7 @@ class PythonInterpreterRegistry(JSONSettingRegistry): def __init__(self): self.vendor = "pypeclub" self.product = "openpype" - name = "python_interpreter_tool" + name = "python_interpreter" path = appdirs.user_data_dir(self.product, self.vendor) super(PythonInterpreterRegistry, self).__init__(name, path) diff --git a/openpype/modules/settings_action.py b/openpype/modules/settings_action.py index 54baf079542..498ea92180a 100644 --- a/openpype/modules/settings_action.py +++ b/openpype/modules/settings_action.py @@ -9,7 +9,7 @@ class SettingsAction(OpenPypeModule, ITrayAction): """Action to show Settings tool.""" name = "settings" label = "Studio Settings" - admin_action = True + submenu = "Admin" def __init__(self, manager, settings): self.settings_window = None @@ -86,7 +86,7 @@ def show_settings_window(self): class LocalSettingsAction(OpenPypeModule, ITrayAction): """Action to show Settings tool.""" name = "local_settings" - label = "Settings" + label = "Local Settings" def __init__(self, manager, settings): self.settings_window = None diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 8a926979205..26c9c495986 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -15,7 +15,7 @@ get_representations, get_representation_by_id, ) -from openpype.modules import OpenPypeModule, ITrayModule, IPluginPaths +from openpype.modules import OpenPypeModule, ITrayAction, IPluginPaths from openpype.settings import ( get_project_settings, get_system_settings, @@ -44,7 +44,7 @@ log = Logger.get_logger("SyncServer") -class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths): +class SyncServerModule(OpenPypeModule, ITrayAction, IPluginPaths): """ Synchronization server that is syncing published files from local to any of implemented providers (like GDrive, S3 etc.) @@ -111,6 +111,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths): name = "sync_server" label = "Sync Queue" + submenu = "More Tools" def initialize(self, module_settings): """ @@ -1366,6 +1367,9 @@ def get_repre_info_for_versions(self, project_name, version_ids, """ End of Public API """ + def on_action_trigger(self): + self.show_widget() + def get_local_file_path(self, project_name, site_name, file_path): """ Externalized for app @@ -1463,19 +1467,6 @@ def server_exit(self): exc_info=True ) - def tray_menu(self, parent_menu): - if not self.enabled: - return - - from qtpy import QtWidgets - """Add menu or action to Tray(or parent)'s menu""" - action = QtWidgets.QAction(self.label, parent_menu) - action.triggered.connect(self.show_widget) - parent_menu.addAction(action) - parent_menu.addSeparator() - - self.action_show_widget = action - @property def is_running(self): return self.sync_server_thread.is_running diff --git a/openpype/modules/update_zxp_extensions_action.py b/openpype/modules/update_zxp_extensions_action.py new file mode 100644 index 00000000000..d94472bb729 --- /dev/null +++ b/openpype/modules/update_zxp_extensions_action.py @@ -0,0 +1,41 @@ +import os + +from openpype import AYON_SERVER_ENABLED +from openpype.modules import OpenPypeModule, ITrayAction +from openpype.settings import get_system_settings + +import igniter # noqa: E402 +from igniter import BootstrapRepos # noqa: E402 + + +class UpdateZXPExtensionsAction(OpenPypeModule, ITrayAction): + name = "update_zxp_extensions" + label = "Update ZXP Extensions" + submenu = "More Tools" + + def __init__(self, manager, settings): + super().__init__(manager, settings) + + def initialize(self, _modules_settings): + self.enabled = True + if AYON_SERVER_ENABLED: + self.enabled = False + + def tray_init(self): + return + + def tray_start(self): + return + + def tray_exit(self): + return + + def on_action_trigger(self): + # install latest version to user data dir + bootstrap = BootstrapRepos() + + openpype_version = bootstrap.find_openpype_version(os.environ["OPENPYPE_VERSION"]) + + system_settings = get_system_settings() + zxp_hosts_to_update = bootstrap.get_zxp_extensions_to_update(openpype_version, system_settings, force=True) + igniter.open_update_window(openpype_version, zxp_hosts_to_update) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index ca548efa7dc..3f6c1bf6134 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -15,6 +15,7 @@ get_openpype_execute_args, run_detached_process, ) +from openpype.lib.local_settings import get_openpype_username from openpype.lib.openpype_version import ( op_version_control_available, get_expected_version, @@ -468,14 +469,40 @@ def _main_thread_execution(self): def initialize_modules(self): """Add modules to tray.""" from openpype.modules import ( - ITrayAction, ITrayService ) - self.modules_manager.initialize(self, self.tray_widget.menu) + # Menu header + system_settings = get_system_settings() + studio_name = system_settings["general"]["studio_name"] + + header_label = QtWidgets.QLabel("OpenPype {}".format(studio_name)) + header_label.setStyleSheet( + "background: qlineargradient(x1: 0, y1: 0, x2: 0.7, y2: 1, stop: 0 #3bebb9, stop: 1.0 #52abd7);" + "font-weight: bold; color: #003740; margin: 0; padding: 8px 6px;") + + header_widget = QtWidgets.QWidgetAction(self.tray_widget.menu) + header_widget.setDefaultWidget(header_label) + + self.tray_widget.menu.addAction(header_widget) + + # Username info as a non-clickable item in the menu + # Note: Double space before the username for readability + username_label = QtWidgets.QLabel("User: {}".format(str(get_openpype_username()))) + username_label.setStyleSheet("margin: 0; padding: 8px 6px;") + + username_widget = QtWidgets.QWidgetAction(self.tray_widget.menu) + username_widget.setDefaultWidget(username_label) + + self.tray_widget.menu.addAction(username_widget) - admin_submenu = ITrayAction.admin_submenu(self.tray_widget.menu) - self.tray_widget.menu.addMenu(admin_submenu) + # Add version item (and potentially "Update & Restart" item) + self._add_version_item() + + self.tray_widget.menu.addSeparator() + + # Add enabled modules + self.modules_manager.initialize(self, self.tray_widget.menu) # Add services if they are services_submenu = ITrayService.services_submenu(self.tray_widget.menu) @@ -484,8 +511,6 @@ def initialize_modules(self): # Add separator self.tray_widget.menu.addSeparator() - self._add_version_item() - # Add Exit action to menu exit_action = QtWidgets.QAction("Exit", self.tray_widget) exit_action.triggered.connect(self.tray_widget.exit) @@ -601,7 +626,7 @@ def _add_version_item(self): subversion = os.environ.get("OPENPYPE_SUBVERSION") client_name = os.environ.get("OPENPYPE_CLIENT") - version_string = openpype.version.__version__ + version_string = "Ver.: {}".format(openpype.version.__version__) # double space for readability if subversion: version_string += " ({})".format(subversion) @@ -619,7 +644,6 @@ def _add_version_item(self): self.tray_widget.menu.addAction(version_action) self.tray_widget.menu.addAction(restart_action) - self.tray_widget.menu.addSeparator() self._restart_action = restart_action diff --git a/pyproject.toml b/pyproject.toml index 3fb751e9c8e..530e00a28dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -167,6 +167,14 @@ hash = "3894dec7e4e521463891a869586850e8605f5fd604858b674c87323bf33e273d" url = "https://distribute.openpype.io/thirdparty/OpenColorIO-Configs-1.0.2.zip" hash = "4ac17c1f7de83465e6f51dd352d7117e07e765b66d00443257916c828e35b6ce" +[openpype.thirdparty.ex_man_cmd.windows] +url = "https://github.com/quadproduction/ex_man_cmd/releases/download/1.0.0/ex_man_cmd-windows.zip" +hash = "e73d705b4077c24c869136a3ba0b5348f579122de2f8821493e4371b34fd9b90" + +[openpype.thirdparty.ex_man_cmd.darwin] +url = "https://github.com/quadproduction/ex_man_cmd/releases/download/1.0.0/ex_man_cmd-macos.zip" +hash = "3f758601b4664ed1b41835236050ee2799fcced5673a7b5b4ce9922a7cb79136" + [tool.pyright] include = [ "igniter", diff --git a/start.py b/start.py index 82d6e71a326..73248bbca5d 100644 --- a/start.py +++ b/start.py @@ -412,6 +412,21 @@ def set_avalon_environments(): }) +def update_zxp_extensions(openpype_version): + from openpype.settings import get_system_settings + + system_settings = get_system_settings() + zxp_hosts_to_update = bootstrap.get_zxp_extensions_to_update(openpype_version, system_settings) + if not zxp_hosts_to_update: + return + + in_headless_mode = os.getenv("OPENPYPE_HEADLESS_MODE") == "1" + if in_headless_mode: + bootstrap.update_zxp_extensions(openpype_version, zxp_hosts_to_update) + else: + igniter.open_update_window(openpype_version, zxp_hosts_to_update) + + def set_modules_environments(): """Set global environments for OpenPype modules. @@ -711,7 +726,7 @@ def _install_and_initialize_version(openpype_version: OpenPypeVersion, delete_zi def _find_frozen_openpype(use_version: str = None, - use_staging: bool = False) -> Path: + use_staging: bool = False) -> OpenPypeVersion: """Find OpenPype to run from frozen code. This will process and modify environment variables: @@ -722,7 +737,7 @@ def _find_frozen_openpype(use_version: str = None, use_staging (bool, optional): Prefer *staging* flavor over production. Returns: - Path: Path to version to be used. + OpenPypeVersion: Version to be used. Raises: RuntimeError: If no OpenPype version are found. @@ -774,12 +789,9 @@ def _find_frozen_openpype(use_version: str = None, # get local frozen version and add it to detected version so if it is # newer it will be used instead. if installed_version == openpype_version: - version_path = _bootstrap_from_code(use_version) - openpype_version = OpenPypeVersion( - version=BootstrapRepos.get_version(version_path), - path=version_path) + openpype_version = _bootstrap_from_code(use_version) _initialize_environment(openpype_version) - return version_path + return openpype_version in_headless_mode = os.getenv("OPENPYPE_HEADLESS_MODE") == "1" if not installed_version.is_compatible(openpype_version): @@ -809,24 +821,29 @@ def _find_frozen_openpype(use_version: str = None, pass if not is_inside: + from openpype.settings import get_system_settings + + system_settings = get_system_settings() # install latest version to user data dir + zxp_hosts_to_update = bootstrap.get_zxp_extensions_to_update(openpype_version, system_settings, force=True) if in_headless_mode: version_path = bootstrap.install_version( openpype_version, force=True ) + bootstrap.update_zxp_extensions(openpype_version, zxp_hosts_to_update) else: - version_path = igniter.open_update_window(openpype_version) + version_path = igniter.open_update_window(openpype_version, zxp_hosts_to_update) openpype_version.path = version_path _initialize_environment(openpype_version) - return openpype_version.path + return openpype_version _install_and_initialize_version(openpype_version) - return openpype_version.path + return openpype_version -def _bootstrap_from_code(use_version): +def _bootstrap_from_code(use_version) -> OpenPypeVersion: """Bootstrap live code (or the one coming with frozen OpenPype). Args: @@ -848,17 +865,18 @@ def _bootstrap_from_code(use_version): if getattr(sys, 'frozen', False): local_version = bootstrap.get_version(Path(_openpype_root)) - switch_str = f" - will switch to {use_version}" if use_version and use_version != local_version else "" # noqa - _print(f" - booting version: {local_version}{switch_str}") - if not local_version: + local_version_str = str(local_version) + switch_str = f" - will switch to {use_version}" if use_version and use_version != local_version_str else "" # noqa + _print(f" - booting version: {local_version_str}{switch_str}") + if not local_version_str: raise OpenPypeVersionNotFound( f"Cannot find version at {_openpype_root}") else: - # get current version of OpenPype - local_version = OpenPypeVersion.get_installed_version_str() + # Get current version of OpenPype + local_version = OpenPypeVersion.get_installed_version() # All cases when should be used different version than build - if use_version and use_version != local_version: + if use_version and use_version != str(local_version): if use_version: # Explicit version should be used version_to_use = bootstrap.find_openpype_version(use_version) @@ -882,8 +900,7 @@ def _bootstrap_from_code(use_version): _openpype_root = version_to_use.path.as_posix() else: - os.environ["OPENPYPE_VERSION"] = local_version - version_path = Path(_openpype_root) + os.environ["OPENPYPE_VERSION"] = str(local_version) os.environ["OPENPYPE_REPOS_ROOT"] = _openpype_root # add self to sys.path of current process @@ -921,7 +938,7 @@ def _bootstrap_from_code(use_version): os.environ["PYTHONPATH"] = os.pathsep.join(split_paths) - return version_path + return local_version def _boot_validate_versions(use_version, local_version): @@ -1108,31 +1125,33 @@ def boot(): # ------------------------------------------------------------------------ # Find OpenPype versions # ------------------------------------------------------------------------ + openpype_version = None # WARNING: Environment OPENPYPE_REPOS_ROOT may change if frozen OpenPype # is executed if getattr(sys, 'frozen', False): # find versions of OpenPype to be used with frozen code try: - version_path = _find_frozen_openpype(use_version, use_staging) + openpype_version = _find_frozen_openpype(use_version, use_staging) except OpenPypeVersionNotFound as exc: _boot_handle_missing_version(local_version, str(exc)) sys.exit(1) - except RuntimeError as e: # no version to run _print(f"!!! {e}", True) sys.exit(1) + # validate version - _print(f">>> Validating version in frozen [ {str(version_path)} ]") - result = bootstrap.validate_openpype_version(version_path) + _print(f">>> Validating version in frozen [ {str(openpype_version.path)} ]") + result = bootstrap.validate_openpype_version(openpype_version.path) + if not result[0]: _print(f"!!! Invalid version: {result[1]}", True) sys.exit(1) + _print("--- version is valid") else: try: - version_path = _bootstrap_from_code(use_version) - + openpype_version = _bootstrap_from_code(use_version) except OpenPypeVersionNotFound as exc: _boot_handle_missing_version(local_version, str(exc)) sys.exit(1) @@ -1159,7 +1178,8 @@ def boot(): # Do the program display popups to the users regarding updates or incompatibilities os.environ["OPENPYPE_VERSION_CHECK_POPUP"] = "False" if "disable_version_popup" in commands else "True" - + _print(">>> update ZXP extensions ...") + update_zxp_extensions(openpype_version) _print(">>> loading environments ...") # Avalon environments must be set before avalon module is imported _print(" - for Avalon ...") @@ -1169,7 +1189,7 @@ def boot(): _print(" - for modules ...") set_modules_environments() - assert version_path, "Version path not defined." + assert openpype_version, "Version path not defined." # print info when not running scripts defined in 'silent commands' if all(arg not in silent_commands for arg in sys.argv): @@ -1177,7 +1197,7 @@ def boot(): from openpype.version import __version__ info = get_info(use_staging) - info.insert(0, f">>> Using OpenPype from [ {version_path} ]") + info.insert(0, f">>> Using OpenPype from [ {str(openpype_version.path.resolve())} ]") t_width = 20 try: diff --git a/tools/create_zip.ps1 b/tools/create_zip.ps1 index e5ebc7678b5..a5f3eee57b6 100644 --- a/tools/create_zip.ps1 +++ b/tools/create_zip.ps1 @@ -92,6 +92,9 @@ if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) { Write-Color -Text "OK" -Color Green } +Write-Color -Text ">>> ", "Re-generating ZXP files ... " -Color Green, Gray -NoNewline +powershell "$PSScriptRoot\zxp\generate_zxp.ps1" + Write-Color -Text ">>> ", "Cleaning cache files ... " -Color Green, Gray -NoNewline Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse| Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force -Recurse Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force @@ -103,3 +106,5 @@ $env:PYTHONPATH="$($openpype_root);$($env:PYTHONPATH)" $env:OPENPYPE_ROOT="$($openpype_root)" & "$($env:POETRY_HOME)\bin\poetry" run python "$($openpype_root)\tools\create_zip.py" $ARGS Set-Location -Path $current_dir + +Write-Host "" diff --git a/tools/zxp/generate_zxp.ps1 b/tools/zxp/generate_zxp.ps1 new file mode 100644 index 00000000000..bfa98052c0c --- /dev/null +++ b/tools/zxp/generate_zxp.ps1 @@ -0,0 +1,22 @@ +$PATH_OPENPYPE_DIR=$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$PSScriptRoot\..\..") + +$PATH_ZXP_SIGN_SOFTWARE=$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$PSScriptRoot\zxp_sign_cmd\windows\win64\ZXPSignCmd.exe") +$PATH_ZXP_CERTIFICATE=$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$PSScriptRoot\sign_certificate.p12") + +$HOSTS="aftereffects","photoshop" + +foreach ($CURR_HOST in $HOSTS) { + $HOST_PATH="$PATH_OPENPYPE_DIR\openpype\hosts\$CURR_HOST" + $HOST_ZXP_SOURCE="$HOST_PATH\api\extension\" + $HOST_ZXP_DEST="$HOST_PATH\api\extension.zxp" + + Write-Host "Generating ZXP for $CURR_HOST, destination: $HOST_ZXP_DEST" + + # First delete previous ZXP file (if exists) + if (Test-Path $HOST_ZXP_DEST) { + Remove-Item $HOST_ZXP_DEST -Force + } + + # Generate and sign the ZXP file with the OpenPype certificate + & $PATH_ZXP_SIGN_SOFTWARE -sign $HOST_ZXP_SOURCE $HOST_ZXP_DEST $PATH_ZXP_CERTIFICATE OpenPype +} diff --git a/tools/zxp/sign_certificate.p12 b/tools/zxp/sign_certificate.p12 new file mode 100644 index 00000000000..90c01581f93 Binary files /dev/null and b/tools/zxp/sign_certificate.p12 differ diff --git a/tools/zxp/zxp_sign_cmd/SigningTechNote_CC.pdf b/tools/zxp/zxp_sign_cmd/SigningTechNote_CC.pdf new file mode 100644 index 00000000000..661e71b2f93 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/SigningTechNote_CC.pdf differ diff --git a/tools/zxp/zxp_sign_cmd/macos/ReleaseNotes.md b/tools/zxp/zxp_sign_cmd/macos/ReleaseNotes.md new file mode 100644 index 00000000000..b4087b7e930 --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/macos/ReleaseNotes.md @@ -0,0 +1,22 @@ +Build Number: 4.1.2 +**Build Information** + +Codex Entry - + +Product : ZXPSign + +Subproducts : Library-64bit-OpenSSL1.1, Dynamic-lib-64-OpenSSL1.1, Library-minimal-64-OpenSSL1.1, CommandLine-64bit-OpenSSL1.1, Dynamic-lib-min-64-OpenSSL1.1 + +Version : 4.1 + +Build : 2 + +Build for ZXPSignLib For Mac built with OpenSSL1.1.0l and Xcode10 ( sdk 10.14 ). This build is only supported for 64-bit binaries. + +**Changes In this build** + +OpenSSL version upgraded from 1.0.2t to 1.1.0l built with Xcode10 ( sdk 10.14 ) + +**Background** + +Major update of OpenSSL version from 1.0.2t to 1.1.0l. diff --git a/tools/zxp/zxp_sign_cmd/macos/VersionInfo.xml b/tools/zxp/zxp_sign_cmd/macos/VersionInfo.xml new file mode 100644 index 00000000000..1ef86276764 --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/macos/VersionInfo.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit new file mode 100644 index 00000000000..d1a22995b79 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit differ diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal new file mode 100644 index 00000000000..39529611546 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal differ diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Info.plist b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Info.plist new file mode 100644 index 00000000000..5c1dfef91f6 --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.ZXPSignCmd-64bit-minimal + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit-minimal b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit-minimal new file mode 100644 index 00000000000..69b56927c53 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit-minimal.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit-minimal differ diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Info.plist b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Info.plist new file mode 100644 index 00000000000..a0bcb903d95 --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.ZXPSignCmd-64bit + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit new file mode 100644 index 00000000000..a499d176487 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/macos/ZXPSignCmd-64bit.dSYM/Contents/Resources/DWARF/ZXPSignCmd-64bit differ diff --git a/tools/zxp/zxp_sign_cmd/windows/ReleaseNotes.md b/tools/zxp/zxp_sign_cmd/windows/ReleaseNotes.md new file mode 100644 index 00000000000..0dc8ae7595d --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/windows/ReleaseNotes.md @@ -0,0 +1,20 @@ +Build Number: 4.1.103 +**Build Information** + +Product : ZXPSign + +Subproducts : Command Line-OpenSSL1.1, Library-minimal-OpenSSL1.1, Library-OpenSSL1.1, Dynamic-library-OpenSSL1.1, Dynamic-lib-minimal-OpenSSL1.1 + +Version : 4.1 + +Build : 103 + +Build for ZXPSignLib For Windows built with Visual Studio 2015 compiled with MT Flags + +**Changes In this build** + +OpenSSL version upgraded from 1.0.2t to 1.1.0l built with Visual Studio 2015 compiled with MT Flags + +**Background** + +Major update of OpenSSL version from 1.0.2t to 1.1.0l compiled with MT Flags diff --git a/tools/zxp/zxp_sign_cmd/windows/win32/VersionInfo.xml b/tools/zxp/zxp_sign_cmd/windows/win32/VersionInfo.xml new file mode 100644 index 00000000000..b5cf387456d --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/windows/win32/VersionInfo.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tools/zxp/zxp_sign_cmd/windows/win32/ZXPSignCmd.exe b/tools/zxp/zxp_sign_cmd/windows/win32/ZXPSignCmd.exe new file mode 100644 index 00000000000..f8c514a1a5d Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/windows/win32/ZXPSignCmd.exe differ diff --git a/tools/zxp/zxp_sign_cmd/windows/win64/VersionInfo.xml b/tools/zxp/zxp_sign_cmd/windows/win64/VersionInfo.xml new file mode 100644 index 00000000000..cad57731cde --- /dev/null +++ b/tools/zxp/zxp_sign_cmd/windows/win64/VersionInfo.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tools/zxp/zxp_sign_cmd/windows/win64/ZXPSignCmd.exe b/tools/zxp/zxp_sign_cmd/windows/win64/ZXPSignCmd.exe new file mode 100644 index 00000000000..a62ef2f2b12 Binary files /dev/null and b/tools/zxp/zxp_sign_cmd/windows/win64/ZXPSignCmd.exe differ