More packaging docs
This commit is contained in:
		@@ -35,9 +35,18 @@ This section of the guide goes through the parts of a package, and
 | 
			
		||||
then tells you how to make your own.  If you're impatient, jump ahead
 | 
			
		||||
to :ref:`spack-create`.
 | 
			
		||||
 | 
			
		||||
Directory Structure
 | 
			
		||||
Package Files
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
It's probably easiest to learn about packages by looking at an
 | 
			
		||||
example.  Let's take a look at ``libelf.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../spack/packages/libelf.py
 | 
			
		||||
   :linenos:
 | 
			
		||||
 | 
			
		||||
Directory Structure
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
A Spack installation directory is structured like a standard UNIX
 | 
			
		||||
install prefix (``bin``, ``lib``, ``include``, ``share``, etc.).  Most
 | 
			
		||||
of the code for Spack lives in ``$SPACK_ROOT/lib/spack``, and this is
 | 
			
		||||
@@ -57,22 +66,13 @@ packages when they're needed for an installation.  All the other files
 | 
			
		||||
in the ``packages`` directory are actual Spack packages used to
 | 
			
		||||
install software.
 | 
			
		||||
 | 
			
		||||
Parts of a package
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
It's probably easiest to learn about packages by looking at an
 | 
			
		||||
example.  Let's take a look at ``libelf.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../spack/packages/libelf.py
 | 
			
		||||
   :linenos:
 | 
			
		||||
 | 
			
		||||
Package Names
 | 
			
		||||
~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
This package lives in a file called ``libelf.py``, and it contains a
 | 
			
		||||
class called ``Libelf``.  The ``Libelf`` class extends Spack's
 | 
			
		||||
``Package`` class (and this is what makes it a Spack package).  The
 | 
			
		||||
**file name** is what users need to provide in their package
 | 
			
		||||
The ``libelf`` package lives in a file called ``libelf.py``, and it
 | 
			
		||||
contains a class called ``Libelf``.  The ``Libelf`` class extends
 | 
			
		||||
Spack's ``Package`` class (and this is what makes it a Spack package).
 | 
			
		||||
The **file name** is what users need to provide in their package
 | 
			
		||||
specs. e.g., if you type any of these:
 | 
			
		||||
 | 
			
		||||
.. code-block:: sh
 | 
			
		||||
@@ -152,55 +152,40 @@ install, but it's better to provide checksums so users don't have to
 | 
			
		||||
install from an unchecked archive.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Install function
 | 
			
		||||
Install method
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
The last element of the ``libelf`` package is its ``install()``
 | 
			
		||||
function.  This is where the real work of installation happens, and
 | 
			
		||||
method.  This is where the real work of installation happens, and
 | 
			
		||||
it's the main part of the package you'll need to customize for each
 | 
			
		||||
piece of software.
 | 
			
		||||
 | 
			
		||||
When a user runs ``spack install``, Spack fetches an archive for the
 | 
			
		||||
correct version of the software, expands the archive, and sets the
 | 
			
		||||
current working directory to the root directory of the expanded
 | 
			
		||||
archive.  It then instantiates a package object and calls its
 | 
			
		||||
``install()`` method.
 | 
			
		||||
 | 
			
		||||
Install takes a ``spec`` object and a ``prefix`` path:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../spack/packages/libelf.py
 | 
			
		||||
   :start-after: 0.8.12
 | 
			
		||||
   :linenos:
 | 
			
		||||
 | 
			
		||||
We'll talk about ``spec`` objects and the types of methods you can
 | 
			
		||||
call on them later.  The ``prefix`` is the path to the directory where
 | 
			
		||||
the package should install the software after it is built.
 | 
			
		||||
``install`` takes a ``spec``: a description of how the package should
 | 
			
		||||
be built, and a ``prefix``: the path to the directory where the
 | 
			
		||||
software should be installed.
 | 
			
		||||
 | 
			
		||||
Inside of the ``install()`` function, things should look pretty
 | 
			
		||||
:ref:`Writing the install method <install-method>` is documented in
 | 
			
		||||
detail later, but in general, the ``install()`` method should look
 | 
			
		||||
familiar.  ``libelf`` uses autotools, so the package first calls
 | 
			
		||||
``configure``, passing the prefix and some other package-specific
 | 
			
		||||
arguments.  It then calls ``make`` and ``make install``.
 | 
			
		||||
 | 
			
		||||
``configure`` and ``make`` look very similar to commands you'd type in
 | 
			
		||||
a shell, but they're actually Python functions.  Spack provides these
 | 
			
		||||
wrapper functions to allow you to call commands more naturally when
 | 
			
		||||
you write packages.  This allows spack to provide some special
 | 
			
		||||
features, as well.  For example, in Spack, ``make`` is parallel by
 | 
			
		||||
default. Spack figures out the number of cores on your machine and
 | 
			
		||||
passes and appropriate value for ``-j<numjobs>`` to the ``make``
 | 
			
		||||
command.  In a package file, you can supply a keyword argument,
 | 
			
		||||
``parallel=False``, to disable parallel make.  We do it here to avoid
 | 
			
		||||
some race conditions in ``libelf``\'s ``install`` target.  The first
 | 
			
		||||
call to ``make()``, which does not have a keyword argument, will still
 | 
			
		||||
build in parallel.
 | 
			
		||||
 | 
			
		||||
We'll go into more detail about shell command functions in later
 | 
			
		||||
sections.
 | 
			
		||||
Spack provides wrapper functions for ``configure`` and ``make`` so
 | 
			
		||||
that you can call them in a similar way to how you'd call a shell
 | 
			
		||||
comamnd.
 | 
			
		||||
 | 
			
		||||
look very similar to commands you'd type in a shell, but they're
 | 
			
		||||
actually Python functions.  Spack provides these wrapper functions to
 | 
			
		||||
allow you to call commands more naturally when you write packages.
 | 
			
		||||
See the section on :ref:`shell wrappers <shell-wrappers>`.
 | 
			
		||||
 | 
			
		||||
.. _spack-create:
 | 
			
		||||
 | 
			
		||||
Creating Packages Automatically
 | 
			
		||||
Creating Packages
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
``spack create``
 | 
			
		||||
@@ -247,8 +232,9 @@ Spack will automatically download the number of tarballs you specify
 | 
			
		||||
 | 
			
		||||
Note that you don't need to do everything up front.  If your package
 | 
			
		||||
is large, you can always choose to download just one tarball for now,
 | 
			
		||||
then run :ref:`spack checksum <spack-checksum>` later if you end up wanting more.  Let's
 | 
			
		||||
say, for now, that you opted to download 3 tarballs:
 | 
			
		||||
then run :ref:`spack checksum <spack-checksum>` later if you end up
 | 
			
		||||
wanting more.  Let's say, for now, that you opted to download 3
 | 
			
		||||
tarballs:
 | 
			
		||||
 | 
			
		||||
.. code-block:: sh
 | 
			
		||||
 | 
			
		||||
@@ -297,7 +283,7 @@ your favorite ``$EDITOR``:
 | 
			
		||||
 | 
			
		||||
       def install(self, spec, prefix):
 | 
			
		||||
           # FIXME: Modify the configure line to suit your build system here.
 | 
			
		||||
           configure("--prefix=%s" % prefix)
 | 
			
		||||
           configure("--prefix=" + prefix)
 | 
			
		||||
 | 
			
		||||
           # FIXME: Add logic to build and install here
 | 
			
		||||
           make()
 | 
			
		||||
@@ -358,7 +344,7 @@ in:
 | 
			
		||||
       versions = { '1.0' : '0123456789abcdef0123456789abcdef' }
 | 
			
		||||
 | 
			
		||||
       def install(self, spec, prefix):
 | 
			
		||||
           configure("--prefix=%s" % prefix)
 | 
			
		||||
           configure("--prefix=" + prefix)
 | 
			
		||||
           make()
 | 
			
		||||
           make("install")
 | 
			
		||||
 | 
			
		||||
@@ -418,13 +404,13 @@ your package in working order.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Optional Package Attributes
 | 
			
		||||
------------------------------
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
In addition to ``homepage``, ``url``, and ``versions``, there are some
 | 
			
		||||
other useful attributes you can add to your package file.
 | 
			
		||||
 | 
			
		||||
``list_url``
 | 
			
		||||
~~~~~~~~~~~~~~~
 | 
			
		||||
^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
When spack tries to find available versions of packages (e.g. in
 | 
			
		||||
``spack checksum``), by default it looks in the parent directory of
 | 
			
		||||
@@ -448,7 +434,7 @@ source code archives.  For these, you can specify a separate
 | 
			
		||||
   :end-before: versions
 | 
			
		||||
 | 
			
		||||
``list_depth``
 | 
			
		||||
~~~~~~~~~~~~~~~~~
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Some packages may not have a listing of available verisons on a single
 | 
			
		||||
page.  For these, you can specify a ``list_depth`` indicating that
 | 
			
		||||
@@ -796,13 +782,19 @@ fork a new process each time we invoke ``install()``.  This allows
 | 
			
		||||
packages to have their own completely sandboxed build environment,
 | 
			
		||||
without impacting other jobs that the main Spack process runs.
 | 
			
		||||
 | 
			
		||||
.. _install-method:
 | 
			
		||||
 | 
			
		||||
Implementing the ``install`` method
 | 
			
		||||
------------------------------------------
 | 
			
		||||
 | 
			
		||||
Now that the metadata is out of the way, we can move on to the
 | 
			
		||||
``install()`` method.  Recall that the ``install()`` method's
 | 
			
		||||
signature looks like this:
 | 
			
		||||
``install()`` method.  When a user runs ``spack install``, Spack
 | 
			
		||||
fetches an archive for the correct version of the software, expands
 | 
			
		||||
the archive, and sets the current working directory to the root
 | 
			
		||||
directory of the expanded archive.  It then instantiates a package
 | 
			
		||||
object and calls the ``install()`` method.
 | 
			
		||||
 | 
			
		||||
The ``install()`` signature looks like this:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
@@ -828,6 +820,11 @@ The parameters are as follows:
 | 
			
		||||
    targets into.  It acts like a string, but it's actually its own
 | 
			
		||||
    special type, :py:class:`Prefix <spack.util.prefix.Prefix>`.
 | 
			
		||||
 | 
			
		||||
``spec`` and ``prefix`` are passed to ``install`` for convenience.
 | 
			
		||||
``spec`` is also available as an attribute on the package
 | 
			
		||||
(``self.spec``), and ``prefix`` is actually an attribute of ``spec``
 | 
			
		||||
(``spec.prefix``).
 | 
			
		||||
 | 
			
		||||
As mentioned in :ref:`install-environment`, you will usually not need
 | 
			
		||||
to refer to most dependencies explicitly in your package file, as
 | 
			
		||||
compiler wrapper take care of most of the heavy lifting here.  There
 | 
			
		||||
@@ -836,62 +833,269 @@ of dependencies, or when you need to do something different depending
 | 
			
		||||
on the version, compiler, dependencies, etc. that your package is
 | 
			
		||||
built with.  These parameters give you access to this type of information.
 | 
			
		||||
 | 
			
		||||
Prefix objects
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. _prefix-objects:
 | 
			
		||||
 | 
			
		||||
For packages that do not have their own install target, or for those
 | 
			
		||||
that implement it poorly (like ``libdwarf``), Spack provides the
 | 
			
		||||
prefix object so you can manually copy things into the install
 | 
			
		||||
directory. You can refer to the prefix directly, e.g.:
 | 
			
		||||
Prefix objects
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
Spack passes the ``prefix`` parameter to the install method so that
 | 
			
		||||
you can pass it to ``configure``, ``cmake``, or some other installer,
 | 
			
		||||
e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   configure('--prefix=' + prefix)
 | 
			
		||||
 | 
			
		||||
The Prefix object will act like a string here.  You can also refer to
 | 
			
		||||
standard subdirectories without having to construct paths yourself, e.g.:
 | 
			
		||||
 | 
			
		||||
For the most part, prefix objects behave exactly like strings.  For
 | 
			
		||||
packages that do not have their own install target, or for those that
 | 
			
		||||
implement it poorly (like ``libdwarf``), you may need to manually copy
 | 
			
		||||
things into particular directories under the prefix.  For this, you
 | 
			
		||||
can refer to standard subdirectories without having to construct paths
 | 
			
		||||
yourself, e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   mkdirp(prefix.bin,
 | 
			
		||||
          prefix.include,
 | 
			
		||||
          prefix.lib,
 | 
			
		||||
          prefix.man1)
 | 
			
		||||
   def install(self, spec, prefix):
 | 
			
		||||
       mkdirp(prefix.bin)
 | 
			
		||||
       install('foo-tool', prefix.bin)
 | 
			
		||||
 | 
			
		||||
       mkdirp(prefix.include)
 | 
			
		||||
       install('foo.h', prefix.include)
 | 
			
		||||
 | 
			
		||||
       mkdirp(prefix.lib)
 | 
			
		||||
       install('libfoo.a', prefix.lib)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Most of the standard UNIX directory names are attributes on the
 | 
			
		||||
``prefix`` object.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
See :py:class:`spack.prefix.Prefix` to see what paths are available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Spec operations
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Multimethods
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Shell commands
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
``prefix`` object.  See :py:class:`spack.prefix.Prefix` for a full
 | 
			
		||||
list.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _spec-objects:
 | 
			
		||||
 | 
			
		||||
Spec objects
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
When ``install`` is called, most parts of the build process are set up
 | 
			
		||||
for you.  The correct version's tarball has been downloaded and
 | 
			
		||||
expanded.  Environment variables like ``CC`` and ``CXX`` are set to
 | 
			
		||||
point to the correct compiler and version.  An install prefix has
 | 
			
		||||
already been selected and passed in as ``prefix``.  In most cases this
 | 
			
		||||
is all you need to get ``configure``, ``cmake``, or another install
 | 
			
		||||
working correctly.
 | 
			
		||||
 | 
			
		||||
There will be times when you need to know more about the build
 | 
			
		||||
configuration.  For example, some software requires that you pass
 | 
			
		||||
special parameters to ``configure``, like
 | 
			
		||||
``--with-libelf=/path/to/libelf`` or ``--with-mpich``.  You might also
 | 
			
		||||
need to supply special compiler flags depending on the compiler.  All
 | 
			
		||||
of this information is available in the spec.
 | 
			
		||||
 | 
			
		||||
Testing spec constraints
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
You can test whether your spec is configured a certain way by using
 | 
			
		||||
the ``satisfies`` method.  For example, if you want to check whether
 | 
			
		||||
the pcakage is at a particular version range, you can use specs to do
 | 
			
		||||
that, e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if spec.satisfies('@1.2:1.4'):
 | 
			
		||||
       configure_args.append("CXXFLAGS='-DWITH_FEATURE'")
 | 
			
		||||
   configure('--prefix=' + prefix, *configure_args)
 | 
			
		||||
 | 
			
		||||
This works for compilers, too:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if spec.satisfies('%gcc'):
 | 
			
		||||
       configure_args.append('CXXFLAGS="-g3 -O3"')
 | 
			
		||||
   if spec.satisfies('%intel'):
 | 
			
		||||
       configure_args.append('CXXFLAGS="-xSSE2 -fast"')
 | 
			
		||||
 | 
			
		||||
Or for combinations of spec constraints:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if spec.satisfies('@1.2%intel'):
 | 
			
		||||
       tty.error("Version 1.2 breaks when using Intel compiler!")
 | 
			
		||||
 | 
			
		||||
You can also do similar satisfaction tests for dependencies:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if spec.satisfies('^dyninst@8.0'):
 | 
			
		||||
       configure_args.append('CXXFLAGS=-DSPECIAL_DYNINST_FEATURE')
 | 
			
		||||
 | 
			
		||||
This could allow you to easily work around a bug in a particular
 | 
			
		||||
dependency version.
 | 
			
		||||
 | 
			
		||||
You can use ``satisfies()`` to test for particular dependencies,
 | 
			
		||||
e.g. ``foo.satisfies('^openmpi@1.2')`` or ``foo.satisfies('^mpich')``,
 | 
			
		||||
or you can use Python's builtin ``in`` operator:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if 'libelf' in spec:
 | 
			
		||||
       print "this package depends on libelf"
 | 
			
		||||
 | 
			
		||||
This is useful for virtual dependencies, as you can easily see what
 | 
			
		||||
implementation was selected for this build:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   if 'openmpi' in spec:
 | 
			
		||||
       configure_args.append('--with-openmpi')
 | 
			
		||||
   elif 'mpich' in spec:
 | 
			
		||||
       configure_args.append('--with-mpich')
 | 
			
		||||
   elif 'mvapich' in spec:
 | 
			
		||||
       configure_args.append('--with-mvapich')
 | 
			
		||||
 | 
			
		||||
It's also a bit more concise than satisfies.  The difference between
 | 
			
		||||
the two functions is that ``satisfies()`` tests whether spec
 | 
			
		||||
constraints overlap at all, while ``in`` tests whether a spec or any
 | 
			
		||||
of its dependencies satisfy the provided spec.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Accessing Dependencies
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
You may need to get at some file or binary that's in the prefix of one
 | 
			
		||||
of your dependencies.  You can do that by subscripting the spec:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   my_mpi = spec['mpich']
 | 
			
		||||
 | 
			
		||||
The value in the brackets needs to be some package name, and spec
 | 
			
		||||
needs to depend on that package, or the operation will fail.  For
 | 
			
		||||
example, the above code will fail if the ``spec`` doesn't depend on
 | 
			
		||||
``mpich``.  The result
 | 
			
		||||
 | 
			
		||||
``my_mpi`` is itself just another ``Spec`` object, so you can
 | 
			
		||||
do all the same things you'd do with the package's own spec:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   mpicc = new_path(my_mpi.prefix.bin, 'mpicc')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Multimethods and ``@when``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
Spack allows you to make multiple versions of instance functions in
 | 
			
		||||
packages, based on whether the package's spec satisfies particular
 | 
			
		||||
criteria.
 | 
			
		||||
 | 
			
		||||
The ``@when`` annotation lets packages declare multiple versions of
 | 
			
		||||
methods like install() that depend on the package's spec.  For
 | 
			
		||||
example:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   class SomePackage(Package):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
       def install(self, prefix):
 | 
			
		||||
           # Do default install
 | 
			
		||||
 | 
			
		||||
       @when('=chaos_5_x86_64_ib')
 | 
			
		||||
       def install(self, prefix):
 | 
			
		||||
           # This will be executed instead of the default install if
 | 
			
		||||
           # the package's sys_type() is chaos_5_x86_64_ib.
 | 
			
		||||
 | 
			
		||||
       @when('=bgqos_0")
 | 
			
		||||
       def install(self, prefix):
 | 
			
		||||
           # This will be executed if the package's sys_type is bgqos_0
 | 
			
		||||
 | 
			
		||||
In the above code there are three versions of install(), two of which
 | 
			
		||||
are specialized for particular platforms.  The version that is called
 | 
			
		||||
depends on the architecture of the package spec.
 | 
			
		||||
 | 
			
		||||
Note that this works for methods other than install, as well.  So,
 | 
			
		||||
if you only have part of the install that is platform specific, you
 | 
			
		||||
could do something more like this:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   class SomePackage(Package):
 | 
			
		||||
      ...
 | 
			
		||||
       # virtual dependence on MPI.
 | 
			
		||||
       # could resolve to mpich, mpich2, OpenMPI
 | 
			
		||||
       depends_on('mpi')
 | 
			
		||||
 | 
			
		||||
       def setup(self):
 | 
			
		||||
           # do nothing in the default case
 | 
			
		||||
           pass
 | 
			
		||||
 | 
			
		||||
       @when('^openmpi')
 | 
			
		||||
       def setup(self):
 | 
			
		||||
           # do something special when this is built with OpenMPI for
 | 
			
		||||
           # its MPI implementations.
 | 
			
		||||
 | 
			
		||||
       def install(self, prefix):
 | 
			
		||||
           # Do common install stuff
 | 
			
		||||
           self.setup()
 | 
			
		||||
           # Do more common install stuff
 | 
			
		||||
 | 
			
		||||
You can write multiple ``@when`` specs that satisfy the package's spec,
 | 
			
		||||
for example:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   class SomePackage(Package):
 | 
			
		||||
       ...
 | 
			
		||||
       depends_on('mpi')
 | 
			
		||||
 | 
			
		||||
       def setup_mpi(self):
 | 
			
		||||
           # the default, called when no @when specs match
 | 
			
		||||
           pass
 | 
			
		||||
 | 
			
		||||
       @when('mpi@3:')
 | 
			
		||||
       def setup_mpi(self):
 | 
			
		||||
           # this will be called when mpi is version 3 or higher
 | 
			
		||||
           pass
 | 
			
		||||
 | 
			
		||||
       @when('mpi@2:')
 | 
			
		||||
       def setup_mpi(self):
 | 
			
		||||
           # this will be called when mpi is version 2 or higher
 | 
			
		||||
           pass
 | 
			
		||||
 | 
			
		||||
       @when('mpi@1:')
 | 
			
		||||
       def setup_mpi(self):
 | 
			
		||||
           # this will be called when mpi is version 1 or higher
 | 
			
		||||
           pass
 | 
			
		||||
 | 
			
		||||
In situations like this, the first matching spec, in declaration order
 | 
			
		||||
will be called.  As before, if no ``@when`` spec matches, the default
 | 
			
		||||
method (the one without the ``@when`` decorator) will be called.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
   The default version of decorated methods must **always** come
 | 
			
		||||
   first.  Otherwise it will override all of the platform-specific
 | 
			
		||||
   versions.  There's not much we can do to get around this because of
 | 
			
		||||
   the way decorators work.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _shell-wrappers:
 | 
			
		||||
 | 
			
		||||
Shell command wrappers
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
This allows spack to provide some special features, as well.  For
 | 
			
		||||
example, in Spack, ``make`` is parallel by default. Spack figures out
 | 
			
		||||
the number of cores on your machine and passes and appropriate value
 | 
			
		||||
for ``-j<numjobs>`` to the ``make`` command.  In a package file, you
 | 
			
		||||
can supply a keyword argument, ``parallel=False``, to disable parallel
 | 
			
		||||
make.  We do it here to avoid some race conditions in ``libelf``\'s
 | 
			
		||||
``install`` target.  The first call to ``make()``, which does not have
 | 
			
		||||
a keyword argument, will still build in parallel.
 | 
			
		||||
 | 
			
		||||
.. _pacakge-lifecycle:
 | 
			
		||||
 | 
			
		||||
Package lifecycle
 | 
			
		||||
------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -140,7 +140,7 @@ def install(self, prefix):
 | 
			
		||||
 | 
			
		||||
       This allows each package to have a default version of install() AND
 | 
			
		||||
       specialized versions for particular platforms.  The version that is
 | 
			
		||||
       called depends on the sys_type of SomePackage.
 | 
			
		||||
       called depends on the architecutre of the instantiated package.
 | 
			
		||||
 | 
			
		||||
       Note that this works for methods other than install, as well.  So,
 | 
			
		||||
       if you only have part of the install that is platform specific, you
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user