From 604b7a0aefdc5e7504710057a187d136c7a99a17 Mon Sep 17 00:00:00 2001 From: Thomas Holder Date: Sat, 5 Aug 2023 15:15:28 +0200 Subject: [PATCH] setuptools --- .github/workflows/build.yml | 3 +- create_shadertext.py | 4 +- modules/pymol/plugins/installation.py | 13 +++-- setup.py | 77 ++++++++++++++++++++++++--- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6db1a6db..0792b1c0e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,10 +25,11 @@ jobs: python-is-python3 python3-biopython python3-dev - python3-distutils python3-numpy python3-pil + python3-pybind11 python3-pytest + python3-setuptools - name: Install collada2gltf run: | diff --git a/create_shadertext.py b/create_shadertext.py index d9ccc600b..1d1c8e811 100644 --- a/create_shadertext.py +++ b/create_shadertext.py @@ -7,7 +7,7 @@ from collections import defaultdict from os.path import dirname from subprocess import Popen, PIPE -from distutils import dir_util +from pathlib import Path def create_all(generated_dir, pymoldir="."): ''' @@ -30,7 +30,7 @@ def __init__(self, filename): self.out = cStringIO.StringIO() self.filename = filename else: - dir_util.mkpath(os.path.dirname(filename)) + Path(filename).parent.mkdir(parents=True, exist_ok=True) self.out = open(filename, "w") self.filename = None def close(self): diff --git a/modules/pymol/plugins/installation.py b/modules/pymol/plugins/installation.py index 2e2d2efb4..69f479052 100644 --- a/modules/pymol/plugins/installation.py +++ b/modules/pymol/plugins/installation.py @@ -55,9 +55,16 @@ def cmp_version(v1, v2): if v2 == '': return 1 try: - from distutils.version import StrictVersion as Version - return cmp(Version(v1), Version(v2)) - except: + from packaging.version import parse as Version + v1 = Version(v1) + v2 = Version(v2) + if v1 < v2: + return -1 + if v1 > v2: + return 1 + return 0 + except Exception as ex: + print(ex) print(' Warning: Version parsing failed for', v1, 'and/or', v2) return 0 diff --git a/setup.py b/setup.py index 0a7730b7a..0de43ebc2 100644 --- a/setup.py +++ b/setup.py @@ -6,9 +6,10 @@ # It may assume that all of PyMOL's external dependencies are # pre-installed into the system. -from distutils.core import setup, Extension -from distutils.util import change_root from glob import glob +from typing import Iterator, Union, Callable +import functools +import shlex import shutil import sys, os, re @@ -66,7 +67,7 @@ class options: if options.help_distutils: sys.argv.append("--help") -if True: +if False: import monkeypatch_distutils monkeypatch_distutils.set_parallel_jobs(options.jobs) @@ -130,10 +131,74 @@ def guess_msgpackc(): return 'no' +def change_root(new_root: str, pathname: str) -> str: + _, pathname = os.path.splitdrive(pathname) + pathname = pathname.lstrip("/\\") + assert not os.path.isabs(pathname) + return os.path.join(new_root, pathname) + + +def test_change_root(): + if os.path.sep == "\\": + assert change_root(r"X:\new\root", r"rel\path") == r"X:\new\root\rel\path" + assert change_root(r"X:\new\root", r"\abs\path") == r"X:\new\root\abs\path" + assert change_root(r"X:\new\root", r"C:\abs\path") == r"X:\new\root\abs\path" + else: + assert change_root("/new/root", "rel/path") == "/new/root/rel/path" + assert change_root("/new/root", "/abs/path") == "/new/root/abs/path" + + +test_change_root() + +cached_getmtime = functools.cache(os.path.getmtime) + + +def get_dot_d_depencendies(obj: str) -> Iterator[str]: + """ + Parse .d file (Makefile syntax) + + Args: + obj: Path to object file + + Returns: + Iterator over dependency paths + """ + with open(os.path.splitext(obj)[0] + '.d') as handle: + contents = handle.read().split(': ', 1)[1] + contents = contents.replace('\\\n', ' ') + for dep in shlex.split(contents): + yield dep + + +def needs_compile_dot_d(obj: str, src: str) -> bool: + """ + Returns true if any source or header dependency changed for the object + file. Parses dependencies from .d files, ignores the `src` argument. + """ + try: + obj_mtime = os.path.getmtime(obj) + return any(obj_mtime < cached_getmtime(dep) + for dep in get_dot_d_depencendies(obj)) + except EnvironmentError: + return True + + # Important: import 'distutils.command' modules after monkeypatch_distutils -from distutils.command.build_ext import build_ext -from distutils.command.build_py import build_py -from distutils.command.install import install +from setuptools import setup +from setuptools import Extension +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.install import install +from pybind11.setup_helpers import ParallelCompile + +os.environ["CPPFLAGS"] = os.getenv("CPPFLAGS", "") + " -MMD" + +if options.jobs: + os.environ["NPY_NUM_BUILD_JOBS"] = str(options.jobs) + +ParallelCompile("NPY_NUM_BUILD_JOBS", + needs_recompile=needs_compile_dot_d).install() + class build_ext_pymol(build_ext): def initialize_options(self):