docs: update old docs for spack.package.PackageBase

- There was a lot of documentation in `PackageBase` dating back to the
  very first versions of Spack.

- It was repetitive and out of date, and the docs at spack.readthedocs.io
  are better.

- Remove the outdated specifics, and leave the minimal useful set of
  developer docs in `package.py`.
This commit is contained in:
Todd Gamblin 2018-07-22 17:56:53 -07:00
parent bd3ffc7b76
commit d4c4effb5e

View File

@ -22,16 +22,12 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""
This is where most of the action happens in Spack.
See the Package docs for detailed instructions on how the class works
and on how to write your own packages.
"""This is where most of the action happens in Spack.
The spack package class structure is based strongly on Homebrew
(http://brew.sh/), mainly because Homebrew makes it very easy to create
packages.
The spack package structure is based strongly on Homebrew
(http://wiki.github.com/mxcl/homebrew/), mainly because
Homebrew makes it very easy to create packages. For a complete
rundown on spack and how it differs from homebrew, look at the
README.
"""
import base64
import contextlib
@ -313,209 +309,39 @@ class PackageBase(with_metaclass(PackageMeta, PackageViewMixin, object)):
***The Package class***
Package is where the bulk of the work of installing packages is done.
A package defines how to fetch, verfiy (via, e.g., md5), build, and
install a piece of software. A Package also defines what other
A package defines how to fetch, verify (via, e.g., sha256), build,
and install a piece of software. A Package also defines what other
packages it depends on, so that dependencies can be installed along
with the package itself. Packages are written in pure python.
with the package itself. Packages are written in pure python by
users of Spack.
Packages live in repositories (see repo.py). If spack is installed
in ``$prefix``, all of its built-in package files are in the builtin
repo at ``$prefix/var/spack/repos/builtin/packages``.
There are two main parts of a Spack package:
All you have to do to create a package is make a new subclass of Package
in this directory. Spack automatically scans the python files there
and figures out which one to import when you invoke it.
1. **The package class**. Classes contain ``directives``, which are
special functions, that add metadata (versions, patches,
dependencies, and other information) to packages (see
``directives.py``). Directives provide the constraints that are
used as input to the concretizer.
**An example package**
2. **Package instances**. Once instantiated, a package is
essentially an installer for a particular piece of
software. Spack calls methods like ``do_install()`` on the
``Package`` object, and it uses those to drive user-implemented
methods like ``patch()``, ``install()``, and other build steps.
To install software, An instantiated package needs a *concrete*
spec, which guides the behavior of the various install methods.
Let's look at the cmake package to start with. This package lives in
``$prefix/var/spack/repos/builtin/packages/cmake/package.py``:
Packages are imported from repos (see ``repo.py``).
.. code-block:: python
**Package DSL**
from spack import *
class Cmake(Package):
homepage = 'https://www.cmake.org'
url = 'http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz'
md5 = '097278785da7182ec0aea8769d06860c'
def install(self, spec, prefix):
configure('--prefix=%s' % prefix,
'--parallel=%s' % make_jobs)
make()
make('install')
**Naming conventions**
There are two names you should care about:
1. The module name, ``cmake``.
* User will refers to this name, e.g. 'spack install cmake'.
* It can include ``_``, ``-``, and numbers (it can even start with a
number).
2. The class name, "Cmake". This is formed by converting `-` or
``_`` in the module name to camel case. If the name starts with
a number, we prefix the class name with ``_``. Examples:
=========== ==========
Module Name Class Name
=========== ==========
foo_bar FooBar
docbook-xml DocbookXml
FooBar Foobar
3proxy _3proxy
=========== ==========
The class name is what spack looks for when it loads a package module.
**Required Attributes**
Aside from proper naming, here is the bare minimum set of things you
need when you make a package:
homepage:
informational URL, so that users know what they're
installing.
url or url_for_version(self, version):
If url, then the URL of the source archive that spack will fetch.
If url_for_version(), then a method returning the URL required
to fetch a particular version.
install():
This function tells spack how to build and install the
software it downloaded.
**Optional Attributes**
You can also optionally add these attributes, if needed:
list_url:
Webpage to scrape for available version strings. Default is the
directory containing the tarball; use this if the default isn't
correct so that invoking 'spack versions' will work for this
package.
url_version(self, version):
When spack downloads packages at particular versions, it just
converts version to string with str(version). Override this if
your package needs special version formatting in its URL. boost
is an example of a package that needs this.
***Creating Packages***
As a package creator, you can probably ignore most of the preceding
information, because you can use the 'spack create' command to do it
all automatically.
You as the package creator generally only have to worry about writing
your install function and specifying dependencies.
**spack create**
Most software comes in nicely packaged tarballs, like this one
http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz
Taking a page from homebrew, spack deduces pretty much everything it
needs to know from the URL above. If you simply type this::
spack create http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz
Spack will download the tarball, generate an md5 hash, figure out the
version and the name of the package from the URL, and create a new
package file for you with all the names and attributes set correctly.
Once this skeleton code is generated, spack pops up the new package in
your $EDITOR so that you can modify the parts that need changes.
**Dependencies**
If your package requires another in order to build, you can specify that
like this:
.. code-block:: python
class Stackwalker(Package):
...
depends_on("libdwarf")
...
This tells spack that before it builds stackwalker, it needs to build
the libdwarf package as well. Note that this is the module name, not
the class name (The class name is really only used by spack to find
your package).
Spack will download and install each dependency before it installs your
package. In addtion, it will add -L, -I, and rpath arguments to your
compiler and linker for each dependency. In most cases, this allows you
to avoid specifying any dependencies in your configure or cmake line;
you can just run configure or cmake without any additional arguments and
it will find the dependencies automatically.
**The Install Function**
The install function is designed so that someone not too terribly familiar
with Python could write a package installer. For example, we put a number
of commands in install scope that you can use almost like shell commands.
These include make, configure, cmake, rm, rmtree, mkdir, mkdirp, and
others.
You can see above in the cmake script that these commands are used to run
configure and make almost like they're used on the command line. The
only difference is that they are python function calls and not shell
commands.
It may be puzzling to you where the commands and functions in install live.
They are NOT instance variables on the class; this would require us to
type 'self.' all the time and it makes the install code unnecessarily long.
Rather, spack puts these commands and variables in *module* scope for your
Package subclass. Since each package has its own module, this doesn't
pollute other namespaces, and it allows you to more easily implement an
install function.
For a full list of commands and variables available in module scope, see
the add_commands_to_module() function in this class. This is where most
of them are created and set on the module.
**Parallel Builds**
By default, Spack will run make in parallel when you run make() in your
install function. Spack figures out how many cores are available on
your system and runs make with -j<cores>. If you do not want this
behavior, you can explicitly mark a package not to use parallel make:
.. code-block:: python
class SomePackage(Package):
...
parallel = False
...
This changes the default behavior so that make is sequential. If you still
want to build some parts in parallel, you can do this in your install
function:
.. code-block:: python
make(parallel=True)
Likewise, if you do not supply parallel = True in your Package, you can
keep the default parallel behavior and run make like this when you want a
sequential build:
.. code-block:: python
make(parallel=False)
Look in ``lib/spack/docs`` or check https://spack.readthedocs.io for
the full documentation of the package domain-specific language. That
used to be partially documented here, but as it grew, the docs here
became increasingly out of date.
**Package Lifecycle**
This section is really only for developers of new spack commands.
A package's lifecycle over a run of Spack looks something like this:
.. code-block:: python
@ -541,8 +367,14 @@ class SomePackage(Package):
package writers to override, and doing so may break the functionality
of the Package class.
Package creators override functions like install() (all of them do this),
clean() (some of them do this), and others to provide custom behavior.
Package creators have a lot of freedom, and they could technically
override anything in this class. That is not usually required.
For most use cases. Package creators typically just add attributes
like ``url`` and ``homepage``, or functions like ``install()``.
There are many custom ``Package`` subclasses in the
``spack.build_systems`` package that make things even easier for
specific build systems.
"""
#