Skip to content

Commit

Permalink
Merge pull request #7 from pypr/improve-build-config
Browse files Browse the repository at this point in the history
Make build more configurable with a config file.
  • Loading branch information
prabhuramachandran committed Jan 10, 2021
2 parents 8d4780f + 9c9498d commit e4a1ab1
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 33 deletions.
79 changes: 74 additions & 5 deletions docs/source/start.rst
Expand Up @@ -35,14 +35,37 @@ These dependencies can be installed using pip::

$ pip install -r requirements.txt

.. note::

If you have a special and customized version of MPI that then you must be
careful and should perhaps install mpi4py from source instead of using pip
directly.

Once this is installed one can install PyZoltan as follows::

$ pip install pyzoltan
$ pip install pyzoltan --no-build-isolation

or via the usual ``setup.py`` method::
or if you have the sources, via the usual ``setup.py`` method::

$ python setup.py install # or develop

Note that PyZoltan requires that the ZOLTAN library be built and be available.
Instructions on how to do this are provided below for different platforms.

You may also have a very customized environment, especially if you are using
PyZoltan on a HPC cluster like say a Cray system. In this case, you will need
to use custom flags to find and link to the MPI libraries. The easiest way to
set this up is to use a configuration file which is documented below in
:ref:`config`.

.. note::

The ``--no-build-isolation`` argument to pip is **necessary** for without
it, pip will attempt to create an isolated environment and build a pyzoltan
wheel inside that isolated environment. This will mean that it will not see
mpi4py that you have built and installed. This could end up causing all
sorts of problems especially if you have a custom MPI library.


Building and linking PyZoltan on OSX/Linux
-------------------------------------------
Expand All @@ -64,7 +87,8 @@ After Zoltan is build, set the environment variable ``ZOLTAN`` to point to the

Note that replace ``$INSTALL_PREFIX`` with the directory you specified above.
After this, follow the instructions to build PySPH. The PyZoltan wrappers will
be compiled and available.
be compiled and available. If you do not wish to set environment variables
each time, you may also want to look at :ref:`config`.

.. note::

Expand Down Expand Up @@ -93,8 +117,9 @@ to build your own Zoltan. With this you may setup PyZoltan as follows::
$ export ZOLTAN_LIBRARY=/usr/lib/x86_64-linux-gnu
$ export USE_TRILINOS=1

After this you can build PyZoltan as usual using ``python setup.py install`` or
``python setup.py develop``.
If you do not wish to set environment variables, you may also want to look at
:ref:`config`. After this you can build PyZoltan as usual using ``python
setup.py install`` or ``python setup.py develop``.


Installing mpi4py and Zoltan on OS X
Expand Down Expand Up @@ -132,6 +157,10 @@ You should be set now and should be able to build/install pyzoltan as::
# or
$ python setup.py develop

If you do not wish to set environment variables, you may also want to look at
:ref:`config`.


.. _Homebrew: http://brew.sh/


Expand All @@ -151,6 +180,46 @@ export any environment variables.
.. _conda: https://conda.io


.. _config:

Using the configuration file
-----------------------------

Instead of setting environment variables and build options on the shell you
can have them setup using a simple configuration file. The file is located in
``~/.compyle/config.py`` (we use the same file for Compyle_ and PySPH_). Here
``~`` is your home directory which on Linux is ``/home/username``, on MacOS
``/Users/username`` and on Windows the location is likely ``\Users\username``.
This file is executed and certain options may be set there.

For example if you wish to set the appropriate C and C++ compiler (icc, Cray,
or PGI), you may set the ``CC`` and ``CXX`` environment variables. You could
do this in the ``~/.compyle/config.py``::

import os

os.environ['CC'] = 'cc'
os.environ['CXX'] = 'CC'

The MPI and ZOLTAN specific options are::

MPI_CFLAGS = ['...'] # must be a list.
MPI_LINK = ['...']

# Zoltan options
USE_TRILINOS = 1 # When set to anything, use "-ltrilinos_zoltan".
ZOLTAN = '/path/to_zoltan' # looks inside this for $ZOLTAN/include/, lib/

# Not needed if using ZOLTAN
ZOLTAN_INCLUDE = 'path/include' # path to zoltan.h
ZOLTAN_LIBRARY = 'path/lib' # path to libzoltan.a



.. _Compyle: https://compyle.readthedocs.io
.. _PySPH: https://pysph.readthedocs.io


Credits
--------

Expand Down
126 changes: 98 additions & 28 deletions setup.py
@@ -1,6 +1,61 @@
'''Setup script for PyZoltan.
You can use some environment variables to control the build. Setting CC/CXX
will let you choose a custom compiler (these can also be set in the config file
discussed below). You can also export ZOLTAN or ZOLTAN_INCLUDE/ZOLTAN_LIBRARY.
These are more configuration file options that trump everything. The file is in
~/.compyle/config.py. The options are:
# MPI options: handy on clusters like a Cray.
MPI_CFLAGS = ['...'] # must be a list.
MPI_LINK = ['...']
# Zoltan options
USE_TRILINOS = 1 # When set to anything, use "-ltrilinos_zoltan".
ZOLTAN = '/path/to_zoltan' # looks inside this for $ZOLTAN/include/, lib/
# Not needed if using ZOLTAN
ZOLTAN_INCLUDE = 'path/include' # path to zoltan.h
ZOLTAN_LIBRARY = 'path/lib' # path to libzoltan.a
'''

import os
import sys
from subprocess import check_output
import sys


# This is taken from compyle.ext_module.
def get_config_file_opts():
'''A global configuration file is used to configure build options
for compyle and other packages. This is located in:
~/.compyle/config.py
The file can contain arbitrary Python that is exec'd. The variables defined
here specify the compile and link args. For example, one may set:
OMP_CFLAGS = ['-fopenmp']
OMP_LINK = ['-fopenmp']
Will use these instead of the defaults that are automatically determined.
These must be lists.
'''
fname = os.path.expanduser(os.path.join('~', '.compyle', 'config.py'))
opts = {}
if os.path.exists(fname):
print('Reading configuration options from %s.' % fname)
with open(fname) as fp:
exec(compile(fp.read(), fname, 'exec'), opts)
opts.pop('__builtins__', None)
return opts


# NOTE: the configuration options in the file trump everything else!
# These are options from the .compyle/config.py
CONFIG_OPTS = get_config_file_opts()


if len(os.environ.get('COVERAGE', '')) > 0:
Expand All @@ -21,6 +76,7 @@
MODE = 'info'

HAVE_MPI = True
USE_ZOLTAN = True
try:
import mpi4py
except ImportError:
Expand Down Expand Up @@ -49,6 +105,9 @@ def get_deps(*args):

def get_zoltan_directory(varname):
global USE_ZOLTAN
if varname in CONFIG_OPTS:
return os.path.expanduser(CONFIG_OPTS[varname])

d = os.environ.get(varname, '')
if len(d) == 0:
USE_ZOLTAN = False
Expand All @@ -73,33 +132,42 @@ def get_mpi_flags():
mpi_link_args = []
if not HAVE_MPI:
return mpi_inc_dirs, mpi_compile_args, mpi_link_args
try:
mpic = 'mpic++'
if compiler == 'intel':
link_args = check_output(
[mpic, '-cc=icc', '-link_info'], universal_newlines=True
).strip()
link_args = link_args[3:]
compile_args = check_output(
[mpic, '-cc=icc', '-compile_info'], universal_newlines=True
).strip()
compile_args = compile_args[3:]
else:
link_args = check_output(
[mpic, '--showme:link'], universal_newlines=True
).strip()
compile_args = check_output(
[mpic, '--showme:compile'], universal_newlines=True
).strip()
except: # noqa: E722
print('-' * 80)
print("Unable to run mpic++ correctly, skipping parallel build")
print('-' * 80)
HAVE_MPI = False
else:
mpi_link_args.extend(link_args.split())
mpi_compile_args.extend(compile_args.split())
elif 'MPI_CFLAGS' in CONFIG_OPTS:
mpi_compile_args = CONFIG_OPTS['MPI_CFLAGS']
mpi_link_args = CONFIG_OPTS['MPI_LINK']
mpi_inc_dirs.append(mpi4py.get_include())
else:
try:
mpic = 'mpic++'
if compiler == 'intel':
link_args = check_output(
[mpic, '-cc=icc', '-link_info'],
universal_newlines=True
).strip()
link_args = link_args[3:]
compile_args = check_output(
[mpic, '-cc=icc', '-compile_info'],
universal_newlines=True
).strip()
compile_args = compile_args[3:]
else:
link_args = check_output(
[mpic, '--showme:link'],
universal_newlines=True
).strip()
compile_args = check_output(
[mpic, '--showme:compile'],
universal_newlines=True
).strip()
except: # noqa: E722
print('-' * 80)
print("Unable to run mpic++ correctly, skipping parallel build")
print('-' * 80)
HAVE_MPI = False
else:
mpi_link_args.extend(link_args.split())
mpi_compile_args.extend(compile_args.split())
mpi_inc_dirs.append(mpi4py.get_include())

return mpi_inc_dirs, mpi_compile_args, mpi_link_args

Expand Down Expand Up @@ -182,7 +250,9 @@ def get_parallel_extensions():
cython_compile_time_env = {'MPI4PY_V2': MPI4PY_V2}

zoltan_lib = 'zoltan'
if os.environ.get('USE_TRILINOS', None) is not None:
if 'USE_TRILINOS' in CONFIG_OPTS:
zoltan_lib = 'trilinos_zoltan'
elif os.environ.get('USE_TRILINOS', None) is not None:
zoltan_lib = 'trilinos_zoltan'

zoltan_modules = [
Expand Down

0 comments on commit e4a1ab1

Please sign in to comment.