diff --git a/.dockerignore b/.dockerignore index 07c1c151ce..9c506b9964 100644 --- a/.dockerignore +++ b/.dockerignore @@ -87,7 +87,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -142,5 +142,6 @@ cython_debug/ .poetry/ .github/ vendor/bin/ +vendor/python/ docs/ website/ diff --git a/.gitignore b/.gitignore index 221a2f2241..fa3fae1ad2 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ Temporary Items /dist/ /vendor/bin/* +/vendor/python/* /.venv /venv/ diff --git a/Dockerfile b/Dockerfile index 2d8ed27b15..cef83b5811 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ # Build Pype docker image -FROM centos:7 AS builder -ARG OPENPYPE_PYTHON_VERSION=3.7.10 +FROM debian:bookworm-slim AS builder +ARG OPENPYPE_PYTHON_VERSION=3.7.12 +LABEL maintainer="info@openpype.io" +LABEL description="Docker Image to build and run OpenPype" LABEL org.opencontainers.image.name="pypeclub/openpype" LABEL org.opencontainers.image.title="OpenPype Docker Image" LABEL org.opencontainers.image.url="https://openpype.io/" @@ -9,56 +11,49 @@ LABEL org.opencontainers.image.source="https://github.com/pypeclub/pype" USER root -# update base -RUN yum -y install deltarpm \ - && yum -y update \ - && yum clean all +ARG DEBIAN_FRONTEND=noninteractive -# add tools we need -RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ - && yum -y install centos-release-scl \ - && yum -y install \ +# update base +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ bash \ - which \ git \ - devtoolset-7-gcc* \ - make \ cmake \ + make \ curl \ wget \ - gcc \ - zlib-devel \ - bzip2 \ - bzip2-devel \ - readline-devel \ - sqlite sqlite-devel \ - openssl-devel \ - tk-devel libffi-devel \ - qt5-qtbase-devel \ - patchelf \ - && yum clean all + build-essential \ + checkinstall \ + libssl-dev \ + zlib1g-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncursesw5-dev \ + xz-utils \ + tk-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libffi-dev \ + liblzma-dev \ + patchelf + +SHELL ["/bin/bash", "-c"] RUN mkdir /opt/openpype -# RUN useradd -m pype -# RUN chown pype /opt/openpype -# USER pype -RUN curl https://pyenv.run | bash -ENV PYTHON_CONFIGURE_OPTS --enable-shared - -RUN echo 'export PATH="$HOME/.pyenv/bin:$PATH"'>> $HOME/.bashrc \ +RUN curl https://pyenv.run | bash \ + && echo 'export PATH="$HOME/.pyenv/bin:$PATH"'>> $HOME/.bashrc \ && echo 'eval "$(pyenv init -)"' >> $HOME/.bashrc \ && echo 'eval "$(pyenv virtualenv-init -)"' >> $HOME/.bashrc \ - && echo 'eval "$(pyenv init --path)"' >> $HOME/.bashrc -RUN source $HOME/.bashrc && pyenv install ${OPENPYPE_PYTHON_VERSION} + && echo 'eval "$(pyenv init --path)"' >> $HOME/.bashrc \ + && source $HOME/.bashrc && pyenv install ${OPENPYPE_PYTHON_VERSION} COPY . /opt/openpype/ -RUN rm -rf /openpype/.poetry || echo "No Poetry installed yet." -# USER root -# RUN chown -R pype /opt/openpype -RUN chmod +x /opt/openpype/tools/create_env.sh && chmod +x /opt/openpype/tools/build.sh -# USER pype +RUN chmod +x /opt/openpype/tools/create_env.sh && chmod +x /opt/openpype/tools/build.sh WORKDIR /opt/openpype @@ -67,16 +62,8 @@ RUN cd /opt/openpype \ && pyenv local ${OPENPYPE_PYTHON_VERSION} RUN source $HOME/.bashrc \ - && ./tools/create_env.sh - -RUN source $HOME/.bashrc \ + && ./tools/create_env.sh \ && ./tools/fetch_thirdparty_libs.sh RUN source $HOME/.bashrc \ - && bash ./tools/build.sh \ - && cp /usr/lib64/libffi* ./build/exe.linux-x86_64-3.7/lib \ - && cp /usr/lib64/libssl* ./build/exe.linux-x86_64-3.7/lib \ - && cp /usr/lib64/libcrypto* ./build/exe.linux-x86_64-3.7/lib - -RUN cd /opt/openpype \ - rm -rf ./vendor/bin + && bash ./tools/build.sh diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 new file mode 100644 index 0000000000..f3b257e66b --- /dev/null +++ b/Dockerfile.centos7 @@ -0,0 +1,98 @@ +# Build Pype docker image +FROM centos:7 AS builder +ARG OPENPYPE_PYTHON_VERSION=3.7.10 + +LABEL org.opencontainers.image.name="pypeclub/openpype" +LABEL org.opencontainers.image.title="OpenPype Docker Image" +LABEL org.opencontainers.image.url="https://openpype.io/" +LABEL org.opencontainers.image.source="https://github.com/pypeclub/pype" + +USER root + +# update base +RUN yum -y install deltarpm \ + && yum -y update \ + && yum clean all + +# add tools we need +RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ + && yum -y install centos-release-scl \ + && yum -y install \ + bash \ + which \ + git \ + make \ + devtoolset-7 \ + cmake \ + curl \ + wget \ + gcc \ + zlib-devel \ + bzip2 \ + bzip2-devel \ + readline-devel \ + sqlite sqlite-devel \ + openssl-devel \ + openssl-libs \ + tk-devel libffi-devel \ + patchelf \ + automake \ + autoconf \ + ncurses \ + ncurses-devel \ + qt5-qtbase-devel \ + && yum clean all + +# we need to build our own patchelf +WORKDIR /temp-patchelf +RUN git clone https://github.com/NixOS/patchelf.git . \ + && source scl_source enable devtoolset-7 \ + && ./bootstrap.sh \ + && ./configure \ + && make \ + && make install + +RUN mkdir /opt/openpype +# RUN useradd -m pype +# RUN chown pype /opt/openpype +# USER pype + +RUN curl https://pyenv.run | bash +# ENV PYTHON_CONFIGURE_OPTS --enable-shared + +RUN echo 'export PATH="$HOME/.pyenv/bin:$PATH"'>> $HOME/.bashrc \ + && echo 'eval "$(pyenv init -)"' >> $HOME/.bashrc \ + && echo 'eval "$(pyenv virtualenv-init -)"' >> $HOME/.bashrc \ + && echo 'eval "$(pyenv init --path)"' >> $HOME/.bashrc +RUN source $HOME/.bashrc && pyenv install ${OPENPYPE_PYTHON_VERSION} + +COPY . /opt/openpype/ +RUN rm -rf /openpype/.poetry || echo "No Poetry installed yet." +# USER root +# RUN chown -R pype /opt/openpype +RUN chmod +x /opt/openpype/tools/create_env.sh && chmod +x /opt/openpype/tools/build.sh + +# USER pype + +WORKDIR /opt/openpype + +RUN cd /opt/openpype \ + && source $HOME/.bashrc \ + && pyenv local ${OPENPYPE_PYTHON_VERSION} + +RUN source $HOME/.bashrc \ + && ./tools/create_env.sh + +RUN source $HOME/.bashrc \ + && ./tools/fetch_thirdparty_libs.sh + +RUN source $HOME/.bashrc \ + && bash ./tools/build.sh + +RUN cp /usr/lib64/libffi* ./build/exe.linux-x86_64-3.7/lib \ + && cp /usr/lib64/libssl* ./build/exe.linux-x86_64-3.7/lib \ + && cp /usr/lib64/libcrypto* ./build/exe.linux-x86_64-3.7/lib \ + && cp /root/.pyenv/versions/${OPENPYPE_PYTHON_VERSION}/lib/libpython* ./build/exe.linux-x86_64-3.7/lib + +RUN cd /opt/openpype \ + rm -rf ./vendor/bin diff --git a/README.md b/README.md index 209af24c75..0e450fc48d 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,12 @@ Easiest way to build OpenPype on Linux is using [Docker](https://www.docker.com/ sudo ./tools/docker_build.sh ``` +This will by default use Debian as base image. If you need to make Centos 7 compatible build, please run: + +```sh +sudo ./tools/docker_build.sh centos7 +``` + If all is successful, you'll find built OpenPype in `./build/` folder. #### Manual build @@ -158,6 +164,11 @@ you'll need also additional libraries for Qt5: ```sh sudo apt install qt5-default ``` +or if you are on Ubuntu > 20.04, there is no `qt5-default` packages so you need to install its content individually: + +```sh +sudo apt-get install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools +```
diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 748c7857a9..7779fff6ec 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -48,7 +48,7 @@ class _ModuleClass(object): def __getattr__(self, attr_name): if attr_name not in self.__attributes__: - if attr_name in ("__path__"): + if attr_name in ("__path__", "__file__"): return None raise ImportError("No module named {}.{}".format( self.name, attr_name @@ -104,6 +104,9 @@ class _InterfacesClass(_ModuleClass): """ def __getattr__(self, attr_name): if attr_name not in self.__attributes__: + if attr_name in ("__path__", "__file__"): + return None + # Fake Interface if is not missing self.__attributes__[attr_name] = type( attr_name, diff --git a/openpype/modules/default_modules/ftrack/ftrack_module.py b/openpype/modules/default_modules/ftrack/ftrack_module.py index 5a1fdbc276..73a4dfee82 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_module.py +++ b/openpype/modules/default_modules/ftrack/ftrack_module.py @@ -371,7 +371,7 @@ class FtrackModule( return self.tray_module.validate() def tray_exit(self): - return self.tray_module.stop_action_server() + self.tray_module.tray_exit() def set_credentials_to_env(self, username, api_key): os.environ["FTRACK_API_USER"] = username or "" diff --git a/openpype/modules/default_modules/ftrack/tray/ftrack_tray.py b/openpype/modules/default_modules/ftrack/tray/ftrack_tray.py index 34e4646767..c6201a94f6 100644 --- a/openpype/modules/default_modules/ftrack/tray/ftrack_tray.py +++ b/openpype/modules/default_modules/ftrack/tray/ftrack_tray.py @@ -289,6 +289,10 @@ class FtrackTrayWrapper: parent_menu.addMenu(tray_menu) + def tray_exit(self): + self.stop_action_server() + self.stop_timer_thread() + # Definition of visibility of each menu actions def set_menu_visibility(self): self.tray_server_menu.menuAction().setVisible(self.bool_logged) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 1199db0611..7687d056f8 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -150,6 +150,7 @@ class TimersManager(OpenPypeModule, ITrayService): def tray_exit(self): if self._idle_manager: self._idle_manager.stop() + self._idle_manager.wait() def start_timer(self, project_name, asset_name, task_name, hierarchy): """ diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index ab6b27bdaf..92fffe6f9c 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -214,7 +214,8 @@ class BaseWidget(QtWidgets.QWidget): def _paste_value_actions(self, menu): output = [] # Allow paste of value only if were copied from this UI - mime_data = QtWidgets.QApplication.clipboard().mimeData() + clipboard = QtWidgets.QApplication.clipboard() + mime_data = clipboard.mimeData() mime_value = mime_data.data("application/copy_settings_value") # Skip if there is nothing to do if not mime_value: diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index be2264340b..5f9051344d 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -508,7 +508,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget): first_invalid_item = invalid_items[0] self.scroll_widget.ensureWidgetVisible(first_invalid_item) if first_invalid_item.isVisible(): - first_invalid_item.setFocus(True) + first_invalid_item.setFocus() return False def on_saved(self, saved_tab_widget): diff --git a/openpype/tools/settings/settings/dict_mutable_widget.py b/openpype/tools/settings/settings/dict_mutable_widget.py index cfb9d4a4b1..9afce7259e 100644 --- a/openpype/tools/settings/settings/dict_mutable_widget.py +++ b/openpype/tools/settings/settings/dict_mutable_widget.py @@ -128,9 +128,9 @@ class ModifiableDictEmptyItem(QtWidgets.QWidget): def add_new_item(self, key=None, label=None): input_field = self.entity_widget.add_new_key(key, label) if self.collapsible_key: - self.key_input.setFocus(True) + self.key_input.setFocus() else: - input_field.key_input.setFocus(True) + input_field.key_input.setFocus() return input_field def _on_add_clicked(self): @@ -563,7 +563,7 @@ class ModifiableDictItem(QtWidgets.QWidget): def on_add_clicked(self): widget = self.entity_widget.add_new_key(None, None) - widget.key_input.setFocus(True) + widget.key_input.setFocus() def on_edit_pressed(self): if not self.key_input.isVisible(): diff --git a/openpype/tools/settings/settings/list_item_widget.py b/openpype/tools/settings/settings/list_item_widget.py index 17412a30b9..128af92631 100644 --- a/openpype/tools/settings/settings/list_item_widget.py +++ b/openpype/tools/settings/settings/list_item_widget.py @@ -357,7 +357,7 @@ class ListWidget(InputWidget): new_entity = self.entity.add_new_item(row) input_field = self._input_fields_by_entity_id.get(new_entity.id) if input_field is not None: - input_field.input_field.setFocus(True) + input_field.input_field.setFocus() return new_entity def add_row(self, child_entity, row=None): diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 3050e206ce..0f817d7130 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -268,7 +268,6 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon): # Set modules self.tray_man = TrayManager(self, self.parent) - self.tray_man.initialize_modules() # Add menu to Context of SystemTrayIcon self.setContextMenu(self.menu) @@ -291,6 +290,17 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon): self._doubleclick = False self._click_pos = None + self._initializing_modules = False + + @property + def initializing_modules(self): + return self._initializing_modules + + def initialize_modules(self): + self._initializing_modules = True + self.tray_man.initialize_modules() + self._initializing_modules = False + def _click_timer_timeout(self): self._click_timer.stop() doubleclick = self._doubleclick @@ -334,38 +344,48 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon): QtCore.QCoreApplication.exit() -class TrayMainWindow(QtWidgets.QMainWindow): - """ TrayMainWindow is base of Pype application. - - Every widget should have set this window as parent because - QSystemTrayIcon widget is not allowed to be a parent of any widget. - """ - +class PypeTrayStarter(QtCore.QObject): def __init__(self, app): - super(TrayMainWindow, self).__init__() - self.app = app + app.setQuitOnLastWindowClosed(False) + self._app = app + self._splash = None - self.tray_widget = SystemTrayIcon(self) - self.tray_widget.show() + main_window = QtWidgets.QMainWindow() + tray_widget = SystemTrayIcon(main_window) + start_timer = QtCore.QTimer() + start_timer.setInterval(100) + start_timer.start() -class PypeTrayApplication(QtWidgets.QApplication): - """Qt application manages application's control flow.""" + start_timer.timeout.connect(self._on_start_timer) - def __init__(self): - super(PypeTrayApplication, self).__init__(sys.argv) - # Allows to close widgets without exiting app - self.setQuitOnLastWindowClosed(False) + self._main_window = main_window + self._tray_widget = tray_widget + self._timer_counter = 0 + self._start_timer = start_timer - # Sets up splash - splash_widget = self.set_splash() + def _on_start_timer(self): + if self._timer_counter == 0: + self._timer_counter += 1 + splash = self._get_splash() + splash.show() + self._tray_widget.show() - splash_widget.show() - self.processEvents() - self.main_window = TrayMainWindow(self) - splash_widget.hide() + elif self._timer_counter == 1: + self._timer_counter += 1 + self._tray_widget.initialize_modules() - def set_splash(self): + elif not self._tray_widget.initializing_modules: + splash = self._get_splash() + splash.hide() + self._start_timer.stop() + + def _get_splash(self): + if self._splash is None: + self._splash = self._create_splash() + return self._splash + + def _create_splash(self): splash_pix = QtGui.QPixmap(resources.get_openpype_splash_filepath()) splash = QtWidgets.QSplashScreen(splash_pix) splash.setMask(splash_pix.mask()) @@ -377,7 +397,12 @@ class PypeTrayApplication(QtWidgets.QApplication): def main(): - app = PypeTrayApplication() + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication([]) + + starter = PypeTrayStarter(app) + # TODO remove when pype.exe will have an icon if os.name == "nt": import ctypes diff --git a/poetry.lock b/poetry.lock index 10b049cd0a..e5f5919a01 100644 --- a/poetry.lock +++ b/poetry.lock @@ -288,15 +288,20 @@ test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pret [[package]] name = "cx-freeze" -version = "6.6" +version = "6.7" description = "Create standalone executables from Python scripts" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -cx-Logging = {version = ">=3.0", markers = "sys_platform == \"win32\""} -importlib-metadata = ">=3.1.1" +cx-logging = {version = ">=3.0", markers = "sys_platform == \"win32\""} +importlib-metadata = ">=4.3.1" + +[package.source] +type = "legacy" +url = "https://distribute.openpype.io/wheels" +reference = "openpype" [[package]] name = "cx-logging" @@ -1013,34 +1018,6 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "pyqt5" -version = "5.15.4" -description = "Python bindings for the Qt cross platform application toolkit" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -PyQt5-Qt5 = ">=5.15" -PyQt5-sip = ">=12.8,<13" - -[[package]] -name = "pyqt5-qt5" -version = "5.15.2" -description = "The subset of a Qt installation needed by PyQt5." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pyqt5-sip" -version = "12.9.0" -description = "The sip module support for PyQt5" -category = "main" -optional = false -python-versions = ">=3.5" - [[package]] name = "pyrsistent" version = "0.17.3" @@ -1814,17 +1791,7 @@ cryptography = [ {file = "cryptography-3.4.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3"}, {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, ] -cx-freeze = [ - {file = "cx_Freeze-6.6-cp36-cp36m-win32.whl", hash = "sha256:b3d3a6bcd1a07c50b4e1c907f14842642156110e63a99cd5c73b8a24751e9b97"}, - {file = "cx_Freeze-6.6-cp36-cp36m-win_amd64.whl", hash = "sha256:1935266ec644ea4f7e584985f44cefc0622a449a09980d990833a1a2afcadac8"}, - {file = "cx_Freeze-6.6-cp37-cp37m-win32.whl", hash = "sha256:1eac2b0f254319cc641ce25bd83337effd7936092562fde701f3ffb40e0274ec"}, - {file = "cx_Freeze-6.6-cp37-cp37m-win_amd64.whl", hash = "sha256:2bc46ef6d510811b6002f34a3ae4cbfdea44e18644febd2a404d3ee8e48a9fc4"}, - {file = "cx_Freeze-6.6-cp38-cp38-win32.whl", hash = "sha256:46eb50ebc46f7ae236d16c6a52671ab0f7bb479bea668da19f4b6de3cc413e9e"}, - {file = "cx_Freeze-6.6-cp38-cp38-win_amd64.whl", hash = "sha256:8c3b00476ce385bb58595bffce55aed031e5a6e16ab6e14d8bee9d1d569e46c3"}, - {file = "cx_Freeze-6.6-cp39-cp39-win32.whl", hash = "sha256:6e9340cbcf52d4836980ecc83ddba4f7704ff6654dd41168c146b74f512977ce"}, - {file = "cx_Freeze-6.6-cp39-cp39-win_amd64.whl", hash = "sha256:2fcf1c8b77ae5c06f45be3a9aff79e1dd808c0d624e97561f840dec5ea9b214a"}, - {file = "cx_Freeze-6.6.tar.gz", hash = "sha256:c4af8ad3f7e7d71e291c1dec5d0fb26bbe92df834b098ed35434c901fbd6762f"}, -] +cx-freeze = [] cx-logging = [ {file = "cx_Logging-3.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9fcd297e5c51470521c47eff0f86ba844aeca6be97e13c3e2114ebdf03fa3c96"}, {file = "cx_Logging-3.0-cp36-cp36m-win32.whl", hash = "sha256:0df4be47c5022cc54316949e283403214568ef599817ced0c0972183d6d4fabb"}, @@ -2355,38 +2322,6 @@ pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] -pyqt5 = [ - {file = "PyQt5-5.15.4-cp36.cp37.cp38.cp39-abi3-macosx_10_13_intel.whl", hash = "sha256:8c0848ba790a895801d5bfd171da31cad3e551dbcc4e59677a3b622de2ceca98"}, - {file = "PyQt5-5.15.4-cp36.cp37.cp38.cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:883a549382fc22d29a0568f3ef20b38c8e7ab633a59498ac4eb63a3bf36d3fd3"}, - {file = "PyQt5-5.15.4-cp36.cp37.cp38.cp39-none-win32.whl", hash = "sha256:a88526a271e846e44779bb9ad7a738c6d3c4a9d01e15a128ecfc6dd4696393b7"}, - {file = "PyQt5-5.15.4-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:213bebd51821ed89b4d5b35bb10dbe67564228b3568f463a351a08e8b1677025"}, - {file = "PyQt5-5.15.4.tar.gz", hash = "sha256:2a69597e0dd11caabe75fae133feca66387819fc9bc050f547e5551bce97e5be"}, -] -pyqt5-qt5 = [ - {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"}, -] -pyqt5-sip = [ - {file = "PyQt5_sip-12.9.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:d85002238b5180bce4b245c13d6face848faa1a7a9e5c6e292025004f2fd619a"}, - {file = "PyQt5_sip-12.9.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:83c3220b1ca36eb8623ba2eb3766637b19eb0ce9f42336ad8253656d32750c0a"}, - {file = "PyQt5_sip-12.9.0-cp36-cp36m-win32.whl", hash = "sha256:d8b2bdff7bbf45bc975c113a03b14fd669dc0c73e1327f02706666a7dd51a197"}, - {file = "PyQt5_sip-12.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:69a3ad4259172e2b1aa9060de211efac39ddd734a517b1924d9c6c0cc4f55f96"}, - {file = "PyQt5_sip-12.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:42274a501ab4806d2c31659170db14c282b8313d2255458064666d9e70d96206"}, - {file = "PyQt5_sip-12.9.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a8701892a01a5a2a4720872361197cc80fdd5f49c8482d488ddf38c9c84f055"}, - {file = "PyQt5_sip-12.9.0-cp37-cp37m-win32.whl", hash = "sha256:ac57d796c78117eb39edd1d1d1aea90354651efac9d3590aac67fa4983f99f1f"}, - {file = "PyQt5_sip-12.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4347bd81d30c8e3181e553b3734f91658cfbdd8f1a19f254777f906870974e6d"}, - {file = "PyQt5_sip-12.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c446971c360a0a1030282a69375a08c78e8a61d568bfd6dab3dcc5cf8817f644"}, - {file = "PyQt5_sip-12.9.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fc43f2d7c438517ee33e929e8ae77132749c15909afab6aeece5fcf4147ffdb5"}, - {file = "PyQt5_sip-12.9.0-cp38-cp38-win32.whl", hash = "sha256:055581c6fed44ba4302b70eeb82e979ff70400037358908f251cd85cbb3dbd93"}, - {file = "PyQt5_sip-12.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:c5216403d4d8d857ec4a61f631d3945e44fa248aa2415e9ee9369ab7c8a4d0c7"}, - {file = "PyQt5_sip-12.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a25b9843c7da6a1608f310879c38e6434331aab1dc2fe6cb65c14f1ecf33780e"}, - {file = "PyQt5_sip-12.9.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:dd05c768c2b55ffe56a9d49ce6cc77cdf3d53dbfad935258a9e347cbfd9a5850"}, - {file = "PyQt5_sip-12.9.0-cp39-cp39-win32.whl", hash = "sha256:4f8e05fe01d54275877c59018d8e82dcdd0bc5696053a8b830eecea3ce806121"}, - {file = "PyQt5_sip-12.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:b09f4cd36a4831229fb77c424d89635fa937d97765ec90685e2f257e56a2685a"}, - {file = "PyQt5_sip-12.9.0.tar.gz", hash = "sha256:d3e4489d7c2b0ece9d203ae66e573939f7f60d4d29e089c9f11daa17cfeaae32"}, -] pyrsistent = [ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, ] diff --git a/pyproject.toml b/pyproject.toml index fb47c7eff6..085538d306 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ Pillow = "^8.1" # only used for slates prototype pyblish-base = "^1.8.8" pynput = "^1.7.2" # idle manager in tray pymongo = "^3.11.2" -pyqt5 = "^5.12.2" # ideally should be replaced with PySide2 "Qt.py" = "^1.3.3" speedcopy = "^2.1" six = "^1.15" @@ -63,14 +62,16 @@ jinxed = [ python3-xlib = { version="*", markers = "sys_platform == 'linux'"} enlighten = "^1.9.0" slack-sdk = "^3.6.0" +requests = "2.25.1" pysftp = "^0.2.9" dropbox = "^11.20.0" + [tool.poetry.dev-dependencies] flake8 = "^3.7" autopep8 = "^1.4" coverage = "*" -cx_freeze = "^6.6" +cx_freeze = { version = "6.7", source = "openpype" } GitPython = "^3.1.17" jedi = "^0.13" Jinja2 = "^2.11" @@ -89,7 +90,6 @@ wheel = "*" enlighten = "*" # cool terminal progress bars toml = "^0.10.2" # for parsing pyproject.toml - [tool.poetry.urls] "Bug Tracker" = "https://github.com/pypeclub/openpype/issues" "Discussions" = "https://github.com/pypeclub/openpype/discussions" @@ -104,6 +104,12 @@ build-backend = "poetry.core.masonry.api" [openpype] +[openpype.pyside2] +# note: in here we can use pip version specifiers as this is installed with pip until +# Poetry will support custom location (-t flag for pip) +# https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers +version = "==5.15.2" + [openpype.thirdparty.ffmpeg.windows] url = "https://distribute.openpype.io/thirdparty/ffmpeg-4.4-windows.zip" hash = "dd51ba29d64ee238e7c4c3c7301b19754c3f0ee2e2a729c20a0e2789e72db925" diff --git a/setup.py b/setup.py index 55098cb0b4..cd3ed4f82c 100644 --- a/setup.py +++ b/setup.py @@ -59,13 +59,14 @@ includes = [] excludes = [ "openpype" ] -bin_includes = [] +bin_includes = [ + "vendor" +] include_files = [ "igniter", "openpype", "repos", "schema", - "vendor", "LICENSE", "README.md" ] diff --git a/start.py b/start.py index a4c58b8fc7..29b4921d19 100644 --- a/start.py +++ b/start.py @@ -124,6 +124,10 @@ else: paths.append(frozen_libs) os.environ["PYTHONPATH"] = os.pathsep.join(paths) +# Vendored python modules that must not be in PYTHONPATH environment but +# are required for OpenPype processes +vendor_python_path = os.path.join(OPENPYPE_ROOT, "vendor", "python") +sys.path.insert(0, vendor_python_path) import blessed # noqa: E402 import certifi # noqa: E402 diff --git a/tools/build.sh b/tools/build.sh index c44e7157af..bc79f03db7 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -58,7 +58,7 @@ BICyan='\033[1;96m' # Cyan BIWhite='\033[1;97m' # White args=$@ -disable_submodule_update = 0 +disable_submodule_update=0 while :; do case $1 in --no-submodule-update) @@ -90,6 +90,7 @@ done ############################################################################### detect_python () { echo -e "${BIGreen}>>>${RST} Using python \c" + command -v python >/dev/null 2>&1 || { echo -e "${BIRed}- NOT FOUND${RST} ${BIYellow}You need Python 3.7 installed to continue.${RST}"; return 1; } local version_command version_command="import sys;print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))" local python_version @@ -122,7 +123,7 @@ clean_pyc () { local path path=$openpype_root echo -e "${BIGreen}>>>${RST} Cleaning pyc at [ ${BIWhite}$path${RST} ] ... \c" - find "$path" -path ./build -prune -o -regex '^.*\(__pycache__\|\.py[co]\)$' -delete + find "$path" -path ./build -o -regex '^.*\(__pycache__\|\.py[co]\)$' -delete echo -e "${BIGreen}DONE${RST}" } @@ -173,7 +174,7 @@ main () { else echo -e "${BIYellow}NOT FOUND${RST}" echo -e "${BIYellow}***${RST} We need to install Poetry and virtual env ..." - . "$openpype_root/tools/create_env.sh" || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return; } + . "$openpype_root/tools/create_env.sh" || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return 1; } fi if [ "$disable_submodule_update" == 1 ]; then @@ -184,9 +185,9 @@ if [ "$disable_submodule_update" == 1 ]; then fi echo -e "${BIGreen}>>>${RST} Building ..." if [[ "$OSTYPE" == "linux-gnu"* ]]; then - "$POETRY_HOME/bin/poetry" run python "$openpype_root/setup.py" build > "$openpype_root/build/build.log" || { echo -e "${BIRed}!!!${RST} Build failed, see the build log."; return; } + "$POETRY_HOME/bin/poetry" run python "$openpype_root/setup.py" build &> "$openpype_root/build/build.log" || { echo -e "${BIRed}!!!${RST} Build failed, see the build log."; return 1; } elif [[ "$OSTYPE" == "darwin"* ]]; then - "$POETRY_HOME/bin/poetry" run python "$openpype_root/setup.py" bdist_mac > "$openpype_root/build/build.log" || { echo -e "${BIRed}!!!${RST} Build failed, see the build log."; return; } + "$POETRY_HOME/bin/poetry" run python "$openpype_root/setup.py" bdist_mac &> "$openpype_root/build/build.log" || { echo -e "${BIRed}!!!${RST} Build failed, see the build log."; return 1; } fi "$POETRY_HOME/bin/poetry" run python "$openpype_root/tools/build_dependencies.py" @@ -210,4 +211,6 @@ if [ "$disable_submodule_update" == 1 ]; then echo -e "${BIWhite}$openpype_root/build${RST} directory." } -main +return_code=0 +main || return_code=$? +exit $return_code diff --git a/tools/build_dependencies.py b/tools/build_dependencies.py index e5a430e220..8eaf9d87e3 100644 --- a/tools/build_dependencies.py +++ b/tools/build_dependencies.py @@ -23,6 +23,7 @@ import sys import site from distutils.util import get_platform import platform +import subprocess from pathlib import Path import shutil import blessed @@ -93,18 +94,18 @@ _print("Getting venv site-packages ...") assert site_pkg, "No venv site-packages are found." _print(f"Working with: {site_pkg}", 2) - -build_dir = "exe.{}-{}".format(get_platform(), sys.version[0:3]) +openpype_root = Path(os.path.dirname(__file__)).parent # create full path if platform.system().lower() == "darwin": - build_dir = Path(os.path.dirname(__file__)).parent.joinpath( + build_dir = openpype_root.joinpath( "build", "OpenPype.app", "Contents", "MacOS") else: - build_dir = Path(os.path.dirname(__file__)).parent / "build" / build_dir + build_subdir = "exe.{}-{}".format(get_platform(), sys.version[0:3]) + build_dir = openpype_root / "build" / build_subdir _print(f"Using build at {build_dir}", 2) if not build_dir.exists(): @@ -112,7 +113,28 @@ if not build_dir.exists(): _print("Probably freezing of code failed. Check ./build/build.log", 3) sys.exit(1) + +def _progress(_base, _names): + progress_bar.update() + return [] + + deps_dir = build_dir / "dependencies" +vendor_dir = build_dir / "vendor" +vendor_src = openpype_root / "vendor" + +# copy vendor files +_print("Copying vendor files ...") + +total_files = count_folders(vendor_src) +progress_bar = enlighten.Counter( + total=total_files, desc="Copying vendor files ...", + units="%", color=(64, 128, 222)) + +shutil.copytree(vendor_src.as_posix(), + vendor_dir.as_posix(), + ignore=_progress) +progress_bar.close() # copy all files _print("Copying dependencies ...") @@ -122,12 +144,6 @@ progress_bar = enlighten.Counter( total=total_files, desc="Processing Dependencies", units="%", color=(53, 178, 202)) - -def _progress(_base, _names): - progress_bar.update() - return [] - - shutil.copytree(site_pkg.as_posix(), deps_dir.as_posix(), ignore=_progress) @@ -135,15 +151,29 @@ progress_bar.close() # iterate over frozen libs and create list to delete libs_dir = build_dir / "lib" -# On Windows "python3.dll" is needed for PyQt5 from the build. -if platform.system().lower() == "windows": - src = Path(libs_dir / "PyQt5" / "python3.dll") - dst = Path(deps_dir / "PyQt5" / "python3.dll") - if src.exists(): - shutil.copyfile(src, dst) - else: - _print("Could not find {}".format(src), 1) - sys.exit(1) + +# On Linux use rpath from source libraries in destination libraries +if platform.system().lower() == "linux": + src_pyside_dir = openpype_root / "vendor" / "python" / "PySide2" + dst_pyside_dir = build_dir / "vendor" / "python" / "PySide2" + src_rpath_per_so_file = {} + for filepath in src_pyside_dir.glob("*.so"): + filename = filepath.name + rpath = ( + subprocess.check_output(["patchelf", "--print-rpath", filepath]) + .decode("utf-8") + .strip() + ) + src_rpath_per_so_file[filename] = rpath + + for filepath in dst_pyside_dir.glob("*.so"): + filename = filepath.name + if filename not in src_rpath_per_so_file: + continue + src_rpath = src_rpath_per_so_file[filename] + subprocess.check_call( + ["patchelf", "--set-rpath", src_rpath, filepath] + ) to_delete = [] # _print("Finding duplicates ...") diff --git a/tools/create_env.sh b/tools/create_env.sh index cc9eddc317..917ddc36ba 100755 --- a/tools/create_env.sh +++ b/tools/create_env.sh @@ -88,6 +88,7 @@ done ############################################################################### detect_python () { echo -e "${BIGreen}>>>${RST} Using python \c" + command -v python >/dev/null 2>&1 || { echo -e "${BIRed}- NOT FOUND${RST} ${BIYellow}You need Python 3.7 installed to continue.${RST}"; return 1; } local version_command="import sys;print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))" local python_version="$(python <<< ${version_command})" oIFS="$IFS" @@ -125,7 +126,7 @@ clean_pyc () { local path path=$openpype_root echo -e "${BIGreen}>>>${RST} Cleaning pyc at [ ${BIWhite}$path${RST} ] ... \c" - find "$path" -path ./build -prune -o -regex '^.*\(__pycache__\|\.py[co]\)$' -delete + find "$path" -path ./build -o -regex '^.*\(__pycache__\|\.py[co]\)$' -delete echo -e "${BIGreen}DONE${RST}" } @@ -166,7 +167,7 @@ main () { echo -e "${BIGreen}OK${RST}" else echo -e "${BIYellow}NOT FOUND${RST}" - install_poetry || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return; } + install_poetry || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return 1; } fi if [ -f "$openpype_root/poetry.lock" ]; then @@ -175,7 +176,11 @@ main () { echo -e "${BIGreen}>>>${RST} Installing dependencies ..." fi - "$POETRY_HOME/bin/poetry" install --no-root $poetry_verbosity || { echo -e "${BIRed}!!!${RST} Poetry environment installation failed"; return; } + "$POETRY_HOME/bin/poetry" install --no-root $poetry_verbosity || { echo -e "${BIRed}!!!${RST} Poetry environment installation failed"; return 1; } + if [ $? -ne 0 ] ; then + echo -e "${BIRed}!!!${RST} Virtual environment creation failed." + return 1 + fi echo -e "${BIGreen}>>>${RST} Cleaning cache files ..." clean_pyc @@ -183,11 +188,15 @@ main () { # reinstall these because of bug in poetry? or cx_freeze? # cx_freeze will crash on missing __pychache__ on these but # reinstalling them solves the problem. - echo -e "${BIGreen}>>>${RST} Fixing pycache bug ..." - "$POETRY_HOME/bin/poetry" run python -m pip install --force-reinstall pip - "$POETRY_HOME/bin/poetry" run pip install --force-reinstall setuptools - "$POETRY_HOME/bin/poetry" run pip install --force-reinstall wheel - "$POETRY_HOME/bin/poetry" run python -m pip install --force-reinstall pip + echo -e "${BIGreen}>>>${RST} Post-venv creation fixes ..." + local openpype_index=$("$POETRY_HOME/bin/poetry" run python "$openpype_root/tools/parse_pyproject.py" tool.poetry.source.0.url) + echo -e "${BIGreen}- ${RST} Using index: ${BIWhite}$openpype_index${RST}" + "$POETRY_HOME/bin/poetry" run pip install setuptools==49.6.0 + "$POETRY_HOME/bin/poetry" run pip install --disable-pip-version-check --force-reinstall wheel + "$POETRY_HOME/bin/poetry" run python -m pip install --disable-pip-version-check --force-reinstall pip + "$POETRY_HOME/bin/poetry" run pip install --disable-pip-version-check --force-reinstall cx_freeze -i $openpype_index --extra-index-url https://pypi.org/simple } -main -3 +return_code=0 +main || return_code=$? +exit $return_code diff --git a/tools/docker_build.sh b/tools/docker_build.sh index 7600fe044b..04c26424eb 100755 --- a/tools/docker_build.sh +++ b/tools/docker_build.sh @@ -20,6 +20,41 @@ realpath () { echo $(cd $(dirname "$1"); pwd)/$(basename "$1") } +create_container () { + if [ ! -f "$openpype_root/build/docker-image.id" ]; then + echo -e "${BIRed}!!!${RST} Docker command failed, cannot find image id." + exit 1 + fi + local id=$(<"$openpype_root/build/docker-image.id") + echo -e "${BIYellow}---${RST} Creating container from $id ..." + cid="$(docker create $id bash)" + if [ $? -ne 0 ] ; then + echo -e "${BIRed}!!!${RST} Cannot create container." + exit 1 + fi +} + +retrieve_build_log () { + create_container + echo -e "${BIYellow}***${RST} Copying build log to ${BIWhite}$openpype_root/build/build.log${RST}" + docker cp "$cid:/opt/openpype/build/build.log" "$openpype_root/build" +} + +openpype_root=$(realpath $(dirname $(dirname "${BASH_SOURCE[0]}"))) + + +if [ -z $1 ]; then + dockerfile="Dockerfile" +else + dockerfile="Dockerfile.$1" + if [ ! -f "$openpype_root/$dockerfile" ]; then + echo -e "${BIRed}!!!${RST} Dockerfile for specifed platform ${BIWhite}$1${RST} doesn't exist." + exit 1 + else + echo -e "${BIGreen}>>>${RST} Using Dockerfile for ${BIWhite}$1${RST} ..." + fi +fi + # Main main () { openpype_root=$(realpath $(dirname $(dirname "${BASH_SOURCE[0]}"))) @@ -28,36 +63,35 @@ main () { echo -e "${BIYellow}---${RST} Cleaning build directory ..." rm -rf "$openpype_root/build" && mkdir "$openpype_root/build" > /dev/null - version_command="import os;exec(open(os.path.join('$openpype_root', 'openpype', 'version.py')).read());print(__version__);" - openpype_version="$(python3 <<< ${version_command})" + local version_command="import os;exec(open(os.path.join('$openpype_root', 'openpype', 'version.py')).read());print(__version__);" + local openpype_version="$(python3 <<< ${version_command})" echo -e "${BIGreen}>>>${RST} Running docker build ..." - docker build --pull --no-cache -t pypeclub/openpype:$openpype_version . + # docker build --pull --no-cache -t pypeclub/openpype:$openpype_version . + docker build --pull --iidfile $openpype_root/build/docker-image.id -t pypeclub/openpype:$openpype_version -f $dockerfile . if [ $? -ne 0 ] ; then + echo $? echo -e "${BIRed}!!!${RST} Docker build failed." + retrieve_build_log return 1 fi echo -e "${BIGreen}>>>${RST} Copying build from container ..." - echo -e "${BIYellow}---${RST} Creating container from pypeclub/openpype:$openpype_version ..." - id="$(docker create -ti pypeclub/openpype:$openpype_version bash)" - if [ $? -ne 0 ] ; then - echo -e "${BIRed}!!!${RST} Cannot create just built container." - return 1 - fi + create_container echo -e "${BIYellow}---${RST} Copying ..." - docker cp "$id:/opt/openpype/build/exe.linux-x86_64-3.7" "$openpype_root/build" + docker cp "$cid:/opt/openpype/build/exe.linux-x86_64-3.7" "$openpype_root/build" + docker cp "$cid:/opt/openpype/build/build.log" "$openpype_root/build" if [ $? -ne 0 ] ; then echo -e "${BIRed}!!!${RST} Copying failed." return 1 fi echo -e "${BIGreen}>>>${RST} Fixing user ownership ..." - username="$(logname)" + local username="$(logname)" chown -R $username ./build echo -e "${BIGreen}>>>${RST} All done, you can delete container:" - echo -e "${BIYellow}$id${RST}" + echo -e "${BIYellow}$cid${RST}" } return_code=0 diff --git a/tools/fetch_thirdparty_libs.py b/tools/fetch_thirdparty_libs.py index 75ee052950..b616beab27 100644 --- a/tools/fetch_thirdparty_libs.py +++ b/tools/fetch_thirdparty_libs.py @@ -20,6 +20,7 @@ import hashlib import tarfile import zipfile import time +import subprocess term = blessed.Terminal() @@ -65,12 +66,40 @@ def _print(msg: str, message_type: int = 0) -> None: print("{}{}".format(header, msg)) - -_print("Processing third-party dependencies ...") start_time = time.time_ns() openpype_root = Path(os.path.dirname(__file__)).parent pyproject = toml.load(openpype_root / "pyproject.toml") +_print("Handling PySide2 Qt framework ...") +pyside2_version = None +try: + pyside2_version = pyproject["openpype"]["pyside2"]["version"] + _print("We'll install PySide2{}".format(pyside2_version)) +except AttributeError: + _print("No PySide2 version was specified, using latest available.", 2) + +pyside2_arg = "PySide2" if not pyside2_version else "PySide2{}".format(pyside2_version) # noqa: E501 +python_vendor_dir = openpype_root / "vendor" / "python" +try: + subprocess.run( + [sys.executable, "-m", "pip", "install", "--upgrade", + pyside2_arg, "-t", str(python_vendor_dir)], + check=True, stdout=subprocess.DEVNULL) +except subprocess.CalledProcessError as e: + _print("Error during PySide2 installation.", 1) + _print(str(e), 1) + sys.exit(1) + +# Remove libraries for QtSql which don't have available libraries +# by default and Postgre library would require to modify rpath of dependency platform_name = platform.system().lower() +if platform_name == "darwin": + pyside2_sqldrivers_dir = ( + python_vendor_dir / "PySide2" / "Qt" / "plugins" / "sqldrivers" + ) + for filepath in pyside2_sqldrivers_dir.iterdir(): + os.remove(str(filepath)) + +_print("Processing third-party dependencies ...") try: thirdparty = pyproject["openpype"]["thirdparty"] diff --git a/tools/fetch_thirdparty_libs.sh b/tools/fetch_thirdparty_libs.sh index 93d0674965..940af76ed6 100755 --- a/tools/fetch_thirdparty_libs.sh +++ b/tools/fetch_thirdparty_libs.sh @@ -98,4 +98,4 @@ main () { "$POETRY_HOME/bin/poetry" run python "$openpype_root/tools/fetch_thirdparty_libs.py" } -main \ No newline at end of file +main diff --git a/tools/parse_pyproject.py b/tools/parse_pyproject.py new file mode 100644 index 0000000000..dacecd88d0 --- /dev/null +++ b/tools/parse_pyproject.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +"""Parse pyproject.toml and return its values. + +Useful for shell scripts to know more about OpenPype build. +""" +import sys +import os +import toml +from pathlib import Path +import click + + +@click.command() +@click.argument("keys", nargs=-1, type=click.STRING) +def main(keys): + """Get values from `pyproject.toml`. + + You can specify dot separated keys from `pyproject.toml` + as arguments and this script will return them on separate + lines. If key doesn't exists, None is returned. + + """ + openpype_root = Path(os.path.dirname(__file__)).parent + py_project = toml.load(openpype_root / "pyproject.toml") + for q in keys: + query = q.split(".") + data = py_project + + for k in query: + if isinstance(data, list): + try: + data = data[int(k)] + except IndexError: + print("None") + sys.exit() + continue + + if isinstance(data, dict): + data = data.get(k) + print(data) + + +if __name__ == "__main__": + main() diff --git a/website/docs/dev_build.md b/website/docs/dev_build.md index b3e0c24fc2..bf606ae7c0 100644 --- a/website/docs/dev_build.md +++ b/website/docs/dev_build.md @@ -19,6 +19,7 @@ We use [CX_Freeze](https://cx-freeze.readthedocs.io/en/latest) to freeze the cod This is outline of build steps. Most of them are done automatically via scripts: - Virtual environment is created using **Poetry** in `.venv` +- Necessary python modules outside of `.venv` are stored to `./vendor/python` (like `PySide2`) - Necessary third-party tools (like [ffmpeg](https://www.ffmpeg.org/), [OpenImageIO](https://github.com/OpenImageIO/oiio) and [usd libraries](https://developer.nvidia.com/usd)) are downloaded to `./vendor/bin` - OpenPype code is frozen with **cx_freeze** to `./build` @@ -55,14 +56,14 @@ For development purposes it is possible to run OpenPype directly from the source To start OpenPype from source you need to 1. Run `.\tools\create_env.ps1` to create virtual environment in `.venv` -2. Run `.\tools\fetch_thirdparty_libs.ps1` to get **ffmpeg**, **oiio** and other tools needed. +2. Run `.\tools\fetch_thirdparty_libs.ps1` to get **PySide2**, **ffmpeg**, **oiio** and other tools needed. 3. Run `.\tools\run_tray.ps1` if you have all required dependencies on your machine you should be greeted with OpenPype igniter window and once you give it your Mongo URL, with OpenPype icon in the system tray. Step 1 and 2 needs to be run only once (or when something was changed). #### To build OpenPype: 1. Run `.\tools\create_env.ps1` to create virtual environment in `.venv` -2. Run `.\tools\fetch_thirdparty_libs.ps1` to get **ffmpeg**, **oiio** and other tools needed. +2. Run `.\tools\fetch_thirdparty_libs.ps1` to get **PySide2**, **ffmpeg**, **oiio** and other tools needed. 3. `.\tools\build.ps1` to build OpenPype to `.\build` @@ -84,6 +85,13 @@ You can use Docker to build OpenPype. Just run: ```shell $ sudo ./tools/docker_build.sh ``` + +This will by default use Debian as base image. If you need to make Centos 7 compatible build, please run: + +```sh +sudo ./tools/docker_build.sh centos7 +``` + and you should have built OpenPype in `build` directory. It is using **Centos 7** as a base image. @@ -178,7 +186,7 @@ For more information about setting your build environment please refer to [pyenv #### To build Pype: 1. Run `./tools/create_env.sh` to create virtual environment in `./venv` -2. Run `./tools/fetch_thirdparty_libs.sh` to get **ffmpeg**, **oiio** and other tools needed. +2. Run `./tools/fetch_thirdparty_libs.sh` to get **PySide2**, **ffmpeg**, **oiio** and other tools needed. 3. Run `./tools/build.sh` to build pype executables in `.\build\` @@ -266,6 +274,19 @@ pywin32 = { version = "300", markers = "sys_platform == 'win32'" } For more information see [Poetry documentation](https://python-poetry.org/docs/dependency-specification/). +### Python modules as thirdparty +There are some python modules that can be available only in OpenPype and should not be propagated to any subprocess. +Best example is **PySide2** which is required to run OpenPype but can be used only in OpenPype and should not be in PYTHONPATH for most of host applications. +We've decided to separate these breaking dependencies to be able run OpenPype from code and from build the same way. + +:::warning +**PySide2** has handled special cases related to it's build process. +### Linux +- We're fixing rpath of shared objects on linux which is modified during cx freeze processing. +### MacOS +- **QtSql** libraries are removed on MacOS because their dependencies are not available and would require to modify rpath of Postgre library. +::: + ### Binary dependencies To add some binary tool or something that doesn't fit standard Python distribution methods, you can use [fetch_thirdparty_libs](#fetch_thirdparty_libs) script. It will take things defined in @@ -323,14 +344,18 @@ Same as: poetry run python ./tools/create_zip.py ``` -### docker_build.sh +### docker_build.sh *[variant]* Script to build OpenPype on [Docker](https://www.docker.com/) enabled systems - usually Linux and Windows with [Docker Desktop](https://docs.docker.com/docker-for-windows/install/) and [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/about) (WSL) installed. It must be run with administrative privileges - `sudo ./docker_build.sh`. -It will use **Centos 7** base image to build OpenPype. You'll see your build in `./build` folder. +It will use latest **Debian** base image to build OpenPype. If you need to build OpenPype for +older systems like Centos 7, use `centos7` as argument. This will use another Dockerfile to build +OpenPype with **Centos 7** as base image. + +You'll see your build in `./build` folder. ### fetch_thirdparty_libs This script will download necessary tools for OpenPype defined in `pyproject.toml` like FFMpeg,