Merge branch 'release-2019' into master
[alexxy/gromacs.git] / python_packaging / src / setup.py
1 #
2 # This file is part of the GROMACS molecular simulation package.
3 #
4 # Copyright (c) 2019, by the GROMACS development team, led by
5 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 # and including many others, as listed in the AUTHORS file in the
7 # top-level source directory and at http://www.gromacs.org.
8 #
9 # GROMACS is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # as published by the Free Software Foundation; either version 2.1
12 # of the License, or (at your option) any later version.
13 #
14 # GROMACS is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with GROMACS; if not, see
21 # http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23 #
24 # If you want to redistribute modifications to GROMACS, please
25 # consider that scientific software is very special. Version
26 # control is crucial - bugs must be traceable. We will be happy to
27 # consider code for inclusion in the official distribution, but
28 # derived work must not be called official GROMACS. Details are found
29 # in the README & COPYING files - if they are missing, get the
30 # official version at http://www.gromacs.org.
31 #
32 # To help us fund GROMACS development, we humbly ask that you cite
33 # the research papers on the package. Check out http://www.gromacs.org.
34
35 # Python setuptools script to build and install the gmxapi Python interface
36 # from a GROMACS installation directory.
37
38 # Usage note: things go smoothly when we stick to the setup.py convention of
39 # having a package source directory with the same name as the package at the
40 # same level as the setup.py script and only expect `pip install .` in the
41 # setup.py directory. If we play with the layout more, it is hard to keep all
42 # of the `pip` and `setup.py` cases working as expected. This is annoying
43 # because running the Python interpreter immediately from the same directory
44 # can find the uninstalled source instead of the installed package. We can
45 # ease this pain by building an sdist in the enclosing CMake build scope
46 # and encouraging users to `pip install the_sdist.archive`. Otherwise, we
47 # just have to document that we only support full build-install of the Python
48 # package from the directory containing setup.py, which may clutter that
49 # directory with some artifacts.
50
51 import os
52 import sys
53
54 from skbuild import setup
55 import cmake
56
57 usage = """
58 The `gmxapi` package requires an existing GROMACS installation, version 2020 or higher.
59 To specify the GROMACS installation to use, provide a GMXTOOLCHAINDIR
60 environment variable when running setup.py or `pip`.
61
62 Example:
63     GMXTOOLCHAINDIR=/path/to/gromacs/share/cmake/gromacs pip install gmxapi
64
65 If you have multiple builds of GROMACS, distinguished by a suffix `$SUFFIX`, the
66 tool chain directory will use that suffix.
67
68 Example:
69     GMXTOOLCHAINDIR=/path/to/gromacs/share/cmake/gromacs$SUFFIX pip install gmxapi
70
71 In the example, `gmxapi` is downloaded automatically from pypi.org. You can
72 replace `gmxapi` with a local directory or archive file to build from a source
73 distribution.
74
75 setup.py will use the location of GMXTOOLCHAINDIR to locate the
76 gmxapi library configured during GROMACS installation. Alternatively, if
77 gmxapi_DIR is provided, or if GMXRC has been "sourced", the toolchain file
78 location may be deduced. Note, though, that if multiple GROMACS installations
79 exist in the same location (with different suffixes) only the first one will be
80 used when guessing a toolchain, because setup.py does not know which corresponds
81 to the gmxapi support library.
82
83 If specifying GMXTOOLCHAINDIR and gmxapi_DIR, the tool chain directory must be 
84 located within a subdirectory of gmxapi_DIR.
85
86 NOTE TO OS X USERS:
87 Refer to https://redmine.gromacs.org/issues/3085 for the status of a bug with
88 the toolchain file on OS X. Until the bug is resolved, OS X users are advised
89 to manually specify (via the CXX environment variable) the C++ compiler used
90 when building GROMACS, and to set gmxapi_DIR instead of GMXTOOLCHAINDIR.
91
92 Example:
93
94     gmxapi_DIR=/path/to/gromacs pip install gmxapi
95
96 Refer to project web site for complete documentation.
97
98 """
99
100
101 class GmxapiInstallError(Exception):
102     """Error processing setup.py for gmxapi Python package."""
103
104
105 gmx_toolchain_dir = os.getenv('GMXTOOLCHAINDIR')
106 gmxapi_DIR = os.getenv('gmxapi_DIR')
107 if gmxapi_DIR is None:
108     # Infer from GMXRC exports, if available.
109     gmxapi_DIR = os.getenv('GROMACS_DIR')
110
111 def _find_first_gromacs_suffix(directory):
112     dir_contents = os.listdir(directory)
113     for entry in dir_contents:
114         if entry.startswith('gromacs'):
115             return entry.strip('gromacs')
116
117 if gmx_toolchain_dir is None:
118     # Try to guess from standard GMXRC environment variables.
119     if gmxapi_DIR is not None:
120         if os.path.exists(gmxapi_DIR) and os.path.isdir(gmxapi_DIR):
121             share_cmake = os.path.join(gmxapi_DIR, 'share', 'cmake')
122             suffix = _find_first_gromacs_suffix(share_cmake)
123             if suffix is not None:
124                 gmx_toolchain_dir = os.path.join(share_cmake, 'gromacs' + suffix)
125
126 if gmx_toolchain_dir is None:
127     print(usage)
128     raise GmxapiInstallError('Could not configure for GROMACS installation. Provide GMXTOOLCHAINDIR.')
129
130 suffix = os.path.basename(gmx_toolchain_dir).strip('gromacs')
131 gmx_toolchain = os.path.abspath(os.path.join(gmx_toolchain_dir, 'gromacs-toolchain' + suffix + '.cmake'))
132
133 if gmxapi_DIR is None:
134     # Example: given /usr/local/gromacs/share/cmake/gromacs/gromacs-toolchain.cmake,
135     # we would want /usr/local/gromacs.
136     # Note that we could point more directly to the gmxapi-config.cmake but,
137     # so far, we have relied on CMake automatically looking into
138     # <package>_DIR/share/cmake/<package>/ for such a file.
139     # We would need a slightly different behavior for packages that link against
140     # libgromacs directly, as sample_restraint currently does.
141     gmxapi_DIR = os.path.join(os.path.dirname(gmx_toolchain), '..', '..', '..')
142
143 gmxapi_DIR = os.path.abspath(gmxapi_DIR)
144
145 if not os.path.exists(gmxapi_DIR) or not os.path.isdir(gmxapi_DIR):
146     print(usage)
147     raise GmxapiInstallError('Please set a valid gmxapi_DIR.')
148
149 if gmxapi_DIR != os.path.commonpath([gmxapi_DIR, gmx_toolchain]):
150     raise GmxapiInstallError('GROMACS toolchain file {} is not in gmxapi_DIR {}'.format(
151         gmx_toolchain,
152         gmxapi_DIR
153     ))
154
155 if sys.platform == 'darwin':
156     # TODO: Reconcile with cross-compilation CMake toolchain.
157     # In some cases, compatibility settings are more relevant to libpython, and
158     # in others libgmxapi. It is not completely clear where they can or should
159     # be determined and set.
160     cmake_platform_hints = ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9',
161                             '-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64']
162 else:
163     # TODO: (Issue 3085) Can toolchain be used on OS X builds?
164     cmake_platform_hints = ['-DCMAKE_TOOLCHAIN_FILE={}'.format(gmx_toolchain)]
165
166 # TODO: Use package-specific hinting variable.
167 # We want to be sure that we find a <package>-config.cmake associated with the
168 # toolchains file, but we want to preempt most of the normal CMake
169 # [search procedure](https://cmake.org/cmake/help/latest/command/find_package.html#id5),
170 # which could lead to hard-to-diagnose build problems.
171 # Note that <package>_ROOT is not standard until CMake 3.12
172 # Reference https://cmake.org/cmake/help/latest/policy/CMP0074.html#policy:CMP0074
173 _cmake_major, _cmake_minor = cmake.__version__.split('.')[0:2]
174 if int(_cmake_major) >= 3 and int(_cmake_minor) >= 12:
175     cmake_gmxapi_hint = '-Dgmxapi_ROOT={}'
176 else:
177     cmake_gmxapi_hint = '-DCMAKE_PREFIX_PATH={}'
178 cmake_gmxapi_hint = cmake_gmxapi_hint.format(gmxapi_DIR)
179
180 cmake_args = list(cmake_platform_hints)
181 cmake_args.append(cmake_gmxapi_hint)
182
183 setup(
184     name='gmxapi',
185
186     # TODO: (pending infrastructure and further discussion) Replace with CMake variables from GMXAPI version.
187     version='0.1.0b2',
188     python_requires='>=3.5, <4',
189     setup_requires=['cmake>=3.9.6',
190                     'setuptools>=28',
191                     'scikit-build>=0.7'],
192
193     packages=['gmxapi', 'gmxapi.simulation'],
194
195     cmake_args=cmake_args,
196
197     author='M. Eric Irrgang',
198     author_email='info@gmxapi.org',
199     description='gmxapi Python interface for GROMACS',
200     license='LGPL',
201     url='http://gmxapi.org/',
202
203     # The installed package will contain compiled C++ extensions that cannot be loaded
204     # directly from a zip file.
205     zip_safe=False
206 )