summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAdam J. Stewart <ajstewart426@gmail.com>2017-06-25 00:39:31 -0500
committerTodd Gamblin <tgamblin@llnl.gov>2017-06-24 22:39:31 -0700
commite5ce7b163954843cf7247e2902535ab837aea320 (patch)
tree52e019d52019b8986a2d327fba981bedb22c6471 /lib
parentcac4362f6491bf48dd287f31f3de9169464713ea (diff)
downloadspack-e5ce7b163954843cf7247e2902535ab837aea320.tar.gz
spack-e5ce7b163954843cf7247e2902535ab837aea320.tar.bz2
spack-e5ce7b163954843cf7247e2902535ab837aea320.tar.xz
spack-e5ce7b163954843cf7247e2902535ab837aea320.zip
Allow arbitrary Prefix attributes (#4591)
* Allow arbitrary Prefix attributes * Test attribute type as well * Flake8 fixes * Remove __new__ method * Fewer uses of join_path in the docs
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/docs/packaging_guide.rst88
-rw-r--r--lib/spack/spack/package.py2
-rw-r--r--lib/spack/spack/test/util/prefix.py66
-rw-r--r--lib/spack/spack/util/prefix.py81
4 files changed, 134 insertions, 103 deletions
diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst
index bf6f3f3cf9..ecc3f58830 100644
--- a/lib/spack/docs/packaging_guide.rst
+++ b/lib/spack/docs/packaging_guide.rst
@@ -2408,15 +2408,21 @@ is handy when a package supports additional variants like
Blas and Lapack libraries
^^^^^^^^^^^^^^^^^^^^^^^^^
-Different packages provide implementation of ``Blas`` and ``Lapack``
+Multiple packages provide implementations of ``Blas`` and ``Lapack``
routines. The names of the resulting static and/or shared libraries
differ from package to package. In order to make the ``install()`` method
independent of the choice of ``Blas`` implementation, each package which
provides it sets up ``self.spec.blas_libs`` to point to the correct
``Blas`` libraries. The same applies to packages which provide
``Lapack``. Package developers are advised to use these variables, for
-example ``spec['blas'].blas_libs.joined()`` instead of hard-coding
-``join_path(spec['blas'].prefix.lib, 'libopenblas.so')``.
+example ``spec['blas'].blas_libs.joined()`` instead of hard-coding them:
+
+.. code-block:: python
+
+ if 'openblas' in spec:
+ libs = join_path(spec['blas'].prefix.lib, 'libopenblas.so')
+ elif 'intel-mkl' in spec:
+ ...
.. _prefix-objects:
@@ -2430,7 +2436,7 @@ e.g.:
.. code-block:: python
- configure('--prefix=' + prefix)
+ configure('--prefix={0}'.format(prefix))
For the most part, prefix objects behave exactly like strings. For
packages that do not have their own install target, or for those that
@@ -2451,29 +2457,27 @@ yourself, e.g.:
mkdirp(prefix.lib)
install('libfoo.a', prefix.lib)
-Most of the standard UNIX directory names are attributes on the
-``prefix`` object. Here is a full list:
-
- ========================= ================================================
- Prefix Attribute Location
- ========================= ================================================
- ``prefix.bin`` ``$prefix/bin``
- ``prefix.sbin`` ``$prefix/sbin``
- ``prefix.etc`` ``$prefix/etc``
- ``prefix.include`` ``$prefix/include``
- ``prefix.lib`` ``$prefix/lib``
- ``prefix.lib64`` ``$prefix/lib64``
- ``prefix.libexec`` ``$prefix/libexec``
- ``prefix.share`` ``$prefix/share``
- ``prefix.doc`` ``$prefix/doc``
- ``prefix.info`` ``$prefix/info``
-
- ``prefix.man`` ``$prefix/man``
- ``prefix.man[1-8]`` ``$prefix/man/man[1-8]``
-
- ``prefix.share_man`` ``$prefix/share/man``
- ``prefix.share_man[1-8]`` ``$prefix/share/man[1-8]``
- ========================= ================================================
+
+Attributes of this object are created on the fly when you request them,
+so any of the following will work:
+
+====================== =======================
+Prefix Attribute Location
+====================== =======================
+``prefix.bin`` ``$prefix/bin``
+``prefix.lib64`` ``$prefix/lib64``
+``prefix.share.man`` ``$prefix/share/man``
+``prefix.foo.bar.baz`` ``$prefix/foo/bar/baz``
+====================== =======================
+
+Of course, this only works if your file or directory is a valid Python
+variable name. If your file or directory contains dashes or dots, use
+``join_path`` instead:
+
+.. code-block:: python
+
+ join_path(prefix.lib, 'libz.a')
+
.. _spec-objects:
@@ -2572,23 +2576,25 @@ 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 sub-scripting the spec:
+You may need to get at some file or binary that's in the installation
+prefix of one of your dependencies. You can do that by sub-scripting
+the spec:
.. code-block:: python
- my_mpi = spec['mpi']
+ spec['mpi']
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
-``mpi``. The value returned and assigned to ``my_mpi``, is itself
-just another ``Spec`` object, so you can do all the same things you
-would do with the package's own spec:
+``mpi``. The value returned is itself just another ``Spec`` object,
+so you can do all the same things you would do with the package's
+own spec:
.. code-block:: python
- mpicc = join_path(my_mpi.prefix.bin, 'mpicc')
+ spec['mpi'].prefix.bin
+ spec['mpi'].version
.. _multimethods:
@@ -3086,7 +3092,7 @@ Filtering functions
.. code-block:: python
filter_file(r'#!/usr/bin/perl',
- '#!/usr/bin/env perl', join_path(prefix.bin, 'bib2xhtml'))
+ '#!/usr/bin/env perl', prefix.bin.bib2xhtml)
#. Switching the compilers used by ``mpich``'s MPI wrapper scripts from
``cc``, etc. to the compilers used by the Spack build:
@@ -3094,10 +3100,10 @@ Filtering functions
.. code-block:: python
filter_file('CC="cc"', 'CC="%s"' % self.compiler.cc,
- join_path(prefix.bin, 'mpicc'))
+ prefix.bin.mpicc)
filter_file('CXX="c++"', 'CXX="%s"' % self.compiler.cxx,
- join_path(prefix.bin, 'mpicxx'))
+ prefix.bin.mpicxx)
:py:func:`change_sed_delimiter(old_delim, new_delim, *filenames) <spack.change_sed_delim>`
Some packages, like TAU, have a build system that can't install
@@ -3134,12 +3140,10 @@ File functions
.. code-block:: python
- install('my-header.h', join_path(prefix.include))
+ install('my-header.h', prefix.include)
-:py:func:`join_path(prefix, *args) <spack.join_path>`
- Like ``os.path.join``, this joins paths using the OS path separator.
- However, this version allows an arbitrary number of arguments, so
- you can string together many path components.
+:py:func:`join_path(*paths) <spack.join_path>`
+ An alias for ``os.path.join``. This joins paths using the OS path separator.
:py:func:`mkdirp(*paths) <spack.mkdirp>`
Create each of the directories in ``paths``, creating any parent
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index d6029d46b1..dc23f6351f 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -1045,7 +1045,7 @@ class PackageBase(with_metaclass(PackageMeta, object)):
touch(join_path(self.prefix.lib, library_name + dso_suffix))
touch(join_path(self.prefix.lib, library_name + '.a'))
- mkdirp(self.prefix.man1)
+ mkdirp(self.prefix.man.man1)
packages_dir = spack.store.layout.build_packages_path(self.spec)
dump_packages(self.spec, packages_dir)
diff --git a/lib/spack/spack/test/util/prefix.py b/lib/spack/spack/test/util/prefix.py
new file mode 100644
index 0000000000..8e8f7c84f9
--- /dev/null
+++ b/lib/spack/spack/test/util/prefix.py
@@ -0,0 +1,66 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+"""Tests various features of :py:class:`spack.util.prefix.Prefix`"""
+
+from spack.util.prefix import Prefix
+
+
+def test_prefix_attributes():
+ """Test normal prefix attributes like ``prefix.bin``"""
+ prefix = Prefix('/usr')
+
+ assert prefix.bin == '/usr/bin'
+ assert prefix.lib == '/usr/lib'
+ assert prefix.include == '/usr/include'
+
+
+def test_multilevel_attributes():
+ """Test attributes of attributes, like ``prefix.share.man``"""
+ prefix = Prefix('/usr/')
+
+ assert prefix.share.man == '/usr/share/man'
+ assert prefix.man.man8 == '/usr/man/man8'
+ assert prefix.foo.bar.baz == '/usr/foo/bar/baz'
+
+ share = prefix.share
+
+ assert isinstance(share, Prefix)
+ assert share.man == '/usr/share/man'
+
+
+def test_string_like_behavior():
+ """Test string-like behavior of the prefix object"""
+ prefix = Prefix('/usr')
+
+ assert prefix == '/usr'
+ assert isinstance(prefix, str)
+
+ assert prefix + '/bin' == '/usr/bin'
+ assert '--prefix=%s' % prefix == '--prefix=/usr'
+ assert '--prefix={0}'.format(prefix) == '--prefix=/usr'
+
+ assert prefix.find('u', 1)
+ assert prefix.upper() == '/USR'
+ assert prefix.lstrip('/') == 'usr'
diff --git a/lib/spack/spack/util/prefix.py b/lib/spack/spack/util/prefix.py
index 8ce9836c66..479956b1c5 100644
--- a/lib/spack/spack/util/prefix.py
+++ b/lib/spack/spack/util/prefix.py
@@ -23,74 +23,35 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""
-This file contains utilities to help with installing packages.
+This file contains utilities for managing the installation prefix of a package.
"""
-from llnl.util.filesystem import join_path
+import os
class Prefix(str):
"""This class represents an installation prefix, but provides useful
- attributes for referring to directories inside the prefix.
+ attributes for referring to directories inside the prefix.
- For example, you can do something like this::
+ Attributes of this object are created on the fly when you request them,
+ so any of the following is valid:
- prefix = Prefix('/usr')
- print(prefix.lib)
- print(prefix.lib64)
- print(prefix.bin)
- print(prefix.share)
- print(prefix.man4)
+ >>> prefix = Prefix('/usr')
+ >>> prefix.bin
+ /usr/bin
+ >>> prefix.lib64
+ /usr/lib64
+ >>> prefix.share.man
+ /usr/share/man
+ >>> prefix.foo.bar.baz
+ /usr/foo/bar/baz
- This program would print:
+ Prefix objects behave identically to strings. In fact, they
+ subclass ``str``. So operators like ``+`` are legal::
- /usr/lib
- /usr/lib64
- /usr/bin
- /usr/share
- /usr/share/man/man4
+ print('foobar ' + prefix)
- Prefix objects behave identically to strings. In fact, they
- subclass str. So operators like + are legal:
-
- print("foobar " + prefix)
-
- This prints 'foobar /usr". All of this is meant to make custom
- installs easy.
+ This prints ``foobar /usr``. All of this is meant to make custom
+ installs easy.
"""
-
- def __new__(cls, path):
- s = super(Prefix, cls).__new__(cls, path)
- s.bin = join_path(s, 'bin')
- s.bin64 = join_path(s, 'bin64')
- s.sbin = join_path(s, 'sbin')
- s.etc = join_path(s, 'etc')
- s.include = join_path(s, 'include')
- s.include64 = join_path(s, 'include64')
- s.lib = join_path(s, 'lib')
- s.lib64 = join_path(s, 'lib64')
- s.libexec = join_path(s, 'libexec')
- s.share = join_path(s, 'share')
- s.doc = join_path(s.share, 'doc')
- s.info = join_path(s.share, 'info')
-
- s.man = join_path(s, 'man')
- s.man1 = join_path(s.man, 'man1')
- s.man2 = join_path(s.man, 'man2')
- s.man3 = join_path(s.man, 'man3')
- s.man4 = join_path(s.man, 'man4')
- s.man5 = join_path(s.man, 'man5')
- s.man6 = join_path(s.man, 'man6')
- s.man7 = join_path(s.man, 'man7')
- s.man8 = join_path(s.man, 'man8')
-
- s.share_man = join_path(s.share, 'man')
- s.share_man1 = join_path(s.share_man, 'man1')
- s.share_man2 = join_path(s.share_man, 'man2')
- s.share_man3 = join_path(s.share_man, 'man3')
- s.share_man4 = join_path(s.share_man, 'man4')
- s.share_man5 = join_path(s.share_man, 'man5')
- s.share_man6 = join_path(s.share_man, 'man6')
- s.share_man7 = join_path(s.share_man, 'man7')
- s.share_man8 = join_path(s.share_man, 'man8')
-
- return s
+ def __getattr__(self, attr):
+ return Prefix(os.path.join(self, attr))