mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
888 lines
24 KiB
Python
888 lines
24 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# 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
|
|
# pre-installed into the system.
|
|
|
|
import argparse
|
|
import glob
|
|
import io as cStringIO
|
|
import os
|
|
import pathlib
|
|
import re
|
|
import shutil
|
|
import sys
|
|
import sysconfig
|
|
import time
|
|
from collections import defaultdict
|
|
from subprocess import PIPE, Popen
|
|
|
|
import numpy
|
|
from setuptools import Extension, setup
|
|
from setuptools.command.build_ext import build_ext
|
|
from setuptools.command.build_py import build_py
|
|
from setuptools.command.install import install
|
|
|
|
# non-empty DEBUG variable turns off optimization and adds -g flag
|
|
DEBUG = bool(os.getenv("DEBUG", ""))
|
|
WIN = sys.platform.startswith("win")
|
|
MAC = sys.platform.startswith("darwin")
|
|
|
|
|
|
# check for mingw compiler on windows
|
|
is_mingw = False
|
|
if WIN:
|
|
from setuptools._distutils import ccompiler
|
|
is_mingw = ccompiler.get_default_compiler() == 'mingw32'
|
|
|
|
|
|
# Have to copy from "create_shadertext.py" script due to the use of pyproject.toml
|
|
# Full explanation:
|
|
# https://github.com/pypa/setuptools/issues/3939
|
|
def create_all(generated_dir, pymoldir="."):
|
|
"""
|
|
Generate various stuff
|
|
"""
|
|
create_shadertext(
|
|
os.path.join(pymoldir, "data", "shaders"),
|
|
generated_dir,
|
|
os.path.join(generated_dir, "ShaderText.h"),
|
|
os.path.join(generated_dir, "ShaderText.cpp"),
|
|
)
|
|
create_buildinfo(generated_dir, pymoldir)
|
|
|
|
|
|
class openw(object):
|
|
"""
|
|
File-like object for writing files. File is actually only
|
|
written if the content changed.
|
|
"""
|
|
|
|
def __init__(self, filename):
|
|
if os.path.exists(filename):
|
|
self.out = cStringIO.StringIO()
|
|
self.filename = filename
|
|
else:
|
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
|
self.out = open(filename, "w")
|
|
self.filename = None
|
|
|
|
def close(self):
|
|
if self.out.closed:
|
|
return
|
|
if self.filename:
|
|
with open(self.filename) as handle:
|
|
oldcontents = handle.read()
|
|
newcontents = self.out.getvalue()
|
|
if oldcontents != newcontents:
|
|
self.out = open(self.filename, "w")
|
|
self.out.write(newcontents)
|
|
self.out.close()
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self.out, name)
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *a, **k):
|
|
self.close()
|
|
|
|
def __del__(self):
|
|
self.close()
|
|
|
|
|
|
def create_shadertext(shaderdir, shaderdir2, outputheader, outputfile):
|
|
outputheader = openw(outputheader)
|
|
outputfile = openw(outputfile)
|
|
|
|
include_deps = defaultdict(set)
|
|
ifdef_deps = defaultdict(set)
|
|
|
|
# get all *.gs *.vs *.fs *.shared from the two input directories
|
|
shaderfiles = set()
|
|
for sdir in [shaderdir, shaderdir2]:
|
|
for ext in ["gs", "vs", "fs", "shared", "tsc", "tse"]:
|
|
shaderfiles.update(
|
|
map(os.path.basename, sorted(glob.glob(os.path.join(sdir, "*." + ext))))
|
|
)
|
|
|
|
varname = "_shader_cache_raw"
|
|
outputheader.write("extern const char * %s[];\n" % varname)
|
|
outputfile.write("const char * %s[] = {\n" % varname)
|
|
|
|
for filename in sorted(shaderfiles):
|
|
shaderfile = os.path.join(shaderdir, filename)
|
|
if not os.path.exists(shaderfile):
|
|
shaderfile = os.path.join(shaderdir2, filename)
|
|
|
|
with open(shaderfile, "r") as handle:
|
|
contents = handle.read()
|
|
|
|
if True:
|
|
outputfile.write('"%s", ""\n' % (filename))
|
|
|
|
for line in contents.splitlines():
|
|
line = line.strip()
|
|
|
|
# skip blank lines and obvious comments
|
|
if not line or line.startswith("//") and not "*/" in line:
|
|
continue
|
|
|
|
# write line, quoted, escaped and with a line feed
|
|
outputfile.write(
|
|
'"%s\\n"\n' % line.replace("\\", "\\\\").replace('"', r"\"")
|
|
)
|
|
|
|
# include and ifdef dependencies
|
|
if line.startswith("#include"):
|
|
include_deps[line.split()[1]].add(filename)
|
|
elif line.startswith("#ifdef") or line.startswith("#ifndef"):
|
|
ifdef_deps[line.split()[1]].add(filename)
|
|
|
|
outputfile.write(",\n")
|
|
|
|
outputfile.write("0};\n")
|
|
|
|
# include and ifdef dependencies
|
|
for varname, deps in [("_include_deps", include_deps), ("_ifdef_deps", ifdef_deps)]:
|
|
outputheader.write("extern const char * %s[];\n" % varname)
|
|
outputfile.write("const char * %s[] = {\n" % varname)
|
|
for name, itemdeps in deps.items():
|
|
outputfile.write('"%s", "%s", 0,\n' % (name, '", "'.join(sorted(itemdeps))))
|
|
outputfile.write("0};\n")
|
|
|
|
outputheader.close()
|
|
outputfile.close()
|
|
|
|
|
|
def create_buildinfo(outputdir, pymoldir="."):
|
|
try:
|
|
sha = (
|
|
Popen(["git", "rev-parse", "HEAD"], cwd=pymoldir, stdout=PIPE)
|
|
.stdout.read()
|
|
.strip()
|
|
.decode()
|
|
)
|
|
except OSError:
|
|
sha = ""
|
|
|
|
with openw(os.path.join(outputdir, "PyMOLBuildInfo.h")) as out:
|
|
print(
|
|
"""
|
|
#define _PyMOL_BUILD_DATE %d
|
|
#define _PYMOL_BUILD_GIT_SHA "%s"
|
|
"""
|
|
% (int(os.environ.get('SOURCE_DATE_EPOCH', time.time())), sha),
|
|
file=out,
|
|
)
|
|
|
|
|
|
# handle extra arguments
|
|
def str2bool(v: str) -> bool:
|
|
if v.lower() in ("true", "yes"):
|
|
return True
|
|
elif v.lower() in ("false", "no"):
|
|
return False
|
|
else:
|
|
raise argparse.ArgumentTypeError("Boolean value expected.")
|
|
|
|
|
|
class options:
|
|
osx_frameworks = True
|
|
jobs = int(os.getenv("JOBS", 0))
|
|
libxml = True
|
|
glut = False
|
|
use_msgpackc = "guess"
|
|
testing = False
|
|
openvr = False
|
|
use_openmp = "no" if MAC else "yes"
|
|
use_vtkm = "no"
|
|
vmd_plugins = True
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--glut", dest="glut", type=str2bool, help="link with GLUT (legacy GUI)"
|
|
)
|
|
parser.add_argument(
|
|
"--osx-frameworks",
|
|
dest="osx_frameworks",
|
|
help="on MacOS use XQuartz instead of native frameworks",
|
|
type=str2bool,
|
|
)
|
|
parser.add_argument(
|
|
"--jobs",
|
|
"-j",
|
|
type=int,
|
|
help="for parallel builds " "(defaults to number of processors)",
|
|
)
|
|
parser.add_argument(
|
|
"--libxml",
|
|
type=str2bool,
|
|
help="skip libxml2 dependency, disables COLLADA export",
|
|
)
|
|
parser.add_argument("--use-openmp", type=str2bool, help="Use OpenMP")
|
|
parser.add_argument(
|
|
"--use-vtkm",
|
|
choices=("2.0", "2.1", "2.2", "2.3", "no"),
|
|
help="Use VTK-m for isosurface generation",
|
|
)
|
|
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("--testing", type=str2bool, help="Build C-level tests")
|
|
parser.add_argument("--openvr", dest="openvr", type=str2bool)
|
|
parser.add_argument(
|
|
"--vmd-plugins",
|
|
dest="vmd_plugins",
|
|
type=str2bool,
|
|
help="Disable VMD molfile plugins (libnetcdf dependency)",
|
|
)
|
|
options, sys.argv[1:] = parser.parse_known_args(namespace=options)
|
|
|
|
|
|
def get_prefix_path() -> list[str]:
|
|
"""
|
|
Return a list of paths which will be searched for "include",
|
|
"include/freetype2", "lib", "lib64" etc.
|
|
"""
|
|
paths = []
|
|
|
|
if (prefix_path := os.environ.get("PREFIX_PATH")) is not None:
|
|
paths += prefix_path.split(os.pathsep)
|
|
|
|
if sys.platform.startswith("freebsd"):
|
|
paths += ["/usr/local"]
|
|
|
|
if not options.osx_frameworks:
|
|
paths += ["usr/X11"]
|
|
|
|
if MAC:
|
|
for prefix in ["/sw", "/opt/local", "/usr/local"]:
|
|
if sys.base_prefix.startswith(prefix):
|
|
paths += [prefix]
|
|
|
|
if is_conda_env():
|
|
if WIN:
|
|
if "CONDA_PREFIX" in os.environ:
|
|
paths += [os.path.join(os.environ["CONDA_PREFIX"], "Library")]
|
|
paths += [os.path.join(sys.prefix, "Library")]
|
|
|
|
paths += [sys.prefix] + paths
|
|
|
|
if WIN and is_mingw:
|
|
if os.path.isdir(sys.prefix):
|
|
paths.append(os.path.normpath(sys.prefix))
|
|
|
|
paths += ["/usr"]
|
|
|
|
return paths
|
|
|
|
|
|
def is_conda_env():
|
|
return (
|
|
"conda" in sys.prefix
|
|
or "conda" in sys.version
|
|
or "Continuum" in sys.version
|
|
or sys.prefix == os.getenv("CONDA_PREFIX")
|
|
)
|
|
|
|
|
|
def guess_msgpackc():
|
|
for prefix in prefix_path:
|
|
for suffix in ["h", "hpp"]:
|
|
f = os.path.join(prefix, "include", "msgpack", f"version_master.{suffix}")
|
|
|
|
try:
|
|
m = re.search(r"MSGPACK_VERSION_MAJOR\s+(\d+)", open(f).read())
|
|
except EnvironmentError:
|
|
continue
|
|
|
|
if m is not None:
|
|
major = int(m.group(1))
|
|
if major > 1:
|
|
return "c++11"
|
|
|
|
return "no"
|
|
|
|
|
|
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) -> None:
|
|
super().initialize_options()
|
|
if DEBUG and not WIN:
|
|
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):
|
|
def run(self):
|
|
build_py.run(self)
|
|
|
|
|
|
class install_pymol(install):
|
|
pymol_path = None
|
|
bundled_pmw = False
|
|
no_launcher = False
|
|
|
|
user_options = install.user_options + [
|
|
("pymol-path=", None, "PYMOL_PATH"),
|
|
("bundled-pmw", None, "install bundled Pmw module"),
|
|
("no-launcher", None, "skip installation of the pymol launcher"),
|
|
]
|
|
|
|
def finalize_options(self):
|
|
install.finalize_options(self)
|
|
|
|
self.pymol_path_is_default = self.pymol_path is None
|
|
|
|
if self.pymol_path is None:
|
|
self.pymol_path = os.path.join(self.install_libbase, "pymol", "pymol_path")
|
|
elif self.root is not None:
|
|
self.pymol_path = install_pymol.change_root(self.root, self.pymol_path)
|
|
|
|
def run(self):
|
|
super().run()
|
|
|
|
assert self.pymol_path is not None
|
|
self.install_pymol_path(self.pymol_path)
|
|
|
|
if not self.no_launcher:
|
|
self.make_launch_script()
|
|
|
|
if self.bundled_pmw:
|
|
raise Exception(
|
|
"--bundled-pmw has been removed, please install Pmw from "
|
|
"https://github.com/schrodinger/pmw-patched"
|
|
)
|
|
|
|
def unchroot(self, name):
|
|
if self.root is not None and name.startswith(self.root):
|
|
return name[len(self.root) :]
|
|
return name
|
|
|
|
def copy_tree_nosvn(self, src, dst):
|
|
def ignore(src, names):
|
|
return set([]).intersection(names)
|
|
|
|
if os.path.exists(dst):
|
|
shutil.rmtree(dst)
|
|
print("copying %s -> %s" % (src, dst))
|
|
shutil.copytree(src, dst, ignore=ignore)
|
|
|
|
def copy(self, src, dst):
|
|
copy = self.copy_tree_nosvn if os.path.isdir(src) else self.copy_file
|
|
copy(src, dst)
|
|
|
|
def install_pymol_path(self, base_path):
|
|
self.mkpath(base_path)
|
|
for name in [
|
|
"LICENSE",
|
|
"data",
|
|
"test",
|
|
"examples",
|
|
]:
|
|
self.copy(name, os.path.join(base_path, name))
|
|
|
|
if options.openvr:
|
|
self.copy(
|
|
"contrib/vr/README.md", os.path.join(base_path, "README-VR.txt")
|
|
)
|
|
|
|
def make_launch_script(self):
|
|
if sys.platform.startswith("win"):
|
|
launch_script = "pymol.bat"
|
|
else:
|
|
launch_script = "pymol"
|
|
|
|
self.mkpath(self.install_scripts)
|
|
launch_script = os.path.join(self.install_scripts, launch_script)
|
|
|
|
python_exe = os.path.abspath(sys.executable)
|
|
site_packages_dir = sysconfig.get_path('purelib')
|
|
pymol_file = self.unchroot(
|
|
os.path.join(site_packages_dir, "pymol", "__init__.py")
|
|
)
|
|
pymol_path = self.unchroot(self.pymol_path)
|
|
|
|
with open(launch_script, "w") as out:
|
|
if WIN:
|
|
if not self.pymol_path_is_default:
|
|
out.write(f"set PYMOL_PATH={pymol_path}" + os.linesep)
|
|
|
|
if is_mingw:
|
|
python_exe = "%~dp0\\python.exe"
|
|
out.write('"%s" -m pymol' % python_exe)
|
|
else:
|
|
# paths relative to launcher, if possible
|
|
try:
|
|
python_exe = "%~dp0\\" + os.path.relpath(
|
|
python_exe, self.install_scripts
|
|
)
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
pymol_file = "%~dp0\\" + os.path.relpath(
|
|
pymol_file, self.install_scripts
|
|
)
|
|
except ValueError:
|
|
pymol_file = os.path.abspath(pymol_file)
|
|
out.write('"%s" "%s"' % (python_exe, pymol_file))
|
|
|
|
out.write(" %*" + os.linesep)
|
|
else:
|
|
out.write("#!/bin/sh" + os.linesep)
|
|
if not self.pymol_path_is_default:
|
|
out.write(f'export PYMOL_PATH="{pymol_path}"' + os.linesep)
|
|
out.write('exec "%s" "%s" "$@"' % (python_exe, pymol_file) + os.linesep)
|
|
|
|
os.chmod(launch_script, 0o755)
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# should be something like (build_base + "/generated"), but that's only
|
|
# known to build and install instances
|
|
generated_dir = os.path.join(os.environ.get("PYMOL_BLD", "build"), "generated")
|
|
|
|
create_all(generated_dir)
|
|
|
|
# can be changed with environment variable PREFIX_PATH
|
|
prefix_path = get_prefix_path()
|
|
|
|
inc_dirs = [
|
|
"include",
|
|
]
|
|
|
|
pymol_src_dirs = [
|
|
"ov/src",
|
|
"layer0",
|
|
"layer1",
|
|
"layer2",
|
|
"layer3",
|
|
"layer4",
|
|
"layer5",
|
|
"layerGraphics",
|
|
"layerGraphics/gl",
|
|
generated_dir,
|
|
]
|
|
|
|
def_macros = [
|
|
("_PYMOL_LIBPNG", None),
|
|
("_PYMOL_FREETYPE", None),
|
|
]
|
|
|
|
if DEBUG and not WIN:
|
|
def_macros += [
|
|
# bounds checking in STL containers
|
|
("_GLIBCXX_ASSERTIONS", None),
|
|
]
|
|
|
|
libs = ["png", "freetype"]
|
|
lib_dirs = []
|
|
ext_comp_args = []
|
|
if is_mingw or not WIN:
|
|
ext_comp_args.extend([
|
|
"-Werror=return-type",
|
|
"-Wunused-variable",
|
|
"-Wno-switch",
|
|
"-Wno-narrowing",
|
|
# legacy stuff
|
|
"-Wno-char-subscripts",
|
|
# optimizations
|
|
"-Og" if DEBUG else "-O3",
|
|
])
|
|
else: # MSVC
|
|
ext_comp_args.extend([
|
|
"/MP",
|
|
"/std:c++17",
|
|
])
|
|
ext_link_args = []
|
|
ext_objects = []
|
|
data_files = []
|
|
ext_modules = []
|
|
|
|
if options.use_openmp == "yes":
|
|
def_macros += [("PYMOL_OPENMP", None)]
|
|
if WIN and not is_mingw: # MSVC
|
|
ext_comp_args.append("/openmp")
|
|
elif MAC: # macOS Clang
|
|
ext_comp_args.extend(["-Xpreprocessor", "-fopenmp"])
|
|
libs.append("omp")
|
|
else: # GCC/Clang on Linux, and MinGW on Windows
|
|
ext_comp_args.append("-fopenmp")
|
|
ext_link_args.append("-fopenmp")
|
|
|
|
if options.vmd_plugins:
|
|
# VMD plugin support
|
|
inc_dirs += [
|
|
"contrib/uiuc/plugins/include",
|
|
]
|
|
pymol_src_dirs += [
|
|
"contrib/uiuc/plugins/molfile_plugin/src",
|
|
]
|
|
def_macros += [
|
|
("_PYMOL_VMD_PLUGINS", None),
|
|
]
|
|
|
|
if options.libxml:
|
|
# COLLADA support
|
|
def_macros += [("_HAVE_LIBXML", None)]
|
|
libs += ["xml2"]
|
|
|
|
if options.use_msgpackc == "guess":
|
|
options.use_msgpackc = guess_msgpackc()
|
|
|
|
if options.use_msgpackc == "no":
|
|
def_macros += [("_PYMOL_NO_MSGPACKC", None)]
|
|
else:
|
|
if options.use_msgpackc == "c++11":
|
|
def_macros += [
|
|
("MMTF_MSGPACK_USE_CPP11", None),
|
|
("MSGPACK_NO_BOOST", None),
|
|
]
|
|
else:
|
|
libs += ["msgpackc"]
|
|
|
|
pymol_src_dirs += ["contrib/mmtf-c"]
|
|
|
|
if not options.glut:
|
|
def_macros += [
|
|
("_PYMOL_NO_MAIN", None),
|
|
]
|
|
|
|
if options.testing:
|
|
pymol_src_dirs += ["layerCTest"]
|
|
def_macros += [("_PYMOL_CTEST", None)]
|
|
|
|
if options.openvr:
|
|
def_macros += [("_PYMOL_OPENVR", None)]
|
|
pymol_src_dirs += [
|
|
"contrib/vr",
|
|
]
|
|
|
|
inc_dirs += pymol_src_dirs
|
|
|
|
inc_dirs += ["contrib/pocketfft"]
|
|
|
|
# ============================================================================
|
|
if MAC:
|
|
libs += ["GLEW"]
|
|
def_macros += [("PYMOL_CURVE_VALIDATE", None)]
|
|
|
|
if options.osx_frameworks:
|
|
ext_link_args += [
|
|
"-framework OpenGL",
|
|
] + (options.glut) * [
|
|
"-framework GLUT",
|
|
]
|
|
def_macros += [
|
|
("_PYMOL_OSX", None),
|
|
]
|
|
else:
|
|
libs += [
|
|
"GL",
|
|
] + (options.glut) * [
|
|
"glut",
|
|
]
|
|
|
|
if WIN:
|
|
# clear
|
|
libs = []
|
|
|
|
def_macros += [
|
|
("WIN32", None),
|
|
]
|
|
|
|
libs += [
|
|
"Advapi32", # Registry (RegCloseKey etc.)
|
|
"Ws2_32", # htonl
|
|
]
|
|
|
|
libs += (
|
|
[
|
|
"glew32",
|
|
"freetype",
|
|
"libpng",
|
|
]
|
|
+ (options.glut)
|
|
* [
|
|
"freeglut",
|
|
]
|
|
+ (options.libxml)
|
|
* [
|
|
"libxml2",
|
|
]
|
|
)
|
|
|
|
if DEBUG:
|
|
if is_mingw:
|
|
ext_comp_args += ["-g"]
|
|
else:
|
|
ext_comp_args += ["/Z7"]
|
|
ext_link_args += ["/DEBUG"]
|
|
|
|
libs += [
|
|
"opengl32",
|
|
]
|
|
if not is_mingw:
|
|
# TODO: Remove when we move to setup-CMake
|
|
ext_comp_args += ["/std:c++17"]
|
|
|
|
if not (MAC or WIN):
|
|
libs += [
|
|
"GL",
|
|
"GLEW",
|
|
] + (options.glut) * [
|
|
"glut",
|
|
]
|
|
|
|
if options.use_vtkm != "no":
|
|
for prefix in prefix_path:
|
|
vtkm_inc_dir = os.path.join(prefix, "include", f"vtkm-{options.use_vtkm}")
|
|
if os.path.exists(vtkm_inc_dir):
|
|
break
|
|
else:
|
|
raise LookupError(
|
|
"VTK-m headers not found." f' PREFIX_PATH={":".join(prefix_path)}'
|
|
)
|
|
def_macros += [
|
|
("_PYMOL_VTKM", None),
|
|
]
|
|
inc_dirs += [
|
|
vtkm_inc_dir,
|
|
vtkm_inc_dir + "/vtkm/thirdparty/diy/vtkmdiy/include",
|
|
vtkm_inc_dir + "/vtkm/thirdparty/lcl/vtkmlcl",
|
|
]
|
|
libs += [
|
|
f"vtkm_cont-{options.use_vtkm}",
|
|
f"vtkm_filter_contour-{options.use_vtkm}",
|
|
f"vtkm_filter_core-{options.use_vtkm}",
|
|
]
|
|
|
|
if options.vmd_plugins:
|
|
libs += [
|
|
"netcdf",
|
|
]
|
|
|
|
if options.openvr:
|
|
libs += [
|
|
"openvr_api",
|
|
]
|
|
|
|
inc_dirs += [
|
|
numpy.get_include(),
|
|
]
|
|
def_macros += [
|
|
("_PYMOL_NUMPY", None),
|
|
]
|
|
|
|
for prefix in prefix_path:
|
|
for dirs, suffixes in [
|
|
[
|
|
inc_dirs,
|
|
[
|
|
("include",),
|
|
("include", "freetype2"),
|
|
("include", "libxml2"),
|
|
("include", "openvr"),
|
|
],
|
|
],
|
|
[lib_dirs, [("lib64",), ("lib",)]],
|
|
]:
|
|
dirs.extend(filter(os.path.isdir, [os.path.join(prefix, *s) for s in suffixes]))
|
|
|
|
# optimization currently causes a clang segfault on OS X 10.9 when
|
|
# compiling layer2/RepCylBond.cpp
|
|
if MAC:
|
|
ext_comp_args += ["-fno-strict-aliasing"]
|
|
|
|
|
|
def get_pymol_version():
|
|
return re.findall(r'_PyMOL_VERSION "(.*)"', open("layer0/Version.h").read())[0]
|
|
|
|
|
|
def get_sources(subdirs, suffixes=(".c", ".cpp")):
|
|
return sorted(
|
|
[f for d in subdirs for s in suffixes for f in glob.glob(d + "/*" + s)]
|
|
)
|
|
|
|
|
|
def get_packages(base, parent="", r=None):
|
|
from os.path import exists, join
|
|
|
|
if r is None:
|
|
r = []
|
|
if parent:
|
|
r.append(parent)
|
|
for name in os.listdir(join(base, parent)):
|
|
if "." not in name and exists(join(base, parent, name, "__init__.py")):
|
|
get_packages(base, join(parent, name), r)
|
|
return r
|
|
|
|
|
|
package_dir = dict(
|
|
(x, os.path.join(base, x)) 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"])
|
|
|
|
champ_libs = []
|
|
|
|
if WIN:
|
|
if not is_mingw:
|
|
# 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))
|
|
else:
|
|
ldversion = (
|
|
sysconfig.get_config_var("LDVERSION")
|
|
or f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
)
|
|
libs.append(f"python{ldversion}")
|
|
champ_libs.append(f"python{ldversion}")
|
|
mingw_libdir = (
|
|
sysconfig.get_config_var("LIBDIR")
|
|
or os.path.join(sys.prefix, "lib")
|
|
)
|
|
if mingw_libdir and os.path.isdir(mingw_libdir):
|
|
lib_dirs.append(mingw_libdir)
|
|
|
|
ext_modules += [
|
|
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,
|
|
libraries=champ_libs,
|
|
library_dirs=lib_dirs,
|
|
),
|
|
]
|
|
|
|
setup(
|
|
cmdclass={
|
|
"build_ext": build_ext_pymol,
|
|
"build_py": build_py_pymol,
|
|
"install": install_pymol,
|
|
},
|
|
version=get_pymol_version(),
|
|
ext_modules=ext_modules,
|
|
)
|