Tutorial setup

If you have not done the prior sections, you’ll need to start the docker image:

docker run -it ghcr.io/spack/tutorial:sc24

and then set Spack up like this:

git clone --depth=100 --branch=releases/v0.23 https://github.com/spack/spack
. spack/share/spack/setup-env.sh
spack tutorial -y
spack bootstrap now
spack compiler find

See the Basic Installation Tutorial for full details on setup. For more help, join us in the #tutorial channel on Slack – get an invitation at slack.spack.io

Scripting with Spack

This tutorial introduces advanced Spack features related to scripting. Specifically, we will show you how to write scripts using spack find and spack python. Earlier sections of the tutorial demonstrated using spack find to list and search installed packages. The spack python command gives you access to all of Spack’s internal APIs, allowing you to write more complex queries, for example.

Since Spack has an extensive API, we’ll only scratch the surface here. We’ll give you enough information to start writing your own scripts and to find what you need, with a little digging.

Scripting with spack find

So far, the output we’ve seen from spack find has been for human consumption. But you can take advantage of some advanced options of the command to generate machine-readable output suitable for piping to a script.

spack find --format

The main job of spack find is to show the user a bunch of concrete specs that correspond to installed packages. By default, we display them with some default attributes, like the @version suffix you’re used to seeing in the output.

The --format argument allows you to display the specs however you choose, using custom format strings. Format strings let you specify the names of particular parts of the specs you want displayed. Let’s see the first option in action.

Suppose you only want to display the name, version, and first ten (10) characters of the hash for every package installed in your Spack instance. You can generate that output with the following command:

$ spack find --format "{name} {version} {hash:10}"
adept-utils 1.0.1 q5xl3h77oc		       gmp 6.3.0 5webgyazi3		   macsio 1.1 o4zotmuc52		   py-pythran 0.16.1 3bcgcevke7
autoconf 2.72 vgucajy3lk		       hdf5 1.14.5 ckoh67fpzb		   meson 1.5.1 xfqgelb63r		   py-scipy 1.14.1 g53zrs6vom
autoconf 2.72 yfxrc66azg		       hdf5 1.14.5 6ddnwvambf		   meson 1.5.1 tp5miymsew		   py-setuptools 69.2.0 snvbpkxkqt
autoconf-archive 2023.02.20 6s34ic3cja	       hdf5 1.14.5 tgtmflo4s6		   mpc 1.3.1 qoauga4bfx			   py-setuptools 69.2.0 wbbopiponk
automake 1.16.5 awgfaon3ds		       hwloc 2.11.1 fwtuzgpmp2		   mpfr 4.2.1 mdfsfjykvy		   py-wheel 0.41.2 pxniiekemg
automake 1.16.5 e4u6ql7bme		       hwloc 2.11.1 2fvcggi3gq		   mpich 4.2.3 cqazuix44i		   py-wheel 0.41.2 gho4mpquga
axl 0.9.0 z6q2ovzd5u			       intel-tbb 2021.12.0 u5vh5hcz3r	   mpich 4.2.3 ezes42nge7		   python 3.11.9 ct26uvgbvv
bc 1.07.1 jasonjuuhs			       json-c 0.16 jiezkp3kmw		   munge 0.5.15 7ccb5fignq		   python 3.11.9 67g7nw4psf
berkeley-db 18.1.40 cexliohp6a		       json-cwx 0.12 pvkmxcbrrq		   ncurses 6.5 rvg7j6bmer		   python 3.13.0 gfyestlsao
berkeley-db 18.1.40 4mqvw3uqym		       kokkos 4.3.01 nxt5zthcpf		   ncurses 6.5 uajmsrcnww		   python 3.13.0 wxt7apsxjg
bison 3.8.2 sllhx5naxi			       krb5 1.21.3 pq3as37xno		   netlib-lapack 3.11.0 nn52q3bloz	   python-venv 1.0 wylyhqnxcx
bison 3.8.2 fky4vihmla			       krb5 1.21.3 frb66wnvsa		   netlib-scalapack 2.2.0 me353pomgl	   python-venv 1.0 pt3p7pdrin
boost 1.72.0 bzwzix4qau			       kvtree 1.5.0 5ubm4uy3f5		   netlib-scalapack 2.2.0 wpymlkhhvz	   rankstr 0.4.0 r6xbtxcwl3
bzip2 1.0.8 ewqc7cx44b			       libbsd 0.12.2 hwya6i63fv		   netlib-scalapack 2.2.0 5fvq6fgjha	   re2c 3.1 tpzsx34tj4
bzip2 1.0.8 j3fd4z5i2t			       libbsd 0.12.2 3oqufcrjxa		   netlib-scalapack 2.2.0 bx3g36nfqn	   re2c 3.1 qgh7e7n6c3
ca-certificates-mozilla 2023-05-30 t2brqssuwb  libdwarf 0.11.0 hz7ptvgac3	   nghttp2 1.63.0 t2qkug7u7i		   readline 8.2 aylebxvheu
ca-certificates-mozilla 2023-05-30 xilbvuysbu  libedit 3.1-20240808 nhrbcomj5l	   nghttp2 1.63.0 ippbbwuh3a		   readline 8.2 tb7zvufjv2
callpath 1.0.4 7hjlu6ddwm		       libedit 3.1-20240808 ht5niwqary	   ninja 1.12.1 xasrhuxnm5		   redset 0.4.0 wn6bxs77ba
cmake 3.30.5 d2nwbxlz2q			       libevent 2.1.12 xbwxobi4cj	   ninja 1.12.1 myjhsx2n7x		   scr 2.0.0 inxkq6ww6n
cmake 3.30.5 yempnazpum			       libevent 2.1.12 nftqerg373	   numactl 2.0.18 euqyy3hu6d		   scr 3.1.0 z6ndebu7r4
curl 8.10.1 fpywomo74r			       libfabric 1.22.0 lr77ky6sw7	   numactl 2.0.18 n4nt7lopsr		   shuffile 0.4.0 6gdnjhlovk
curl 8.10.1 wsmx6spfnt			       libfabric 1.22.0 pvefo63faf	   openblas 0.3.28 w2fghghowf		   silo 4.11.1 tf6qseltlk
diffutils 3.10 lljulvxt5h		       libffi 3.4.6 ltl5sqyu2f		   openblas 0.3.28 aliqr5t6k2		   slurm 23-11-1-1 wtptlf72di
diffutils 3.10 ioferp7x7e		       libffi 3.4.6 4ga47g7tj4		   openmpi 5.0.5 jvwxvxe7dg		   spath 0.4.0 phgpwllzdk
dtcmp 1.1.5 m2vb6sgvtv			       libgcrypt 1.11.0 tau3vgrmq6	   openmpi 5.0.5 suepxfzmhy		   sqlite 3.46.0 ogdosm6dqm
dyninst 13.0.0 idhmtve2yu		       libgpg-error 1.50 3g7e2nwelj	   openssh 9.9p1 wz7w27t54u		   sqlite 3.46.0 b63y5mxrvt
ed 1.4 p72c4zcdrx			       libiberty 2.41 ojjef3cyse	   openssh 9.9p1 3jdzvdmyxc		   tar 1.34 mmv6i4naeg
elfutils 0.191 zw2tlkv425		       libiconv 1.17 bsga3novxp		   openssl 3.4.0 5gigqgwld4		   tar 1.34 j5dljnv4mc
er 0.5.0 vcbjtu7365			       libiconv 1.17 gdtmvyudyk		   openssl 3.4.0 i5fmca4xdy		   tcl 8.6.12 imrm45j44x
expat 2.6.4 hj6jtyrxtq			       libmd 1.0.4 qcfzttdxov		   pcre2 10.44 pim2bkxw3c		   tcl 8.6.12 w4gwr4ezme
expat 2.6.4 vlatljjv2t			       libmd 1.0.4 cpmq5mdiri		   pdsh 2.31 oe5lfjf4gn			   texinfo 7.1 ljsbwjcqdl
findutils 4.9.0 2sbkhchnzz		       libpciaccess 0.17 omw5cc44g5	   perl 5.40.0 cwpt5ec6gc		   trilinos 16.0.0 ney6hmmpuf
findutils 4.9.0 wqmzrkeupx		       libpciaccess 0.17 ybxtd2t7kr	   perl 5.40.0 tt4vkz363s		   trilinos 16.0.0 63sfm7p6ve
gawk 5.3.1 x6mwj3lnzl			       libsigsegv 2.14 ea6qziv7ob	   pigz 2.8 pcwnu2wfae			   unzip 6.0 akxwy4wvo4
gcc 12.3.0 jjgfqpdmih			       libsigsegv 2.14 u2gofvqdvw	   pigz 2.8 zgwe2uay3t			   util-linux-uuid 2.40.2 xesify5chx
gcc-runtime 10.5.0 g7dc4oue6r		       libtool 2.4.7 tt3byemikn		   pkgconf 2.2.0 oplsmxryrc		   util-linux-uuid 2.40.2 64aencak6b
gcc-runtime 11.4.0 hshzy762rn		       libtool 2.4.7 xfe5xhomit		   pkgconf 2.2.0 gutpiwqutu		   util-macros 1.20.1 vdnwjqe534
gcc-runtime 12.3.0 w5nlv2kp2n		       libxcrypt 4.4.35 v64tnmrjg7	   pmix 5.0.3 5wqqmswtib		   util-macros 1.20.1 vau55h7rdi
gdbm 1.23 jj2atvsdpr			       libxcrypt 4.4.35 apxktfacu5	   pmix 5.0.3 t7gzekhpro		   xz 5.4.6 2fvrfr67ah
gdbm 1.23 ttyuhau65q			       libxml2 2.13.4 wozcmyn2bi	   py-beniget 0.4.1 oyzng5hw6k		   xz 5.4.6 ifvv7czmux
gettext 0.22.5 bexdfw2vfc		       libxml2 2.13.4 4fawd6mhag	   py-cython 3.0.11 efpxrfoxiv		   yaksa 0.3 eogugqp4p6
gettext 0.22.5 jbitwptksu		       libyogrt 1.35 gswz3l2d2q		   py-flit-core 3.9.0 ltdymw47r2	   yaksa 0.3 ferxascsq3
glib 2.78.3 rf4t3xsios			       lmod 8.7.18 saucmo6quz		   py-gast 0.5.4 rbt72pqun5		   zlib-ng 2.0.7 ocxrmffc7q
glibc 2.35 xvsfmyvk5j			       lua 5.4.6 qcxkktpc5l		   py-meson-python 0.16.0 jgr2tnqohn	   zlib-ng 2.0.7 umrbkwvnyd
glibc 2.35 tcngwuvjec			       lua-luafilesystem 1.8.0 4lpeywju4b  py-numpy 2.1.2 ckezggl7xc		   zlib-ng 2.2.1 jjykcjtfyo
glibc 2.35 a7drdl4tlx			       lua-luaposix 36.1 hdnpce3ade	   py-packaging 24.1 i5hagd3pwt		   zlib-ng 2.2.1 fhud6vqkh7
glibc 2.35 gkoh6axllp			       lwgrp 1.0.6 euguteoau5		   py-pip 23.1.2 vdyo63uzwk		   zlib-ng 2.2.1 i4mwsvmsv2
gmake 4.4.1 gcbcod5uik			       lz4 1.10.0 itxj6zzegx		   py-pip 23.1.2 3eaudob5lk		   zstd 1.5.6 t7h6imj35r
gmake 4.4.1 he2qdcb6bg			       m4 1.4.19 qaspjyq5yq		   py-ply 3.11 ohaizhq76a		   zstd 1.5.6 yk3lkd4xdu
gmake 4.4.1 srkzfjru5z			       m4 1.4.19 grpnckwm3e		   py-pybind11 2.13.5 bdx5tpnvwz
gmake 4.4.1 zoiouaigyc			       macsio 1.1 523dspnnce		   py-pyproject-metadata 0.7.1 p27mstg5gu

Note that name, version, and hash are attributes of Spack’s internal Spec object and enclosing them in braces ensures they are output according to your format string.

Using spack find --format allows you to retrieve just the information you need to do things like pipe the output to typical UNIX command-line tools like sort or uniq.

spack find --json

Alternatively, you can get a serialized version of Spec objects in the JSON format using the --json option. For example, you can get attributes for all installations of zlib-ng by entering:

$ spack find --json zlib-ng

The spack find --json command gives you everything we know about the specs in a structured format. You can pipe its output to JSON filtering tools like jq to extract just the parts you want.

Check out the basic usage docs for more examples.

Introducing the spack python command

What if we need to perform more advanced queries?

Spack provides the spack python command to launch a python interpreter with Spack’s python modules available to import. It uses the underlying python for the rest of its commands. So you can write scripts to:

  • run Spack commands;

  • explore abstract and concretized specs; and

  • directly access other internal components of Spack.

Let’s launch a Spack-aware python interpreter by entering:

$ spack python
exit()
Spack version 0.23.0
Python 3.10.12, Linux x86_64
>>> exit()

Since we are in a python interpreter, use exit() to end the session and return to the terminal.

Accessing the Spec object

Now let’s take a look at the internal representation of the Spack Spec. As you already know, specs can be either abstract or concrete. The specs you’ve seen in package.py files (e.g., in the install() method) have been concrete, or fully specified. The specs you’ve typed on the command line have been abstract. Understanding the differences between the two types is key to using Spack’s internal API.

Let’s open another python interpreter with spack python, instantiate the zlib spec, and check a few properties of an abstract spec:

  >>> from spack.spec import Spec
  >>> s = Spec('zlib target=x86_64_v3')
  >>> s.concrete
  False
  >>> s.version
  Traceback (most recent call last):
    File "<console>", line 1, in <module>
    File "/home/spack/spack/lib/spack/spack/spec.py", line 3166, in version
      raise SpecError("Spec version is not concrete: " + str(self))
  SpecError: Spec version is not concrete: zlib arch=None-None-x86_64_v3
  >>> s.versions
  [:]
  >>> str(s.architecture)
  None-None-x86_64_v3

Notice that there are Spec properties and methods that are not accessible to abstract specs; specifically:

  • an exception – SpecError – is raised if we try to access its version;

  • there are no associated versions; and

  • the spec’s operating system is None.

Now, without exiting the interpreter, let’s concretize the spec and try again:

  >>> s.concretize()
  >>> s.concrete
  True
  >>> s.version
  Version('1.3.1')
  >>> s.versions
  [Version('1.3.1')]
  >>> str(s.architecture)
  linux-ubuntu22.04-x86_64_v3

Notice that the concretized spec now:

  • has a version;

  • has a single entry in its versions list; and

  • the operating system is now ubuntu22.04.

It is not necessary to store the intermediate abstract spec – you can use the .concretized() method as shorthand:

  >>> t = Spec('zlib target=x86_64_v3').concretized()
  >>> s == t
  True

Querying the Spack database

Even more powerful queries are available when we look at the information stored in the Spack database. The Database object in Spack is in the spack.store.STORE.db variable. We’ll interact with it mainly through the query() method. Let’s see the documentation available for query() using python’s built-in help() function:

  >>> import spack.store
  >>> help(spack.store.STORE.db.query)
  Help on method query in module spack.database:

  query(*args, **kwargs) method of spack.database.Database instance
      Query the Spack database including all upstream databases.

      Args:
          query_spec: queries iterate through specs in the database and
              return those that satisfy the supplied ``query_spec``. If
              query_spec is `any`, This will match all specs in the
              database.  If it is a spec, we'll evaluate
              ``spec.satisfies(query_spec)``

          known (bool or any, optional): Specs that are "known" are those
              for which Spack can locate a ``package.py`` file -- i.e.,
              Spack "knows" how to install them.  Specs that are unknown may
              represent packages that existed in a previous version of
              Spack, but have since either changed their name or
              been removed

          installed (bool or any, or InstallStatus or iterable of
              InstallStatus, optional): if ``True``, includes only installed
              specs in the search; if ``False`` only missing specs, and if
              ``any``, all specs in database. If an InstallStatus or iterable
              of InstallStatus, returns specs whose install status
              (installed, deprecated, or missing) matches (one of) the
              InstallStatus. (default: True)

          explicit (bool or any, optional): A spec that was installed
              following a specific user request is marked as explicit. If
              instead it was pulled-in as a dependency of a user requested
              spec it's considered implicit.

          start_date (datetime, optional): filters the query discarding
              specs that have been installed before ``start_date``.

          end_date (datetime, optional): filters the query discarding
              specs that have been installed after ``end_date``.

          hashes (container): list or set of hashes that we can use to
              restrict the search

      Returns:
          list of specs that match the query
  (END)

We will primarily make use of the query_spec argument.

Recall that queries using the spack find command are limited to queries of attributes with matching values, not values they do not have. In other words, we cannot use the spack find command for all packages that do not satisfy a certain criterion.

We can use the python interface to write these types of queries. For example, let’s find all packages that were compiled with gcc but do not depend on mpich. We can do this by using custom python code and Spack database queries. We will use the spack.cmd.display_specs for output to achieve the same printing functionality as the spack find command:

  >>> gcc_query_spec = Spec('%gcc')
  >>> gcc_specs = spack.store.STORE.db.query(gcc_query_spec)
  >>> result = filter(lambda spec: not spec.satisfies('^mpich'), gcc_specs)
  >>> import spack.cmd
  >>> spack.cmd.display_specs(result)
  -- linux-ubuntu22.04-x86_64_v3 / gcc@11.4.0 -------------------------
autoconf@2.72                       gdbm@1.23             libsigsegv@2.14   perl@5.38.0
automake@1.16.5                     gettext@0.22.5        libtool@2.4.7     pigz@2.8
berkeley-db@18.1.40                 glibc@2.35            libxcrypt@4.4.35  pkgconf@2.2.0
bison@3.8.2                         gmake@4.4.1           libxml2@2.10.3    pmix@5.0.1
bzip2@1.0.8                         hdf5@1.14.3           m4@1.4.19         readline@8.2
ca-certificates-mozilla@2023-05-30  hwloc@2.9.1           ncurses@6.5       tar@1.34
cmake@3.27.9                        krb5@1.20.1           nghttp2@1.57.0    util-macros@1.19.3
curl@8.7.1                          libedit@3.1-20230828  numactl@2.0.14    xz@5.4.6
diffutils@3.10                      libevent@2.1.12       openmpi@5.0.3     zlib-ng@2.1.6
findutils@4.9.0                     libiconv@1.17         openssh@9.7p1     zstd@1.5.6
gcc-runtime@11.4.0                  libpciaccess@0.17     openssl@3.3.0

Now we have a powerful query not available through spack find.

Let’s exit the interpreter to take us back to the command line:

>>> exit()

before generalizing the functionality for reuse.

Using scripts

Now let’s parameterize our script to accept arguments on the command line. With a few generalizations to use the include and exclude specs as arguments, we can create a powerful, general-purpose query script.

Open a file called find_exclude.py in your preferred editor and add the following code:

from spack.spec import Spec
import spack.store
import spack.cmd
import sys

include_spec = Spec(sys.argv[1])
exclude_spec = Spec(sys.argv[2])

all_included = spack.store.STORE.db.query(include_spec)
result = filter(lambda spec: not spec.satisfies(exclude_spec), all_included)

spack.cmd.display_specs(result)

Notice we added importing and using the system package (sys) to access the first and second command line arguments.

Now we can run our new script by entering the following:

$ spack python find_exclude.py %gcc ^mpich
-- linux-ubuntu22.04-x86_64_v3 / gcc@10.5.0 ---------------------
gcc-runtime@10.5.0  glibc@2.35	gmake@4.4.1

-- linux-ubuntu22.04-x86_64_v3 / gcc@11.4.0 ---------------------
adept-utils@1.0.1		    dtcmp@1.1.5		gmp@6.3.0	      libffi@3.4.6	       lua-luaposix@36.1  openblas@0.3.28	python-venv@1.0	 texinfo@7.1
autoconf@2.72			    dyninst@13.0.0	hdf5@1.14.5	      libgcrypt@1.11.0	       lwgrp@1.0.6	  openmpi@5.0.5		rankstr@0.4.0	 trilinos@16.0.0
autoconf-archive@2023.02.20	    ed@1.4		hdf5@1.14.5	      libgpg-error@1.50	       lz4@1.10.0	  openssh@9.9p1		re2c@3.1	 unzip@6.0
automake@1.16.5			    elfutils@0.191	hwloc@2.11.1	      libiberty@2.41	       m4@1.4.19	  openssl@3.4.0		readline@8.2	 util-linux-uuid@2.40.2
axl@0.9.0			    er@0.5.0		intel-tbb@2021.12.0   libiconv@1.17	       macsio@1.1	  pcre2@10.44		redset@0.4.0	 util-macros@1.20.1
bc@1.07.1			    expat@2.6.4		json-c@0.16	      libmd@1.0.4	       macsio@1.1	  pdsh@2.31		scr@2.0.0	 xz@5.4.6
berkeley-db@18.1.40		    findutils@4.9.0	json-cwx@0.12	      libpciaccess@0.17	       meson@1.5.1	  perl@5.40.0		scr@3.1.0	 yaksa@0.3
bison@3.8.2			    gawk@5.3.1		kokkos@4.3.01	      libsigsegv@2.14	       mpc@1.3.1	  pigz@2.8		shuffile@0.4.0	 zlib-ng@2.0.7
boost@1.72.0			    gcc@12.3.0		krb5@1.21.3	      libtool@2.4.7	       mpfr@4.2.1	  pkgconf@2.2.0		silo@4.11.1	 zlib-ng@2.0.7
bzip2@1.0.8			    gcc-runtime@11.4.0	kvtree@1.5.0	      libxcrypt@4.4.35	       mpich@4.2.3	  pmix@5.0.3		slurm@23-11-1-1	 zlib-ng@2.2.1
ca-certificates-mozilla@2023-05-30  gdbm@1.23		libbsd@0.12.2	      libxml2@2.13.4	       munge@0.5.15	  py-pip@23.1.2		spath@0.4.0	 zstd@1.5.6
callpath@1.0.4			    gettext@0.22.5	libdwarf@0.11.0	      libyogrt@1.35	       ncurses@6.5	  py-setuptools@69.2.0	sqlite@3.46.0
cmake@3.30.5			    glib@2.78.3		libedit@3.1-20240808  lmod@8.7.18	       nghttp2@1.63.0	  py-wheel@0.41.2	tar@1.34
curl@8.10.1			    glibc@2.35		libevent@2.1.12	      lua@5.4.6		       ninja@1.12.1	  python@3.11.9		tcl@8.6.12
diffutils@3.10			    gmake@4.4.1		libfabric@1.22.0      lua-luafilesystem@1.8.0  numactl@2.0.18	  python@3.13.0		tcl@8.6.12

-- linux-ubuntu22.04-x86_64_v3 / gcc@12.3.0 ---------------------
autoconf@2.72			    gcc-runtime@12.3.0	  libffi@3.4.6	     ncurses@6.5	     perl@5.40.0	     py-pip@23.1.2		  re2c@3.1
automake@1.16.5			    gdbm@1.23		  libiconv@1.17	     netlib-lapack@3.11.0    pigz@2.8		     py-ply@3.11		  readline@8.2
berkeley-db@18.1.40		    gettext@0.22.5	  libmd@1.0.4	     netlib-scalapack@2.2.0  pkgconf@2.2.0	     py-pybind11@2.13.5		  sqlite@3.46.0
bison@3.8.2			    glibc@2.35		  libpciaccess@0.17  netlib-scalapack@2.2.0  pmix@5.0.3		     py-pyproject-metadata@0.7.1  tar@1.34
bzip2@1.0.8			    gmake@4.4.1		  libsigsegv@2.14    nghttp2@1.63.0	     py-beniget@0.4.1	     py-pythran@0.16.1		  util-linux-uuid@2.40.2
ca-certificates-mozilla@2023-05-30  hwloc@2.11.1	  libtool@2.4.7	     ninja@1.12.1	     py-cython@3.0.11	     py-scipy@1.14.1		  util-macros@1.20.1
cmake@3.30.5			    krb5@1.21.3		  libxcrypt@4.4.35   numactl@2.0.18	     py-flit-core@3.9.0	     py-setuptools@69.2.0	  xz@5.4.6
curl@8.10.1			    libbsd@0.12.2	  libxml2@2.13.4     openblas@0.3.28	     py-gast@0.5.4	     py-wheel@0.41.2		  yaksa@0.3
diffutils@3.10			    libedit@3.1-20240808  m4@1.4.19	     openmpi@5.0.5	     py-meson-python@0.16.0  python@3.11.9		  zlib-ng@2.2.1
expat@2.6.4			    libevent@2.1.12	  meson@1.5.1	     openssh@9.9p1	     py-numpy@2.1.2	     python@3.13.0		  zstd@1.5.6
findutils@4.9.0			    libfabric@1.22.0	  mpich@4.2.3	     openssl@3.4.0	     py-packaging@24.1	     python-venv@1.0

This is great for us, as long as we remember to use Spack’s python command to run it.

Using the spack-python executable

What if we want to make our script available for others to use without the hassle of having to remember to use spack python?

We can take advantage of the shebang line typically added as the first line of python executable files. But there is a catch, as we will soon see.

Open the find_exclude.py script we created above in your preferred editor and add the shebang line with spack python as the arguments to env:

#!/usr/bin/env spack python
from spack.spec import Spec
import spack.store
import spack.cmd
import sys

include_spec = Spec(sys.argv[1])
exclude_spec = Spec(sys.argv[2])

all_included = spack.store.STORE.db.query(include_spec)
result = filter(lambda spec: not spec.satisfies(exclude_spec), all_included)

spack.cmd.display_specs(result)

Then exit our editor and add execute permissions to the script before running it as follows:

$ chmod u+x find_exclude.py
$ ./find_exclude.py %gcc ^mpich
/usr/bin/env: ‘spack python’: No such file or directory
/usr/bin/env: use -[v]S to pass options in shebang lines

If you are lucky, it worked on your system, but there is no guarantee. Some systems only support a single argument on the shebang line (see here). spack-python, which is a wrapper script for spack python, solves this issue.

Bring up the file in your editor again and change the env argument to spack-python as follows:

#!/usr/bin/env spack-python
from spack.spec import Spec
import spack.store
import spack.cmd
import sys

include_spec = Spec(sys.argv[1])
exclude_spec = Spec(sys.argv[2])

all_included = spack.store.STORE.db.query(include_spec)
result = filter(lambda spec: not spec.satisfies(exclude_spec), all_included)

spack.cmd.display_specs(result)

Exit your editor and let’s run the script again:

$ ./find_exclude.py %gcc ^mpich
-- linux-ubuntu22.04-x86_64_v3 / gcc@10.5.0 ---------------------
gcc-runtime@10.5.0  glibc@2.35	gmake@4.4.1

-- linux-ubuntu22.04-x86_64_v3 / gcc@11.4.0 ---------------------
adept-utils@1.0.1		    dtcmp@1.1.5		gmp@6.3.0	      libffi@3.4.6	       lua-luaposix@36.1  openblas@0.3.28	python-venv@1.0	 texinfo@7.1
autoconf@2.72			    dyninst@13.0.0	hdf5@1.14.5	      libgcrypt@1.11.0	       lwgrp@1.0.6	  openmpi@5.0.5		rankstr@0.4.0	 trilinos@16.0.0
autoconf-archive@2023.02.20	    ed@1.4		hdf5@1.14.5	      libgpg-error@1.50	       lz4@1.10.0	  openssh@9.9p1		re2c@3.1	 unzip@6.0
automake@1.16.5			    elfutils@0.191	hwloc@2.11.1	      libiberty@2.41	       m4@1.4.19	  openssl@3.4.0		readline@8.2	 util-linux-uuid@2.40.2
axl@0.9.0			    er@0.5.0		intel-tbb@2021.12.0   libiconv@1.17	       macsio@1.1	  pcre2@10.44		redset@0.4.0	 util-macros@1.20.1
bc@1.07.1			    expat@2.6.4		json-c@0.16	      libmd@1.0.4	       macsio@1.1	  pdsh@2.31		scr@2.0.0	 xz@5.4.6
berkeley-db@18.1.40		    findutils@4.9.0	json-cwx@0.12	      libpciaccess@0.17	       meson@1.5.1	  perl@5.40.0		scr@3.1.0	 yaksa@0.3
bison@3.8.2			    gawk@5.3.1		kokkos@4.3.01	      libsigsegv@2.14	       mpc@1.3.1	  pigz@2.8		shuffile@0.4.0	 zlib-ng@2.0.7
boost@1.72.0			    gcc@12.3.0		krb5@1.21.3	      libtool@2.4.7	       mpfr@4.2.1	  pkgconf@2.2.0		silo@4.11.1	 zlib-ng@2.0.7
bzip2@1.0.8			    gcc-runtime@11.4.0	kvtree@1.5.0	      libxcrypt@4.4.35	       mpich@4.2.3	  pmix@5.0.3		slurm@23-11-1-1	 zlib-ng@2.2.1
ca-certificates-mozilla@2023-05-30  gdbm@1.23		libbsd@0.12.2	      libxml2@2.13.4	       munge@0.5.15	  py-pip@23.1.2		spath@0.4.0	 zstd@1.5.6
callpath@1.0.4			    gettext@0.22.5	libdwarf@0.11.0	      libyogrt@1.35	       ncurses@6.5	  py-setuptools@69.2.0	sqlite@3.46.0
cmake@3.30.5			    glib@2.78.3		libedit@3.1-20240808  lmod@8.7.18	       nghttp2@1.63.0	  py-wheel@0.41.2	tar@1.34
curl@8.10.1			    glibc@2.35		libevent@2.1.12	      lua@5.4.6		       ninja@1.12.1	  python@3.11.9		tcl@8.6.12
diffutils@3.10			    gmake@4.4.1		libfabric@1.22.0      lua-luafilesystem@1.8.0  numactl@2.0.18	  python@3.13.0		tcl@8.6.12

-- linux-ubuntu22.04-x86_64_v3 / gcc@12.3.0 ---------------------
autoconf@2.72			    gcc-runtime@12.3.0	  libffi@3.4.6	     ncurses@6.5	     perl@5.40.0	     py-pip@23.1.2		  re2c@3.1
automake@1.16.5			    gdbm@1.23		  libiconv@1.17	     netlib-lapack@3.11.0    pigz@2.8		     py-ply@3.11		  readline@8.2
berkeley-db@18.1.40		    gettext@0.22.5	  libmd@1.0.4	     netlib-scalapack@2.2.0  pkgconf@2.2.0	     py-pybind11@2.13.5		  sqlite@3.46.0
bison@3.8.2			    glibc@2.35		  libpciaccess@0.17  netlib-scalapack@2.2.0  pmix@5.0.3		     py-pyproject-metadata@0.7.1  tar@1.34
bzip2@1.0.8			    gmake@4.4.1		  libsigsegv@2.14    nghttp2@1.63.0	     py-beniget@0.4.1	     py-pythran@0.16.1		  util-linux-uuid@2.40.2
ca-certificates-mozilla@2023-05-30  hwloc@2.11.1	  libtool@2.4.7	     ninja@1.12.1	     py-cython@3.0.11	     py-scipy@1.14.1		  util-macros@1.20.1
cmake@3.30.5			    krb5@1.21.3		  libxcrypt@4.4.35   numactl@2.0.18	     py-flit-core@3.9.0	     py-setuptools@69.2.0	  xz@5.4.6
curl@8.10.1			    libbsd@0.12.2	  libxml2@2.13.4     openblas@0.3.28	     py-gast@0.5.4	     py-wheel@0.41.2		  yaksa@0.3
diffutils@3.10			    libedit@3.1-20240808  m4@1.4.19	     openmpi@5.0.5	     py-meson-python@0.16.0  python@3.11.9		  zlib-ng@2.2.1
expat@2.6.4			    libevent@2.1.12	  meson@1.5.1	     openssh@9.9p1	     py-numpy@2.1.2	     python@3.13.0		  zstd@1.5.6
findutils@4.9.0			    libfabric@1.22.0	  mpich@4.2.3	     openssl@3.4.0	     py-packaging@24.1	     python-venv@1.0

Congratulations! It will now work on any system with Spack installed.

You now have the basic tools to create your own custom Spack queries and prototype ideas. We hope one day you’ll contribute them back to Spack.