Skip to content

Commit

Permalink
Replace monkeypatch distutils with CMake setuptools Extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
JarrettSJohnson committed May 20, 2024
1 parent 6a7e094 commit 74ffc07
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 39 deletions.
31 changes: 31 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.13)

project(${TARGET_NAME})

set(CMAKE_VERBOSE_MAKEFILE on)

add_library(${TARGET_NAME} SHARED ${ALL_SRC})

target_compile_options(${TARGET_NAME} PRIVATE ${ALL_COMP_ARGS})

set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ${SHARED_SUFFIX})

target_compile_features(${TARGET_NAME} PRIVATE cxx_std_17)

set_target_properties(${TARGET_NAME} PROPERTIES PREFIX "")

target_include_directories(${TARGET_NAME} PUBLIC ${ALL_INC_DIR})

target_link_directories(${TARGET_NAME} PUBLIC ${ALL_LIB_DIR})


if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup")
endif()

target_link_libraries(${TARGET_NAME}
${ALL_LIB}
${ALL_EXT_LINK}
)

target_compile_definitions(${TARGET_NAME} PUBLIC ${ALL_DEF})
1 change: 1 addition & 0 deletions INSTALL
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ See also: http://pymolwiki.org/index.php/Linux_Install
REQUIREMENTS

- C++17 compiler (e.g. gcc 8+)
- CMake (3.13+)
- Python 3.6+
- Pmw (Python Megawidgets) (optional, for legacy GUI/plugins)
https://github.com/schrodinger/pmw-patched
Expand Down
2 changes: 1 addition & 1 deletion contrib/champ/champ_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ static PyMethodDef champ_methods[] = {
{NULL, NULL} /* sentinel */
};

PyObject * PyInit__champ(void)
PyMODINIT_FUNC PyInit__champ(void)
{
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
Expand Down
2 changes: 1 addition & 1 deletion layer4/Cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6531,7 +6531,7 @@ static PyMethodDef Cmd_methods[] = {
extern "C" {
#endif

PyObject * PyInit__cmd(void)
PyMODINIT_FUNC PyInit__cmd(void)
{
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
Expand Down
176 changes: 139 additions & 37 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# This script only applies if you are performing a Python Distutils-based
# This script only applies if you are performing a Python setuptools-based
# installation of PyMOL.
#
# It may assume that all of PyMOL's external dependencies are
Expand All @@ -9,12 +9,16 @@
import argparse
import glob
import os
import pathlib
import re
import sys
import sysconfig
import shutil

from distutils.core import setup, Extension
from distutils.util import change_root
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.build_py import build_py
from setuptools.command.install import install

import create_shadertext

Expand All @@ -33,7 +37,6 @@ class options:
no_libxml = False
no_glut = True
use_msgpackc = 'guess'
help_distutils = False
testing = False
openvr = False
use_openmp = 'no' if MAC else 'yes'
Expand All @@ -58,8 +61,6 @@ class options:
parser.add_argument('--use-msgpackc', choices=('c++11', 'c', 'guess', 'no'),
help="c++11: use msgpack-c header-only library; c: link against "
"shared library; no: disable fast MMTF load support")
parser.add_argument('--help-distutils', action="store_true",
help="show help for distutils options and exit")
parser.add_argument('--testing', action="store_true",
help="Build C-level tests")
parser.add_argument('--openvr', dest='openvr', action='store_true')
Expand All @@ -68,10 +69,7 @@ class options:
help='Disable VMD molfile plugins (libnetcdf dependency)')
options, sys.argv[1:] = parser.parse_known_args(namespace=options)

if options.help_distutils:
sys.argv.append("--help")

if True:
if False:
import monkeypatch_distutils
monkeypatch_distutils.set_parallel_jobs(options.jobs)

Expand Down Expand Up @@ -137,16 +135,104 @@ def guess_msgpackc():
return 'no'


# 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
class CMakeExtension(Extension):

def __init__(self,
name,
sources,
include_dirs=[],
libraries=[],
library_dirs=[],
define_macros=[],
extra_link_args=[],
extra_compile_args=[]):
# don't invoke the original build_ext for this special extension
super().__init__(name, sources=[])
self.sources = sources
self.include_dirs = include_dirs
self.libraries = libraries
self.library_dirs = library_dirs
self.define_macros = define_macros
self.extra_link_args = extra_link_args
self.extra_compile_args = extra_compile_args


class build_ext_pymol(build_ext):
def initialize_options(self):
build_ext.initialize_options(self)
def initialize_options(self) -> None:
super().initialize_options()
if DEBUG and not WIN:
self.debug = True
self.debug = False

def run(self):
for ext in self.extensions:
self.build_cmake(ext)

def build_cmake(self, ext):
cwd = pathlib.Path().absolute()

# these dirs will be created in build_py, so if you don't have
# any python sources to bundle, the dirs will be missing
name_split = ext.name.split('.')
target_name = name_split[-1]
build_temp = pathlib.Path(self.build_temp) / target_name
build_temp.mkdir(parents=True, exist_ok=True)
extdir = pathlib.Path(self.get_ext_fullpath(ext.name))
extdirabs = extdir.absolute()

extdir.parent.mkdir(parents=True, exist_ok=True)

def concat_paths(paths):
return ''.join(path.replace('\\', '/') + ";" for path in paths)

config = 'Debug' if DEBUG else 'Release'
lib_output_dir = str(extdir.parent.absolute())
all_files = ext.sources
all_src = concat_paths(all_files)
all_defs = ''.join(mac[0] + ";" for mac in ext.define_macros)
all_libs = ''.join(f"{lib};" for lib in ext.libraries)
all_ext_link = ' '.join(ext.extra_link_args)
all_comp_args = ''.join(f"{arg};" for arg in ext.extra_compile_args)
all_lib_dirs = concat_paths(ext.library_dirs)
all_inc_dirs = concat_paths(ext.include_dirs)

lib_mode = "RUNTIME" if WIN else "LIBRARY"

shared_suffix = sysconfig.get_config_var('EXT_SUFFIX')

cmake_args = [
f"-DTARGET_NAME={target_name}",
f"-DCMAKE_{lib_mode}_OUTPUT_DIRECTORY={lib_output_dir}",
f"-DCMAKE_BUILD_TYPE={config}",
f"-DALL_INC_DIR={all_inc_dirs}",
f"-DALL_SRC={all_src}",
f"-DALL_DEF={all_defs}",
f"-DALL_LIB_DIR={all_lib_dirs}",
f"-DALL_LIB={all_libs}",
f"-DALL_COMP_ARGS={all_comp_args}",
f"-DALL_EXT_LINK={all_ext_link}",
f"-DSHARED_SUFFIX={shared_suffix}"
]

# example of build args
build_args = ['--config', config]
if not WIN: # Win /MP flag on compilation level
cpu_count = os.cpu_count() or 1
build_args += [f'-j{cpu_count}']

os.chdir(str(build_temp))
self.spawn(['cmake', str(cwd)] + cmake_args)
if not self.dry_run:
self.spawn(['cmake', '--build', '.'] + build_args)

if WIN:
# Move up from VS release folder
cmake_lib_loc = pathlib.Path(lib_output_dir, "Release", f"{target_name}{shared_suffix}")
if cmake_lib_loc.exists():
shutil.move(cmake_lib_loc, extdirabs)

# Troubleshooting: if fail on line above then delete all possible
# temporary CMake files including "CMakeCache.txt" in top level dir.
os.chdir(str(cwd))


class build_py_pymol(build_py):
Expand Down Expand Up @@ -175,10 +261,11 @@ def finalize_options(self):
self.pymol_path = os.path.join(
self.install_libbase, 'pymol', 'pymol_path')
elif self.root is not None:
self.pymol_path = change_root(self.root, self.pymol_path)
self.pymol_path = install_pymol.change_root(
self.root, self.pymol_path)

def run(self):
install.run(self)
super().run()
self.install_pymol_path()

if not self.no_launcher:
Expand Down Expand Up @@ -304,7 +391,7 @@ def make_launch_script(self):
'-Wno-char-subscripts',
# optimizations
"-Og" if DEBUG else "-O3",
] if not WIN else []
] if not WIN else ["/MP"]
ext_link_args = []
ext_objects = []
data_files = []
Expand Down Expand Up @@ -382,9 +469,9 @@ def make_launch_script(self):

if options.osx_frameworks:
ext_link_args += [
"-framework", "OpenGL",
"-framework OpenGL",
] + (not options.no_glut) * [
"-framework", "GLUT",
"-framework GLUT",
]
def_macros += [
("_PYMOL_OSX", None),
Expand Down Expand Up @@ -527,22 +614,37 @@ def get_packages(base, parent='', r=None):
for base in ['modules']
for x in get_packages(base))

# Python includes
inc_dirs.append(sysconfig.get_paths()['include'])
inc_dirs.append(sysconfig.get_paths()['platinclude'])

champ_inc_dirs = ['contrib/champ']
champ_inc_dirs.append(sysconfig.get_paths()['include'])
champ_inc_dirs.append(sysconfig.get_paths()['platinclude'])

if WIN:
# pyconfig.py forces linking against pythonXY.lib on MSVC
py_lib = pathlib.Path(sysconfig.get_paths()['stdlib']).parent / 'libs'
lib_dirs.append(str(py_lib))

ext_modules += [
Extension("pymol._cmd",
get_sources(pymol_src_dirs),
include_dirs=inc_dirs,
libraries=libs,
library_dirs=lib_dirs,
define_macros=def_macros,
extra_link_args=ext_link_args,
extra_compile_args=ext_comp_args,
extra_objects=ext_objects,
),

Extension("chempy.champ._champ",
get_sources(['contrib/champ']),
include_dirs=["contrib/champ"],
),
CMakeExtension(
name="pymol._cmd",
sources=get_sources(pymol_src_dirs),
include_dirs=inc_dirs,
libraries=libs,
library_dirs=lib_dirs,
define_macros=def_macros,
extra_link_args=ext_link_args,
extra_compile_args=ext_comp_args,
),

CMakeExtension(
name="chempy.champ._champ",
sources=get_sources(['contrib/champ']),
include_dirs=champ_inc_dirs,
library_dirs=lib_dirs,
),
]

distribution = setup( # Distribution meta-data
Expand Down

0 comments on commit 74ffc07

Please sign in to comment.