summaryrefslogtreecommitdiff
path: root/lib/spack/docs/build_systems/custompackage.rst
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/docs/build_systems/custompackage.rst')
-rw-r--r--lib/spack/docs/build_systems/custompackage.rst209
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/spack/docs/build_systems/custompackage.rst b/lib/spack/docs/build_systems/custompackage.rst
new file mode 100644
index 0000000000..d02c9c1a0a
--- /dev/null
+++ b/lib/spack/docs/build_systems/custompackage.rst
@@ -0,0 +1,209 @@
+.. Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
+ Spack Project Developers. See the top-level COPYRIGHT file for details.
+
+ SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+.. _custompackage:
+
+--------------------
+Custom Build Systems
+--------------------
+
+While the build systems listed above should meet your needs for the
+vast majority of packages, some packages provide custom build scripts.
+This guide is intended for the following use cases:
+
+* Packaging software with its own custom build system
+* Adding support for new build systems
+
+If you want to add support for a new build system, a good place to
+start is to look at the definitions of other build systems. This guide
+focuses mostly on how Spack's build systems work.
+
+In this guide, we will be using the
+`perl <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/perl/package.py>`_ and
+`cmake <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/cmake/package.py>`_
+packages as examples. ``perl``'s build system is a hand-written
+``Configure`` shell script, while ``cmake`` bootstraps itself during
+installation. Both of these packages require custom build systems.
+
+^^^^^^^^^^
+Base class
+^^^^^^^^^^
+
+If your package does not belong to any of the aforementioned build
+systems that Spack already supports, you should inherit from the
+``Package`` base class. ``Package`` is a simple base class with a
+single phase: ``install``. If your package is simple, you may be able
+to simply write an ``install`` method that gets the job done. However,
+if your package is more complex and installation involves multiple
+steps, you should add separate phases as mentioned in the next section.
+
+If you are creating a new build system base class, you should inherit
+from ``PackageBase``. This is the superclass for all build systems in
+Spack.
+
+^^^^^^
+Phases
+^^^^^^
+
+The most important concept in Spack's build system support is the idea
+of phases. Each build system defines a set of phases that are necessary
+to install the package. They usually follow some sort of "configure",
+"build", "install" guideline, but any of those phases may be missing
+or combined with another phase.
+
+If you look at the ``perl`` package, you'll see:
+
+.. code-block:: python
+
+ phases = ['configure', 'build', 'install']
+
+Similarly, ``cmake`` defines:
+
+.. code-block:: python
+
+ phases = ['bootstrap', 'build', 'install']
+
+If we look at the ``cmake`` example, this tells Spack's ``PackageBase``
+class to run the ``bootstrap``, ``build``, and ``install`` functions
+in that order. It is now up to you to define these methods.
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Phase and phase_args functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If we look at ``perl``, we see that it defines a ``configure`` method:
+
+.. code-block:: python
+
+ def configure(self, spec, prefix):
+ configure = Executable('./Configure')
+ configure(*self.configure_args())
+
+There is also a corresponding ``configure_args`` function that handles
+all of the arguments to pass to ``Configure``, just like in
+``AutotoolsPackage``. Comparatively, the ``build`` and ``install``
+phases are pretty simple:
+
+.. code-block:: python
+
+ def build(self, spec, prefix):
+ make()
+
+ def install(self, spec, prefix):
+ make('install')
+
+The ``cmake`` package looks very similar, but with a ``bootstrap``
+function instead of ``configure``:
+
+.. code-block:: python
+
+ def bootstrap(self, spec, prefix):
+ bootstrap = Executable('./bootstrap')
+ bootstrap(*self.bootstrap_args())
+
+ def build(self, spec, prefix):
+ make()
+
+ def install(self, spec, prefix):
+ make('install')
+
+Again, there is a ``boostrap_args`` function that determines the
+correct bootstrap flags to use.
+
+^^^^^^^^^^^^^^^^^^^^
+run_before/run_after
+^^^^^^^^^^^^^^^^^^^^
+
+Occasionally, you may want to run extra steps either before or after
+a given phase. This applies not just to custom build systems, but to
+existing build systems as well. You may need to patch a file that is
+generated by configure, or install extra files in addition to what
+``make install`` copies to the installation prefix. This is where
+``@run_before`` and ``@run_after`` come in.
+
+These Python decorators allow you to write functions that are called
+before or after a particular phase. For example, in ``perl``, we see:
+
+.. code-block:: python
+
+ @run_after('install')
+ def install_cpanm(self):
+ spec = self.spec
+
+ if '+cpanm' in spec:
+ with working_dir(join_path('cpanm', 'cpanm')):
+ perl = spec['perl'].command
+ perl('Makefile.PL')
+ make()
+ make('install')
+
+This extra step automatically installs ``cpanm`` in addition to the
+base Perl installation.
+
+^^^^^^^^^^^^^^^^^^^^^
+on_package_attributes
+^^^^^^^^^^^^^^^^^^^^^
+
+The ``run_before``/``run_after`` logic discussed above becomes
+particularly powerful when combined with the ``@on_package_attributes``
+decorator. This decorator allows you to conditionally run certain
+functions depending on the attributes of that package. The most
+common example is conditional testing. Many unit tests are prone to
+failure, even when there is nothing wrong with the installation.
+Unfortunately, non-portable unit tests and tests that are
+"supposed to fail" are more common than we would like. Instead of
+always running unit tests on installation, Spack lets users
+conditionally run tests with the ``--test=root`` flag.
+
+If we wanted to define a function that would conditionally run
+if and only if this flag is set, we would use the following line:
+
+.. code-block:: python
+
+ @on_package_attributes(run_tests=True)
+
+^^^^^^^
+Testing
+^^^^^^^
+
+Let's put everything together and add unit tests to our package.
+In the ``perl`` package, we can see:
+
+.. code-block:: python
+
+ @run_after('build')
+ @on_package_attributes(run_tests=True)
+ def test(self):
+ make('test')
+
+As you can guess, this runs ``make test`` *after* building the package,
+if and only if testing is requested. Again, this is not specific to
+custom build systems, it can be added to existing build systems as well.
+
+Ideally, every package in Spack will have some sort of test to ensure
+that it was built correctly. It is up to the package authors to make
+sure this happens. If you are adding a package for some software and
+the developers list commands to test the installation, please add these
+tests to your ``package.py``.
+
+.. warning::
+
+ The order of decorators matters. The following ordering:
+
+ .. code-block:: python
+
+ @run_after('install')
+ @on_package_attributes(run_tests=True)
+
+ works as expected. However, if you reverse the ordering:
+
+ .. code-block:: python
+
+ @on_package_attributes(run_tests=True)
+ @run_after('install')
+
+ the tests will always be run regardless of whether or not
+ ``--test=root`` is requested. See https://github.com/spack/spack/issues/3833
+ for more information