summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/docs/basic_usage.rst356
-rw-r--r--lib/spack/docs/configuration.rst23
-rw-r--r--lib/spack/docs/developer_guide.rst9
-rw-r--r--lib/spack/docs/mirrors.rst18
-rw-r--r--lib/spack/docs/packaging_guide.rst347
-rw-r--r--lib/spack/llnl/util/filesystem.py12
-rw-r--r--lib/spack/spack/__init__.py5
-rw-r--r--lib/spack/spack/build_environment.py56
-rw-r--r--lib/spack/spack/cmd/checksum.py21
-rw-r--r--lib/spack/spack/cmd/create.py147
-rw-r--r--lib/spack/spack/cmd/diy.py2
-rw-r--r--lib/spack/spack/cmd/fetch.py2
-rw-r--r--lib/spack/spack/cmd/info.py19
-rw-r--r--lib/spack/spack/cmd/install.py9
-rw-r--r--lib/spack/spack/cmd/load.py2
-rw-r--r--lib/spack/spack/cmd/mirror.py2
-rw-r--r--lib/spack/spack/cmd/module.py113
-rw-r--r--lib/spack/spack/cmd/package-list.py30
-rw-r--r--lib/spack/spack/cmd/setup.py91
-rw-r--r--lib/spack/spack/cmd/test-install.py7
-rw-r--r--lib/spack/spack/concretize.py16
-rw-r--r--lib/spack/spack/database.py33
-rw-r--r--lib/spack/spack/directives.py39
-rw-r--r--lib/spack/spack/directory_layout.py21
-rw-r--r--lib/spack/spack/fetch_strategy.py12
-rw-r--r--lib/spack/spack/graph.py10
-rw-r--r--lib/spack/spack/modules.py2
-rw-r--r--lib/spack/spack/package.py277
-rw-r--r--lib/spack/spack/platforms/linux.py15
-rw-r--r--lib/spack/spack/spec.py586
-rw-r--r--lib/spack/spack/test/__init__.py19
-rw-r--r--lib/spack/spack/test/architecture.py64
-rw-r--r--lib/spack/spack/test/build_system_guess.py (renamed from lib/spack/spack/test/configure_guess.py)26
-rw-r--r--lib/spack/spack/test/cmd/test_install.py33
-rw-r--r--lib/spack/spack/test/concretize.py24
-rw-r--r--lib/spack/spack/test/mock_packages_test.py26
-rw-r--r--lib/spack/spack/test/mock_repo.py4
-rw-r--r--lib/spack/spack/test/spec_dag.py88
-rw-r--r--lib/spack/spack/test/stage.py4
-rw-r--r--lib/spack/spack/test/versions.py39
-rw-r--r--lib/spack/spack/version.py27
41 files changed, 1977 insertions, 659 deletions
diff --git a/lib/spack/docs/basic_usage.rst b/lib/spack/docs/basic_usage.rst
index ec193e767d..50a161a175 100644
--- a/lib/spack/docs/basic_usage.rst
+++ b/lib/spack/docs/basic_usage.rst
@@ -114,13 +114,13 @@ that the packages is installed:
$ spack install mpileaks
==> Installing mpileaks
- ==> mpich is already installed in /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/mpich@3.0.4.
- ==> callpath is already installed in /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/callpath@1.0.2-5dce4318.
- ==> adept-utils is already installed in /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/adept-utils@1.0-5adef8da.
+ ==> mpich is already installed in /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/mpich@3.0.4.
+ ==> callpath is already installed in /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/callpath@1.0.2-5dce4318.
+ ==> adept-utils is already installed in /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/adept-utils@1.0-5adef8da.
==> Trying to fetch from https://github.com/hpc/mpileaks/releases/download/v1.0/mpileaks-1.0.tar.gz
######################################################################## 100.0%
- ==> Staging archive: /home/gamblin2/spack/var/spack/stage/mpileaks@1.0%gcc@4.4.7 arch=chaos_5_x86_64_ib-59f6ad23/mpileaks-1.0.tar.gz
- ==> Created stage in /home/gamblin2/spack/var/spack/stage/mpileaks@1.0%gcc@4.4.7 arch=chaos_5_x86_64_ib-59f6ad23.
+ ==> Staging archive: /home/gamblin2/spack/var/spack/stage/mpileaks@1.0%gcc@4.4.7 arch=linux-debian7-x86_64-59f6ad23/mpileaks-1.0.tar.gz
+ ==> Created stage in /home/gamblin2/spack/var/spack/stage/mpileaks@1.0%gcc@4.4.7 arch=linux-debian7-x86_64-59f6ad23.
==> No patches needed for mpileaks.
==> Building mpileaks.
@@ -128,7 +128,7 @@ that the packages is installed:
==> Successfully installed mpileaks.
Fetch: 2.16s. Build: 9.82s. Total: 11.98s.
- [+] /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/mpileaks@1.0-59f6ad23
+ [+] /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/mpileaks@1.0-59f6ad23
The last line, with the ``[+]``, indicates where the package is
installed.
@@ -183,7 +183,7 @@ To uninstall a package and every package that depends on it, you may give the
spack uninstall --dependents mpich
-will display a list of all the packages that depends on `mpich` and, upon confirmation,
+will display a list of all the packages that depend on `mpich` and, upon confirmation,
will uninstall them in the right order.
A line like
@@ -230,7 +230,7 @@ Running ``spack find`` with no arguments lists installed packages:
$ spack find
==> 74 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
ImageMagick@6.8.9-10 libdwarf@20130729 py-dateutil@2.4.0
adept-utils@1.0 libdwarf@20130729 py-ipython@2.3.1
atk@2.14.0 libelf@0.8.12 py-matplotlib@1.4.2
@@ -256,7 +256,7 @@ Running ``spack find`` with no arguments lists installed packages:
lcms@2.6 pixman@0.32.6 xz@5.2.0
libdrm@2.4.33 py-dateutil@2.4.0 zlib@1.2.8
- -- chaos_5_x86_64_ib / gcc@4.9.2 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.9.2 --------------------------------
libelf@0.8.10 mpich@3.0.4
Packages are divided into groups according to their architecture and
@@ -279,7 +279,7 @@ in more detail using ``spack find -d``, and by asking only to show
$ spack find --deps libdwarf
==> 2 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
libdwarf@20130729-d9b90962
^libelf@0.8.12
libdwarf@20130729-b52fac98
@@ -295,7 +295,7 @@ want to know whether two packages' dependencies differ, you can use
$ spack find -l libdwarf
==> 2 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
libdwarf@20130729-d9b90962 libdwarf@20130729-b52fac98
Now the ``libwarf`` installs have hashes after their names. These are
@@ -309,14 +309,14 @@ use ``spack find -p``:
$ spack find -p
==> 74 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
- ImageMagick@6.8.9-10 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/ImageMagick@6.8.9-10-4df950dd
- adept-utils@1.0 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/adept-utils@1.0-5adef8da
- atk@2.14.0 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/atk@2.14.0-3d09ac09
- boost@1.55.0 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/boost@1.55.0
- bzip2@1.0.6 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/bzip2@1.0.6
- cairo@1.14.0 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/cairo@1.14.0-fcc2ab44
- callpath@1.0.2 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/callpath@1.0.2-5dce4318
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
+ ImageMagick@6.8.9-10 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/ImageMagick@6.8.9-10-4df950dd
+ adept-utils@1.0 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/adept-utils@1.0-5adef8da
+ atk@2.14.0 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/atk@2.14.0-3d09ac09
+ boost@1.55.0 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/boost@1.55.0
+ bzip2@1.0.6 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/bzip2@1.0.6
+ cairo@1.14.0 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/cairo@1.14.0-fcc2ab44
+ callpath@1.0.2 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/callpath@1.0.2-5dce4318
...
And, finally, you can restrict your search to a particular package
@@ -325,10 +325,10 @@ by supplying its name:
.. code-block:: sh
$ spack find -p libelf
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
- libelf@0.8.11 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/libelf@0.8.11
- libelf@0.8.12 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/libelf@0.8.12
- libelf@0.8.13 /home/gamblin2/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/libelf@0.8.13
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
+ libelf@0.8.11 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/libelf@0.8.11
+ libelf@0.8.12 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/libelf@0.8.12
+ libelf@0.8.13 /home/gamblin2/spack/opt/linux-debian7-x86_64/gcc@4.4.7/libelf@0.8.13
``spack find`` actually does a lot more than this. You can use
*specs* to query for specific configurations and builds of each
@@ -338,7 +338,7 @@ package. If you want to find only libelf versions greater than version
.. code-block:: sh
$ spack find libelf@0.8.12:
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
libelf@0.8.12 libelf@0.8.13
Finding just the versions of libdwarf built with a particular version
@@ -348,7 +348,7 @@ of libelf would look like this:
$ spack find -l libdwarf ^libelf@0.8.12
==> 1 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
libdwarf@20130729-d9b90962
We can also search for packages that have a certain attribute. For example,
@@ -359,6 +359,7 @@ will find every installed package with a 'debug' compile-time option enabled.
The full spec syntax is discussed in detail in :ref:`sec-specs`.
+.. _compiler-config:
Compiler configuration
-----------------------------------
@@ -445,7 +446,7 @@ If you want to see specifics on a particular compiler, you can run
fc = /usr/local/bin/ifort-15.0.090
This shows which C, C++, and Fortran compilers were detected by Spack.
-Notice also that we didn't have to be too specific about the
+Notice also that we didn\'t have to be too specific about the
version. We just said ``intel@15``, and information about the only
matching Intel compiler was displayed.
@@ -460,19 +461,17 @@ editing your ``~/.spack/compilers.yaml`` file. You can do this by running
Each compiler configuration in the file looks like this::
...
- chaos_5_x86_64_ib:
- ...
- intel@15.0.0:
+ compilers:
+ - compiler:
+ modules = []
+ operating_system: OS
+ paths:
cc: /usr/local/bin/icc-15.0.024-beta
cxx: /usr/local/bin/icpc-15.0.024-beta
f77: /usr/local/bin/ifort-15.0.024-beta
fc: /usr/local/bin/ifort-15.0.024-beta
- ...
-The chaos_5_x86_64_ib string is an architecture string, and multiple
-compilers can be listed underneath an architecture. The architecture
-string may be replaced with the string 'all' to signify compilers that
-work on all architectures.
+ spec: intel@15.0.0:
For compilers, like ``clang``, that do not support Fortran, put
``None`` for ``f77`` and ``fc``::
@@ -488,10 +487,11 @@ list displayed by ``spack compilers``.
You can also add compiler flags to manually configured compilers. The
valid flags are ``cflags``, ``cxxflags``, ``fflags``, ``cppflags``,
-``ldflags``, and ``ldlibs``. For example,::
+``ldflags``, and ``ldlibs``. For example::
...
- chaos_5_x86_64_ib:
+ compilers:
+ - compiler:
...
intel@15.0.0:
cc: /usr/local/bin/icc-15.0.024-beta
@@ -518,10 +518,10 @@ Spack, that descriptor is called a *spec*. Spack uses specs to refer
to a particular build configuration (or configurations) of a package.
Specs are more than a package name and a version; you can use them to
specify the compiler, compiler version, architecture, compile options,
-and dependency options for a build. In this section, we'll go over
+and dependency options for a build. In this section, we\'ll go over
the full syntax of specs.
-Here is an example of a much longer spec than we've seen thus far::
+Here is an example of a much longer spec than we\'ve seen thus far::
mpileaks @1.2:1.4 %gcc@4.7.5 +debug -qt arch=bgq_os ^callpath @1.1 %gcc@4.7.2
@@ -543,10 +543,11 @@ More formally, a spec consists of the following pieces:
* ``+`` or ``-`` or ``~`` Optional variant specifiers (``+debug``,
``-qt``, or ``~qt``) for boolean variants
* ``name=<value>`` Optional variant specifiers that are not restricted to
-boolean variants
+ boolean variants
* ``name=<value>`` Optional compiler flag specifiers. Valid flag names are
-``cflags``, ``cxxflags``, ``fflags``, ``cppflags``, ``ldflags``, and ``ldlibs``.
-* ``arch=<value>`` Optional architecture specifier (``arch=bgq_os``)
+ ``cflags``, ``cxxflags``, ``fflags``, ``cppflags``, ``ldflags``, and ``ldlibs``.
+* ``target=<value> os=<value>`` Optional architecture specifier
+ (``target=haswell os=CNL10``)
* ``^`` Dependency specs (``^callpath@1.1``)
There are two things to notice here. The first is that specs are
@@ -626,7 +627,7 @@ compilers, variants, and architectures just like any other spec.
Specifiers are associated with the nearest package name to their left.
For example, above, ``@1.1`` and ``%gcc@4.7.2`` associates with the
``callpath`` package, while ``@1.2:1.4``, ``%gcc@4.7.5``, ``+debug``,
-``-qt``, and ``arch=bgq_os`` all associate with the ``mpileaks`` package.
+``-qt``, and ``target=haswell os=CNL10`` all associate with the ``mpileaks`` package.
In the diagram above, ``mpileaks`` depends on ``mpich`` with an
unspecified version, but packages can depend on other packages with
@@ -758,14 +759,18 @@ in gnu autotools. If all flags are set, the order is
Architecture specifiers
~~~~~~~~~~~~~~~~~~~~~~~
-.. Note::
+The architecture can be specified by using the reserved
+words ``target`` and/or ``os`` (``target=x86-64 os=debian7``). You can also
+use the triplet form of platform, operating system and processor.
- Architecture specifiers are part of specs but are not yet
- functional. They will be in Spack version 1.0, due in Q3 2015.
+.. code-block:: sh
+
+ spack install libelf arch=cray_xc-CNL10-haswell
-The architecture specifier looks identical to a variant specifier for a
-non-boolean variant. The architecture can be specified only using the
-reserved name ``arch`` (``arch=bgq_os``).
+Users on non-Cray systems won't have to worry about specifying the architecture.
+Spack will autodetect what kind of operating system is on your machine as well
+as the processor. For more information on how the architecture can be
+used on Cray machines, check here :ref:`spack-cray`
.. _sec-virtual-dependencies:
@@ -985,7 +990,7 @@ of installed packages.
$ module avail
- ------- /home/gamblin2/spack/share/spack/modules/chaos_5_x86_64_ib --------
+ ------- /home/gamblin2/spack/share/spack/modules/linux-debian7-x86_64 --------
adept-utils@1.0%gcc@4.4.7-5adef8da libelf@0.8.13%gcc@4.4.7
automaded@1.0%gcc@4.4.7-d9691bb0 libelf@0.8.13%intel@15.0.0
boost@1.55.0%gcc@4.4.7 mpc@1.0.2%gcc@4.4.7-559607f5
@@ -1056,7 +1061,7 @@ Spack. For example, this will add the ``mpich`` package built with
$ spack use mpich %gcc@4.4.7
Prepending: mpich@3.0.4%gcc@4.4.7 (ok)
$ which mpicc
- ~/src/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/mpich@3.0.4/bin/mpicc
+ ~/src/spack/opt/linux-debian7-x86_64/gcc@4.4.7/mpich@3.0.4/bin/mpicc
Or, similarly with modules, you could type:
@@ -1089,8 +1094,8 @@ than one installed package matches it), then Spack will warn you:
$ spack load libelf
==> Error: Multiple matches for spec libelf. Choose one:
- libelf@0.8.13%gcc@4.4.7 arch=chaos_5_x86_64_ib
- libelf@0.8.13%intel@15.0.0 arch=chaos_5_x86_64_ib
+ libelf@0.8.13%gcc@4.4.7 arch=linux-debian7-x86_64
+ libelf@0.8.13%intel@15.0.0 arch=linux-debian7-x86_64
You can either type the ``spack load`` command again with a fully
qualified argument, or you can add just enough extra constraints to
@@ -1131,6 +1136,79 @@ of module files:
"""Set up the compile and runtime environments for a package."""
pass
+
+Recursive Modules
+``````````````````
+
+In some cases, it is desirable to load not just a module, but also all
+the modules it depends on. This is not required for most modules
+because Spack builds binaries with RPATH support. However, not all
+packages use RPATH to find their dependencies: this can be true in
+particular for Python extensions, which are currently *not* built with
+RPATH.
+
+Modules may be loaded recursively with the command:
+
+.. code-block:: sh
+
+ $ module load `spack module tcl --dependencies <spec>...
+
+More than one spec may be placed on the command line here.
+
+Module Commands for Shell Scripts
+``````````````````````````````````
+
+Although Spack is flexbile, the ``module`` command is much faster.
+This could become an issue when emitting a series of ``spack load``
+commands inside a shell script. By adding the ``--shell`` flag,
+``spack module find`` may also be used to generate code that can be
+cut-and-pasted into a shell script. For example:
+
+.. code-block:: sh
+
+ $ spack module find tcl --dependencies --shell py-numpy git
+ # bzip2@1.0.6%gcc@4.9.3=linux-x86_64
+ module load bzip2-1.0.6-gcc-4.9.3-ktnrhkrmbbtlvnagfatrarzjojmkvzsx
+ # ncurses@6.0%gcc@4.9.3=linux-x86_64
+ module load ncurses-6.0-gcc-4.9.3-kaazyneh3bjkfnalunchyqtygoe2mncv
+ # zlib@1.2.8%gcc@4.9.3=linux-x86_64
+ module load zlib-1.2.8-gcc-4.9.3-v3ufwaahjnviyvgjcelo36nywx2ufj7z
+ # sqlite@3.8.5%gcc@4.9.3=linux-x86_64
+ module load sqlite-3.8.5-gcc-4.9.3-a3eediswgd5f3rmto7g3szoew5nhehbr
+ # readline@6.3%gcc@4.9.3=linux-x86_64
+ module load readline-6.3-gcc-4.9.3-se6r3lsycrwxyhreg4lqirp6xixxejh3
+ # python@3.5.1%gcc@4.9.3=linux-x86_64
+ module load python-3.5.1-gcc-4.9.3-5q5rsrtjld4u6jiicuvtnx52m7tfhegi
+ # py-setuptools@20.5%gcc@4.9.3=linux-x86_64
+ module load py-setuptools-20.5-gcc-4.9.3-4qr2suj6p6glepnedmwhl4f62x64wxw2
+ # py-nose@1.3.7%gcc@4.9.3=linux-x86_64
+ module load py-nose-1.3.7-gcc-4.9.3-pwhtjw2dvdvfzjwuuztkzr7b4l6zepli
+ # openblas@0.2.17%gcc@4.9.3+shared=linux-x86_64
+ module load openblas-0.2.17-gcc-4.9.3-pw6rmlom7apfsnjtzfttyayzc7nx5e7y
+ # py-numpy@1.11.0%gcc@4.9.3+blas+lapack=linux-x86_64
+ module load py-numpy-1.11.0-gcc-4.9.3-mulodttw5pcyjufva4htsktwty4qd52r
+ # curl@7.47.1%gcc@4.9.3=linux-x86_64
+ module load curl-7.47.1-gcc-4.9.3-ohz3fwsepm3b462p5lnaquv7op7naqbi
+ # autoconf@2.69%gcc@4.9.3=linux-x86_64
+ module load autoconf-2.69-gcc-4.9.3-bkibjqhgqm5e3o423ogfv2y3o6h2uoq4
+ # cmake@3.5.0%gcc@4.9.3~doc+ncurses+openssl~qt=linux-x86_64
+ module load cmake-3.5.0-gcc-4.9.3-x7xnsklmgwla3ubfgzppamtbqk5rwn7t
+ # expat@2.1.0%gcc@4.9.3=linux-x86_64
+ module load expat-2.1.0-gcc-4.9.3-6pkz2ucnk2e62imwakejjvbv6egncppd
+ # git@2.8.0-rc2%gcc@4.9.3+curl+expat=linux-x86_64
+ module load git-2.8.0-rc2-gcc-4.9.3-3bib4hqtnv5xjjoq5ugt3inblt4xrgkd
+
+The script may be further edited by removing unnecessary modules.
+This script may be directly executed in bash via
+
+.. code-block :: sh
+
+ source <( spack module find tcl --dependencies --shell py-numpy git )
+
+
+Regenerating Module files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
.. code-block:: python
def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
@@ -1395,23 +1473,23 @@ files in the ``cmake`` package while retaining its dependencies.
.. code-block:: sh
-
+
$ spack view -v symlink myview cmake@3.5.2
==> Linking package: "ncurses"
==> Linking package: "zlib"
==> Linking package: "openssl"
==> Linking package: "cmake"
-
+
$ ls myview/
bin doc etc include lib share
$ ls myview/bin/
captoinfo clear cpack ctest infotocap openssl tabs toe tset
ccmake cmake c_rehash infocmp ncurses6-config reset tic tput
-
+
$ spack view -v -d false rm myview cmake@3.5.2
==> Removing package: "cmake"
-
+
$ ls myview/bin/
captoinfo c_rehash infotocap openssl tabs toe tset
clear infocmp ncurses6-config reset tic tput
@@ -1421,7 +1499,7 @@ Limitations of Filesystem Views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section describes some limitations that should be considered in
-using filesystems views.
+using filesystems views.
Filesystem views are merely organizational. The binary executable
programs, shared libraries and other build products found in a view
@@ -1470,7 +1548,7 @@ an *extension*. Suppose you have Python installed like so:
$ spack find python
==> 1 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
python@2.7.8
.. _spack-extensions:
@@ -1483,7 +1561,7 @@ You can find extensions for your Python installation like this:
.. code-block:: sh
$ spack extensions python
- ==> python@2.7.8%gcc@4.4.7 arch=chaos_5_x86_64_ib-703c7a96
+ ==> python@2.7.8%gcc@4.4.7 arch=linux-debian7-x86_64-703c7a96
==> 36 extensions:
geos py-ipython py-pexpect py-pyside py-sip
py-basemap py-libxml2 py-pil py-pytz py-six
@@ -1495,7 +1573,7 @@ You can find extensions for your Python installation like this:
py-h5py py-numpy py-pyqt py-shiboken
==> 12 installed:
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
py-dateutil@2.4.0 py-nose@1.3.4 py-pyside@1.2.2
py-dateutil@2.4.0 py-numpy@1.9.1 py-pytz@2014.10
py-ipython@2.3.1 py-pygments@2.0.1 py-setuptools@11.3.1
@@ -1511,8 +1589,8 @@ prefixes, and you can see this with ``spack find -p``:
$ spack find -p py-numpy
==> 1 installed packages.
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
- py-numpy@1.9.1 /g/g21/gamblin2/src/spack/opt/chaos_5_x86_64_ib/gcc@4.4.7/py-numpy@1.9.1-66733244
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
+ py-numpy@1.9.1 /g/g21/gamblin2/src/spack/opt/linux-debian7-x86_64/gcc@4.4.7/py-numpy@1.9.1-66733244
However, even though this package is installed, you cannot use it
directly when you run ``python``:
@@ -1573,9 +1651,9 @@ installation:
.. code-block:: sh
$ spack activate py-numpy
- ==> Activated extension py-setuptools@11.3.1%gcc@4.4.7 arch=chaos_5_x86_64_ib-3c74eb69 for python@2.7.8%gcc@4.4.7.
- ==> Activated extension py-nose@1.3.4%gcc@4.4.7 arch=chaos_5_x86_64_ib-5f70f816 for python@2.7.8%gcc@4.4.7.
- ==> Activated extension py-numpy@1.9.1%gcc@4.4.7 arch=chaos_5_x86_64_ib-66733244 for python@2.7.8%gcc@4.4.7.
+ ==> Activated extension py-setuptools@11.3.1%gcc@4.4.7 arch=linux-debian7-x86_64-3c74eb69 for python@2.7.8%gcc@4.4.7.
+ ==> Activated extension py-nose@1.3.4%gcc@4.4.7 arch=linux-debian7-x86_64-5f70f816 for python@2.7.8%gcc@4.4.7.
+ ==> Activated extension py-numpy@1.9.1%gcc@4.4.7 arch=linux-debian7-x86_64-66733244 for python@2.7.8%gcc@4.4.7.
Several things have happened here. The user requested that
``py-numpy`` be activated in the ``python`` installation it was built
@@ -1590,7 +1668,7 @@ packages listed as activated:
.. code-block:: sh
$ spack extensions python
- ==> python@2.7.8%gcc@4.4.7 arch=chaos_5_x86_64_ib-703c7a96
+ ==> python@2.7.8%gcc@4.4.7 arch=linux-debian7-x86_64-703c7a96
==> 36 extensions:
geos py-ipython py-pexpect py-pyside py-sip
py-basemap py-libxml2 py-pil py-pytz py-six
@@ -1602,14 +1680,14 @@ packages listed as activated:
py-h5py py-numpy py-pyqt py-shiboken
==> 12 installed:
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
py-dateutil@2.4.0 py-nose@1.3.4 py-pyside@1.2.2
py-dateutil@2.4.0 py-numpy@1.9.1 py-pytz@2014.10
py-ipython@2.3.1 py-pygments@2.0.1 py-setuptools@11.3.1
py-matplotlib@1.4.2 py-pyparsing@2.0.3 py-six@1.9.0
==> 3 currently activated:
- -- chaos_5_x86_64_ib / gcc@4.4.7 --------------------------------
+ -- linux-debian7-x86_64 / gcc@4.4.7 --------------------------------
py-nose@1.3.4 py-numpy@1.9.1 py-setuptools@11.3.1
@@ -1638,7 +1716,7 @@ dependencies, you can use ``spack activate -f``:
.. code-block:: sh
$ spack activate -f py-numpy
- ==> Activated extension py-numpy@1.9.1%gcc@4.4.7 arch=chaos_5_x86_64_ib-66733244 for python@2.7.8%gcc@4.4.7.
+ ==> Activated extension py-numpy@1.9.1%gcc@4.4.7 arch=linux-debian7-x86_64-66733244 for python@2.7.8%gcc@4.4.7.
.. _spack-deactivate:
@@ -1670,7 +1748,7 @@ Spack currently needs to be run from a filesystem that supports
``flock`` locking semantics. Nearly all local filesystems and recent
versions of NFS support this, but parallel filesystems may be mounted
without ``flock`` support enabled. You can determine how your
-filesystems are mounted with ``mount -p``. The output for a Lustre
+ filesystems are mounted with ``mount -p``. The output for a Lustre
filesystem might look like this:
.. code-block:: sh
@@ -1691,7 +1769,7 @@ This issue typically manifests with the error below:
Traceback (most recent call last):
File "./spack", line 176, in <module>
main()
- File "./spack", line 154, in main
+ File "./spack", line 154,' in main
return_val = command(parser, args)
File "./spack/lib/spack/spack/cmd/find.py", line 170, in find
specs = set(spack.installed_db.query(**q_args))
@@ -1709,6 +1787,142 @@ This issue typically manifests with the error below:
A nicer error message is TBD in future versions of Spack.
+
+.. _spack-cray:
+
+Spack on Cray
+-----------------------------
+
+Spack differs slightly when used on a Cray system. The architecture spec
+can differentiate between the front-end and back-end processor and operating system.
+For example, on Edison at NERSC, the back-end target processor
+is \"Ivy Bridge\", so you can specify to use the back-end this way:
+
+.. code-block:: sh
+
+ spack install zlib target=ivybridge
+
+You can also use the operating system to build against the back-end:
+
+.. code-block:: sh
+
+ spack install zlib os=CNL10
+
+Notice that the name includes both the operating system name and the major
+version number concatenated together.
+
+Alternatively, if you want to build something for the front-end,
+you can specify the front-end target processor. The processor for a login node
+on Edison is \"Sandy bridge\" so we specify on the command line like so:
+
+.. code-block:: sh
+
+ spack install zlib target=sandybridge
+
+And the front-end operating system is:
+
+.. code-block:: sh
+
+ spack install zlib os=SuSE11
+
+
+
+Cray compiler detection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Spack can detect compilers using two methods. For the front-end, we treat
+everything the same. The difference lies in back-end compiler detection.
+Back-end compiler detection is made via the Tcl module avail command.
+Once it detects the compiler it writes the appropriate PrgEnv and compiler
+module name to compilers.yaml and sets the paths to each compiler with Cray\'s
+compiler wrapper names (i.e. cc, CC, ftn). During build time, Spack will load
+the correct PrgEnv and compiler module and will call appropriate wrapper.
+
+The compilers.yaml config file will also differ. There is a
+modules section that is filled with the compiler\'s Programming Environment
+and module name. On other systems, this field is empty []::
+
+ ...
+ - compiler:
+ modules:
+ - PrgEnv-intel
+ - intel/15.0.109
+ ...
+
+As mentioned earlier, the compiler paths will look different on a Cray system.
+Since most compilers are invoked using cc, CC and ftn, the paths for each
+compiler are replaced with their respective Cray compiler wrapper names::
+
+ ...
+ paths:
+ cc: cc
+ cxx: CC
+ f77: ftn
+ fc: ftn
+ ...
+
+As opposed to an explicit path to the compiler executable. This allows Spack
+to call the Cray compiler wrappers during build time.
+
+For more on compiler configuration, check out :ref:`compiler-config`.
+
+Setting defaults and using Cray modules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to use default compilers for each PrgEnv and also be able
+to load cray external modules, you will need to set up a packages.yaml.
+
+Here\'s an example of an external configuration for cray modules:
+
+.. code-block:: yaml
+
+ packages:
+ mpi:
+ modules:
+ mpich@7.3.1%gcc@5.2.0 arch=cray_xc-haswell-CNL10: cray-mpich
+ mpich@7.3.1%intel@16.0.0.109 arch=cray_xc-haswell-CNL10: cray-mpich
+
+This tells Spack that for whatever package that depends on mpi, load the
+cray-mpich module into the environment. You can then be able to use whatever
+environment variables, libraries, etc, that are brought into the environment
+via module load.
+
+You can set the default compiler that Spack can use for each compiler type.
+If you want to use the Cray defaults, then set them under *all:* in packages.yaml.
+In the compiler field, set the compiler specs in your order of preference.
+Whenever you build with that compiler type, Spack will concretize to that version.
+
+Here is an example of a full packages.yaml used at NERSC
+
+.. code-block:: yaml
+
+ packages:
+ mpi:
+ modules:
+ mpich@7.3.1%gcc@5.2.0 arch=cray_xc-CNL10-ivybridge: cray-mpich
+ mpich@7.3.1%intel@16.0.0.109 arch=cray_xc-SuSE11-ivybridge: cray-mpich
+ buildable: False
+ netcdf:
+ modules:
+ netcdf@4.3.3.1%gcc@5.2.0 arch=cray_xc-CNL10-ivybridge: cray-netcdf
+ netcdf@4.3.3.1%intel@16.0.0.109 arch=cray_xc-CNL10-ivybridge: cray-netcdf
+ buildable: False
+ hdf5:
+ modules:
+ hdf5@1.8.14%gcc@5.2.0 arch=cray_xc-CNL10-ivybridge: cray-hdf5
+ hdf5@1.8.14%intel@16.0.0.109 arch=cray_xc-CNL10-ivybridge: cray-hdf5
+ buildable: False
+ all:
+ compiler: [gcc@5.2.0, intel@16.0.0.109]
+
+Here we tell spack that whenever we want to build with gcc use version 5.2.0 or
+if we want to build with intel compilers, use version 16.0.0.109. We add a spec
+for each compiler type for each cray modules. This ensures that for each
+compiler on our system we can use that external module.
+
+
+For more on external packages check out the section :ref:`sec-external_packages`.
+
Getting Help
-----------------------
diff --git a/lib/spack/docs/configuration.rst b/lib/spack/docs/configuration.rst
index a6f876b2aa..f2ffa07264 100644
--- a/lib/spack/docs/configuration.rst
+++ b/lib/spack/docs/configuration.rst
@@ -53,6 +53,7 @@ in the first directory it finds to which it has write access. Add
more elements to the list to indicate where your own site's temporary
directory is.
+.. _sec-external_packages:
External Packages
----------------------------
@@ -70,20 +71,20 @@ directory. Here's an example of an external configuration:
packages:
openmpi:
paths:
- openmpi@1.4.3%gcc@4.4.7 arch=chaos_5_x86_64_ib: /opt/openmpi-1.4.3
- openmpi@1.4.3%gcc@4.4.7 arch=chaos_5_x86_64_ib+debug: /opt/openmpi-1.4.3-debug
- openmpi@1.6.5%intel@10.1 arch=chaos_5_x86_64_ib: /opt/openmpi-1.6.5-intel
+ openmpi@1.4.3%gcc@4.4.7 arch=linux-x86_64-debian7: /opt/openmpi-1.4.3
+ openmpi@1.4.3%gcc@4.4.7 arch=linux-x86_64-debian7+debug: /opt/openmpi-1.4.3-debug
+ openmpi@1.6.5%intel@10.1 arch=linux-x86_64-debian7: /opt/openmpi-1.6.5-intel
This example lists three installations of OpenMPI, one built with gcc,
one built with gcc and debug information, and another built with Intel.
If Spack is asked to build a package that uses one of these MPIs as a
dependency, it will use the the pre-installed OpenMPI in
-the given directory.
+the given directory. Packages.yaml can also be used to specify modules
Each ``packages.yaml`` begins with a ``packages:`` token, followed
-by a list of package names. To specify externals, add a ``paths``
+by a list of package names. To specify externals, add a ``paths`` or ``modules``
token under the package name, which lists externals in a
-``spec : /path`` format. Each spec should be as
+``spec: /path`` or ``spec: module-name`` format. Each spec should be as
well-defined as reasonably possible. If a
package lacks a spec component, such as missing a compiler or
package version, then Spack will guess the missing component based
@@ -108,9 +109,9 @@ be:
packages:
openmpi:
paths:
- openmpi@1.4.3%gcc@4.4.7 arch=chaos_5_x86_64_ib: /opt/openmpi-1.4.3
- openmpi@1.4.3%gcc@4.4.7 arch=chaos_5_x86_64_ib+debug: /opt/openmpi-1.4.3-debug
- openmpi@1.6.5%intel@10.1 arch=chaos_5_x86_64_ib: /opt/openmpi-1.6.5-intel
+ openmpi@1.4.3%gcc@4.4.7 arch=linux-x86_64-debian7: /opt/openmpi-1.4.3
+ openmpi@1.4.3%gcc@4.4.7 arch=linux-x86_64-debian7+debug: /opt/openmpi-1.4.3-debug
+ openmpi@1.6.5%intel@10.1 arch=linux-x86_64-debian7: /opt/openmpi-1.6.5-intel
buildable: False
The addition of the ``buildable`` flag tells Spack that it should never build
@@ -118,6 +119,9 @@ its own version of OpenMPI, and it will instead always rely on a pre-built
OpenMPI. Similar to ``paths``, ``buildable`` is specified as a property under
a package name.
+If an external module is specified as not buildable, then Spack will load the
+external module into the build environment which can be used for linking.
+
The ``buildable`` does not need to be paired with external packages.
It could also be used alone to forbid packages that may be
buggy or otherwise undesirable.
@@ -181,7 +185,6 @@ concretization rules. A provider lists a value that packages may
dependency.
-
Profiling
------------------
diff --git a/lib/spack/docs/developer_guide.rst b/lib/spack/docs/developer_guide.rst
index 0b618aa683..d28fe4b2a5 100644
--- a/lib/spack/docs/developer_guide.rst
+++ b/lib/spack/docs/developer_guide.rst
@@ -80,10 +80,11 @@ with a high level view of Spack's directory structure::
var/
spack/ <- build & stage directories
- repos/ <- contains package repositories
- builtin/ <- pkg repository that comes with Spack
- repo.yaml <- descriptor for the builtin repository
- packages/ <- directories under here contain packages
+ repos/ <- contains package repositories
+ builtin/ <- pkg repository that comes with Spack
+ repo.yaml <- descriptor for the builtin repository
+ packages/ <- directories under here contain packages
+ cache/ <- saves resources downloaded during installs
opt/
spack/ <- packages are installed here
diff --git a/lib/spack/docs/mirrors.rst b/lib/spack/docs/mirrors.rst
index dad04d053b..583575a565 100644
--- a/lib/spack/docs/mirrors.rst
+++ b/lib/spack/docs/mirrors.rst
@@ -214,3 +214,21 @@ Adding a mirror really adds a line in ``~/.spack/mirrors.yaml``::
If you want to change the order in which mirrors are searched for
packages, you can edit this file and reorder the sections. Spack will
search the topmost mirror first and the bottom-most mirror last.
+
+.. _caching:
+
+Local Default Cache
+----------------------------
+
+Spack caches resources that are downloaded as part of installs. The cache is
+a valid spack mirror: it uses the same directory structure and naming scheme
+as other Spack mirrors (so it can be copied anywhere and referenced with a URL
+like other mirrors). The mirror is maintained locally (within the Spack
+installation directory) at :file:`var/spack/cache/`. It is always enabled (and
+is always searched first when attempting to retrieve files for an installation)
+but can be cleared with :ref:`purge <spack-purge>`; the cache directory can also
+be deleted manually without issue.
+
+Caching includes retrieved tarball archives and source control repositories, but
+only resources with an associated digest or commit ID (e.g. a revision number
+for SVN) will be cached.
diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst
index 54b886310a..6bafaecc7d 100644
--- a/lib/spack/docs/packaging_guide.rst
+++ b/lib/spack/docs/packaging_guide.rst
@@ -36,10 +36,11 @@ Creating & editing packages
``spack create``
~~~~~~~~~~~~~~~~~~~~~
-The ``spack create`` command generates a boilerplate package template
-from a URL. The URL should point to a tarball or other software
-archive. In most cases, ``spack create`` plus a few modifications is
-all you need to get a package working.
+The ``spack create`` command creates a directory with the package name and
+generates a ``package.py`` file with a boilerplate package template from a URL.
+The URL should point to a tarball or other software archive. In most cases,
+``spack create`` plus a few modifications is all you need to get a package
+working.
Here's an example:
@@ -47,12 +48,16 @@ Here's an example:
$ spack create http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
-Spack examines the tarball URL and tries to figure out the name of the
-package to be created. It also tries to determine what version strings
-look like for this package. Using this information, it will try to
-find *additional* versions by spidering the package's webpage. If it
-finds multiple versions, Spack prompts you to tell it how many
-versions you want to download and checksum:
+Spack examines the tarball URL and tries to figure out the name of the package
+to be created. Once the name is determined a directory in the appropriate
+repository is created with that name. Spack prefers, but does not require, that
+names be lower case so the directory name will be lower case when ``spack
+create`` generates it. In cases where it is desired to have mixed case or upper
+case simply rename the directory. Spack also tries to determine what version
+strings look like for this package. Using this information, it will try to find
+*additional* versions by spidering the package's webpage. If it finds multiple
+versions, Spack prompts you to tell it how many versions you want to download
+and checksum:
.. code-block:: sh
@@ -297,9 +302,10 @@ directories or files (like patches) that it needs to build.
Package Names
~~~~~~~~~~~~~~~~~~
-Packages are named after the directory containing ``package.py``. So,
-``libelf``'s ``package.py`` lives in a directory called ``libelf``.
-The ``package.py`` file defines a class called ``Libelf``, which
+Packages are named after the directory containing ``package.py``. It is
+preferred, but not required, that the directory, and thus the package name, are
+lower case. So, ``libelf``'s ``package.py`` lives in a directory called
+``libelf``. The ``package.py`` file defines a class called ``Libelf``, which
extends Spack's ``Package`` class. for example, here is
``$SPACK_ROOT/var/spack/repos/builtin/packages/libelf/package.py``:
@@ -377,6 +383,8 @@ add a line like this in the package class:
version('8.2.1', '4136d7b4c04df68b686570afa26988ac')
...
+Versions should be listed with the newest version first.
+
Version URLs
~~~~~~~~~~~~~~~~~
@@ -385,8 +393,21 @@ in the package. For example, Spack is smart enough to download
version ``8.2.1.`` of the ``Foo`` package above from
``http://example.com/foo-8.2.1.tar.gz``.
-If spack *cannot* extrapolate the URL from the ``url`` field, or if
-the package doesn't have a ``url`` field, you can add a URL explicitly
+If spack *cannot* extrapolate the URL from the ``url`` field by
+default, you can write your own URL generation algorithm in place of
+the ``url`` declaration. For example:
+
+.. code-block:: python
+ :linenos:
+
+ class Foo(Package):
+ def url_for_version(self, version):
+ return 'http://example.com/version_%s/foo-%s.tar.gz' \
+ % (version, version)
+ version('8.2.1', '4136d7b4c04df68b686570afa26988ac')
+ ...
+
+If a URL cannot be derived systematically, you can add an explicit URL
for a particular version:
.. code-block:: python
@@ -547,7 +568,7 @@ The package author is responsible for coming up with a sensible name
for each version to be fetched from a repository. For example, if
you're fetching from a tag like ``v1.0``, you might call that ``1.0``.
If you're fetching a nameless git commit or an older subversion
-revision, you might give the commit an intuitive name, like ``dev``
+revision, you might give the commit an intuitive name, like ``develop``
for a development version, or ``some-fancy-new-feature`` if you want
to be more specific.
@@ -557,6 +578,17 @@ branches move forward over time and you aren't guaranteed to get the
same thing every time you fetch a particular version. Life isn't
always simple, though, so this is not strictly enforced.
+When fetching from from the branch corresponding to the development version
+(often called ``master``,``trunk`` or ``dev``), it is recommended to
+call this version ``develop``. Spack has special treatment for this version so
+ that ``@develop`` will satisfy dependencies like
+``depends_on(abc, when="@x.y.z:")``. In other words, ``@develop`` is
+greater than any other version. The rationale is that certain features or
+options first appear in the development branch. Therefore if a package author
+wants to keep the package on the bleeding edge and provide support for new
+features, it is advised to use ``develop`` for such a version which will
+greatly simplify writing dependencies and version-related conditionals.
+
In some future release, Spack may support extrapolating repository
versions as it does for tarball URLs, but currently this is not
supported.
@@ -572,6 +604,7 @@ Git fetching is enabled with the following parameters to ``version``:
* ``tag``: name of a tag to fetch.
* ``branch``: name of a branch to fetch.
* ``commit``: SHA hash (or prefix) of a commit to fetch.
+ * ``submodules``: Also fetch submodules when checking out this repository.
Only one of ``tag``, ``branch``, or ``commit`` can be used at a time.
@@ -582,7 +615,7 @@ Default branch
class Example(Package):
...
- version('dev', git='https://github.com/example-project/example.git')
+ version('develop', git='https://github.com/example-project/example.git')
This is not recommended, as the contents of the default branch
change over time.
@@ -628,6 +661,17 @@ Commits
could just use the abbreviated commit hash. It's up to the package
author to decide what makes the most sense.
+Submodules
+
+ You can supply ``submodules=True`` to cause Spack to fetch submodules
+ along with the repository at fetch time.
+
+ .. code-block:: python
+
+ version('1.0.1', git='https://github.com/example-project/example.git',
+ tag='v1.0.1', submdoules=True)
+
+
Installing
^^^^^^^^^^^^^^
@@ -655,7 +699,7 @@ Default
.. code-block:: python
- version('hg-head', hg='https://jay.grs.rwth-aachen.de/hg/example')
+ version('develop', hg='https://jay.grs.rwth-aachen.de/hg/example')
Note that this is not recommended; try to fetch a particular
revision instead.
@@ -687,7 +731,7 @@ Fetching the head
.. code-block:: python
- version('svn-head', svn='https://outreach.scidac.gov/svn/libmonitor/trunk')
+ version('develop', svn='https://outreach.scidac.gov/svn/libmonitor/trunk')
This is not recommended, as the head will move forward over time.
@@ -697,12 +741,19 @@ Fetching a revision
.. code-block:: python
- version('svn-head', svn='https://outreach.scidac.gov/svn/libmonitor/trunk',
+ version('develop', svn='https://outreach.scidac.gov/svn/libmonitor/trunk',
revision=128)
Subversion branches are handled as part of the directory structure, so
you can check out a branch or tag by changing the ``url``.
+Automatic caching of files fetched during installation
+------------------------------------------------------
+
+Spack maintains a cache (described :ref:`here <caching>`) which saves files
+retrieved during package installations to avoid re-downloading in the case that
+a package is installed with a different specification (but the same version) or
+reinstalled on account of a change in the hashing scheme.
.. _license:
@@ -776,7 +827,7 @@ Spack will create a global license file located at
file using the editor set in ``$EDITOR``, or vi if unset. It will look like
this:
-.. code-block::
+.. code-block:: sh
# A license is required to use pgi.
#
@@ -807,7 +858,7 @@ You can add your license directly to this file, or tell FlexNet to use a
license stored on a separate license server. Here is an example that
points to a license server called licman1:
-.. code-block::
+.. code-block:: sh
SERVER licman1.mcs.anl.gov 00163eb7fba5 27200
USE_SERVER
@@ -1235,6 +1286,31 @@ command line to find installed packages or to install packages with
particular constraints, and package authors can use specs to describe
relationships between packages.
+Additionally, dependencies may be specified for specific use cases:
+
+.. code-block:: python
+
+ depends_on("cmake", type="build")
+ depends_on("libelf", type=("build", "link"))
+ depends_on("python", type="run")
+
+The dependency types are:
+
+ * **"build"**: made available during the project's build. The package will
+ be added to ``PATH``, the compiler include paths, and ``PYTHONPATH``.
+ Other projects which depend on this one will not have these modified
+ (building project X doesn't need project Y's build dependencies).
+ * **"link"**: the project is linked to by the project. The package will be
+ added to the current package's ``rpath``.
+ * **"run"**: the project is used by the project at runtime. The package will
+ be added to ``PATH`` and ``PYTHONPATH``.
+
+If not specified, ``type`` is assumed to be ``("build", "link")``. This is the
+common case for compiled language usage. Also available are the aliases
+``alldeps`` for all dependency types and ``nolink`` (``("build", "run")``) for
+use by dependencies which are not expressed via a linker (e.g., Python or Lua
+module loading).
+
.. _setup-dependent-environment:
``setup_dependent_environment()``
@@ -1339,6 +1415,19 @@ Now, the ``py-numpy`` package can be used as an argument to ``spack
activate``. When it is activated, all the files in its prefix will be
symbolically linked into the prefix of the python package.
+Many packages produce Python extensions for *some* variants, but not
+others: they should extend ``python`` only if the apropriate
+variant(s) are selected. This may be accomplished with conditional
+``extends()`` declarations:
+
+.. code-block:: python
+
+ class FooLib(Package):
+ variant('python', default=True, description= \
+ 'Build the Python extension Module')
+ extends('python', when='+python')
+ ...
+
Sometimes, certain files in one package will conflict with those in
another, which means they cannot both be activated (symlinked) at the
same time. In this case, you can tell Spack to ignore those files
@@ -1625,21 +1714,21 @@ the user runs ``spack install`` and the time the ``install()`` method
is called. The concretized version of the spec above might look like
this::
- mpileaks@2.3%gcc@4.7.3 arch=linux-ppc64
- ^callpath@1.0%gcc@4.7.3+debug arch=linux-ppc64
- ^dyninst@8.1.2%gcc@4.7.3 arch=linux-ppc64
- ^libdwarf@20130729%gcc@4.7.3 arch=linux-ppc64
- ^libelf@0.8.11%gcc@4.7.3 arch=linux-ppc64
- ^mpich@3.0.4%gcc@4.7.3 arch=linux-ppc64
+ mpileaks@2.3%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^callpath@1.0%gcc@4.7.3+debug arch=linux-debian7-x86_64
+ ^dyninst@8.1.2%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^libdwarf@20130729%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^libelf@0.8.11%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^mpich@3.0.4%gcc@4.7.3 arch=linux-debian7-x86_64
.. graphviz::
digraph {
- "mpileaks@2.3\n%gcc@4.7.3\n arch=linux-ppc64" -> "mpich@3.0.4\n%gcc@4.7.3\n arch=linux-ppc64"
- "mpileaks@2.3\n%gcc@4.7.3\n arch=linux-ppc64" -> "callpath@1.0\n%gcc@4.7.3+debug\n arch=linux-ppc64" -> "mpich@3.0.4\n%gcc@4.7.3\n arch=linux-ppc64"
- "callpath@1.0\n%gcc@4.7.3+debug\n arch=linux-ppc64" -> "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-ppc64"
- "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-ppc64" -> "libdwarf@20130729\n%gcc@4.7.3\n arch=linux-ppc64" -> "libelf@0.8.11\n%gcc@4.7.3\n arch=linux-ppc64"
- "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-ppc64" -> "libelf@0.8.11\n%gcc@4.7.3\n arch=linux-ppc64"
+ "mpileaks@2.3\n%gcc@4.7.3\n arch=linux-debian7-x86_64" -> "mpich@3.0.4\n%gcc@4.7.3\n arch=linux-debian7-x86_64"
+ "mpileaks@2.3\n%gcc@4.7.3\n arch=linux-debian7-x86_64" -> "callpath@1.0\n%gcc@4.7.3+debug\n arch=linux-debian7-x86_64" -> "mpich@3.0.4\n%gcc@4.7.3\n arch=linux-debian7-x86_64"
+ "callpath@1.0\n%gcc@4.7.3+debug\n arch=linux-debian7-x86_64" -> "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-debian7-x86_64"
+ "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-debian7-x86_64" -> "libdwarf@20130729\n%gcc@4.7.3\n arch=linux-debian7-x86_64" -> "libelf@0.8.11\n%gcc@4.7.3\n arch=linux-debian7-x86_64"
+ "dyninst@8.1.2\n%gcc@4.7.3\n arch=linux-debian7-x86_64" -> "libelf@0.8.11\n%gcc@4.7.3\n arch=linux-debian7-x86_64"
}
Here, all versions, compilers, and platforms are filled in, and there
@@ -1668,9 +1757,9 @@ running ``spack spec``. For example:
^libdwarf
^libelf
- dyninst@8.0.1%gcc@4.7.3 arch=linux-ppc64
- ^libdwarf@20130729%gcc@4.7.3 arch=linux-ppc64
- ^libelf@0.8.13%gcc@4.7.3 arch=linux-ppc64
+ dyninst@8.0.1%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^libdwarf@20130729%gcc@4.7.3 arch=linux-debian7-x86_64
+ ^libelf@0.8.13%gcc@4.7.3 arch=linux-debian7-x86_64
This is useful when you want to know exactly what Spack will do when
you ask for a particular spec.
@@ -1844,7 +1933,7 @@ discover its dependencies.
If you want to see the environment that a package will build with, or
if you want to run commands in that environment to test them out, you
-can use the :ref:```spack env`` <spack-env>` command, documented
+can use the :ref:`spack env <spack-env>` command, documented
below.
.. _compiler-wrappers:
@@ -2175,12 +2264,12 @@ example:
def install(self, prefix):
# Do default install
- @when('arch=chaos_5_x86_64_ib')
+ @when('arch=linux-debian7-x86_64')
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('arch=bgqos_0")
+ @when('arch=linux-debian7-x86_64")
def install(self, prefix):
# This will be executed if the package's sys_type is bgqos_0
@@ -2308,7 +2397,7 @@ build system.
.. _sanity-checks:
-Sanity checking an intallation
+Sanity checking an installation
--------------------------------
By default, Spack assumes that a build has failed if nothing is
@@ -2524,6 +2613,59 @@ File functions
.. _package-lifecycle:
+Coding Style Guidelines
+---------------------------
+
+The following guidelines are provided, in the interests of making
+Spack packages work in a consistent manner:
+
+
+Variant Names
+~~~~~~~~~~~~~~
+
+Spack packages with variants similar to already-existing Spack
+packages should use the same name for their variants. Standard
+variant names are:
+
+ ======= ======== ========================
+ Name Default Description
+ ======= ======== ========================
+ shared True Build shared libraries
+ static Build static libraries
+ mpi Use MPI
+ python Build Python extension
+ ======= ======== ========================
+
+If specified in this table, the corresponding default should be used
+when declaring a variant.
+
+
+Version Lists
+~~~~~~~~~~~~~~
+
+Spack packges should list supported versions with the newest first.
+
+Special Versions
+~~~~~~~~~~~~~~~~~
+
+The following *special* version names may be used when building a package:
+
+* *@system*: Indicates a hook to the OS-installed version of the
+ package. This is useful, for example, to tell Spack to use the
+ OS-installed version in ``packages.yaml``::
+
+ openssl:
+ paths:
+ openssl@system: /usr
+ buildable: False
+
+ Certain Spack internals look for the *@system* version and do
+ appropriate things in that case.
+
+* *@local*: Indicates the version was built manually from some source
+ tree of unknown provenance (see ``spack setup``).
+
+
Packaging workflow commands
---------------------------------
@@ -2618,11 +2760,16 @@ build process will start from scratch.
``spack purge``
~~~~~~~~~~~~~~~~~
-Cleans up all of Spack's temporary files. Use this to recover disk
-space if temporary files from interrupted or failed installs
-accumulate in the staging area. This is equivalent to running ``spack
-clean`` for every package you have fetched or staged.
+Cleans up all of Spack's temporary and cached files. This can be used to
+recover disk space if temporary files from interrupted or failed installs
+accumulate in the staging area.
+When called with ``--stage`` or ``--all`` (or without arguments, in which case
+the default is ``--all``) this removes all staged files; this is equivalent to
+running ``spack clean`` for every package you have fetched or staged.
+
+When called with ``--cache`` or ``--all`` this will clear all resources
+:ref:`cached <caching>` during installs.
Keeping the stage directory on success
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2770,11 +2917,11 @@ build it:
$ spack stage libelf
==> Trying to fetch from http://www.mr511.de/software/libelf-0.8.13.tar.gz
######################################################################## 100.0%
- ==> Staging archive: /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-ppc64/libelf-0.8.13.tar.gz
- ==> Created stage in /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-ppc64.
+ ==> Staging archive: /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-debian7-x86_64/libelf-0.8.13.tar.gz
+ ==> Created stage in /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-debian7-x86_64.
$ spack cd libelf
$ pwd
- /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-ppc64/libelf-0.8.13
+ /Users/gamblin2/src/spack/var/spack/stage/libelf@0.8.13%gcc@4.8.3 arch=linux-debian7-x86_64/libelf-0.8.13
``spack cd`` here changed he current working directory to the
directory containing the expanded ``libelf`` source code. There are a
@@ -2839,3 +2986,109 @@ might write:
DWARF_PREFIX = $(spack location -i libdwarf)
CXXFLAGS += -I$DWARF_PREFIX/include
CXXFLAGS += -L$DWARF_PREFIX/lib
+
+Build System Configuration Support
+----------------------------------
+
+Imagine a developer creating a CMake-based (or Autotools) project in a local
+directory, which depends on libraries A-Z. Once Spack has installed
+those dependencies, one would like to run ``cmake`` with appropriate
+command line and environment so CMake can find them. The ``spack
+setup`` command does this conveniently, producing a CMake
+configuration that is essentially the same as how Spack *would have*
+configured the project. This can be demonstrated with a usage
+example:
+
+.. code-block:: bash
+
+ cd myproject
+ spack setup myproject@local
+ mkdir build; cd build
+ ../spconfig.py ..
+ make
+ make install
+
+Notes:
+ * Spack must have ``myproject/package.py`` in its repository for
+ this to work.
+ * ``spack setup`` produces the executable script ``spconfig.py`` in
+ the local directory, and also creates the module file for the
+ package. ``spconfig.py`` is normally run from the user's
+ out-of-source build directory.
+ * The version number given to ``spack setup`` is arbitrary, just
+ like ``spack diy``. ``myproject/package.py`` does not need to
+ have any valid downloadable versions listed (typical when a
+ project is new).
+ * spconfig.py produces a CMake configuration that *does not* use the
+ Spack wrappers. Any resulting binaries *will not* use RPATH,
+ unless the user has enabled it. This is recommended for
+ development purposes, not production.
+ * ``spconfig.py`` is human readable, and can serve as a developer
+ reference of what dependencies are being used.
+ * ``make install`` installs the package into the Spack repository,
+ where it may be used by other Spack packages.
+ * CMake-generated makefiles re-run CMake in some circumstances. Use
+ of ``spconfig.py`` breaks this behavior, requiring the developer
+ to manually re-run ``spconfig.py`` when a ``CMakeLists.txt`` file
+ has changed.
+
+CMakePackage
+~~~~~~~~~~~~
+
+In order ot enable ``spack setup`` functionality, the author of
+``myproject/package.py`` must subclass from ``CMakePackage`` instead
+of the standard ``Package`` superclass. Because CMake is
+standardized, the packager does not need to tell Spack how to run
+``cmake; make; make install``. Instead the packager only needs to
+create (optional) methods ``configure_args()`` and ``configure_env()``, which
+provide the arguments (as a list) and extra environment variables (as
+a dict) to provide to the ``cmake`` command. Usually, these will
+translate variant flags into CMake definitions. For example:
+
+.. code-block:: python
+
+ def configure_args(self):
+ spec = self.spec
+ return [
+ '-DUSE_EVERYTRACE=%s' % ('YES' if '+everytrace' in spec else 'NO'),
+ '-DBUILD_PYTHON=%s' % ('YES' if '+python' in spec else 'NO'),
+ '-DBUILD_GRIDGEN=%s' % ('YES' if '+gridgen' in spec else 'NO'),
+ '-DBUILD_COUPLER=%s' % ('YES' if '+coupler' in spec else 'NO'),
+ '-DUSE_PISM=%s' % ('YES' if '+pism' in spec else 'NO')]
+
+If needed, a packager may also override methods defined in
+``StagedPackage`` (see below).
+
+
+StagedPackage
+~~~~~~~~~~~~~
+
+``CMakePackage`` is implemented by subclassing the ``StagedPackage``
+superclass, which breaks down the standard ``Package.install()``
+method into several sub-stages: ``setup``, ``configure``, ``build``
+and ``install``. Details:
+
+* Instead of implementing the standard ``install()`` method, package
+ authors implement the methods for the sub-stages
+ ``install_setup()``, ``install_configure()``,
+ ``install_build()``, and ``install_install()``.
+
+* The ``spack install`` command runs the sub-stages ``configure``,
+ ``build`` and ``install`` in order. (The ``setup`` stage is
+ not run by default; see below).
+* The ``spack setup`` command runs the sub-stages ``setup``
+ and a dummy install (to create the module file).
+* The sub-stage install methods take no arguments (other than
+ ``self``). The arguments ``spec`` and ``prefix`` to the standard
+ ``install()`` method may be accessed via ``self.spec`` and
+ ``self.prefix``.
+
+GNU Autotools
+~~~~~~~~~~~~~
+
+The ``setup`` functionality is currently only available for
+CMake-based packages. Extending this functionality to GNU
+Autotools-based packages would be easy (and should be done by a
+developer who actively uses Autotools). Packages that use
+non-standard build systems can gain ``setup`` functionality by
+subclassing ``StagedPackage`` directly.
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index e800c6717a..6e4cd338fe 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -42,7 +42,7 @@ __all__ = ['set_install_permissions', 'install', 'install_tree',
'FileFilter', 'change_sed_delimiter', 'is_exe', 'force_symlink',
'set_executable', 'copy_mode', 'unset_executable_mode',
'remove_dead_links', 'remove_linked_tree', 'find_library_path',
- 'fix_darwin_install_name', 'to_link_flags']
+ 'fix_darwin_install_name', 'to_link_flags', 'to_lib_name']
def filter_file(regex, repl, *filenames, **kwargs):
@@ -431,6 +431,13 @@ def fix_darwin_install_name(path):
break
+def to_lib_name(library):
+ """Transforms a path to the library /path/to/lib<name>.xyz into <name>
+ """
+ # Assume libXYZ.suffix
+ return os.path.basename(library)[3:].split(".")[0]
+
+
def to_link_flags(library):
"""Transforms a path to a <library> into linking flags -L<dir> -l<name>.
@@ -438,8 +445,7 @@ def to_link_flags(library):
A string of linking flags.
"""
dir = os.path.dirname(library)
- # Assume libXYZ.suffix
- name = os.path.basename(library)[3:].split(".")[0]
+ name = to_lib_name(library)
res = '-L%s -l%s' % (dir, name)
return res
diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py
index 965e3a7f78..d67585aac4 100644
--- a/lib/spack/spack/__init__.py
+++ b/lib/spack/spack/__init__.py
@@ -176,9 +176,12 @@ sys_type = None
# TODO: it's not clear where all the stuff that needs to be included in packages
# should live. This file is overloaded for spack core vs. for packages.
#
-__all__ = ['Package', 'Version', 'when', 'ver']
+__all__ = ['Package', 'StagedPackage', 'CMakePackage', \
+ 'Version', 'when', 'ver', 'alldeps', 'nolink']
from spack.package import Package, ExtensionConflictError
+from spack.package import StagedPackage, CMakePackage
from spack.version import Version, ver
+from spack.spec import DependencySpec, alldeps, nolink
from spack.multimethod import when
import llnl.util.filesystem
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 99c4cae020..93fb0690f7 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -224,9 +224,12 @@ def set_compiler_environment_variables(pkg, env):
return env
-def set_build_environment_variables(pkg, env):
+def set_build_environment_variables(pkg, env, dirty=False):
"""
- This ensures a clean install environment when we build packages
+ This ensures a clean install environment when we build packages.
+
+ Arguments:
+ dirty -- skip unsetting the user's environment settings.
"""
# Add spack build environment path with compiler wrappers first in
# the path. We add both spack.env_path, which includes default
@@ -251,7 +254,8 @@ def set_build_environment_variables(pkg, env):
env.set_path(SPACK_ENV_PATH, env_paths)
# Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES
- dep_prefixes = [d.prefix for d in pkg.spec.traverse(root=False)]
+ dep_prefixes = [d.prefix
+ for d in pkg.spec.traverse(root=False, deptype='build')]
env.set_path(SPACK_DEPENDENCIES, dep_prefixes)
# Add dependencies to CMAKE_PREFIX_PATH
env.set_path('CMAKE_PREFIX_PATH', dep_prefixes)
@@ -262,12 +266,26 @@ def set_build_environment_variables(pkg, env):
# Install root prefix
env.set(SPACK_INSTALL, spack.install_path)
- # Remove these vars from the environment during build because they
- # can affect how some packages find libraries. We want to make
- # sure that builds never pull in unintended external dependencies.
- env.unset('LD_LIBRARY_PATH')
- env.unset('LD_RUN_PATH')
- env.unset('DYLD_LIBRARY_PATH')
+ # Stuff in here sanitizes the build environemnt to eliminate
+ # anything the user has set that may interfere.
+ if not dirty:
+ # Remove these vars from the environment during build because they
+ # can affect how some packages find libraries. We want to make
+ # sure that builds never pull in unintended external dependencies.
+ env.unset('LD_LIBRARY_PATH')
+ env.unset('LIBRARY_PATH')
+ env.unset('CPATH')
+ env.unset('LD_RUN_PATH')
+ env.unset('DYLD_LIBRARY_PATH')
+
+ # Remove any macports installs from the PATH. The macports ld can
+ # cause conflicts with the built-in linker on el capitan. Solves
+ # assembler issues, e.g.:
+ # suffix or operands invalid for `movq'"
+ path = get_path('PATH')
+ for p in path:
+ if '/macports/' in p:
+ env.remove_path('PATH', p)
# Add bin directories from dependencies to the PATH for the build.
bin_dirs = reversed(
@@ -320,10 +338,6 @@ def set_module_variables_for_package(pkg, module):
# Don't use which for this; we want to find it in the current dir.
m.configure = Executable('./configure')
- # TODO: shouldn't really use "which" here. Consider adding notion
- # TODO: of build dependencies, as opposed to link dependencies.
- # TODO: Currently, everything is a link dependency, but tools like
- # TODO: this shouldn't be.
m.cmake = Executable('cmake')
m.ctest = Executable('ctest')
@@ -371,9 +385,10 @@ def set_module_variables_for_package(pkg, module):
def get_rpaths(pkg):
"""Get a list of all the rpaths for a package."""
rpaths = [pkg.prefix.lib, pkg.prefix.lib64]
- rpaths.extend(d.prefix.lib for d in pkg.spec.dependencies.values()
+ deps = pkg.spec.dependencies(deptype='link')
+ rpaths.extend(d.prefix.lib for d in deps
if os.path.isdir(d.prefix.lib))
- rpaths.extend(d.prefix.lib64 for d in pkg.spec.dependencies.values()
+ rpaths.extend(d.prefix.lib64 for d in deps
if os.path.isdir(d.prefix.lib64))
# Second module is our compiler mod name. We use that to get rpaths from
# module show output.
@@ -405,7 +420,7 @@ def load_external_modules(pkg):
load_module(dep.external_module)
-def setup_package(pkg):
+def setup_package(pkg, dirty=False):
"""Execute all environment setup routines."""
spack_env = EnvironmentModifications()
run_env = EnvironmentModifications()
@@ -428,11 +443,11 @@ def setup_package(pkg):
s.package.spec = s
set_compiler_environment_variables(pkg, spack_env)
- set_build_environment_variables(pkg, spack_env)
+ set_build_environment_variables(pkg, spack_env, dirty)
load_external_modules(pkg)
# traverse in postorder so package can use vars from its dependencies
spec = pkg.spec
- for dspec in pkg.spec.traverse(order='post', root=False):
+ for dspec in pkg.spec.traverse(order='post', root=False, deptype='build'):
# If a user makes their own package repo, e.g.
# spack.repos.mystuff.libelf.Libelf, and they inherit from
# an existing class like spack.repos.original.libelf.Libelf,
@@ -457,7 +472,7 @@ def setup_package(pkg):
spack_env.apply_modifications()
-def fork(pkg, function):
+def fork(pkg, function, dirty=False):
"""Fork a child process to do part of a spack build.
Arguments:
@@ -465,6 +480,7 @@ def fork(pkg, function):
pkg -- pkg whose environemnt we should set up the
forked process for.
function -- arg-less function to run in the child process.
+ dirty -- If True, do NOT clean the environment before building.
Usage:
def child_fun():
@@ -488,7 +504,7 @@ def fork(pkg, function):
if pid == 0:
# Give the child process the package's build environment.
- setup_package(pkg)
+ setup_package(pkg, dirty=dirty)
try:
# call the forked function.
diff --git a/lib/spack/spack/cmd/checksum.py b/lib/spack/spack/cmd/checksum.py
index 95bd4771ed..aedb0fd99c 100644
--- a/lib/spack/spack/cmd/checksum.py
+++ b/lib/spack/spack/cmd/checksum.py
@@ -42,7 +42,8 @@ def setup_parser(subparser):
'--keep-stage', action='store_true', dest='keep_stage',
help="Don't clean up staging area when command completes.")
subparser.add_argument(
- 'versions', nargs=argparse.REMAINDER, help='Versions to generate checksums for')
+ 'versions', nargs=argparse.REMAINDER,
+ help='Versions to generate checksums for')
def get_checksums(versions, urls, **kwargs):
@@ -59,10 +60,10 @@ def get_checksums(versions, urls, **kwargs):
with Stage(url, keep=keep_stage) as stage:
stage.fetch()
if i == 0 and first_stage_function:
- first_stage_function(stage)
+ first_stage_function(stage, url)
- hashes.append((version,
- spack.util.crypto.checksum(hashlib.md5, stage.archive_file)))
+ hashes.append((version, spack.util.crypto.checksum(
+ hashlib.md5, stage.archive_file)))
i += 1
except FailedDownloadError as e:
tty.msg("Failed to fetch %s" % url)
@@ -79,12 +80,12 @@ def checksum(parser, args):
# If the user asked for specific versions, use those.
if args.versions:
versions = {}
- for v in args.versions:
- v = ver(v)
- if not isinstance(v, Version):
+ for version in args.versions:
+ version = ver(version)
+ if not isinstance(version, Version):
tty.die("Cannot generate checksums for version lists or " +
"version ranges. Use unambiguous versions.")
- versions[v] = pkg.url_for_version(v)
+ versions[version] = pkg.url_for_version(version)
else:
versions = pkg.fetch_remote_versions()
if not versions:
@@ -111,5 +112,7 @@ def checksum(parser, args):
if not version_hashes:
tty.die("Could not fetch any versions for %s" % pkg.name)
- version_lines = [" version('%s', '%s')" % (v, h) for v, h in version_hashes]
+ version_lines = [
+ " version('%s', '%s')" % (v, h) for v, h in version_hashes
+ ]
tty.msg("Checksummed new versions of %s:" % pkg.name, *version_lines)
diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py
index f5f234d7a7..2c440096d1 100644
--- a/lib/spack/spack/cmd/create.py
+++ b/lib/spack/spack/cmd/create.py
@@ -103,6 +103,64 @@ ${versions}
${install}
""")
+# Build dependencies and extensions
+dependencies_dict = {
+ 'autotools': "# depends_on('foo')",
+ 'cmake': "depends_on('cmake')",
+ 'scons': "depends_on('scons')",
+ 'python': "extends('python')",
+ 'R': "extends('R')",
+ 'octave': "extends('octave')",
+ 'unknown': "# depends_on('foo')"
+}
+
+# Default installation instructions
+install_dict = {
+ 'autotools': """\
+ # FIXME: Modify the configure line to suit your build system here.
+ configure('--prefix={0}'.format(prefix))
+
+ # FIXME: Add logic to build and install here.
+ make()
+ make('install')""",
+
+ 'cmake': """\
+ with working_dir('spack-build', create=True):
+ # FIXME: Modify the cmake line to suit your build system here.
+ cmake('..', *std_cmake_args)
+
+ # FIXME: Add logic to build and install here.
+ make()
+ make('install')""",
+
+ 'scons': """\
+ # FIXME: Add logic to build and install here.
+ scons('prefix={0}'.format(prefix))
+ scons('install')""",
+
+ 'python': """\
+ # FIXME: Add logic to build and install here.
+ python('setup.py', 'install', '--prefix={0}'.format(prefix))""",
+
+ 'R': """\
+ # FIXME: Add logic to build and install here.
+ R('CMD', 'INSTALL', '--library={0}'.format(self.module.r_lib_dir),
+ self.stage.source_path)""",
+
+ 'octave': """\
+ # FIXME: Add logic to build and install here.
+ octave('--quiet', '--norc',
+ '--built-in-docstrings-file=/dev/null',
+ '--texi-macros-file=/dev/null',
+ '--eval', 'pkg prefix {0}; pkg install {1}'.format(
+ prefix, self.stage.archive_file))""",
+
+ 'unknown': """\
+ # FIXME: Unknown build system
+ make()
+ make('install')"""
+}
+
def make_version_calls(ver_hash_tuples):
"""Adds a version() call to the package for each version found."""
@@ -133,60 +191,17 @@ def setup_parser(subparser):
setup_parser.subparser = subparser
-class ConfigureGuesser(object):
- def __call__(self, stage):
- """Try to guess the type of build system used by the project.
- Set any necessary build dependencies or extensions.
- Set the appropriate default installation instructions."""
-
- # Build dependencies and extensions
- dependenciesDict = {
- 'autotools': "# depends_on('foo')",
- 'cmake': "depends_on('cmake')",
- 'scons': "depends_on('scons')",
- 'python': "extends('python')",
- 'R': "extends('R')",
- 'unknown': "# depends_on('foo')"
- }
-
- # Default installation instructions
- installDict = {
- 'autotools': """\
- # FIXME: Modify the configure line to suit your build system here.
- configure('--prefix={0}'.format(prefix))
-
- # FIXME: Add logic to build and install here.
- make()
- make('install')""",
-
- 'cmake': """\
- with working_dir('spack-build', create=True):
- # FIXME: Modify the cmake line to suit your build system here.
- cmake('..', *std_cmake_args)
+class BuildSystemGuesser(object):
+ def __call__(self, stage, url):
+ """Try to guess the type of build system used by a project based on
+ the contents of its archive or the URL it was downloaded from."""
- # FIXME: Add logic to build and install here.
- make()
- make('install')""",
-
- 'scons': """\
- # FIXME: Add logic to build and install here.
- scons('prefix={0}'.format(prefix))
- scons('install')""",
-
- 'python': """\
- # FIXME: Add logic to build and install here.
- python('setup.py', 'install', '--prefix={0}'.format(prefix))""",
-
- 'R': """\
- # FIXME: Add logic to build and install here.
- R('CMD', 'INSTALL', '--library={0}'.format(self.module.r_lib_dir),
- self.stage.source_path)""",
-
- 'unknown': """\
- # FIXME: Unknown build system
- make()
- make('install')"""
- }
+ # Most octave extensions are hosted on Octave-Forge:
+ # http://octave.sourceforge.net/index.html
+ # They all have the same base URL.
+ if 'downloads.sourceforge.net/octave/' in url:
+ self.build_system = 'octave'
+ return
# A list of clues that give us an idea of the build system a package
# uses. If the regular expression matches a file contained in the
@@ -224,12 +239,6 @@ class ConfigureGuesser(object):
self.build_system = build_system
- # Set any necessary build dependencies or extensions.
- self.dependencies = dependenciesDict[build_system]
-
- # Set the appropriate default installation instructions
- self.install = installDict[build_system]
-
def guess_name_and_version(url, args):
# Try to deduce name and version of the new package from the URL
@@ -325,7 +334,7 @@ def create(parser, args):
# Figure out a name and repo for the package.
name, version = guess_name_and_version(url, args)
spec = Spec(name)
- name = spec.name # factors out namespace, if any
+ name = spec.name.lower() # factors out namespace, if any
repo = find_repository(spec, args)
tty.msg("This looks like a URL for %s version %s" % (name, version))
@@ -334,8 +343,8 @@ def create(parser, args):
# Fetch tarballs (prompting user if necessary)
versions, urls = fetch_tarballs(url, name, version)
- # Try to guess what configure system is used.
- guesser = ConfigureGuesser()
+ # Try to guess what build system is used.
+ guesser = BuildSystemGuesser()
ver_hash_tuples = spack.cmd.checksum.get_checksums(
versions, urls,
first_stage_function=guesser,
@@ -344,13 +353,13 @@ def create(parser, args):
if not ver_hash_tuples:
tty.die("Could not fetch any tarballs for %s" % name)
- # Prepend 'py-' to python package names, by convention.
+ # Add prefix to package name if it is an extension.
if guesser.build_system == 'python':
- name = 'py-%s' % name
-
- # Prepend 'r-' to R package names, by convention.
+ name = 'py-{0}'.format(name)
if guesser.build_system == 'R':
- name = 'r-%s' % name
+ name = 'r-{0}'.format(name)
+ if guesser.build_system == 'octave':
+ name = 'octave-{0}'.format(name)
# Create a directory for the new package.
pkg_path = repo.filename_for_package_name(name)
@@ -367,8 +376,8 @@ def create(parser, args):
class_name=mod_to_class(name),
url=url,
versions=make_version_calls(ver_hash_tuples),
- dependencies=guesser.dependencies,
- install=guesser.install))
+ dependencies=dependencies_dict[guesser.build_system],
+ install=install_dict[guesser.build_system]))
# If everything checks out, go ahead and edit.
spack.editor(pkg_path)
diff --git a/lib/spack/spack/cmd/diy.py b/lib/spack/spack/cmd/diy.py
index 39faf59a17..643e6374b2 100644
--- a/lib/spack/spack/cmd/diy.py
+++ b/lib/spack/spack/cmd/diy.py
@@ -50,7 +50,7 @@ def setup_parser(subparser):
help="Do not display verbose build output while installing.")
subparser.add_argument(
'spec', nargs=argparse.REMAINDER,
- help="specs to use for install. Must contain package AND verison.")
+ help="specs to use for install. Must contain package AND version.")
def diy(self, args):
diff --git a/lib/spack/spack/cmd/fetch.py b/lib/spack/spack/cmd/fetch.py
index e40caaa234..1afc51d9fa 100644
--- a/lib/spack/spack/cmd/fetch.py
+++ b/lib/spack/spack/cmd/fetch.py
@@ -51,7 +51,7 @@ def fetch(parser, args):
for spec in specs:
if args.missing or args.dependencies:
to_fetch = set()
- for s in spec.traverse():
+ for s in spec.traverse(deptype_query=spack.alldeps):
package = spack.repo.get(s)
if args.missing and package.installed:
continue
diff --git a/lib/spack/spack/cmd/info.py b/lib/spack/spack/cmd/info.py
index 64d0d20e24..498518057b 100644
--- a/lib/spack/spack/cmd/info.py
+++ b/lib/spack/spack/cmd/info.py
@@ -29,9 +29,11 @@ import spack.fetch_strategy as fs
description = "Get detailed information on a particular package"
+
def padder(str_list, extra=0):
"""Return a function to pad elements of a list."""
length = max(len(str(s)) for s in str_list) + extra
+
def pad(string):
string = str(string)
padding = max(0, length - len(string))
@@ -40,7 +42,8 @@ def padder(str_list, extra=0):
def setup_parser(subparser):
- subparser.add_argument('name', metavar="PACKAGE", help="Name of package to get info for.")
+ subparser.add_argument(
+ 'name', metavar="PACKAGE", help="Name of package to get info for.")
def print_text_info(pkg):
@@ -81,12 +84,14 @@ def print_text_info(pkg):
print " " + fmt % (name, default, desc)
- print
- print "Dependencies:"
- if pkg.dependencies:
- colify(pkg.dependencies, indent=4)
- else:
- print " None"
+ for deptype in ('build', 'link', 'run'):
+ print
+ print "%s Dependencies:" % deptype.capitalize()
+ deps = pkg.dependencies_of_type(deptype)
+ if deps:
+ colify(deps, indent=4)
+ else:
+ print " None"
print
print "Virtual packages: "
diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py
index 9d3175786b..4c076322a9 100644
--- a/lib/spack/spack/cmd/install.py
+++ b/lib/spack/spack/cmd/install.py
@@ -54,7 +54,14 @@ def setup_parser(subparser):
'--fake', action='store_true', dest='fake',
help="Fake install. Just remove the prefix and touch a fake file in it.")
subparser.add_argument(
+ '--dirty', action='store_true', dest='dirty',
+ help="Install a package *without* cleaning the environment.")
+ subparser.add_argument(
'packages', nargs=argparse.REMAINDER, help="specs of packages to install")
+ subparser.add_argument(
+ '--run-tests', action='store_true', dest='run_tests',
+ help="Run tests during installation of a package.")
+
def install(parser, args):
@@ -77,6 +84,8 @@ def install(parser, args):
keep_stage=args.keep_stage,
ignore_deps=args.ignore_deps,
make_jobs=args.jobs,
+ run_tests=args.run_tests,
verbose=args.verbose,
fake=args.fake,
+ dirty=args.dirty,
explicit=True)
diff --git a/lib/spack/spack/cmd/load.py b/lib/spack/spack/cmd/load.py
index 54cf01eb43..205abbb6b3 100644
--- a/lib/spack/spack/cmd/load.py
+++ b/lib/spack/spack/cmd/load.py
@@ -31,7 +31,7 @@ def setup_parser(subparser):
"""Parser is only constructed so that this prints a nice help
message with -h. """
subparser.add_argument(
- 'spec', nargs=argparse.REMAINDER, help='Spec of package to load with modules.')
+ 'spec', nargs=argparse.REMAINDER, help="Spec of package to load with modules. (If -, read specs from STDIN)")
def load(parser, args):
diff --git a/lib/spack/spack/cmd/mirror.py b/lib/spack/spack/cmd/mirror.py
index d5f7abe212..0cf682fc4f 100644
--- a/lib/spack/spack/cmd/mirror.py
+++ b/lib/spack/spack/cmd/mirror.py
@@ -179,7 +179,7 @@ def mirror_create(args):
new_specs = set()
for spec in specs:
spec.concretize()
- for s in spec.traverse():
+ for s in spec.traverse(deptype_query=spack.alldeps):
new_specs.add(s)
specs = list(new_specs)
diff --git a/lib/spack/spack/cmd/module.py b/lib/spack/spack/cmd/module.py
index 5292d42225..55826d133c 100644
--- a/lib/spack/spack/cmd/module.py
+++ b/lib/spack/spack/cmd/module.py
@@ -22,6 +22,7 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
+from __future__ import print_function
import os
import shutil
import sys
@@ -41,46 +42,98 @@ def setup_parser(subparser):
sp.add_parser('refresh', help='Regenerate all module files.')
find_parser = sp.add_parser('find', help='Find module files for packages.')
- find_parser.add_argument('module_type',
- help="Type of module to find file for. [" +
- '|'.join(module_types) + "]")
- find_parser.add_argument('spec',
- nargs='+',
- help='spec to find a module file for.')
+ find_parser.add_argument(
+ 'module_type',
+ help="Type of module to find file for. [" +
+ '|'.join(module_types) + "]")
-def module_find(mtype, spec_array):
+ find_parser.add_argument(
+ '-r', '--dependencies', action='store_true',
+ dest='recurse_dependencies',
+ help='Recursively traverse dependencies for modules to load.')
+
+ find_parser.add_argument(
+ '-s', '--shell', action='store_true', dest='shell',
+ help='Generate shell script (instead of input for module command)')
+
+ find_parser.add_argument(
+ '-p', '--prefix', dest='prefix',
+ help='Prepend to module names when issuing module load commands')
+
+ find_parser.add_argument(
+ 'spec', nargs='+',
+ help='spec to find a module file for.')
+
+
+def module_find(mtype, flags, spec_array):
"""Look at all installed packages and see if the spec provided
matches any. If it does, check whether there is a module file
of type <mtype> there, and print out the name that the user
should type to use that package's module.
+ prefix:
+ Prepend this to module names when issuing "module load" commands.
+ Some systems seem to need it.
"""
if mtype not in module_types:
tty.die("Invalid module type: '%s'. Options are %s" %
(mtype, comma_or(module_types)))
- specs = spack.cmd.parse_specs(spec_array)
- if len(specs) > 1:
- tty.die("You can only pass one spec.")
- spec = specs[0]
-
- specs = spack.installed_db.query(spec)
- if len(specs) == 0:
- tty.die("No installed packages match spec %s" % spec)
-
- if len(specs) > 1:
- tty.error("Multiple matches for spec %s. Choose one:" % spec)
- for s in specs:
- sys.stderr.write(s.tree(color=True))
- sys.exit(1)
-
- mt = module_types[mtype]
- mod = mt(specs[0])
- if not os.path.isfile(mod.file_name):
- tty.die("No %s module is installed for %s" % (mtype, spec))
-
- print(mod.use_name)
-
+ # --------------------------------------
+ def _find_modules(spec, modules_list):
+ """Finds all modules and sub-modules for a spec"""
+ if str(spec.version) == 'system':
+ # No Spack module for system-installed packages
+ return
+
+ if flags.recurse_dependencies:
+ for dep in spec.dependencies():
+ _find_modules(dep, modules_list)
+
+ mod = module_types[mtype](spec)
+ if not os.path.isfile(mod.file_name):
+ tty.die("No %s module is installed for %s" % (mtype, spec))
+ modules_list.append((spec, mod))
+
+
+ # --------------------------------------
+ raw_specs = spack.cmd.parse_specs(spec_array)
+ modules = set() # Modules we will load
+ seen = set()
+ for raw_spec in raw_specs:
+
+ # ----------- Make sure the spec only resolves to ONE thing
+ specs = spack.installed_db.query(raw_spec)
+ if len(specs) == 0:
+ tty.die("No installed packages match spec %s" % raw_spec)
+
+ if len(specs) > 1:
+ tty.error("Multiple matches for spec %s. Choose one:" % raw_spec)
+ for s in specs:
+ sys.stderr.write(s.tree(color=True))
+ sys.exit(1)
+ spec = specs[0]
+
+ # ----------- Chase down modules for it and all its dependencies
+ modules_dups = list()
+ _find_modules(spec, modules_dups)
+
+ # Remove duplicates while keeping order
+ modules_unique = list()
+ for spec,mod in modules_dups:
+ if mod.use_name not in seen:
+ modules_unique.append((spec,mod))
+ seen.add(mod.use_name)
+
+ # Output...
+ if flags.shell:
+ module_cmd = {'tcl': 'module load', 'dotkit': 'dotkit use'}[mtype]
+ for spec,mod in modules_unique:
+ if flags.shell:
+ print('# %s' % spec.format())
+ print('%s %s%s' % (module_cmd, flags.prefix, mod.use_name))
+ else:
+ print(mod.use_name)
def module_refresh():
"""Regenerate all module files for installed packages known to
@@ -101,4 +154,4 @@ def module(parser, args):
module_refresh()
elif args.module_command == 'find':
- module_find(args.module_type, args.spec)
+ module_find(args.module_type, args, args.spec)
diff --git a/lib/spack/spack/cmd/package-list.py b/lib/spack/spack/cmd/package-list.py
index 6c5c4ae8c6..a27502d30e 100644
--- a/lib/spack/spack/cmd/package-list.py
+++ b/lib/spack/spack/cmd/package-list.py
@@ -22,10 +22,8 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
-import re
import cgi
from StringIO import StringIO
-import llnl.util.tty as tty
from llnl.util.tty.colify import *
import spack
@@ -34,21 +32,22 @@ description = "Print a list of all packages in reStructuredText."
def github_url(pkg):
"""Link to a package file on github."""
- return ("https://github.com/llnl/spack/blob/master/var/spack/packages/%s/package.py" %
- pkg.name)
+ url = "https://github.com/llnl/spack/blob/master/var/spack/packages/%s/package.py" # NOQA: ignore=E501
+ return (url % pkg.name)
def rst_table(elts):
"""Print out a RST-style table."""
cols = StringIO()
ncol, widths = colify(elts, output=cols, tty=True)
- header = " ".join("=" * (w-1) for w in widths)
+ header = " ".join("=" * (w - 1) for w in widths)
return "%s\n%s%s" % (header, cols.getvalue(), header)
def print_rst_package_list():
"""Print out information on all packages in restructured text."""
- pkgs = sorted(spack.repo.all_packages(), key=lambda s:s.name.lower())
+ pkgs = sorted(spack.repo.all_packages(), key=lambda s: s.name.lower())
+ pkg_names = [p.name for p in pkgs]
print ".. _package-list:"
print
@@ -62,7 +61,7 @@ def print_rst_package_list():
print "Spack currently has %d mainline packages:" % len(pkgs)
print
- print rst_table("`%s`_" % p.name for p in pkgs)
+ print rst_table("`%s`_" % p for p in pkg_names)
print
print "-----"
@@ -79,12 +78,17 @@ def print_rst_package_list():
print
if pkg.versions:
print "Versions:"
- print " " + ", ".join(str(v) for v in reversed(sorted(pkg.versions)))
- if pkg.dependencies:
- print "Dependencies"
- print " " + ", ".join("`%s`_" % d if d != "mpi" else d
- for d in pkg.dependencies)
- print
+ print " " + ", ".join(str(v) for v in
+ reversed(sorted(pkg.versions)))
+
+ for deptype in spack.alldeps:
+ deps = pkg.dependencies_of_type(deptype)
+ if deps:
+ print "%s Dependencies" % deptype.capitalize()
+ print " " + ", ".join("%s_" % d if d in pkg_names
+ else d for d in deps)
+ print
+
print "Description:"
print pkg.format_doc(indent=2)
print
diff --git a/lib/spack/spack/cmd/setup.py b/lib/spack/spack/cmd/setup.py
new file mode 100644
index 0000000000..04f3d663df
--- /dev/null
+++ b/lib/spack/spack/cmd/setup.py
@@ -0,0 +1,91 @@
+##############################################################################
+# Copyright (c) 2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Written by Elizabeth Fischer
+# 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 General Public License (as published by
+# the Free Software Foundation) version 2.1 dated 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 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
+##############################################################################
+import sys
+import os
+import argparse
+
+import llnl.util.tty as tty
+
+import spack
+import spack.cmd
+from spack.cmd.edit import edit_package
+from spack.stage import DIYStage
+
+description = "Create a configuration script and module, but don't build."
+
+def setup_parser(subparser):
+ subparser.add_argument(
+ '-i', '--ignore-dependencies', action='store_true', dest='ignore_deps',
+ help="Do not try to install dependencies of requested packages.")
+ subparser.add_argument(
+ '-v', '--verbose', action='store_true', dest='verbose',
+ help="Display verbose build output while installing.")
+ subparser.add_argument(
+ 'spec', nargs=argparse.REMAINDER,
+ help="specs to use for install. Must contain package AND version.")
+
+
+def setup(self, args):
+ if not args.spec:
+ tty.die("spack setup requires a package spec argument.")
+
+ specs = spack.cmd.parse_specs(args.spec)
+ if len(specs) > 1:
+ tty.die("spack setup only takes one spec.")
+
+ # Take a write lock before checking for existence.
+ with spack.installed_db.write_transaction():
+ spec = specs[0]
+ if not spack.repo.exists(spec.name):
+ tty.warn("No such package: %s" % spec.name)
+ create = tty.get_yes_or_no("Create this package?", default=False)
+ if not create:
+ tty.msg("Exiting without creating.")
+ sys.exit(1)
+ else:
+ tty.msg("Running 'spack edit -f %s'" % spec.name)
+ edit_package(spec.name, spack.repo.first_repo(), None, True)
+ return
+
+ if not spec.versions.concrete:
+ tty.die("spack setup spec must have a single, concrete version. Did you forget a package version number?")
+
+ spec.concretize()
+ package = spack.repo.get(spec)
+
+ # It's OK if the package is already installed.
+
+ # Forces the build to run out of the current directory.
+ package.stage = DIYStage(os.getcwd())
+
+ # TODO: make this an argument, not a global.
+ spack.do_checksum = False
+
+ package.do_install(
+ keep_prefix=True, # Don't remove install directory, even if you think you should
+ ignore_deps=args.ignore_deps,
+ verbose=args.verbose,
+ keep_stage=True, # don't remove source dir for SETUP.
+ install_phases = set(['setup', 'provenance']))
diff --git a/lib/spack/spack/cmd/test-install.py b/lib/spack/spack/cmd/test-install.py
index 45592a7dda..14c06d136d 100644
--- a/lib/spack/spack/cmd/test-install.py
+++ b/lib/spack/spack/cmd/test-install.py
@@ -133,7 +133,12 @@ def fetch_log(path):
def failed_dependencies(spec):
- return set(item for item in spec.dependencies.itervalues() if not spack.repo.get(item).installed)
+ def get_deps(deptype):
+ return set(item for item in spec.dependencies(deptype)
+ if not spack.repo.get(item).installed)
+ link_deps = get_deps('link')
+ run_deps = get_deps('run')
+ return link_deps.union(run_deps)
def get_top_spec_or_die(args):
diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py
index f792008c49..386df08b2e 100644
--- a/lib/spack/spack/concretize.py
+++ b/lib/spack/spack/concretize.py
@@ -103,7 +103,7 @@ class DefaultConcretizer(object):
usable.sort(cmp=cmp_externals)
return usable
-
+ # XXX(deptypes): Look here.
def choose_virtual_or_external(self, spec):
"""Given a list of candidate virtual and external packages, try to
find one that is most ABI compatible.
@@ -166,7 +166,11 @@ class DefaultConcretizer(object):
valid_versions.sort(key=prefer_key, reverse=True)
if valid_versions:
- spec.versions = ver([valid_versions[0]])
+ # Disregard @develop and take the next valid version
+ if ver(valid_versions[0]) == ver('develop') and len(valid_versions) > 1:
+ spec.versions = ver([valid_versions[1]])
+ else:
+ spec.versions = ver([valid_versions[0]])
else:
# We don't know of any SAFE versions that match the given
# spec. Grab the spec's versions and grab the highest
@@ -390,8 +394,10 @@ def find_spec(spec, condition):
"""Searches the dag from spec in an intelligent order and looks
for a spec that matches a condition"""
# First search parents, then search children
- dagiter = chain(spec.traverse(direction='parents', root=False),
- spec.traverse(direction='children', root=False))
+ deptype = ('build', 'link')
+ dagiter = chain(
+ spec.traverse(direction='parents', deptype=deptype, root=False),
+ spec.traverse(direction='children', deptype=deptype, root=False))
visited = set()
for relative in dagiter:
if condition(relative):
@@ -399,7 +405,7 @@ def find_spec(spec, condition):
visited.add(id(relative))
# Then search all other relatives in the DAG *except* spec
- for relative in spec.root.traverse():
+ for relative in spec.root.traverse(deptypes=spack.alldeps):
if relative is spec: continue
if id(relative) in visited: continue
if condition(relative):
diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py
index a4bbff3d5a..317b0d5784 100644
--- a/lib/spack/spack/database.py
+++ b/lib/spack/spack/database.py
@@ -60,7 +60,7 @@ from spack.repository import UnknownPackageError
_db_dirname = '.spack-db'
# DB version. This is stuck in the DB file to track changes in format.
-_db_version = Version('0.9.1')
+_db_version = Version('0.9.2')
# Default timeout for spack database locks is 5 min.
_db_lock_timeout = 60
@@ -215,9 +215,10 @@ class Database(object):
# Add dependencies from other records in the install DB to
# form a full spec.
if 'dependencies' in spec_dict[spec.name]:
- for dep_hash in spec_dict[spec.name]['dependencies'].values():
- child = self._read_spec_from_yaml(dep_hash, installs, hash_key)
- spec._add_dependency(child)
+ yaml_deps = spec_dict[spec.name]['dependencies']
+ for dname, dhash, dtypes in Spec.read_yaml_dep_specs(yaml_deps):
+ child = self._read_spec_from_yaml(dhash, installs, hash_key)
+ spec._add_dependency(child, dtypes)
# Specs from the database need to be marked concrete because
# they represent actual installations.
@@ -334,7 +335,10 @@ class Database(object):
counts = {}
for key, rec in self._data.items():
counts.setdefault(key, 0)
- for dep in rec.spec.dependencies.values():
+ # XXX(deptype): This checks all dependencies, but build
+ # dependencies might be able to be dropped in the
+ # future.
+ for dep in rec.spec.dependencies():
dep_key = dep.dag_hash()
counts.setdefault(dep_key, 0)
counts[dep_key] += 1
@@ -406,7 +410,7 @@ class Database(object):
else:
self._data[key] = InstallRecord(spec, path, True,
explicit=explicit)
- for dep in spec.dependencies.values():
+ for dep in spec.dependencies(('link', 'run')):
self._increment_ref_count(dep, directory_layout)
def _increment_ref_count(self, spec, directory_layout=None):
@@ -421,7 +425,7 @@ class Database(object):
self._data[key] = InstallRecord(spec.copy(), path, installed)
- for dep in spec.dependencies.values():
+ for dep in spec.dependencies('link'):
self._increment_ref_count(dep)
self._data[key].ref_count += 1
@@ -466,7 +470,7 @@ class Database(object):
if rec.ref_count == 0 and not rec.installed:
del self._data[key]
- for dep in spec.dependencies.values():
+ for dep in spec.dependencies('link'):
self._decrement_ref_count(dep)
def _remove(self, spec):
@@ -480,7 +484,7 @@ class Database(object):
return rec.spec
del self._data[key]
- for dep in rec.spec.dependencies.values():
+ for dep in rec.spec.dependencies('link'):
self._decrement_ref_count(dep)
# Returns the concrete spec so we know it in the case where a
@@ -631,13 +635,14 @@ class WriteTransaction(_Transaction):
class CorruptDatabaseError(SpackError):
def __init__(self, path, msg=''):
super(CorruptDatabaseError, self).__init__(
- "Spack database is corrupt: %s. %s." + \
- "Try running `spack reindex` to fix." % (path, msg))
+ "Spack database is corrupt: %s. %s." % (path, msg),
+ "Try running `spack reindex` to fix.")
class InvalidDatabaseVersionError(SpackError):
def __init__(self, expected, found):
super(InvalidDatabaseVersionError, self).__init__(
- "Expected database version %s but found version %s." + \
- "Try running `spack reindex` to fix." %
- (expected, found))
+ "Expected database version %s but found version %s."
+ % (expected, found),
+ "`spack reindex` may fix this, or you may need a newer "
+ "Spack version.")
diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py
index ca8f21dc08..88d2aaf472 100644
--- a/lib/spack/spack/directives.py
+++ b/lib/spack/spack/directives.py
@@ -171,7 +171,7 @@ def version(pkg, ver, checksum=None, **kwargs):
pkg.versions[Version(ver)] = kwargs
-def _depends_on(pkg, spec, when=None):
+def _depends_on(pkg, spec, when=None, type=None):
# If when is False do nothing
if when is False:
return
@@ -180,10 +180,29 @@ def _depends_on(pkg, spec, when=None):
when = pkg.name
when_spec = parse_anonymous_spec(when, pkg.name)
+ if type is None:
+ # The default deptype is build and link because the common case is to
+ # build against a library which then turns into a runtime dependency
+ # due to the linker.
+ # XXX(deptype): Add 'run' to this? It's an uncommon dependency type,
+ # but is most backwards-compatible.
+ type = ('build', 'link')
+
+ if isinstance(type, str):
+ type = (type,)
+
+ for deptype in type:
+ if deptype not in spack.spec.alldeps:
+ raise UnknownDependencyTypeError('depends_on', pkg.name, deptype)
+
dep_spec = Spec(spec)
if pkg.name == dep_spec.name:
raise CircularReferenceError('depends_on', pkg.name)
+ pkg_deptypes = pkg._deptypes.setdefault(dep_spec.name, set())
+ for deptype in type:
+ pkg_deptypes.add(deptype)
+
conditions = pkg.dependencies.setdefault(dep_spec.name, {})
if when_spec in conditions:
conditions[when_spec].constrain(dep_spec, deps=False)
@@ -191,13 +210,13 @@ def _depends_on(pkg, spec, when=None):
conditions[when_spec] = dep_spec
-@directive('dependencies')
-def depends_on(pkg, spec, when=None):
+@directive(('dependencies', '_deptypes'))
+def depends_on(pkg, spec, when=None, type=None):
"""Creates a dict of deps with specs defining when they apply."""
- _depends_on(pkg, spec, when=when)
+ _depends_on(pkg, spec, when=when, type=type)
-@directive(('extendees', 'dependencies'))
+@directive(('extendees', 'dependencies', '_deptypes'))
def extends(pkg, spec, **kwargs):
"""Same as depends_on, but dependency is symlinked into parent prefix.
@@ -326,3 +345,13 @@ class CircularReferenceError(DirectiveError):
directive,
"Package '%s' cannot pass itself to %s" % (package, directive))
self.package = package
+
+
+class UnknownDependencyTypeError(DirectiveError):
+ """This is raised when a dependency is of an unknown type."""
+ def __init__(self, directive, package, deptype):
+ super(UnknownDependencyTypeError, self).__init__(
+ directive,
+ "Package '%s' cannot depend on a package via %s." %
+ (package, deptype))
+ self.package = package
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index ee13e2dcbc..8150a6da2b 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -34,6 +34,7 @@ import yaml
import llnl.util.tty as tty
from llnl.util.filesystem import join_path, mkdirp
+import spack
from spack.spec import Spec
from spack.error import SpackError
@@ -223,8 +224,14 @@ class YamlDirectoryLayout(DirectoryLayout):
def read_spec(self, path):
"""Read the contents of a file and parse them as a spec"""
- with open(path) as f:
- spec = Spec.from_yaml(f)
+ try:
+ with open(path) as f:
+ spec = Spec.from_yaml(f)
+ except Exception as e:
+ if spack.debug:
+ raise
+ raise SpecReadError(
+ 'Unable to read file: %s' % path, 'Cause: ' + str(e))
# Specs read from actual installations are always concrete
spec._mark_concrete()
@@ -285,7 +292,7 @@ class YamlDirectoryLayout(DirectoryLayout):
return path
if spec.dag_hash() == installed_spec.dag_hash():
- raise SpecHashCollisionError(installed_hash, spec_hash)
+ raise SpecHashCollisionError(spec, installed_spec)
else:
raise InconsistentInstallDirectoryError(
'Spec file in %s does not match hash!' % spec_file_path)
@@ -431,7 +438,7 @@ class SpecHashCollisionError(DirectoryLayoutError):
def __init__(self, installed_spec, new_spec):
super(SpecHashCollisionError, self).__init__(
'Specs %s and %s have the same SHA-1 prefix!'
- % installed_spec, new_spec)
+ % (installed_spec, new_spec))
class RemoveFailedError(DirectoryLayoutError):
@@ -456,10 +463,12 @@ class InstallDirectoryAlreadyExistsError(DirectoryLayoutError):
"Install path %s already exists!")
+class SpecReadError(DirectoryLayoutError):
+ """Raised when directory layout can't read a spec."""
+
+
class InvalidExtensionSpecError(DirectoryLayoutError):
"""Raised when an extension file has a bad spec in it."""
- def __init__(self, message):
- super(InvalidExtensionSpecError, self).__init__(message)
class ExtensionAlreadyInstalledError(DirectoryLayoutError):
diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py
index 69c04f7920..bcb33bd0e6 100644
--- a/lib/spack/spack/fetch_strategy.py
+++ b/lib/spack/spack/fetch_strategy.py
@@ -307,9 +307,6 @@ class URLFetchStrategy(FetchStrategy):
if not self.archive_file:
raise NoArchiveFileError("Cannot call archive() before fetching.")
- if not extension(destination) == extension(self.archive_file):
- raise ValueError("Cannot archive without matching extensions.")
-
shutil.copy(self.archive_file, destination)
@_needs_stage
@@ -369,8 +366,8 @@ class CacheURLFetchStrategy(URLFetchStrategy):
try:
self.check()
except ChecksumError:
- # Future fetchers will assume they don't need to download if the
- # file remains
+ # Future fetchers will assume they don't need to
+ # download if the file remains
os.remove(self.archive_file)
raise
@@ -520,6 +517,7 @@ class GitFetchStrategy(VCSFetchStrategy):
super(GitFetchStrategy, self).__init__(
'git', 'tag', 'branch', 'commit', **forwarded_args)
self._git = None
+ self.submodules = kwargs.get('submodules', False)
@property
def git_version(self):
@@ -598,6 +596,10 @@ class GitFetchStrategy(VCSFetchStrategy):
self.git('pull', '--tags', ignore_errors=1)
self.git('checkout', self.tag)
+ # Init submodules if the user asked for them.
+ if self.submodules:
+ self.git('submodule', 'update', '--init')
+
def archive(self, destination):
super(GitFetchStrategy, self).archive(destination, exclude='.git')
diff --git a/lib/spack/spack/graph.py b/lib/spack/spack/graph.py
index 22058d41d8..063e4647b6 100644
--- a/lib/spack/spack/graph.py
+++ b/lib/spack/spack/graph.py
@@ -80,12 +80,14 @@ def topological_sort(spec, **kwargs):
"""
reverse = kwargs.get('reverse', False)
+ # XXX(deptype): iterate over a certain kind of dependency. Maybe color
+ # edges based on the type of dependency?
if not reverse:
- parents = lambda s: s.dependents
- children = lambda s: s.dependencies
+ parents = lambda s: s.dependents()
+ children = lambda s: s.dependencies()
else:
- parents = lambda s: s.dependencies
- children = lambda s: s.dependents
+ parents = lambda s: s.dependencies()
+ children = lambda s: s.dependents()
# Work on a copy so this is nondestructive.
spec = spec.copy()
diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py
index ce46047fa3..a2e528d295 100644
--- a/lib/spack/spack/modules.py
+++ b/lib/spack/spack/modules.py
@@ -120,7 +120,7 @@ def dependencies(spec, request='all'):
return []
if request == 'direct':
- return [xx for _, xx in spec.dependencies.items()]
+ return spec.dependencies()
# FIXME : during module file creation nodes seem to be visited multiple
# FIXME : times even if cover='nodes' is given. This work around permits
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index b73056fd30..6a92c548fb 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -37,6 +37,7 @@ import os
import re
import textwrap
import time
+import string
import llnl.util.tty as tty
import spack
@@ -50,6 +51,8 @@ import spack.mirror
import spack.repository
import spack.url
import spack.util.web
+
+from urlparse import urlparse
from StringIO import StringIO
from llnl.util.filesystem import *
from llnl.util.lang import *
@@ -58,9 +61,11 @@ from llnl.util.tty.log import log_output
from spack.stage import Stage, ResourceStage, StageComposite
from spack.util.compression import allowed_archive
from spack.util.environment import dump_environment
-from spack.util.executable import ProcessError
+from spack.util.executable import ProcessError, which
from spack.version import *
-from urlparse import urlparse
+from spack import directory_layout
+
+
"""Allowed URL schemes for spack packages."""
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
@@ -305,6 +310,8 @@ class Package(object):
parallel = True
"""# jobs to use for parallel make. If set, overrides default of ncpus."""
make_jobs = None
+ """By default do not run tests within package's install()"""
+ run_tests = False
"""Most packages are NOT extendable. Set to True if you want extensions."""
extendable = False
"""List of prefix-relative file paths (or a single path). If these do
@@ -402,7 +409,6 @@ class Package(object):
"""Return the directory where the package.py file lives."""
return os.path.dirname(self.module.__file__)
-
@property
def global_license_dir(self):
"""Returns the directory where global license files for all
@@ -557,6 +563,11 @@ class Package(object):
def fetcher(self, f):
self._fetcher = f
+ def dependencies_of_type(self, *deptypes):
+ """Get subset of the dependencies with certain types."""
+ return dict((name, conds) for name, conds in self.dependencies.items()
+ if any(d in self._deptypes[name] for d in deptypes))
+
@property
def extendee_spec(self):
"""
@@ -569,7 +580,7 @@ class Package(object):
name = next(iter(self.extendees))
# If the extendee is in the spec's deps already, return that.
- for dep in self.spec.traverse():
+ for dep in self.spec.traverse(deptypes=('link', 'run')):
if name == dep.name:
return dep
@@ -634,12 +645,13 @@ class Package(object):
yield self
for name in sorted(self.dependencies.keys()):
- spec = self.dependencies[name]
+ dep_spec = self.get_dependency(name)
+ spec = dep_spec.spec
- # currently, we do not descend into virtual dependencies, as this
+ # Currently, we do not descend into virtual dependencies, as this
# makes doing a sensible traversal much harder. We just assume
# that ANY of the virtual deps will work, which might not be true
- # (due to conflicts or unsatisfiable specs). For now this is ok
+ # (due to conflicts or unsatisfiable specs). For now this is ok,
# but we might want to reinvestigate if we start using a lot of
# complicated virtual dependencies
# TODO: reinvestigate this.
@@ -677,7 +689,9 @@ class Package(object):
for spec in spack.installed_db.query():
if self.name == spec.name:
continue
- for dep in spec.traverse():
+ # XXX(deptype): Should build dependencies not count here?
+ # for dep in spec.traverse(deptype=('run')):
+ for dep in spec.traverse(deptype=spack.alldeps):
if self.spec == dep:
dependents.append(spec)
return dependents
@@ -688,13 +702,13 @@ class Package(object):
return self.spec.prefix
@property
- #TODO: Change this to architecture
+ # TODO: Change this to architecture
def compiler(self):
"""Get the spack.compiler.Compiler object used to build this package"""
if not self.spec.concrete:
raise ValueError("Can only get a compiler for a concrete package.")
return spack.compilers.compiler_for_spec(self.spec.compiler,
- self.spec.architecture)
+ self.spec.architecture)
def url_version(self, version):
"""
@@ -749,7 +763,6 @@ class Package(object):
self.stage.check()
self.stage.cache_local()
-
def do_stage(self, mirror_only=False):
"""Unpacks the fetched tarball, then changes into the expanded tarball
@@ -867,6 +880,8 @@ class Package(object):
resource_stage_folder = '-'.join(pieces)
return resource_stage_folder
+ install_phases = set(['configure', 'build', 'install', 'provenance'])
+
def do_install(self,
keep_prefix=False,
keep_stage=False,
@@ -874,8 +889,11 @@ class Package(object):
skip_patch=False,
verbose=False,
make_jobs=None,
+ run_tests=False,
fake=False,
- explicit=False):
+ explicit=False,
+ dirty=False,
+ install_phases=install_phases):
"""Called by commands to install a package and its dependencies.
Package implementations should override install() to describe
@@ -891,10 +909,13 @@ class Package(object):
fake -- Don't really build -- install fake stub files instead.
skip_patch -- Skip patch stage of build if True.
verbose -- Display verbose build output (by default, suppresses it)
+ dirty -- Don't clean the build environment before installing.
make_jobs -- Number of make jobs to use for install. Default is ncpus
+ run_tests -- Runn tests within the package's install()
"""
if not self.spec.concrete:
- raise ValueError("Can only install concrete packages.")
+ raise ValueError("Can only install concrete packages: %s."
+ % self.spec.name)
# No installation needed if package is external
if self.spec.external:
@@ -903,7 +924,8 @@ class Package(object):
return
# Ensure package is not already installed
- if spack.install_layout.check_installed(self.spec):
+ layout = spack.install_layout
+ if 'install' in install_phases and layout.check_installed(self.spec):
tty.msg("%s is already installed in %s" % (self.name, self.prefix))
rec = spack.installed_db.get_record(self.spec)
if (not rec.explicit) and explicit:
@@ -922,7 +944,11 @@ class Package(object):
fake=fake,
skip_patch=skip_patch,
verbose=verbose,
- make_jobs=make_jobs)
+ make_jobs=make_jobs,
+ run_tests=run_tests)
+
+ # Set run_tests flag before starting build.
+ self.run_tests = run_tests
# Set parallelism before starting build.
self.make_jobs = make_jobs
@@ -942,6 +968,10 @@ class Package(object):
tty.msg("Building %s" % self.name)
self.stage.keep = keep_stage
+ self.install_phases = install_phases
+ self.build_directory = join_path(self.stage.path, 'spack-build')
+ self.source_directory = self.stage.source_path
+
with self.stage:
# Run the pre-install hook in the child process after
# the directory is created.
@@ -973,19 +1003,26 @@ class Package(object):
raise e
# Ensure that something was actually installed.
- self.sanity_check_prefix()
+ if 'install' in self.install_phases:
+ self.sanity_check_prefix()
# Copy provenance into the install directory on success
- log_install_path = spack.install_layout.build_log_path(
- self.spec)
- env_install_path = spack.install_layout.build_env_path(
- self.spec)
- packages_dir = spack.install_layout.build_packages_path(
- self.spec)
-
- install(log_path, log_install_path)
- install(env_path, env_install_path)
- dump_packages(self.spec, packages_dir)
+ if 'provenance' in self.install_phases:
+ log_install_path = layout.build_log_path(self.spec)
+ env_install_path = layout.build_env_path(self.spec)
+ packages_dir = layout.build_packages_path(self.spec)
+
+ # Remove first if we're overwriting another build
+ # (can happen with spack setup)
+ try:
+ # log_install_path and env_install_path are here
+ shutil.rmtree(packages_dir)
+ except:
+ pass
+
+ install(log_path, log_install_path)
+ install(env_path, env_install_path)
+ dump_packages(self.spec, packages_dir)
# Run post install hooks before build stage is removed.
spack.hooks.post_install(self)
@@ -1003,7 +1040,19 @@ class Package(object):
try:
# Create the install prefix and fork the build process.
spack.install_layout.create_install_directory(self.spec)
- spack.build_environment.fork(self, build_process)
+ except directory_layout.InstallDirectoryAlreadyExistsError:
+ if 'install' in install_phases:
+ # Abort install if install directory exists.
+ # But do NOT remove it (you'd be overwriting someone's data)
+ tty.warn("Keeping existing install prefix in place.")
+ raise
+ else:
+ # We're not installing anyway, so don't worry if someone
+ # else has already written in the install directory
+ pass
+
+ try:
+ spack.build_environment.fork(self, build_process, dirty=dirty)
except:
# remove the install prefix if anything went wrong during install.
if not keep_prefix:
@@ -1013,7 +1062,7 @@ class Package(object):
"Spack will think this package is installed. " +
"Manually remove this directory to fix:",
self.prefix,
- wrap=True)
+ wrap=False)
raise
# note: PARENT of the build process adds the new package to
@@ -1045,7 +1094,7 @@ class Package(object):
def do_install_dependencies(self, **kwargs):
# Pass along paths of dependencies here
- for dep in self.spec.dependencies.values():
+ for dep in self.spec.dependencies():
dep.package.do_install(**kwargs)
@property
@@ -1226,7 +1275,7 @@ class Package(object):
(self.name, self.extendee.name))
def do_activate(self, force=False):
- """Called on an etension to invoke the extendee's activate method.
+ """Called on an extension to invoke the extendee's activate method.
Commands should call this routine, and should not call
activate() directly.
@@ -1238,7 +1287,7 @@ class Package(object):
# Activate any package dependencies that are also extensions.
if not force:
- for spec in self.spec.traverse(root=False):
+ for spec in self.spec.traverse(root=False, deptype='run'):
if spec.package.extends(self.extendee_spec):
if not spec.package.activated:
spec.package.do_activate(force=force)
@@ -1284,7 +1333,7 @@ class Package(object):
for name, aspec in activated.items():
if aspec == self.spec:
continue
- for dep in aspec.traverse():
+ for dep in aspec.traverse(deptype='run'):
if self.spec == dep:
raise ActivationError(
"Cannot deactivate %s because %s is activated and depends on it." # NOQA: ignore=E501
@@ -1370,9 +1419,10 @@ class Package(object):
def rpath(self):
"""Get the rpath this package links with, as a list of paths."""
rpaths = [self.prefix.lib, self.prefix.lib64]
- rpaths.extend(d.prefix.lib for d in self.spec.traverse(root=False)
+ deps = self.spec.dependencies(deptype='link')
+ rpaths.extend(d.prefix.lib for d in deps
if os.path.isdir(d.prefix.lib))
- rpaths.extend(d.prefix.lib64 for d in self.spec.traverse(root=False)
+ rpaths.extend(d.prefix.lib64 for d in deps
if os.path.isdir(d.prefix.lib64))
return rpaths
@@ -1486,6 +1536,165 @@ def _hms(seconds):
return ' '.join(parts)
+class StagedPackage(Package):
+ """A Package subclass where the install() is split up into stages."""
+
+ def install_setup(self):
+ """Creates a spack_setup.py script to configure the package later."""
+ raise InstallError(
+ "Package %s provides no install_setup() method!" % self.name)
+
+ def install_configure(self):
+ """Runs the configure process."""
+ raise InstallError(
+ "Package %s provides no install_configure() method!" % self.name)
+
+ def install_build(self):
+ """Runs the build process."""
+ raise InstallError(
+ "Package %s provides no install_build() method!" % self.name)
+
+ def install_install(self):
+ """Runs the install process."""
+ raise InstallError(
+ "Package %s provides no install_install() method!" % self.name)
+
+ def install(self, spec, prefix):
+ if 'setup' in self.install_phases:
+ self.install_setup()
+
+ if 'configure' in self.install_phases:
+ self.install_configure()
+
+ if 'build' in self.install_phases:
+ self.install_build()
+
+ if 'install' in self.install_phases:
+ self.install_install()
+ else:
+ # Create a dummy file so the build doesn't fail.
+ # That way, the module file will also be created.
+ with open(os.path.join(prefix, 'dummy'), 'w'):
+ pass
+
+
+# stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python
+def make_executable(path):
+ mode = os.stat(path).st_mode
+ mode |= (mode & 0o444) >> 2 # copy R bits to X
+ os.chmod(path, mode)
+
+
+class CMakePackage(StagedPackage):
+ def make_make(self):
+ import multiprocessing
+ # number of jobs spack will to build with.
+ jobs = multiprocessing.cpu_count()
+ if not self.parallel:
+ jobs = 1
+ elif self.make_jobs:
+ jobs = self.make_jobs
+
+ make = spack.build_environment.MakeExecutable('make', jobs)
+ return make
+
+ def configure_args(self):
+ """Returns package-specific arguments to be provided to
+ the configure command.
+ """
+ return list()
+
+ def configure_env(self):
+ """Returns package-specific environment under which the
+ configure command should be run.
+ """
+ return dict()
+
+ def transitive_inc_path(self):
+ return ';'.join(
+ os.path.join(dep, 'include')
+ for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep)
+ )
+
+ def install_setup(self):
+ cmd = [str(which('cmake'))]
+ cmd += spack.build_environment.get_std_cmake_args(self)
+ cmd += ['-DCMAKE_INSTALL_PREFIX=%s' % os.environ['SPACK_PREFIX'],
+ '-DCMAKE_C_COMPILER=%s' % os.environ['SPACK_CC'],
+ '-DCMAKE_CXX_COMPILER=%s' % os.environ['SPACK_CXX'],
+ '-DCMAKE_Fortran_COMPILER=%s' % os.environ['SPACK_FC']]
+ cmd += self.configure_args()
+
+ env = {
+ 'PATH': os.environ['PATH'],
+ 'SPACK_TRANSITIVE_INCLUDE_PATH': self.transitive_inc_path(),
+ 'CMAKE_PREFIX_PATH': os.environ['CMAKE_PREFIX_PATH']
+ }
+
+ setup_fname = 'spconfig.py'
+ with open(setup_fname, 'w') as fout:
+ fout.write(r"""#!%s
+#
+
+import sys
+import os
+import subprocess
+
+def cmdlist(str):
+ return list(x.strip().replace("'",'') for x in str.split('\n') if x)
+env = dict(os.environ)
+""" % sys.executable)
+
+ env_vars = sorted(list(env.keys()))
+ for name in env_vars:
+ val = env[name]
+ if string.find(name, 'PATH') < 0:
+ fout.write('env[%s] = %s\n' % (repr(name), repr(val)))
+ else:
+ if name == 'SPACK_TRANSITIVE_INCLUDE_PATH':
+ sep = ';'
+ else:
+ sep = ':'
+
+ fout.write('env[%s] = "%s".join(cmdlist("""\n'
+ % (repr(name), sep))
+ for part in string.split(val, sep):
+ fout.write(' %s\n' % part)
+ fout.write('"""))\n')
+
+ fout.write("env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = "
+ "env['SPACK_TRANSITIVE_INCLUDE_PATH'] # Deprecated\n")
+ fout.write('\ncmd = cmdlist("""\n')
+ fout.write('%s\n' % cmd[0])
+ for arg in cmd[1:]:
+ fout.write(' %s\n' % arg)
+ fout.write('""") + sys.argv[1:]\n')
+ fout.write('\nproc = subprocess.Popen(cmd, env=env)\n')
+ fout.write('proc.wait()\n')
+ make_executable(setup_fname)
+
+ def install_configure(self):
+ cmake = which('cmake')
+ with working_dir(self.build_directory, create=True):
+ env = os.environ
+ env.update(self.configure_env())
+ env['SPACK_TRANSITIVE_INCLUDE_PATH'] = self.transitive_inc_path()
+
+ options = self.configure_args()
+ options += spack.build_environment.get_std_cmake_args(self)
+ cmake(self.source_directory, *options)
+
+ def install_build(self):
+ make = self.make_make()
+ with working_dir(self.build_directory, create=False):
+ make()
+
+ def install_install(self):
+ make = self.make_make()
+ with working_dir(self.build_directory, create=False):
+ make('install')
+
+
class FetchError(spack.error.SpackError):
"""Raised when something goes wrong during fetch."""
diff --git a/lib/spack/spack/platforms/linux.py b/lib/spack/spack/platforms/linux.py
index 4d8adac384..4d3f59c320 100644
--- a/lib/spack/spack/platforms/linux.py
+++ b/lib/spack/spack/platforms/linux.py
@@ -1,16 +1,23 @@
import subprocess
+import platform
from spack.architecture import Platform, Target
from spack.operating_systems.linux_distro import LinuxDistro
class Linux(Platform):
priority = 90
- front_end = 'x86_64'
- back_end = 'x86_64'
- default = 'x86_64'
def __init__(self):
super(Linux, self).__init__('linux')
- self.add_target(self.default, Target(self.default))
+ self.add_target('x86_64', Target('x86_64'))
+ self.add_target('ppc64le', Target('ppc64le'))
+
+ self.default = platform.machine()
+ self.front_end = platform.machine()
+ self.back_end = platform.machine()
+
+ if self.default not in self.targets:
+ self.add_target(self.default, Target(self.default))
+
linux_dist = LinuxDistro()
self.default_os = str(linux_dist)
self.front_os = self.default_os
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 77bc57147d..e694f2b2da 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -96,7 +96,6 @@ specs to avoid ambiguity. Both are provided because ~ can cause shell
expansion when it is the first character in an id typed on the command line.
"""
import sys
-import itertools
import hashlib
import base64
import imp
@@ -116,8 +115,6 @@ import spack.parse
import spack.error
import spack.compilers as compilers
-# TODO: move display_specs to some other location.
-from spack.cmd.find import display_specs
from spack.version import *
from spack.util.string import *
from spack.util.prefix import Prefix
@@ -155,6 +152,10 @@ _separators = '[%s]' % ''.join(color_formats.keys())
every time we call str()"""
_any_version = VersionList([':'])
+# Special types of dependencies.
+alldeps = ('build', 'link', 'run')
+nolink = ('build', 'run')
+
def index_specs(specs):
"""Take a list of specs and return a dict of lists. Dict is
@@ -292,6 +293,32 @@ class CompilerSpec(object):
@key_ordering
+class DependencySpec(object):
+ """Dependencies can be one (or more) of several types:
+
+ - build: needs to be in the PATH at build time.
+ - link: is linked to and added to compiler flags.
+ - run: needs to be in the PATH for the package to run.
+
+ Fields:
+ - spec: the spack.spec.Spec description of a dependency.
+ - deptypes: strings representing the type of dependency this is.
+ """
+ def __init__(self, spec, deptypes):
+ self.spec = spec
+ self.deptypes = deptypes
+
+ def _cmp_key(self):
+ return self.spec
+
+ def copy(self):
+ return DependencySpec(self.spec.copy(), self.deptype)
+
+ def __str__(self):
+ return str(self.spec)
+
+
+@key_ordering
class VariantSpec(object):
"""Variants are named, build-time options for a package. Names depend
@@ -440,11 +467,11 @@ class DependencyMap(HashableMap):
The DependencyMap is keyed by name. """
@property
def concrete(self):
- return all(d.concrete for d in self.values())
+ return all(d.spec.concrete for d in self.values())
def __str__(self):
return ''.join(
- ["^" + str(self[name]) for name in sorted(self.keys())])
+ ["^" + str(self[name].spec) for name in sorted(self.keys())])
@key_ordering
@@ -472,13 +499,13 @@ class Spec(object):
# writes directly into this Spec object.
other = spec_list[0]
self.name = other.name
- self.dependents = other.dependents
self.versions = other.versions
self.architecture = other.architecture
self.compiler = other.compiler
self.compiler_flags = other.compiler_flags
self.compiler_flags.spec = self
- self.dependencies = other.dependencies
+ self._dependencies = other._dependencies
+ self._dependents = other._dependents
self.variants = other.variants
self.variants.spec = self
self.namespace = other.namespace
@@ -500,7 +527,49 @@ class Spec(object):
# Spec(a, b) will copy a but just add b as a dep.
for dep in dep_like:
spec = dep if isinstance(dep, Spec) else Spec(dep)
- self._add_dependency(spec)
+ # XXX(deptype): default deptypes
+ self._add_dependency(spec, ('build', 'link'))
+
+ def get_dependency(self, name):
+ dep = self._dependencies.get(name)
+ if dep is not None:
+ return dep
+ raise InvalidDependencyException(
+ self.name + " does not depend on " + comma_or(name))
+
+ def _deptype_norm(self, deptype):
+ if deptype is None:
+ return alldeps
+ # Force deptype to be a set object so that we can do set intersections.
+ if isinstance(deptype, str):
+ return (deptype,)
+ return deptype
+
+ def _find_deps(self, where, deptype):
+ deptype = self._deptype_norm(deptype)
+
+ return [dep.spec
+ for dep in where.values()
+ if deptype and any(d in deptype for d in dep.deptypes)]
+
+ def dependencies(self, deptype=None):
+ return self._find_deps(self._dependencies, deptype)
+
+ def dependents(self, deptype=None):
+ return self._find_deps(self._dependents, deptype)
+
+ def _find_deps_dict(self, where, deptype):
+ deptype = self._deptype_norm(deptype)
+
+ return dict((dep.spec.name, dep)
+ for dep in where.values()
+ if deptype and any(d in deptype for d in dep.deptypes))
+
+ def dependencies_dict(self, deptype=None):
+ return self._find_deps_dict(self._dependencies, deptype)
+
+ def dependents_dict(self, deptype=None):
+ return self._find_deps_dict(self._dependents, deptype)
#
# Private routines here are called by the parser when building a spec.
@@ -578,7 +647,8 @@ class Spec(object):
mod = imp.load_source(mod_name, path)
class_name = mod_to_class(value)
if not hasattr(mod, class_name):
- tty.die('No class %s defined in %s' % (class_name, mod_name))
+ tty.die(
+ 'No class %s defined in %s' % (class_name, mod_name))
cls = getattr(mod, class_name)
if not inspect.isclass(cls):
tty.die('%s.%s is not a class' % (mod_name, class_name))
@@ -601,29 +671,32 @@ class Spec(object):
def _set_os(self, value):
"""Called by the parser to set the architecture operating system"""
- if self.architecture.platform:
- self.architecture.platform_os = self.architecture.platform.operating_system(value)
+ arch = self.architecture
+ if arch.platform:
+ arch.platform_os = arch.platform.operating_system(value)
def _set_target(self, value):
"""Called by the parser to set the architecture target"""
- if self.architecture.platform:
- self.architecture.target = self.architecture.platform.target(value)
+ arch = self.architecture
+ if arch.platform:
+ arch.target = arch.platform.target(value)
- def _add_dependency(self, spec):
+ def _add_dependency(self, spec, deptypes):
"""Called by the parser to add another spec as a dependency."""
- if spec.name in self.dependencies:
+ if spec.name in self._dependencies:
raise DuplicateDependencyError(
"Cannot depend on '%s' twice" % spec)
- self.dependencies[spec.name] = spec
- spec.dependents[self.name] = self
+ self._dependencies[spec.name] = DependencySpec(spec, deptypes)
+ spec._dependents[self.name] = DependencySpec(self, deptypes)
#
# Public interface
#
@property
def fullname(self):
- return (('%s.%s' % (self.namespace, self.name)) if self.namespace else
- (self.name if self.name else ''))
+ return (
+ ('%s.%s' % (self.namespace, self.name)) if self.namespace else
+ (self.name if self.name else ''))
@property
def root(self):
@@ -632,15 +705,15 @@ class Spec(object):
installed). This will throw an assertion error if that is not
the case.
"""
- if not self.dependents:
+ if not self._dependents:
return self
# If the spec has multiple dependents, ensure that they all
# lead to the same place. Spack shouldn't deal with any DAGs
# with multiple roots, so something's wrong if we find one.
- depiter = iter(self.dependents.values())
- first_root = next(depiter).root
- assert(all(first_root is d.root for d in depiter))
+ depiter = iter(self._dependents.values())
+ first_root = next(depiter).spec.root
+ assert(all(first_root is d.spec.root for d in depiter))
return first_root
@property
@@ -679,18 +752,29 @@ class Spec(object):
if self._concrete:
return True
- self._concrete = bool(not self.virtual
- and self.namespace is not None
- and self.versions.concrete
- and self.variants.concrete
- and self.architecture
- and self.architecture.concrete
- and self.compiler and self.compiler.concrete
- and self.compiler_flags.concrete
- and self.dependencies.concrete)
+ self._concrete = bool(not self.virtual and
+ self.namespace is not None and
+ self.versions.concrete and
+ self.variants.concrete and
+ self.architecture and
+ self.architecture.concrete and
+ self.compiler and self.compiler.concrete and
+ self.compiler_flags.concrete and
+ self._dependencies.concrete)
return self._concrete
- def traverse(self, visited=None, d=0, **kwargs):
+ def traverse(self, visited=None, deptype=None, **kwargs):
+ traversal = self.traverse_with_deptype(visited=visited,
+ deptype=deptype,
+ **kwargs)
+ if kwargs.get('depth', False):
+ return [(s[0], s[1].spec) for s in traversal]
+ else:
+ return [s.spec for s in traversal]
+
+ def traverse_with_deptype(self, visited=None, d=0, deptype=None,
+ deptype_query=None, _self_deptype=None,
+ **kwargs):
"""Generic traversal of the DAG represented by this spec.
This will yield each node in the spec. Options:
@@ -742,6 +826,12 @@ class Spec(object):
direction = kwargs.get('direction', 'children')
order = kwargs.get('order', 'pre')
+ if deptype is None:
+ deptype = alldeps
+
+ if deptype_query is None:
+ deptype_query = ('link', 'run')
+
# Make sure kwargs have legal values; raise ValueError if not.
def validate(name, val, allowed_values):
if val not in allowed_values:
@@ -759,30 +849,37 @@ class Spec(object):
if key in visited and cover == 'nodes':
return
- # Determine whether and what to yield for this node.
+ def return_val(res):
+ return (d, res) if depth else res
+
yield_me = yield_root or d > 0
- result = (d, self) if depth else self
# Preorder traversal yields before successors
if yield_me and order == 'pre':
- yield result
+ yield return_val(DependencySpec(self, _self_deptype))
+
+ deps = self.dependencies_dict(deptype)
# Edge traversal yields but skips children of visited nodes
if not (key in visited and cover == 'edges'):
# This code determines direction and yields the children/parents
- successors = self.dependencies
+ successors = deps
if direction == 'parents':
- successors = self.dependents
+ successors = self.dependents_dict()
visited.add(key)
for name in sorted(successors):
child = successors[name]
- for elt in child.traverse(visited, d + 1, **kwargs):
+ children = child.spec.traverse_with_deptype(
+ visited, d=d + 1, deptype=deptype_query,
+ deptype_query=deptype_query,
+ _self_deptype=child.deptypes, **kwargs)
+ for elt in children:
yield elt
# Postorder traversal yields after successors
if yield_me and order == 'post':
- yield result
+ yield return_val(DependencySpec(self, _self_deptype))
@property
def short_spec(self):
@@ -807,6 +904,7 @@ class Spec(object):
if self._hash:
return self._hash[:length]
else:
+ # XXX(deptype): ignore 'build' dependencies here
yaml_text = yaml.dump(
self.to_node_dict(), default_flow_style=True, width=sys.maxint)
sha = hashlib.sha1(yaml_text)
@@ -819,11 +917,15 @@ class Spec(object):
params = dict((name, v.value) for name, v in self.variants.items())
params.update(dict((name, value)
for name, value in self.compiler_flags.items()))
+ deps = self.dependencies_dict(deptype=('link', 'run'))
d = {
- 'parameters' : params,
- 'arch' : self.architecture,
- 'dependencies' : dict((d, self.dependencies[d].dag_hash())
- for d in sorted(self.dependencies))
+ 'parameters': params,
+ 'arch': self.architecture,
+ 'dependencies': dict(
+ (name, {
+ 'hash': dspec.spec.dag_hash(),
+ 'type': [str(s) for s in dspec.deptypes]})
+ for name, dspec in deps.items())
}
# Older concrete specs do not have a namespace. Omit for
@@ -848,7 +950,7 @@ class Spec(object):
def to_yaml(self, stream=None):
node_list = []
- for s in self.traverse(order='pre'):
+ for s in self.traverse(order='pre', deptype=('link', 'run')):
node = s.to_node_dict()
node[s.name]['hash'] = s.dag_hash()
node_list.append(node)
@@ -889,9 +991,34 @@ class Spec(object):
raise SpackRecordError(
"Did not find a valid format for variants in YAML file")
+ # Don't read dependencies here; from_node_dict() is used by
+ # from_yaml() to read the root *and* each dependency spec.
+
return spec
@staticmethod
+ def read_yaml_dep_specs(dependency_dict):
+ """Read the DependencySpec portion of a YAML-formatted Spec.
+
+ This needs to be backward-compatible with older spack spec
+ formats so that reindex will work on old specs/databases.
+ """
+ for dep_name, elt in dependency_dict.items():
+ if isinstance(elt, basestring):
+ # original format, elt is just the dependency hash.
+ dag_hash, deptypes = elt, ['build', 'link']
+ elif isinstance(elt, tuple):
+ # original deptypes format: (used tuples, not future-proof)
+ dag_hash, deptypes = elt
+ elif isinstance(elt, dict):
+ # new format: elements of dependency spec are keyed.
+ dag_hash, deptypes = elt['hash'], elt['type']
+ else:
+ raise SpecError("Couldn't parse dependency types in spec.")
+
+ yield dep_name, dag_hash, list(deptypes)
+
+ @staticmethod
def from_yaml(stream):
"""Construct a spec from YAML.
@@ -902,25 +1029,30 @@ class Spec(object):
represent more than the DAG does.
"""
- deps = {}
- spec = None
-
try:
yfile = yaml.load(stream)
except MarkedYAMLError, e:
raise SpackYAMLError("error parsing YAML spec:", str(e))
- for node in yfile['spec']:
- name = next(iter(node))
- dep = Spec.from_node_dict(node)
- if not spec:
- spec = dep
- deps[dep.name] = dep
+ nodes = yfile['spec']
+
+ # Read nodes out of list. Root spec is the first element;
+ # dependencies are the following elements.
+ dep_list = [Spec.from_node_dict(node) for node in nodes]
+ if not dep_list:
+ raise SpecError("YAML spec contains no nodes.")
+ deps = dict((spec.name, spec) for spec in dep_list)
+ spec = dep_list[0]
- for node in yfile['spec']:
+ for node in nodes:
+ # get dependency dict from the node.
name = next(iter(node))
- for dep_name in node[name]['dependencies']:
- deps[name].dependencies[dep_name] = deps[dep_name]
+ yaml_deps = node[name]['dependencies']
+ for dname, dhash, dtypes in Spec.read_yaml_dep_specs(yaml_deps):
+ # Fill in dependencies by looking them up by name in deps dict
+ deps[name]._dependencies[dname] = DependencySpec(
+ deps[dname], set(dtypes))
+
return spec
def _concretize_helper(self, presets=None, visited=None):
@@ -940,8 +1072,9 @@ class Spec(object):
changed = False
# Concretize deps first -- this is a bottom-up process.
- for name in sorted(self.dependencies.keys()):
- changed |= self.dependencies[name]._concretize_helper(presets, visited)
+ for name in sorted(self._dependencies.keys()):
+ changed |= self._dependencies[
+ name].spec._concretize_helper(presets, visited)
if self.name in presets:
changed |= self.constrain(presets[self.name])
@@ -965,13 +1098,16 @@ class Spec(object):
def _replace_with(self, concrete):
"""Replace this virtual spec with a concrete spec."""
assert(self.virtual)
- for name, dependent in self.dependents.items():
+ for name, dep_spec in self._dependents.items():
+ dependent = dep_spec.spec
+ deptypes = dep_spec.deptypes
+
# remove self from all dependents.
- del dependent.dependencies[self.name]
+ del dependent._dependencies[self.name]
# add the replacement, unless it is already a dep of dependent.
- if concrete.name not in dependent.dependencies:
- dependent._add_dependency(concrete)
+ if concrete.name not in dependent._dependencies:
+ dependent._add_dependency(concrete, deptypes)
def _replace_node(self, replacement):
"""Replace this spec with another.
@@ -982,13 +1118,15 @@ class Spec(object):
to be normalized.
"""
- for name, dependent in self.dependents.items():
- del dependent.dependencies[self.name]
- dependent._add_dependency(replacement)
+ for name, dep_spec in self._dependents.items():
+ dependent = dep_spec.spec
+ deptypes = dep_spec.deptypes
+ del dependent._dependencies[self.name]
+ dependent._add_dependency(replacement, deptypes)
- for name, dep in self.dependencies.items():
- del dep.dependents[self.name]
- del self.dependencies[dep.name]
+ for name, dep_spec in self._dependencies.items():
+ del dep_spec.spec.dependents[self.name]
+ del self._dependencies[dep.name]
def _expand_virtual_packages(self):
"""Find virtual packages in this spec, replace them with providers,
@@ -1008,13 +1146,14 @@ class Spec(object):
a problem.
"""
# Make an index of stuff this spec already provides
+ # XXX(deptype): 'link' and 'run'?
self_index = ProviderIndex(self.traverse(), restrict=True)
changed = False
done = False
while not done:
done = True
-
+ # XXX(deptype): 'link' and 'run'?
for spec in list(self.traverse()):
replacement = None
if spec.virtual:
@@ -1054,24 +1193,26 @@ class Spec(object):
# If replacement is external then trim the dependencies
if replacement.external or replacement.external_module:
- if (spec.dependencies):
+ if (spec._dependencies):
changed = True
- spec.dependencies = DependencyMap()
- replacement.dependencies = DependencyMap()
+ spec._dependencies = DependencyMap()
+ replacement._dependencies = DependencyMap()
replacement.architecture = self.architecture
# TODO: could this and the stuff in _dup be cleaned up?
def feq(cfield, sfield):
return (not cfield) or (cfield == sfield)
- if replacement is spec or (feq(replacement.name, spec.name) and
- feq(replacement.versions, spec.versions) and
- feq(replacement.compiler, spec.compiler) and
- feq(replacement.architecture, spec.architecture) and
- feq(replacement.dependencies, spec.dependencies) and
- feq(replacement.variants, spec.variants) and
- feq(replacement.external, spec.external) and
- feq(replacement.external_module, spec.external_module)):
+ if replacement is spec or (
+ feq(replacement.name, spec.name) and
+ feq(replacement.versions, spec.versions) and
+ feq(replacement.compiler, spec.compiler) and
+ feq(replacement.architecture, spec.architecture) and
+ feq(replacement._dependencies, spec._dependencies) and
+ feq(replacement.variants, spec.variants) and
+ feq(replacement.external, spec.external) and
+ feq(replacement.external_module,
+ spec.external_module)):
continue
# Refine this spec to the candidate. This uses
# replace_with AND dup so that it can work in
@@ -1116,7 +1257,7 @@ class Spec(object):
changed = any(changes)
force = True
- for s in self.traverse():
+ for s in self.traverse(deptype_query=alldeps):
# After concretizing, assign namespaces to anything left.
# Note that this doesn't count as a "change". The repository
# configuration is constant throughout a spack run, and
@@ -1128,10 +1269,10 @@ class Spec(object):
if s.namespace is None:
s.namespace = spack.repo.repo_for_pkg(s.name).namespace
-
for s in self.traverse(root=False):
if s.external_module:
- compiler = spack.compilers.compiler_for_spec(s.compiler, s.architecture)
+ compiler = spack.compilers.compiler_for_spec(
+ s.compiler, s.architecture)
for mod in compiler.modules:
load_module(mod)
@@ -1146,7 +1287,7 @@ class Spec(object):
Only for internal use -- client code should use "concretize"
unless there is a need to force a spec to be concrete.
"""
- for s in self.traverse():
+ for s in self.traverse(deptype_query=alldeps):
s._normal = True
s._concrete = True
@@ -1159,6 +1300,13 @@ class Spec(object):
return clone
def flat_dependencies(self, **kwargs):
+ flat_deps = DependencyMap()
+ flat_deps_deptypes = self.flat_dependencies_with_deptype(**kwargs)
+ for name, depspec in flat_deps_deptypes.items():
+ flat_deps[name] = depspec.spec
+ return flat_deps
+
+ def flat_dependencies_with_deptype(self, **kwargs):
"""Return a DependencyMap containing all of this spec's
dependencies with their constraints merged.
@@ -1169,23 +1317,31 @@ class Spec(object):
returns them.
"""
copy = kwargs.get('copy', True)
+ deptype_query = kwargs.get('deptype_query')
flat_deps = DependencyMap()
try:
- for spec in self.traverse(root=False):
+ deptree = self.traverse_with_deptype(root=False,
+ deptype_query=deptype_query)
+ for depspec in deptree:
+ spec = depspec.spec
+ deptypes = depspec.deptypes
+
if spec.name not in flat_deps:
if copy:
- flat_deps[spec.name] = spec.copy(deps=False)
+ dep_spec = DependencySpec(spec.copy(deps=False),
+ deptypes)
else:
- flat_deps[spec.name] = spec
+ dep_spec = DependencySpec(spec, deptypes)
+ flat_deps[spec.name] = dep_spec
else:
- flat_deps[spec.name].constrain(spec)
+ flat_deps[spec.name].spec.constrain(spec)
if not copy:
- for dep in flat_deps.values():
- dep.dependencies.clear()
- dep.dependents.clear()
- self.dependencies.clear()
+ for depspec in flat_deps.values():
+ depspec.spec._dependencies.clear()
+ depspec.spec._dependents.clear()
+ self._dependencies.clear()
return flat_deps
@@ -1200,17 +1356,11 @@ class Spec(object):
"""Return DependencyMap that points to all the dependencies in this
spec."""
dm = DependencyMap()
+ # XXX(deptype): use a deptype kwarg.
for spec in self.traverse():
dm[spec.name] = spec
return dm
- def flatten(self):
- """Pull all dependencies up to the root (this spec).
- Merge constraints for dependencies with the same name, and if they
- conflict, throw an exception. """
- for dep in self.flat_dependencies(copy=False):
- self._add_dependency(dep)
-
def _evaluate_dependency_conditions(self, name):
"""Evaluate all the conditions on a dependency with this name.
@@ -1267,7 +1417,8 @@ class Spec(object):
elif required:
raise UnsatisfiableProviderSpecError(required[0], vdep)
- def _merge_dependency(self, dep, visited, spec_deps, provider_index):
+ def _merge_dependency(self, dep, deptypes, visited, spec_deps,
+ provider_index):
"""Merge the dependency into this spec.
This is the core of normalize(). There are some basic steps:
@@ -1294,7 +1445,9 @@ class Spec(object):
dep = provider
else:
index = ProviderIndex([dep], restrict=True)
- for vspec in (v for v in spec_deps.values() if v.virtual):
+ for vspec in (v.spec
+ for v in spec_deps.values()
+ if v.spec.virtual):
if index.providers_for(vspec):
vspec._replace_with(dep)
del spec_deps[vspec.name]
@@ -1307,25 +1460,25 @@ class Spec(object):
# If the spec isn't already in the set of dependencies, clone
# it from the package description.
if dep.name not in spec_deps:
- spec_deps[dep.name] = dep.copy()
+ spec_deps[dep.name] = DependencySpec(dep.copy(), deptypes)
changed = True
# Constrain package information with spec info
try:
- changed |= spec_deps[dep.name].constrain(dep)
+ changed |= spec_deps[dep.name].spec.constrain(dep)
except UnsatisfiableSpecError, e:
e.message = "Invalid spec: '%s'. "
e.message += "Package %s requires %s %s, but spec asked for %s"
- e.message %= (spec_deps[dep.name], dep.name, e.constraint_type,
- e.required, e.provided)
+ e.message %= (spec_deps[dep.name].spec, dep.name,
+ e.constraint_type, e.required, e.provided)
raise e
# Add merged spec to my deps and recurse
dependency = spec_deps[dep.name]
- if dep.name not in self.dependencies:
- self._add_dependency(dependency)
+ if dep.name not in self._dependencies:
+ self._add_dependency(dependency.spec, dependency.deptypes)
- changed |= dependency._normalize_helper(
+ changed |= dependency.spec._normalize_helper(
visited, spec_deps, provider_index)
return changed
@@ -1351,10 +1504,11 @@ class Spec(object):
for dep_name in pkg.dependencies:
# Do we depend on dep_name? If so pkg_dep is not None.
pkg_dep = self._evaluate_dependency_conditions(dep_name)
+ deptypes = pkg._deptypes[dep_name]
# If pkg_dep is a dependency, merge it.
if pkg_dep:
changed |= self._merge_dependency(
- pkg_dep, visited, spec_deps, provider_index)
+ pkg_dep, deptypes, visited, spec_deps, provider_index)
any_change |= changed
return any_change
@@ -1385,11 +1539,13 @@ class Spec(object):
# Ensure first that all packages & compilers in the DAG exist.
self.validate_names()
# Get all the dependencies into one DependencyMap
- spec_deps = self.flat_dependencies(copy=False)
+ spec_deps = self.flat_dependencies_with_deptype(
+ copy=False, deptype_query=alldeps)
# Initialize index of virtual dependency providers if
# concretize didn't pass us one already
- provider_index = ProviderIndex(spec_deps.values(), restrict=True)
+ provider_index = ProviderIndex(
+ [s.spec for s in spec_deps.values()], restrict=True)
# traverse the package DAG and fill out dependencies according
# to package files & their 'when' specs
@@ -1462,20 +1618,17 @@ class Spec(object):
other.variants[v])
# TODO: Check out the logic here
- if self.architecture is not None and other.architecture is not None:
- if self.architecture.platform is not None and other.architecture.platform is not None:
- if self.architecture.platform != other.architecture.platform:
- raise UnsatisfiableArchitectureSpecError(self.architecture,
- other.architecture)
- if self.architecture.platform_os is not None and other.architecture.platform_os is not None:
- if self.architecture.platform_os != other.architecture.platform_os:
- raise UnsatisfiableArchitectureSpecError(self.architecture,
- other.architecture)
- if self.architecture.target is not None and other.architecture.target is not None:
- if self.architecture.target != other.architecture.target:
- raise UnsatisfiableArchitectureSpecError(self.architecture,
- other.architecture)
-
+ sarch, oarch = self.architecture, other.architecture
+ if sarch is not None and oarch is not None:
+ if sarch.platform is not None and oarch.platform is not None:
+ if sarch.platform != oarch.platform:
+ raise UnsatisfiableArchitectureSpecError(sarch, oarch)
+ if sarch.platform_os is not None and oarch.platform_os is not None:
+ if sarch.platform_os != oarch.platform_os:
+ raise UnsatisfiableArchitectureSpecError(sarch, oarch)
+ if sarch.target is not None and oarch.target is not None:
+ if sarch.target != oarch.target:
+ raise UnsatisfiableArchitectureSpecError(sarch, oarch)
changed = False
if self.compiler is not None and other.compiler is not None:
@@ -1490,15 +1643,16 @@ class Spec(object):
changed |= self.compiler_flags.constrain(other.compiler_flags)
old = str(self.architecture)
- if self.architecture is None or other.architecture is None:
- self.architecture = self.architecture or other.architecture
+ sarch, oarch = self.architecture, other.architecture
+ if sarch is None or other.architecture is None:
+ self.architecture = sarch or oarch
else:
- if self.architecture.platform is None or other.architecture.platform is None:
- self.architecture.platform = self.architecture.platform or other.architecture.platform
- if self.architecture.platform_os is None or other.architecture.platform_os is None:
- self.architecture.platform_os = self.architecture.platform_os or other.architecture.platform_os
- if self.architecture.target is None or other.architecture.target is None:
- self.architecture.target = self.architecture.target or other.architecture.target
+ if sarch.platform is None or oarch.platform is None:
+ self.architecture.platform = sarch.platform or oarch.platform
+ if sarch.platform_os is None or oarch.platform_os is None:
+ sarch.platform_os = sarch.platform_os or oarch.platform_os
+ if sarch.target is None or oarch.target is None:
+ sarch.target = sarch.target or oarch.target
changed |= (str(self.architecture) != old)
if deps:
@@ -1510,7 +1664,7 @@ class Spec(object):
"""Apply constraints of other spec's dependencies to this spec."""
other = self._autospec(other)
- if not self.dependencies or not other.dependencies:
+ if not self._dependencies or not other._dependencies:
return False
# TODO: might want more detail than this, e.g. specific deps
@@ -1526,13 +1680,17 @@ class Spec(object):
# Update with additional constraints from other spec
for name in other.dep_difference(self):
- self._add_dependency(other[name].copy())
+ dep_spec_copy = other.get_dependency(name)
+ dep_copy = dep_spec_copy.spec
+ deptypes = dep_spec_copy.deptypes
+ self._add_dependency(dep_copy.copy(), deptypes)
changed = True
return changed
def common_dependencies(self, other):
"""Return names of dependencies that self an other have in common."""
+ # XXX(deptype): handle deptypes via deptype kwarg.
common = set(
s.name for s in self.traverse(root=False))
common.intersection_update(
@@ -1625,15 +1783,25 @@ class Spec(object):
# Architecture satisfaction is currently just string equality.
# If not strict, None means unconstrained.
- if self.architecture and other.architecture:
- if ((self.architecture.platform and other.architecture.platform and self.architecture.platform != other.architecture.platform) or
- (self.architecture.platform_os and other.architecture.platform_os and self.architecture.platform_os != other.architecture.platform_os) or
- (self.architecture.target and other.architecture.target and self.architecture.target != other.architecture.target)):
+ sarch, oarch = self.architecture, other.architecture
+ if sarch and oarch:
+ if ((sarch.platform and
+ oarch.platform and
+ sarch.platform != oarch.platform) or
+
+ (sarch.platform_os and
+ oarch.platform_os and
+ sarch.platform_os != oarch.platform_os) or
+
+ (sarch.target and
+ oarch.target and
+ sarch.target != oarch.target)):
return False
- elif strict and ((other.architecture and not self.architecture) or
- (other.architecture.platform and not self.architecture.platform) or
- (other.architecture.platform_os and not self.architecture.platform_os) or
- (other.architecture.target and not self.architecture.target)):
+
+ elif strict and ((oarch and not sarch) or
+ (oarch.platform and not sarch.platform) or
+ (oarch.platform_os and not sarch.platform_os) or
+ (oarch.target and not sarch.target)):
return False
if not self.compiler_flags.satisfies(
@@ -1657,13 +1825,14 @@ class Spec(object):
other = self._autospec(other)
if strict:
- if other.dependencies and not self.dependencies:
+ if other._dependencies and not self._dependencies:
return False
- if not all(dep in self.dependencies for dep in other.dependencies):
+ if not all(dep in self._dependencies
+ for dep in other._dependencies):
return False
- elif not self.dependencies or not other.dependencies:
+ elif not self._dependencies or not other._dependencies:
# if either spec doesn't restrict dependencies then both are
# compatible.
return True
@@ -1714,11 +1883,16 @@ class Spec(object):
# We don't count dependencies as changes here
changed = True
if hasattr(self, 'name'):
- changed = (self.name != other.name and self.versions != other.versions and \
- self.architecture != other.architecture and self.compiler != other.compiler and \
- self.variants != other.variants and self._normal != other._normal and \
- self.concrete != other.concrete and self.external != other.external and \
- self.external_module != other.external_module and self.compiler_flags != other.compiler_flags)
+ changed = (self.name != other.name and
+ self.versions != other.versions and
+ self.architecture != other.architecture and
+ self.compiler != other.compiler and
+ self.variants != other.variants and
+ self._normal != other._normal and
+ self.concrete != other.concrete and
+ self.external != other.external and
+ self.external_module != other.external_module and
+ self.compiler_flags != other.compiler_flags)
# Local node attributes get copied first.
self.name = other.name
@@ -1726,8 +1900,8 @@ class Spec(object):
self.architecture = other.architecture
self.compiler = other.compiler.copy() if other.compiler else None
if kwargs.get('cleardeps', True):
- self.dependents = DependencyMap()
- self.dependencies = DependencyMap()
+ self._dependents = DependencyMap()
+ self._dependencies = DependencyMap()
self.compiler_flags = other.compiler_flags.copy()
self.variants = other.variants.copy()
self.variants.spec = self
@@ -1739,15 +1913,30 @@ class Spec(object):
# If we copy dependencies, preserve DAG structure in the new spec
if kwargs.get('deps', True):
# This copies the deps from other using _dup(deps=False)
- new_nodes = other.flat_dependencies()
+ # XXX(deptype): We can keep different instances of specs here iff
+ # it is only a 'build' dependency (from its parent).
+ # All other instances must be shared (due to symbol
+ # and PATH contention). These should probably search
+ # for any existing installation which can satisfy the
+ # build and latch onto that because if 3 things need
+ # the same build dependency and it is *not*
+ # available, we only want to build it once.
+ new_nodes = other.flat_dependencies(deptype_query=alldeps)
new_nodes[self.name] = self
- # Hook everything up properly here by traversing.
- for spec in other.traverse(cover='nodes'):
- parent = new_nodes[spec.name]
- for child in spec.dependencies:
- if child not in parent.dependencies:
- parent._add_dependency(new_nodes[child])
+ stack = [other]
+ while stack:
+ cur_spec = stack.pop(0)
+ new_spec = new_nodes[cur_spec.name]
+
+ for depspec in cur_spec._dependencies.values():
+ stack.append(depspec.spec)
+
+ # XXX(deptype): add any new deptypes that may have appeared
+ # here.
+ if depspec.spec.name not in new_spec._dependencies:
+ new_spec._add_dependency(
+ new_nodes[depspec.spec.name], depspec.deptypes)
# Since we preserved structure, we can copy _normal safely.
self._normal = other._normal
@@ -1790,7 +1979,7 @@ class Spec(object):
raise KeyError("No spec with name %s in %s" % (name, self))
def __contains__(self, spec):
- """True if this spec satisfis the provided spec, or if any dependency
+ """True if this spec satisfies the provided spec, or if any dependency
does. If the spec has no name, then we parse this one first.
"""
spec = self._autospec(spec)
@@ -1814,13 +2003,13 @@ class Spec(object):
if self.ne_node(other):
return False
- if len(self.dependencies) != len(other.dependencies):
+ if len(self._dependencies) != len(other._dependencies):
return False
- ssorted = [self.dependencies[name]
- for name in sorted(self.dependencies)]
- osorted = [other.dependencies[name]
- for name in sorted(other.dependencies)]
+ ssorted = [self._dependencies[name].spec
+ for name in sorted(self._dependencies)]
+ osorted = [other._dependencies[name].spec
+ for name in sorted(other._dependencies)]
for s, o in zip(ssorted, osorted):
visited_s = id(s) in vs
@@ -1858,7 +2047,6 @@ class Spec(object):
self.compiler,
self.compiler_flags)
-
def eq_node(self, other):
"""Equality with another spec, not including dependencies."""
return self._cmp_node() == other._cmp_node()
@@ -1874,9 +2062,10 @@ class Spec(object):
1. A tuple describing this node in the DAG.
2. The hash of each of this node's dependencies' cmp_keys.
"""
+ dep_dict = self.dependencies_dict(deptype=('link', 'run'))
return self._cmp_node() + (
- tuple(hash(self.dependencies[name])
- for name in sorted(self.dependencies)),)
+ tuple(hash(dep_dict[name])
+ for name in sorted(dep_dict)),)
def colorized(self):
return colorize_spec(self)
@@ -2053,41 +2242,39 @@ class Spec(object):
def dep_string(self):
return ''.join("^" + dep.format() for dep in self.sorted_deps())
-
def __cmp__(self, other):
- #Package name sort order is not configurable, always goes alphabetical
+ # Package name sort order is not configurable, always goes alphabetical
if self.name != other.name:
return cmp(self.name, other.name)
- #Package version is second in compare order
+ # Package version is second in compare order
pkgname = self.name
if self.versions != other.versions:
- return spack.pkgsort.version_compare(pkgname,
- self.versions, other.versions)
+ return spack.pkgsort.version_compare(
+ pkgname, self.versions, other.versions)
- #Compiler is third
+ # Compiler is third
if self.compiler != other.compiler:
- return spack.pkgsort.compiler_compare(pkgname,
- self.compiler, other.compiler)
+ return spack.pkgsort.compiler_compare(
+ pkgname, self.compiler, other.compiler)
- #Variants
+ # Variants
if self.variants != other.variants:
- return spack.pkgsort.variant_compare(pkgname,
- self.variants, other.variants)
+ return spack.pkgsort.variant_compare(
+ pkgname, self.variants, other.variants)
- #Target
+ # Target
if self.architecture != other.architecture:
- return spack.pkgsort.architecture_compare(pkgname,
- self.architecture, other.architecture)
+ return spack.pkgsort.architecture_compare(
+ pkgname, self.architecture, other.architecture)
- #Dependency is not configurable
- if self.dependencies != other.dependencies:
- return -1 if self.dependencies < other.dependencies else 1
+ # Dependency is not configurable
+ if self._dependencies != other._dependencies:
+ return -1 if self._dependencies < other._dependencies else 1
- #Equal specs
+ # Equal specs
return 0
-
def __str__(self):
return self.format() + self.dep_string()
@@ -2101,12 +2288,14 @@ class Spec(object):
indent = kwargs.pop('indent', 0)
fmt = kwargs.pop('format', '$_$@$%@+$+$=')
prefix = kwargs.pop('prefix', None)
+ deptypes = kwargs.pop('deptypes', ('build', 'link'))
check_kwargs(kwargs, self.tree)
out = ""
cur_id = 0
ids = {}
- for d, node in self.traverse(order='pre', cover=cover, depth=True):
+ for d, node in self.traverse(
+ order='pre', cover=cover, depth=True, deptypes=deptypes):
if prefix is not None:
out += prefix(node)
out += " " * indent
@@ -2160,8 +2349,8 @@ class SpecLexer(spack.parse.Lexer):
# Lexer is always the same for every parser.
_lexer = SpecLexer()
-class SpecParser(spack.parse.Parser):
+class SpecParser(spack.parse.Parser):
def __init__(self):
super(SpecParser, self).__init__(_lexer)
self.previous = None
@@ -2196,10 +2385,13 @@ class SpecParser(spack.parse.Parser):
specs.append(self.spec(None))
self.previous = None
if self.accept(HASH):
- specs[-1]._add_dependency(self.spec_by_hash())
+ dep = self.spec_by_hash()
else:
self.expect(ID)
- specs[-1]._add_dependency(self.spec(self.token.value))
+ dep = self.spec(self.token.value)
+ # XXX(deptype): default deptypes
+ def_deptypes = ('build', 'link')
+ specs[-1]._add_dependency(dep, def_deptypes)
else:
# Attempt to construct an anonymous spec, but check that
@@ -2211,8 +2403,8 @@ class SpecParser(spack.parse.Parser):
except spack.parse.ParseError, e:
raise SpecParseError(e)
-
- # If the spec has an os or a target and no platform, give it the default platform
+ # If the spec has an os or a target and no platform, give it
+ # the default platform
for spec in specs:
for s in spec.traverse():
if s.architecture.os_string or s.architecture.target_string:
@@ -2263,8 +2455,8 @@ class SpecParser(spack.parse.Parser):
spec.external = None
spec.external_module = None
spec.compiler_flags = FlagMap(spec)
- spec.dependents = DependencyMap()
- spec.dependencies = DependencyMap()
+ spec._dependents = DependencyMap()
+ spec._dependencies = DependencyMap()
spec.namespace = spec_namespace
spec._hash = None
diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py
index fb91f24721..9f7da46a1a 100644
--- a/lib/spack/spack/test/__init__.py
+++ b/lib/spack/spack/test/__init__.py
@@ -32,15 +32,16 @@ from llnl.util.tty.colify import colify
from spack.test.tally_plugin import Tally
"""Names of tests to be included in Spack's test suite"""
-test_names = ['architecture', 'versions', 'url_parse', 'url_substitution', 'packages', 'stage',
- 'spec_syntax', 'spec_semantics', 'spec_dag', 'concretize',
- 'multimethod', 'install', 'package_sanity', 'config',
- 'directory_layout', 'pattern', 'python_version', 'git_fetch',
- 'svn_fetch', 'hg_fetch', 'mirror', 'modules', 'url_extrapolate',
- 'cc', 'link_tree', 'spec_yaml', 'optional_deps',
- 'make_executable', 'configure_guess', 'lock', 'database',
- 'namespace_trie', 'yaml', 'sbang', 'environment', 'cmd.find',
- 'cmd.uninstall', 'cmd.test_install', 'cmd.test_compiler_cmd']
+test_names = [
+ 'architecture', 'versions', 'url_parse', 'url_substitution', 'packages',
+ 'stage', 'spec_syntax', 'spec_semantics', 'spec_dag', 'concretize',
+ 'multimethod', 'install', 'package_sanity', 'config', 'directory_layout',
+ 'pattern', 'python_version', 'git_fetch', 'svn_fetch', 'hg_fetch',
+ 'mirror', 'modules', 'url_extrapolate', 'cc', 'link_tree', 'spec_yaml',
+ 'optional_deps', 'make_executable', 'build_system_guess', 'lock',
+ 'database', 'namespace_trie', 'yaml', 'sbang', 'environment', 'cmd.find',
+ 'cmd.uninstall', 'cmd.test_install', 'cmd.test_compiler_cmd'
+]
def list_tests():
diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py
index ae3f08deed..09bdb021af 100644
--- a/lib/spack/spack/test/architecture.py
+++ b/lib/spack/spack/test/architecture.py
@@ -1,7 +1,31 @@
+##############################################################################
+# 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
+##############################################################################
""" Test checks if the architecture class is created correctly and also that
the functions are looking for the correct architecture name
"""
-import unittest
+import itertools
import os
import platform as py_platform
import spack
@@ -14,9 +38,8 @@ from spack.platforms.darwin import Darwin
from spack.test.mock_packages_test import *
-#class ArchitectureTest(unittest.TestCase):
-class ArchitectureTest(MockPackagesTest):
+class ArchitectureTest(MockPackagesTest):
def setUp(self):
super(ArchitectureTest, self).setUp()
self.platform = spack.architecture.platform()
@@ -36,24 +59,22 @@ class ArchitectureTest(MockPackagesTest):
self.assertEqual(arch, new_arch)
- self.assertTrue( isinstance(arch, spack.architecture.Arch) )
- self.assertTrue( isinstance(arch.platform, spack.architecture.Platform) )
- self.assertTrue( isinstance(arch.platform_os,
- spack.architecture.OperatingSystem) )
- self.assertTrue( isinstance(arch.target,
- spack.architecture.Target) )
- self.assertTrue( isinstance(new_arch, spack.architecture.Arch) )
- self.assertTrue( isinstance(new_arch.platform,
- spack.architecture.Platform) )
- self.assertTrue( isinstance(new_arch.platform_os,
- spack.architecture.OperatingSystem) )
- self.assertTrue( isinstance(new_arch.target,
- spack.architecture.Target) )
-
+ self.assertTrue(isinstance(arch, spack.architecture.Arch))
+ self.assertTrue(isinstance(arch.platform, spack.architecture.Platform))
+ self.assertTrue(isinstance(arch.platform_os,
+ spack.architecture.OperatingSystem))
+ self.assertTrue(isinstance(arch.target,
+ spack.architecture.Target))
+ self.assertTrue(isinstance(new_arch, spack.architecture.Arch))
+ self.assertTrue(isinstance(new_arch.platform,
+ spack.architecture.Platform))
+ self.assertTrue(isinstance(new_arch.platform_os,
+ spack.architecture.OperatingSystem))
+ self.assertTrue(isinstance(new_arch.target,
+ spack.architecture.Target))
def test_platform(self):
output_platform_class = spack.architecture.platform()
- my_arch_class = None
if os.path.exists('/opt/cray/craype'):
my_platform_class = CrayXc()
elif os.path.exists('/bgsys'):
@@ -91,7 +112,7 @@ class ArchitectureTest(MockPackagesTest):
default_os = self.platform.operating_system("default_os")
default_target = self.platform.target("default_target")
- default_spec = Spec("libelf") # default is no args
+ default_spec = Spec("libelf") # default is no args
default_spec.concretize()
self.assertEqual(default_os, default_spec.architecture.platform_os)
self.assertEqual(default_target, default_spec.architecture.target)
@@ -107,10 +128,11 @@ class ArchitectureTest(MockPackagesTest):
combinations = itertools.product(os_list, target_list)
results = []
for arch in combinations:
- o,t = arch
+ o, t = arch
spec = Spec("libelf os=%s target=%s" % (o, t))
spec.concretize()
- results.append(spec.architecture.platform_os == self.platform.operating_system(o))
+ results.append(spec.architecture.platform_os ==
+ self.platform.operating_system(o))
results.append(spec.architecture.target == self.platform.target(t))
res = all(results)
diff --git a/lib/spack/spack/test/configure_guess.py b/lib/spack/spack/test/build_system_guess.py
index bad3673e7a..e728a47cf4 100644
--- a/lib/spack/spack/test/configure_guess.py
+++ b/lib/spack/spack/test/build_system_guess.py
@@ -28,14 +28,14 @@ import tempfile
import unittest
from llnl.util.filesystem import *
-from spack.cmd.create import ConfigureGuesser
+from spack.cmd.create import BuildSystemGuesser
from spack.stage import Stage
from spack.test.mock_packages_test import *
from spack.util.executable import which
class InstallTest(unittest.TestCase):
- """Tests the configure guesser in spack create"""
+ """Tests the build system guesser in spack create"""
def setUp(self):
self.tar = which('tar')
@@ -44,12 +44,10 @@ class InstallTest(unittest.TestCase):
os.chdir(self.tmpdir)
self.stage = None
-
def tearDown(self):
shutil.rmtree(self.tmpdir, ignore_errors=True)
os.chdir(self.orig_dir)
-
def check_archive(self, filename, system):
mkdirp('archive')
touch(join_path('archive', filename))
@@ -60,24 +58,24 @@ class InstallTest(unittest.TestCase):
with Stage(url) as stage:
stage.fetch()
- guesser = ConfigureGuesser()
- guesser(stage)
+ guesser = BuildSystemGuesser()
+ guesser(stage, url)
self.assertEqual(system, guesser.build_system)
-
- def test_python(self):
- self.check_archive('setup.py', 'python')
-
-
def test_autotools(self):
self.check_archive('configure', 'autotools')
-
def test_cmake(self):
self.check_archive('CMakeLists.txt', 'cmake')
+ def test_scons(self):
+ self.check_archive('SConstruct', 'scons')
- def test_unknown(self):
- self.check_archive('foobar', 'unknown')
+ def test_python(self):
+ self.check_archive('setup.py', 'python')
+ def test_R(self):
+ self.check_archive('NAMESPACE', 'R')
+ def test_unknown(self):
+ self.check_archive('foobar', 'unknown')
diff --git a/lib/spack/spack/test/cmd/test_install.py b/lib/spack/spack/test/cmd/test_install.py
index d17e013ed2..a94d3c8bba 100644
--- a/lib/spack/spack/test/cmd/test_install.py
+++ b/lib/spack/spack/test/cmd/test_install.py
@@ -58,16 +58,39 @@ test_install = __import__("spack.cmd.test-install", fromlist=['test_install'])
class MockSpec(object):
def __init__(self, name, version, hashStr=None):
- self.dependencies = {}
+ self._dependencies = {}
self.name = name
self.version = version
self.hash = hashStr if hashStr else hash((name, version))
+ def _deptype_norm(self, deptype):
+ if deptype is None:
+ return spack.alldeps
+ # Force deptype to be a tuple so that we can do set intersections.
+ if isinstance(deptype, str):
+ return (deptype,)
+ return deptype
+
+ def _find_deps(self, where, deptype):
+ deptype = self._deptype_norm(deptype)
+
+ return [dep.spec
+ for dep in where.values()
+ if deptype and any(d in deptype for d in dep.deptypes)]
+
+ def dependencies(self, deptype=None):
+ return self._find_deps(self._dependencies, deptype)
+
+ def dependents(self, deptype=None):
+ return self._find_deps(self._dependents, deptype)
+
def traverse(self, order=None):
- for _, spec in self.dependencies.items():
- yield spec
+ for _, spec in self._dependencies.items():
+ yield spec.spec
yield self
- #allDeps = itertools.chain.from_iterable(i.traverse() for i in self.dependencies.itervalues())
+ #from_iterable = itertools.chain.from_iterable
+ #allDeps = from_iterable(i.traverse()
+ # for i in self.dependencies())
#return set(itertools.chain([self], allDeps))
def dag_hash(self):
@@ -104,7 +127,7 @@ def mock_fetch_log(path):
specX = MockSpec('X', "1.2.0")
specY = MockSpec('Y', "2.3.8")
-specX.dependencies['Y'] = specY
+specX._dependencies['Y'] = spack.DependencySpec(specY, spack.alldeps)
pkgX = MockPackage(specX, 'logX')
pkgY = MockPackage(specY, 'logY')
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index ce02b08bc3..ae3ceecfc8 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -197,32 +197,36 @@ class ConcretizeTest(MockPackagesTest):
def test_virtual_is_fully_expanded_for_callpath(self):
# force dependence on fake "zmpi" by asking for MPI 10.0
spec = Spec('callpath ^mpi@10.0')
- self.assertTrue('mpi' in spec.dependencies)
+ self.assertTrue('mpi' in spec._dependencies)
self.assertFalse('fake' in spec)
spec.concretize()
- self.assertTrue('zmpi' in spec.dependencies)
- self.assertTrue(all(not 'mpi' in d.dependencies for d in spec.traverse()))
+ self.assertTrue('zmpi' in spec._dependencies)
+ self.assertTrue(all('mpi' not in d._dependencies
+ for d in spec.traverse()))
self.assertTrue('zmpi' in spec)
self.assertTrue('mpi' in spec)
- self.assertTrue('fake' in spec.dependencies['zmpi'])
+ self.assertTrue('fake' in spec._dependencies['zmpi'].spec)
def test_virtual_is_fully_expanded_for_mpileaks(self):
spec = Spec('mpileaks ^mpi@10.0')
- self.assertTrue('mpi' in spec.dependencies)
+ self.assertTrue('mpi' in spec._dependencies)
self.assertFalse('fake' in spec)
spec.concretize()
- self.assertTrue('zmpi' in spec.dependencies)
- self.assertTrue('callpath' in spec.dependencies)
- self.assertTrue('zmpi' in spec.dependencies['callpath'].dependencies)
- self.assertTrue('fake' in spec.dependencies['callpath'].dependencies['zmpi'].dependencies)
+ self.assertTrue('zmpi' in spec._dependencies)
+ self.assertTrue('callpath' in spec._dependencies)
+ self.assertTrue('zmpi' in spec._dependencies['callpath'].
+ spec._dependencies)
+ self.assertTrue('fake' in spec._dependencies['callpath'].
+ spec._dependencies['zmpi'].
+ spec._dependencies)
- self.assertTrue(all(not 'mpi' in d.dependencies for d in spec.traverse()))
+ self.assertTrue(all(not 'mpi' in d._dependencies for d in spec.traverse()))
self.assertTrue('zmpi' in spec)
self.assertTrue('mpi' in spec)
diff --git a/lib/spack/spack/test/mock_packages_test.py b/lib/spack/spack/test/mock_packages_test.py
index e1acc32cb7..9d96622a6e 100644
--- a/lib/spack/spack/test/mock_packages_test.py
+++ b/lib/spack/spack/test/mock_packages_test.py
@@ -84,15 +84,6 @@ compilers:
modules: 'None'
- compiler:
spec: clang@3.3
- operating_system: redhat6
- paths:
- cc: /path/to/clang
- cxx: /path/to/clang++
- f77: None
- fc: None
- modules: 'None'
-- compiler:
- spec: clang@3.3
operating_system: yosemite
paths:
cc: /path/to/clang
@@ -124,15 +115,6 @@ compilers:
cxx: /path/to/g++
f77: /path/to/gfortran
fc: /path/to/gfortran
- operating_system: redhat6
- spec: gcc@4.5.0
- modules: 'None'
-- compiler:
- paths:
- cc: /path/to/gcc
- cxx: /path/to/g++
- f77: /path/to/gfortran
- fc: /path/to/gfortran
operating_system: yosemite
spec: gcc@4.5.0
modules: 'None'
@@ -209,8 +191,7 @@ class MockPackagesTest(unittest.TestCase):
# restore later.
self.saved_deps = {}
-
- def set_pkg_dep(self, pkg_name, spec):
+ def set_pkg_dep(self, pkg_name, spec, deptypes=spack.alldeps):
"""Alters dependence information for a package.
Adds a dependency on <spec> to pkg.
@@ -224,7 +205,9 @@ class MockPackagesTest(unittest.TestCase):
self.saved_deps[pkg_name] = (pkg, pkg.dependencies.copy())
# Change dep spec
- pkg.dependencies[spec.name] = { Spec(pkg_name) : spec }
+ # XXX(deptype): handle deptypes.
+ pkg.dependencies[spec.name] = {Spec(pkg_name): spec}
+ pkg._deptypes[spec.name] = set(deptypes)
def cleanmock(self):
@@ -234,6 +217,7 @@ class MockPackagesTest(unittest.TestCase):
shutil.rmtree(self.temp_config, ignore_errors=True)
spack.config.clear_config_caches()
+ # XXX(deptype): handle deptypes.
# Restore dependency changes that happened during the test
for pkg_name, (pkg, deps) in self.saved_deps.items():
pkg.dependencies.clear()
diff --git a/lib/spack/spack/test/mock_repo.py b/lib/spack/spack/test/mock_repo.py
index a8098b8eec..386af282e7 100644
--- a/lib/spack/spack/test/mock_repo.py
+++ b/lib/spack/spack/test/mock_repo.py
@@ -103,6 +103,8 @@ class MockGitRepo(MockVCSRepo):
def __init__(self):
super(MockGitRepo, self).__init__('mock-git-stage', 'mock-git-repo')
+ self.url = 'file://' + self.path
+
with working_dir(self.path):
git('init')
@@ -140,8 +142,6 @@ class MockGitRepo(MockVCSRepo):
self.r1 = self.rev_hash(self.branch)
self.r1_file = self.branch_file
- self.url = self.path
-
def rev_hash(self, rev):
return git('rev-parse', rev, output=str).strip()
diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py
index c56c70b1fe..972e79aa20 100644
--- a/lib/spack/spack/test/spec_dag.py
+++ b/lib/spack/spack/test/spec_dag.py
@@ -148,10 +148,12 @@ class SpecDagTest(MockPackagesTest):
# Normalize then add conflicting constraints to the DAG (this is an
# extremely unlikely scenario, but we test for it anyway)
mpileaks.normalize()
- mpileaks.dependencies['mpich'] = Spec('mpich@1.0')
- mpileaks.dependencies['callpath'].dependencies['mpich'] = Spec('mpich@2.0')
+ mpileaks._dependencies['mpich'].spec = Spec('mpich@1.0')
+ mpileaks._dependencies['callpath']. \
+ spec._dependencies['mpich'].spec = Spec('mpich@2.0')
- self.assertRaises(spack.spec.InconsistentSpecError, mpileaks.flatten)
+ self.assertRaises(spack.spec.InconsistentSpecError,
+ lambda: mpileaks.flat_dependencies(copy=False))
def test_normalize_twice(self):
@@ -197,15 +199,17 @@ class SpecDagTest(MockPackagesTest):
def check_links(self, spec_to_check):
for spec in spec_to_check.traverse():
- for dependent in spec.dependents.values():
+ for dependent in spec.dependents():
self.assertTrue(
- spec.name in dependent.dependencies,
- "%s not in dependencies of %s" % (spec.name, dependent.name))
+ spec.name in dependent.dependencies_dict(),
+ "%s not in dependencies of %s" %
+ (spec.name, dependent.name))
- for dependency in spec.dependencies.values():
+ for dependency in spec.dependencies():
self.assertTrue(
- spec.name in dependency.dependents,
- "%s not in dependents of %s" % (spec.name, dependency.name))
+ spec.name in dependency.dependents_dict(),
+ "%s not in dependents of %s" %
+ (spec.name, dependency.name))
def test_dependents_and_dependencies_are_correct(self):
@@ -442,3 +446,69 @@ class SpecDagTest(MockPackagesTest):
orig_ids = set(id(s) for s in orig.traverse())
copy_ids = set(id(s) for s in copy.traverse())
self.assertFalse(orig_ids.intersection(copy_ids))
+
+ """
+ Here is the graph with deptypes labeled (assume all packages have a 'dt'
+ prefix). Arrows are marked with the deptypes ('b' for 'build', 'l' for
+ 'link', 'r' for 'run').
+
+ use -bl-> top
+
+ top -b-> build1
+ top -bl-> link1
+ top -r-> run1
+
+ build1 -b-> build2
+ build1 -bl-> link2
+ build1 -r-> run2
+
+ link1 -bl-> link3
+
+ run1 -bl-> link5
+ run1 -r-> run3
+
+ link3 -b-> build2
+ link3 -bl-> link4
+
+ run3 -b-> build3
+ """
+ def test_deptype_traversal(self):
+ dag = Spec('dtuse')
+ dag.normalize()
+
+ names = ['dtuse', 'dttop', 'dtlink1', 'dtlink3', 'dtlink4',
+ 'dtrun1', 'dtlink5', 'dtrun3']
+
+ traversal = dag.traverse()
+ self.assertEqual([x.name for x in traversal], names)
+
+ def test_deptype_traversal_with_builddeps(self):
+ dag = Spec('dttop')
+ dag.normalize()
+
+ names = ['dttop', 'dtbuild1', 'dtlink2', 'dtrun2', 'dtlink1',
+ 'dtlink3', 'dtlink4', 'dtrun1', 'dtlink5', 'dtrun3']
+
+ traversal = dag.traverse()
+ self.assertEqual([x.name for x in traversal], names)
+
+ def test_deptype_traversal_full(self):
+ dag = Spec('dttop')
+ dag.normalize()
+
+ names = ['dttop', 'dtbuild1', 'dtbuild2', 'dtlink2', 'dtrun2',
+ 'dtlink1', 'dtlink3', 'dtlink4', 'dtrun1', 'dtlink5',
+ 'dtrun3', 'dtbuild3']
+
+ traversal = dag.traverse(deptype_query=spack.alldeps)
+ self.assertEqual([x.name for x in traversal], names)
+
+ def test_deptype_traversal_pythonpath(self):
+ dag = Spec('dttop')
+ dag.normalize()
+
+ names = ['dttop', 'dtbuild1', 'dtrun2', 'dtlink1', 'dtrun1',
+ 'dtrun3']
+
+ traversal = dag.traverse(deptype=spack.nolink, deptype_query='run')
+ self.assertEqual([x.name for x in traversal], names)
diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py
index 6d8c3ac67c..d3e3bf1383 100644
--- a/lib/spack/spack/test/stage.py
+++ b/lib/spack/spack/test/stage.py
@@ -35,8 +35,8 @@ from llnl.util.filesystem import *
from spack.stage import Stage
from spack.util.executable import which
-test_files_dir = join_path(spack.stage_path, '.test')
-test_tmp_path = join_path(test_files_dir, 'tmp')
+test_files_dir = os.path.realpath(join_path(spack.stage_path, '.test'))
+test_tmp_path = os.path.realpath(join_path(test_files_dir, 'tmp'))
archive_dir = 'test-files'
archive_name = archive_dir + '.tar.gz'
diff --git a/lib/spack/spack/test/versions.py b/lib/spack/spack/test/versions.py
index 4624f901c8..41d72e7c34 100644
--- a/lib/spack/spack/test/versions.py
+++ b/lib/spack/spack/test/versions.py
@@ -92,6 +92,9 @@ class VersionsTest(unittest.TestCase):
self.assert_ver_eq('1.0', '1.0')
self.assert_ver_lt('1.0', '2.0')
self.assert_ver_gt('2.0', '1.0')
+ self.assert_ver_eq('develop', 'develop')
+ self.assert_ver_lt('1.0', 'develop')
+ self.assert_ver_gt('develop', '1.0')
def test_three_segments(self):
self.assert_ver_eq('2.0.1', '2.0.1')
@@ -389,3 +392,39 @@ class VersionsTest(unittest.TestCase):
self.assertEqual(v.dotted, '1.2.3')
self.assertEqual(v.dashed, '1-2-3')
self.assertEqual(v.underscored, '1_2_3')
+
+ def test_repr_and_str(self):
+
+ def check_repr_and_str(vrs):
+ a = Version(vrs)
+ self.assertEqual(repr(a), 'Version(\'' + vrs + '\')')
+ b = eval(repr(a))
+ self.assertEqual(a, b)
+ self.assertEqual(str(a), vrs)
+ self.assertEqual(str(a), str(b))
+
+ check_repr_and_str('1.2.3')
+ check_repr_and_str('R2016a')
+ check_repr_and_str('R2016a.2-3_4')
+
+ def test_get_item(self):
+ a = Version('0.1_2-3')
+ self.assertTrue(isinstance(a[1], int))
+ # Test slicing
+ b = a[0:2]
+ self.assertTrue(isinstance(b, Version))
+ self.assertEqual(b, Version('0.1'))
+ self.assertEqual(repr(b), 'Version(\'0.1\')')
+ self.assertEqual(str(b), '0.1')
+ b = a[0:3]
+ self.assertTrue(isinstance(b, Version))
+ self.assertEqual(b, Version('0.1_2'))
+ self.assertEqual(repr(b), 'Version(\'0.1_2\')')
+ self.assertEqual(str(b), '0.1_2')
+ b = a[1:]
+ self.assertTrue(isinstance(b, Version))
+ self.assertEqual(b, Version('1_2-3'))
+ self.assertEqual(repr(b), 'Version(\'1_2-3\')')
+ self.assertEqual(str(b), '1_2-3')
+ # Raise TypeError on tuples
+ self.assertRaises(TypeError, b.__getitem__, 1, 2)
diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py
index 858d581472..6f6c83b3d8 100644
--- a/lib/spack/spack/version.py
+++ b/lib/spack/spack/version.py
@@ -44,6 +44,7 @@ be called on any of the types::
concrete
"""
import re
+import numbers
from bisect import bisect_left
from functools import wraps
@@ -194,10 +195,24 @@ class Version(object):
return iter(self.version)
def __getitem__(self, idx):
- return tuple(self.version[idx])
+ cls = type(self)
+ if isinstance(idx, numbers.Integral):
+ return self.version[idx]
+ elif isinstance(idx, slice):
+ # Currently len(self.separators) == len(self.version) - 1
+ extendend_separators = self.separators + ('',)
+ string_arg = []
+ for token, sep in zip(self.version, extendend_separators)[idx]:
+ string_arg.append(str(token))
+ string_arg.append(str(sep))
+ string_arg.pop() # We don't need the last separator
+ string_arg = ''.join(string_arg)
+ return cls(string_arg)
+ message = '{cls.__name__} indices must be integers'
+ raise TypeError(message.format(cls=cls))
def __repr__(self):
- return self.string
+ return 'Version(' + repr(self.string) + ')'
def __str__(self):
return self.string
@@ -221,6 +236,14 @@ class Version(object):
if self.version == other.version:
return False
+ # dev is __gt__ than anything but itself.
+ if other.string == 'develop':
+ return True
+
+ # If lhs is dev then it can't be < than anything
+ if self.string == 'develop':
+ return False
+
for a, b in zip(self.version, other.version):
if a == b:
continue