More packaging documentation.
This commit is contained in:
parent
a4cda94524
commit
208db9b002
10 changed files with 509 additions and 62 deletions
|
@ -13,18 +13,16 @@
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{%- if show_copyright %}
|
© Copyright 2013,
|
||||||
{%- if hasdoc('copyright') %}
|
<a href="https://scalability.llnl.gov/">Lawrence Livermore National Laboratory</a>.
|
||||||
{% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
|
<br/>
|
||||||
{%- else %}
|
Written by Todd Gamblin, <a href="mailto:tgamblin@llnl.gov">tgamblin@llnl.gov</a>
|
||||||
{% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
|
<br/>
|
||||||
{%- endif %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
{%- if last_updated %}
|
{%- if last_updated %}
|
||||||
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{% trans %}<a href="https://www.github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="http://readthedocs.org">Read the Docs</a>{% endtrans %}
|
{% trans %}<br/><a href="https://www.github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="http://readthedocs.org">Read the Docs</a>{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'Spack'
|
project = u'Spack'
|
||||||
copyright = u'2013, Todd Gamblin'
|
copyright = u'2013, Lawrence Livermore National Laboratory'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
@ -149,7 +149,7 @@
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _developer_guide:
|
||||||
|
|
||||||
Developer Guide
|
Developer Guide
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|
|
@ -4,34 +4,439 @@ Packaging Guide
|
||||||
This guide is intended for developers or administrators who want to
|
This guide is intended for developers or administrators who want to
|
||||||
*package* their software so that Spack can install it. We assume that
|
*package* their software so that Spack can install it. We assume that
|
||||||
you have at least some familiarty with Python, and that you've read
|
you have at least some familiarty with Python, and that you've read
|
||||||
the :ref:`guide for regular users <basic_usage>`, especially the part
|
the :ref:`basic usage guide <basic_usage>`, especially the part
|
||||||
about *specs*.
|
about :ref:`specs <sec-specs>`.
|
||||||
|
|
||||||
|
There are two key parts of Spack:
|
||||||
|
|
||||||
Package files
|
#. **specs**: a language for describing builds of software, and
|
||||||
-------------------------
|
#. **packages**: Python modules that build software according to a
|
||||||
|
spec.
|
||||||
|
|
||||||
There are two parts of Spack, a language for describing builds of
|
The package allows the developer to encapsulate build logic for
|
||||||
software (*specs*), and *packages*: Python modules thatactually build
|
different versions, compilers, and platforms in one place.
|
||||||
the software. A package essentially takes a spec and implements it
|
|
||||||
for a particular piece of software. It allows a developer to
|
|
||||||
encapsulate build logic for different versions, compilers, and
|
|
||||||
platforms in one place, and it is designed to make things easy for
|
|
||||||
you, the packager, as much as possible.
|
|
||||||
|
|
||||||
Packages in spack live in ``$prefix/lib/spack/spack/packages``:
|
Packages in Spack are written in pure Python, so you can do anything
|
||||||
|
in Spack that you can do in Python. Python was chosen as the
|
||||||
|
implementation language for two reasons. First, Python is getting to
|
||||||
|
be ubiquitous in the HPC community due to its use in numerical codes.
|
||||||
|
Second, it's a modern language and has many powerful features to help
|
||||||
|
make package writing easy.
|
||||||
|
|
||||||
|
Finally, we've gone to great lengths to make it *easy* to create
|
||||||
|
packages. The ``spack create`` command lets you generate a
|
||||||
|
boilerplate package template from a tarball URL, and ideally you'll
|
||||||
|
only need to run this once and slightly modify the boilerplate to get
|
||||||
|
your package working.
|
||||||
|
|
||||||
|
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
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
also the top-level include directory for Python code. When Spack
|
||||||
|
runs, it adds this directory to its ``PYTHONPATH``.
|
||||||
|
|
||||||
|
Spack packages live in the ``spack.packages`` Python package, which
|
||||||
|
means that they need to go in ``$prefix/lib/spack/spack/packages``.
|
||||||
|
If you list that directory, you'll see all the existing packages:
|
||||||
|
|
||||||
.. command-output:: cd $SPACK_ROOT/lib/spack/spack/packages; ls *.py
|
.. command-output:: cd $SPACK_ROOT/lib/spack/spack/packages; ls *.py
|
||||||
:shell:
|
:shell:
|
||||||
:ellipsis: 5
|
:ellipsis: 5
|
||||||
|
|
||||||
|
``__init__.py`` contains some utility functions used by Spack to load
|
||||||
|
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
|
||||||
|
specs. e.g., if you type any of these:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
spack install libelf
|
||||||
|
spack install libelf@0.8.13
|
||||||
|
|
||||||
|
Spack sees the package name in the spec and looks for a file called
|
||||||
|
``libelf.py`` in its ``packages`` directory. Likewise, if you say
|
||||||
|
``spack install docbook-xml``, then Spack looks for a file called
|
||||||
|
``docbook-xml.py``.
|
||||||
|
|
||||||
|
We use the filename for the package name to give packagers more
|
||||||
|
freedom in naming their packages. Package names can contain letters,
|
||||||
|
numbers, dashes, and underscores, and there are no other restrictions.
|
||||||
|
You can name a package ``3proxy`` or ``_foo`` and Spack won't care --
|
||||||
|
it just needs to see that name in the package spec. Experienced
|
||||||
|
Python programmers will notice that package names are actually Python
|
||||||
|
module names, and but they're not necessarily valid Python
|
||||||
|
identifiers. i.e., you can't actually ``import 3proxy`` in Python.
|
||||||
|
You'll get a syntax error because the identifier doesn't start with a
|
||||||
|
letter or underscore. For more details on why this is still ok, see
|
||||||
|
the :ref:`developer guide<developer_guide>`.
|
||||||
|
|
||||||
|
.. literalinclude:: ../spack/packages/libelf.py
|
||||||
|
:linenos:
|
||||||
|
:lines: 3
|
||||||
|
|
||||||
|
The *class name* is formed by converting words separated by `-` or
|
||||||
|
``_`` in the file name to camel case. If the name starts with a
|
||||||
|
number, we prefix the class name with ``Num_``. Here are some
|
||||||
|
examples:
|
||||||
|
|
||||||
|
================= =================
|
||||||
|
Module Name Class Name
|
||||||
|
================= =================
|
||||||
|
``foo_bar`` ``FooBar``
|
||||||
|
``docbook-xml`` ``DocbookXml``
|
||||||
|
``FooBar`` ``Foobar``
|
||||||
|
``3proxy`` ``Num_3proxy``
|
||||||
|
================= =================
|
||||||
|
|
||||||
|
The class name is needed by Spack to properly import a package, but
|
||||||
|
not for much else. In general, you won't have to remember this naming
|
||||||
|
convention because ``spack create`` will generate a boilerplate class
|
||||||
|
for you, and you can just fill in the blanks.
|
||||||
|
|
||||||
|
|
||||||
|
Metadata
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Just under the class name is a description of the ``libelf`` package.
|
||||||
|
In Python, this is called a *docstring*, and it's a multi-line,
|
||||||
|
triple-quoted (``"""``) string that comes just after the definition of
|
||||||
|
a class. Spack uses the docstring to generate the description of the
|
||||||
|
package that is shown when you run ``spack info``. If you don't provide
|
||||||
|
a description, Spack will just print "None" for the description.
|
||||||
|
|
||||||
|
In addition the package description, there are a few fields you'll
|
||||||
|
need to fill out. They are as follows:
|
||||||
|
|
||||||
|
``homepage``
|
||||||
|
This is the URL where you can learn about the package and get
|
||||||
|
information. It is displayed to users when they run ``spack info``.
|
||||||
|
|
||||||
|
``url``
|
||||||
|
This is the URL where you can download a distribution tarball of
|
||||||
|
the pacakge's source code.
|
||||||
|
|
||||||
|
``versions``
|
||||||
|
This is a `dictionary
|
||||||
|
<http://docs.python.org/2/tutorial/datastructures.html#dictionaries>`_
|
||||||
|
mapping versions to MD5 hashes. Spack uses the hashes to checksum
|
||||||
|
archives when it downloads a particular version.
|
||||||
|
|
||||||
|
The homepage and URL are required fields, and ``versions`` is not
|
||||||
|
required but it's recommended. Spack will warn usrs if they try to
|
||||||
|
install a spec (e.g., ``libelf@0.8.10`` for which there is not a
|
||||||
|
checksum available. They can force it to download the new version and
|
||||||
|
install, but it's better to provide checksums so users don't have to
|
||||||
|
install from an unchecked archive.
|
||||||
|
|
||||||
|
|
||||||
|
Install function
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The last element of the ``libelf`` package is its ``install()``
|
||||||
|
function. 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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Inside of the ``install()`` function, things should look pretty
|
||||||
|
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-create:
|
||||||
|
|
||||||
|
Creating Packages Automatically
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
``spack create``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``spack create`` command takes the drudgery out of making
|
||||||
|
packages. It generates boilerplate code that conforms to Spack's idea
|
||||||
|
of a package should be, so that you can focus on getting your pacakge
|
||||||
|
working.
|
||||||
|
|
||||||
|
All you need is the URL to a tarball you want to package:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ spack create http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
|
||||||
|
|
||||||
|
When you run this, Spack will look at the tarball URL, and it will try
|
||||||
|
to figure out the of the package to be created. It also tries to
|
||||||
|
figure out what version strings for that package look like. Once that
|
||||||
|
is done, it tries to find *additional* versions by spidering the
|
||||||
|
package's webpage. Spack then prompts you to tell it how many
|
||||||
|
versions you want to download and checksum.
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
==> Creating template for package cmake
|
||||||
|
==> Found 18 versions of cmake.
|
||||||
|
2.8.12.1 http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
|
||||||
|
2.8.12 http://www.cmake.org/files/v2.8/cmake-2.8.12.tar.gz
|
||||||
|
2.8.11.2 http://www.cmake.org/files/v2.8/cmake-2.8.11.2.tar.gz
|
||||||
|
2.8.11.1 http://www.cmake.org/files/v2.8/cmake-2.8.11.1.tar.gz
|
||||||
|
2.8.11 http://www.cmake.org/files/v2.8/cmake-2.8.11.tar.gz
|
||||||
|
2.8.10.2 http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz
|
||||||
|
2.8.10.1 http://www.cmake.org/files/v2.8/cmake-2.8.10.1.tar.gz
|
||||||
|
2.8.10 http://www.cmake.org/files/v2.8/cmake-2.8.10.tar.gz
|
||||||
|
2.8.9 http://www.cmake.org/files/v2.8/cmake-2.8.9.tar.gz
|
||||||
|
...
|
||||||
|
2.8.0 http://www.cmake.org/files/v2.8/cmake-2.8.0.tar.gz
|
||||||
|
|
||||||
|
Include how many checksums in the package file? (default is 5, q to abort)
|
||||||
|
|
||||||
|
Spack will automatically download the number of tarballs you specify
|
||||||
|
(starting with the most recent) and checksum each of them.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
Include how many checksums in the package file? (default is 5, q to abort) 3
|
||||||
|
==> Downloading...
|
||||||
|
==> Fetching http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
|
||||||
|
###################################################################### 98.6%
|
||||||
|
==> Fetching http://www.cmake.org/files/v2.8/cmake-2.8.12.tar.gz
|
||||||
|
##################################################################### 96.7%
|
||||||
|
==> Fetching http://www.cmake.org/files/v2.8/cmake-2.8.11.2.tar.gz
|
||||||
|
#################################################################### 95.2%
|
||||||
|
|
||||||
|
Now Spack generates some boilerplate and open the package file in
|
||||||
|
your favorite ``$EDITOR``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
# FIXME:
|
||||||
|
# This is a template package file for Spack. We've conveniently
|
||||||
|
# put "FIXME" labels next to all the things you'll want to change.
|
||||||
|
#
|
||||||
|
# Once you've edited all the FIXME's, delete this whole message,
|
||||||
|
# save this file, and test out your package like this:
|
||||||
|
#
|
||||||
|
# spack install cmake
|
||||||
|
#
|
||||||
|
# You can always get back here to change things with:
|
||||||
|
#
|
||||||
|
# spack edit cmake
|
||||||
|
#
|
||||||
|
# See the spack documentation for more information on building
|
||||||
|
# packages.
|
||||||
|
#
|
||||||
|
from spack import *
|
||||||
|
|
||||||
|
class Cmake(Package):
|
||||||
|
"""FIXME: put a proper description of your package here."""
|
||||||
|
# FIXME: add a proper url for your package's homepage here.
|
||||||
|
homepage = "http://www.example.com"
|
||||||
|
url = "http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz"
|
||||||
|
|
||||||
|
versions = { '2.8.12.1' : '9d38cd4e2c94c3cea97d0e2924814acc',
|
||||||
|
'2.8.12' : '105bc6d21cc2e9b6aff901e43c53afea',
|
||||||
|
'2.8.11.2' : '6f5d7b8e7534a5d9e1a7664ba63cf882', }
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
# FIXME: Modify the configure line to suit your build system here.
|
||||||
|
configure("--prefix=%s" % prefix)
|
||||||
|
|
||||||
|
# FIXME: Add logic to build and install here
|
||||||
|
make()
|
||||||
|
make("install")
|
||||||
|
|
||||||
|
The tedious stuff (creating the class, checksumming archives) has been
|
||||||
|
done for you.
|
||||||
|
|
||||||
|
All the things you still need to change are marked with ``FIXME``
|
||||||
|
labels. The first ``FIXME`` refers to the commented instructions at
|
||||||
|
the top of the file. You can delete these after reading them. The
|
||||||
|
rest of them are as follows:
|
||||||
|
|
||||||
|
#. Add a description in your package's docstring.
|
||||||
|
#. Change the homepage to a useful URL (not ``example.com``).
|
||||||
|
#. Get the ``install()`` method working.
|
||||||
|
|
||||||
|
|
||||||
|
``spack edit``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once you've created a package, you can go back and edit it using
|
||||||
|
``spack edit``. For example, this:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
spack edit libelf
|
||||||
|
|
||||||
|
will open ``$SPACK_ROOT/lib/spack/spack/packages/libelf.py`` in
|
||||||
|
``$EDITOR``. If you try to edit a package that doesn't exist, Spack
|
||||||
|
will recommend using ``spack create``:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ spack edit foo
|
||||||
|
==> Error: No package 'foo'. Use spack create, or supply -f/--force to edit a new file.
|
||||||
|
|
||||||
|
And, finally, if you *really* want to skip all the automatic stuff
|
||||||
|
that ``spack create`` does for you, then you can run ``spack edit
|
||||||
|
-f/--force``:
|
||||||
|
|
||||||
|
$ spack edit -f foo
|
||||||
|
|
||||||
|
Which will generate a *very* minimal package structure for you to fill
|
||||||
|
in:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
from spack import *
|
||||||
|
|
||||||
|
class Foo(Package):
|
||||||
|
"""Description"""
|
||||||
|
|
||||||
|
homepage = "http://www.example.com"
|
||||||
|
url = "http://www.example.com/foo-1.0.tar.gz"
|
||||||
|
|
||||||
|
versions = { '1.0' : '0123456789abcdef0123456789abcdef' }
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
configure("--prefix=%s" % prefix)
|
||||||
|
make()
|
||||||
|
make("install")
|
||||||
|
|
||||||
|
We recommend using this only when you have to, as it's generally more
|
||||||
|
work than using ``spack create``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _spack-checksum:
|
||||||
|
|
||||||
|
``spack checksum``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you've already created a package and you want to add more version
|
||||||
|
checksums to it, this is automated with ``spack checksum``. Here's an
|
||||||
|
example for ``libelf``:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ spack checksum libelf
|
||||||
|
==> Found 16 versions of libelf.
|
||||||
|
0.8.13 http://www.mr511.de/software/libelf-0.8.13.tar.gz
|
||||||
|
0.8.12 http://www.mr511.de/software/libelf-0.8.12.tar.gz
|
||||||
|
0.8.11 http://www.mr511.de/software/libelf-0.8.11.tar.gz
|
||||||
|
0.8.10 http://www.mr511.de/software/libelf-0.8.10.tar.gz
|
||||||
|
0.8.9 http://www.mr511.de/software/libelf-0.8.9.tar.gz
|
||||||
|
0.8.8 http://www.mr511.de/software/libelf-0.8.8.tar.gz
|
||||||
|
0.8.7 http://www.mr511.de/software/libelf-0.8.7.tar.gz
|
||||||
|
0.8.6 http://www.mr511.de/software/libelf-0.8.6.tar.gz
|
||||||
|
0.8.5 http://www.mr511.de/software/libelf-0.8.5.tar.gz
|
||||||
|
...
|
||||||
|
0.5.2 http://www.mr511.de/software/libelf-0.5.2.tar.gz
|
||||||
|
|
||||||
|
How many would you like to checksum? (default is 5, q to abort)
|
||||||
|
|
||||||
|
This does the same thing that ``spack create`` did, it just allows you
|
||||||
|
to go back and create more checksums for an existing package. It
|
||||||
|
fetches the tarballs you ask for and prints out a dict ready to copy
|
||||||
|
and paste into your package file:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
==> Checksummed new versions of libelf:
|
||||||
|
{
|
||||||
|
'0.8.13' : '4136d7b4c04df68b686570afa26988ac',
|
||||||
|
'0.8.12' : 'e21f8273d9f5f6d43a59878dc274fec7',
|
||||||
|
'0.8.11' : 'e931910b6d100f6caa32239849947fbf',
|
||||||
|
'0.8.10' : '9db4d36c283d9790d8fa7df1f4d7b4d9',
|
||||||
|
}
|
||||||
|
|
||||||
|
You should be able to add these checksums directly to the versions
|
||||||
|
field in your package.
|
||||||
|
|
||||||
|
Note that for ``spack checksum`` to work, Spack needs to be able to
|
||||||
|
``import`` your pacakge in Python. That means it can't have any
|
||||||
|
syntax errors, or the ``import`` will fail. Use this once you've got
|
||||||
|
your package in working order.
|
||||||
|
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Virtual dependencies
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Install environment
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Package lifecycle
|
Package lifecycle
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
``spack install`` command performs a number of tasks before it finally
|
The ``spack install`` command performs a number of tasks before it
|
||||||
installs each package. It downloads an archive, expands it in a
|
finally installs each package. It downloads an archive, expands it in
|
||||||
temporary directory, and then performs the installation. Spack has
|
a temporary directory, and then performs the installation. Spack has
|
||||||
several commands that allow finer-grained control over each stage of
|
several commands that allow finer-grained control over each stage of
|
||||||
the build process.
|
the build process.
|
||||||
|
|
||||||
|
@ -100,27 +505,8 @@ files.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Virtual dependencies
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Packaging commands
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
``spack edit``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``spack create``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``spack checksum``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``spack graph``
|
``spack graph``
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -80,3 +80,19 @@ def parse_specs(args, **kwargs):
|
||||||
except spack.spec.SpecError, e:
|
except spack.spec.SpecError, e:
|
||||||
tty.error(e.message)
|
tty.error(e.message)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def elide_list(line_list, max_num=10):
|
||||||
|
"""Takes a long list and limits it to a smaller number of elements,
|
||||||
|
replacing intervening elements with '...'. For example::
|
||||||
|
|
||||||
|
elide_list([1,2,3,4,5,6], 4)
|
||||||
|
|
||||||
|
gives::
|
||||||
|
|
||||||
|
[1, 2, 3, '...', 6]
|
||||||
|
"""
|
||||||
|
if len(line_list) > max_num:
|
||||||
|
return line_list[:max_num-1] + ['...'] + line_list[-1:]
|
||||||
|
else:
|
||||||
|
return line_list
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
|
|
||||||
|
import spack.cmd
|
||||||
import spack.tty as tty
|
import spack.tty as tty
|
||||||
import spack.packages as packages
|
import spack.packages as packages
|
||||||
import spack.util.crypto
|
import spack.util.crypto
|
||||||
|
@ -47,6 +48,7 @@ def get_checksums(versions, urls, **kwargs):
|
||||||
return zip(versions, hashes)
|
return zip(versions, hashes)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def checksum(parser, args):
|
def checksum(parser, args):
|
||||||
# get the package we're going to generate checksums for
|
# get the package we're going to generate checksums for
|
||||||
pkg = packages.get(args.package)
|
pkg = packages.get(args.package)
|
||||||
|
@ -68,7 +70,8 @@ def checksum(parser, args):
|
||||||
|
|
||||||
|
|
||||||
tty.msg("Found %s versions of %s." % (len(urls), pkg.name),
|
tty.msg("Found %s versions of %s." % (len(urls), pkg.name),
|
||||||
*["%-10s%s" % (v,u) for v, u in zip(versions, urls)])
|
*spack.cmd.elide_list(
|
||||||
|
["%-10s%s" % (v,u) for v, u in zip(versions, urls)]))
|
||||||
print
|
print
|
||||||
archives_to_fetch = tty.get_number(
|
archives_to_fetch = tty.get_number(
|
||||||
"How many would you like to checksum?", default=5, abort='q')
|
"How many would you like to checksum?", default=5, abort='q')
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
import spack.cmd
|
||||||
import spack.package
|
import spack.package
|
||||||
import spack.packages as packages
|
import spack.packages as packages
|
||||||
import spack.tty as tty
|
import spack.tty as tty
|
||||||
|
@ -45,11 +46,11 @@ class ${class_name}(Package):
|
||||||
|
|
||||||
versions = ${versions}
|
versions = ${versions}
|
||||||
|
|
||||||
def install(self, prefix):
|
def install(self, spec, prefix):
|
||||||
# FIXME: Modify the configure line to suit your build system here.
|
# FIXME: Modify the configure line to suit your build system here.
|
||||||
${configure}
|
${configure}
|
||||||
|
|
||||||
# FIXME:
|
# FIXME: Add logic to build and install here
|
||||||
make()
|
make()
|
||||||
make("install")
|
make("install")
|
||||||
""")
|
""")
|
||||||
|
@ -84,6 +85,15 @@ def __call__(self, stage):
|
||||||
self.configure = '%s\n # %s' % (autotools, cmake)
|
self.configure = '%s\n # %s' % (autotools, cmake)
|
||||||
|
|
||||||
|
|
||||||
|
def make_version_dict(ver_hash_tuples):
|
||||||
|
max_len = max(len(str(v)) for v,hfg in ver_hash_tuples)
|
||||||
|
width = max_len + 2
|
||||||
|
format = "%-" + str(width) + "s : '%s',"
|
||||||
|
sep = '\n '
|
||||||
|
return '{ ' + sep.join(format % ("'%s'" % v, h)
|
||||||
|
for v, h in ver_hash_tuples) + ' }'
|
||||||
|
|
||||||
|
|
||||||
def create(parser, args):
|
def create(parser, args):
|
||||||
url = args.url
|
url = args.url
|
||||||
|
|
||||||
|
@ -118,8 +128,9 @@ def create(parser, args):
|
||||||
else:
|
else:
|
||||||
urls = [spack.url.substitute_version(url, v) for v in versions]
|
urls = [spack.url.substitute_version(url, v) for v in versions]
|
||||||
if len(urls) > 1:
|
if len(urls) > 1:
|
||||||
tty.msg("Found %s versions of %s to checksum." % (len(urls), name),
|
tty.msg("Found %s versions of %s." % (len(urls), name),
|
||||||
*["%-10s%s" % (v,u) for v, u in zip(versions, urls)])
|
*spack.cmd.elide_list(
|
||||||
|
["%-10s%s" % (v,u) for v, u in zip(versions, urls)]))
|
||||||
print
|
print
|
||||||
archives_to_fetch = tty.get_number(
|
archives_to_fetch = tty.get_number(
|
||||||
"Include how many checksums in the package file?",
|
"Include how many checksums in the package file?",
|
||||||
|
@ -130,17 +141,13 @@ def create(parser, args):
|
||||||
return
|
return
|
||||||
|
|
||||||
guesser = ConfigureGuesser()
|
guesser = ConfigureGuesser()
|
||||||
version_hashes = spack.cmd.checksum.get_checksums(
|
ver_hash_tuples = spack.cmd.checksum.get_checksums(
|
||||||
versions[:archives_to_fetch], urls[:archives_to_fetch],
|
versions[:archives_to_fetch], urls[:archives_to_fetch],
|
||||||
first_stage_function=guesser)
|
first_stage_function=guesser)
|
||||||
|
|
||||||
if not version_hashes:
|
if not ver_hash_tuples:
|
||||||
tty.die("Could not fetch any tarballs for %s." % name)
|
tty.die("Could not fetch any tarballs for %s." % name)
|
||||||
|
|
||||||
sep = '\n '
|
|
||||||
versions_string = '{ ' + sep.join(
|
|
||||||
"'%s' : '%s'," % (v, h) for v, h in version_hashes) + ' }'
|
|
||||||
|
|
||||||
# Write out a template for the file
|
# Write out a template for the file
|
||||||
with closing(open(pkg_path, "w")) as pkg_file:
|
with closing(open(pkg_path, "w")) as pkg_file:
|
||||||
pkg_file.write(
|
pkg_file.write(
|
||||||
|
@ -149,7 +156,7 @@ def create(parser, args):
|
||||||
configure=guesser.configure,
|
configure=guesser.configure,
|
||||||
class_name=class_name,
|
class_name=class_name,
|
||||||
url=url,
|
url=url,
|
||||||
versions=versions_string))
|
versions=make_version_dict(ver_hash_tuples)))
|
||||||
|
|
||||||
# If everything checks out, go ahead and edit.
|
# If everything checks out, go ahead and edit.
|
||||||
spack.editor(pkg_path)
|
spack.editor(pkg_path)
|
||||||
|
|
|
@ -1,11 +1,36 @@
|
||||||
import os
|
import os
|
||||||
|
import string
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.packages as packages
|
import spack.packages as packages
|
||||||
import spack.tty as tty
|
import spack.tty as tty
|
||||||
|
|
||||||
description = "Open package files in $EDITOR"
|
description = "Open package files in $EDITOR"
|
||||||
|
|
||||||
|
# When -f is supplied, we'll create a very minimal skeleton.
|
||||||
|
package_template = string.Template("""\
|
||||||
|
from spack import *
|
||||||
|
|
||||||
|
class ${class_name}(Package):
|
||||||
|
""\"Description""\"
|
||||||
|
|
||||||
|
homepage = "http://www.example.com"
|
||||||
|
url = "http://www.example.com/${name}-1.0.tar.gz"
|
||||||
|
|
||||||
|
versions = { '1.0' : '0123456789abcdef0123456789abcdef' }
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
configure("--prefix=%s" % prefix)
|
||||||
|
make()
|
||||||
|
make("install")
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
subparser.add_argument(
|
||||||
|
'-f', '--force', dest='force', action='store_true',
|
||||||
|
help="Open a new file in $EDITOR even if package doesn't exist.")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'name', nargs='?', default=None, help="name of package to edit")
|
'name', nargs='?', default=None, help="name of package to edit")
|
||||||
|
|
||||||
|
@ -24,8 +49,15 @@ def edit(parser, args):
|
||||||
tty.die("Something's wrong. '%s' is not a file!" % path)
|
tty.die("Something's wrong. '%s' is not a file!" % path)
|
||||||
if not os.access(path, os.R_OK|os.W_OK):
|
if not os.access(path, os.R_OK|os.W_OK):
|
||||||
tty.die("Insufficient permissions on '%s'!" % path)
|
tty.die("Insufficient permissions on '%s'!" % path)
|
||||||
|
elif not args.force:
|
||||||
|
tty.die("No package '%s'. Use spack create, or supply -f/--force "
|
||||||
|
"to edit a new file." % name)
|
||||||
else:
|
else:
|
||||||
tty.die("No package for %s. Use spack create.")
|
class_name = packages.class_name_for_package_name(name)
|
||||||
|
|
||||||
|
with closing(open(path, "w")) as pkg_file:
|
||||||
|
pkg_file.write(
|
||||||
|
package_template.substitute(name=name, class_name=class_name))
|
||||||
|
|
||||||
# If everything checks out, go ahead and edit.
|
# If everything checks out, go ahead and edit.
|
||||||
spack.editor(path)
|
spack.editor(path)
|
||||||
|
|
|
@ -108,10 +108,6 @@ def install(self, spec, prefix):
|
||||||
url
|
url
|
||||||
URL of the source archive that spack will fetch.
|
URL of the source archive that spack will fetch.
|
||||||
|
|
||||||
md5
|
|
||||||
md5 hash of the source archive, so that we can
|
|
||||||
verify that it was downloaded securely and correctly.
|
|
||||||
|
|
||||||
install()
|
install()
|
||||||
This function tells spack how to build and install the
|
This function tells spack how to build and install the
|
||||||
software it downloaded.
|
software it downloaded.
|
||||||
|
@ -119,6 +115,7 @@ def install(self, spec, prefix):
|
||||||
**Optional Attributes**
|
**Optional Attributes**
|
||||||
|
|
||||||
You can also optionally add these attributes, if needed:
|
You can also optionally add these attributes, if needed:
|
||||||
|
|
||||||
list_url
|
list_url
|
||||||
Webpage to scrape for available version strings. Default is the
|
Webpage to scrape for available version strings. Default is the
|
||||||
directory containing the tarball; use this if the default isn't
|
directory containing the tarball; use this if the default isn't
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
from spack import *
|
from spack import *
|
||||||
|
|
||||||
class Libelf(Package):
|
class Libelf(Package):
|
||||||
|
"""libelf lets you read, modify or create ELF object files in an
|
||||||
|
architecture-independent way. The library takes care of size
|
||||||
|
and endian issues, e.g. you can process a file for SPARC
|
||||||
|
processors on an Intel-based system."""
|
||||||
|
|
||||||
homepage = "http://www.mr511.de/software/english.html"
|
homepage = "http://www.mr511.de/software/english.html"
|
||||||
url = "http://www.mr511.de/software/libelf-0.8.13.tar.gz"
|
url = "http://www.mr511.de/software/libelf-0.8.13.tar.gz"
|
||||||
|
|
||||||
versions = { '0.8.13' : '4136d7b4c04df68b686570afa26988ac' }
|
versions = { '0.8.13' : '4136d7b4c04df68b686570afa26988ac',
|
||||||
|
'0.8.12' : 'e21f8273d9f5f6d43a59878dc274fec7', }
|
||||||
|
|
||||||
def install(self, spec, prefix):
|
def install(self, spec, prefix):
|
||||||
configure("--prefix=%s" % prefix,
|
configure("--prefix=%s" % prefix,
|
||||||
|
|
Loading…
Reference in a new issue