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:sc25

and then set Spack up like this:

git clone --depth=2 --branch=releases/v1.1 https://github.com/spack/spack
. spack/share/spack/setup-env.sh
spack repo update builtin --tag v2025.11.0
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 scripting features available in Spack, using the spack find and spack python commands. We’ve already seen how to list and search installed packages with spack find. The spack python command allows us to write more complex queries, as it gives access to all of Spack’s internal APIs.

Since Spack has an extensive API, we’ll only scratch the surface here.

Scripting with spack find

The output we’ve seen from spack find has been for human consumption. We can take advantage of the command’s advanced features to generate machine-readable output suitable for piping to a script.

spack find --format

The main function of spack find is to display concrete specs that correspond to installed packages. By default, they are shown with default attributes, like the @version suffix.

The --format argument allows us to display the specs using custom format strings.

Suppose we only want to see the name, version, and first ten (10) characters of the hash for every package installed in the Spack instance. This output can be generated with the following command:

$ spack find --format "{name} {version} {hash:10}"
adept-utils 1.0.1 bkfrcjldbo		       m4 1.4.20 lxvpwtivih
autoconf 2.72 nzbffupyig		       macsio 1.1 3uiweakny3
autoconf 2.72 fgsf5ijzhn		       macsio 1.1 2qq3umgema
autoconf 2.72 r3ant5t3pr		       mbedtls 2.28.9 bz3ghzheol
autoconf-archive 2023.02.20 7p7gq7uzoa	       meson 1.8.5 muzyskyvwm
automake 1.16.5 pzmnwzmdyf		       meson 1.8.5 c5mkho7hg3
automake 1.16.5 6ib4pvm3dv		       mpc 1.3.1 sczvrkuwv7
automake 1.16.5 zmzk4bb3og		       mpfr 4.2.1 m3nwaltjkw
bc 1.07.1 s3qhnq46lg			       mpich 4.3.2 amwozyio5q
berkeley-db 18.1.40 vdgigswy4f		       mpich 4.3.2 uaioo2homc
bison 3.8.2 n7yzkylf4g			       munge 0.5.15 itsnhnf3jn
boost 1.72.0 3bt6bi5q4j			       ncurses 6.5-20250705 ncdxq3juvb
bzip2 1.0.8 x7t4najic2			       ncurses 6.5-20250705 ytk4y2kvs7
bzip2 1.0.8 zom5sydb4b			       netlib-lapack 3.12.1 rhz3ueke3w
ca-certificates-mozilla 2025-08-12 etqlnw5hd6  netlib-scalapack 2.2.2 pglf7pu6w2
callpath 1.0.4 p4i3pshhj3		       netlib-scalapack 2.2.2 3t7cajdej6
cmake 3.31.9 dmmnd4uqwk			       netlib-scalapack 2.2.2 apaz22ojhz
cmake 3.31.9 4a7thjgehx			       netlib-scalapack 2.2.2 yxweja4r47
cmake 3.31.9 ivvor7vdsq			       nghttp2 1.48.0 ft5kpbdiz6
compiler-wrapper 1.0 ntccuj2fi3		       ninja 1.13.0 2stmhpcux6
curl 8.15.0 jtps3jq4as			       numactl 2.0.18 yzbm5q6nbl
curl 8.15.0 zianoaa5jz			       numactl 2.0.18 t2nikrtxvu
curl 8.15.0 isdtvvdzii			       openblas 0.3.30 dwjd7g2k2x
diffutils 3.12 cih4xrzw7y		       openblas 0.3.30 qz3ay7bvee
diffutils 3.12 msusunhi5v		       openmpi 5.0.8 tqxbnvoiw3
dtcmp 1.1.5 2kcmd2x76s			       openmpi 5.0.8 p2gmhau5ko
dyninst 13.0.0 jiygq6syjs		       openssh 9.9p1 cxdcxo5pdx
ed 1.4 4cbvnhde46			       openssh 9.9p1 lip7jtpgco
elfutils 0.193 brbwl6lkh3		       openssl 3.6.0 gv7wpik32u
expat 2.7.3 7yr5v6w2xu			       openssl 3.6.0 oa4vrqqj43
expat 2.7.3 ikeoatlzkk			       openssl 3.6.0 q4fenforzt
findutils 4.10.0 irvrytslml		       pcre2 10.44 z5vjj7qwop
flex 2.6.3 gmhq65uhcn			       pdsh 2.31 eicmuq5waz
gawk 5.3.1 irhvzcha2s			       perl 5.42.0 62kt5y4kl7
gcc 10.5.0 ntkvysydhi			       perl 5.42.0 cvuukniuti
gcc 11.4.0 rpw3bukias			       perl 5.42.0 jpuw66splu
gcc 11.4.0 ml7cem5pfe			       pigz 2.8 kaz756eya6
gcc 12.3.0 fmnvqo44pc			       pigz 2.8 zjrnsfoh55
gcc-runtime 10.5.0 ahapkrsgqx		       pigz 2.8 bs2taxmcal
gcc-runtime 11.4.0 qmbrryiepg		       pkgconf 2.5.1 f4qiprwjcd
gcc-runtime 11.4.0 nokfxvaa4a		       pmix 6.0.0 yqlblh6p4u
gcc-runtime 12.3.0 pgwnyw7ukf		       pmix 6.0.0 76oyav4s3n
gdbm 1.25 aq7qwy6yez			       prrte 4.0.0 buin62mint
gdbm 1.25 y5bsvao3cx			       prrte 4.0.0 zalt7csrls
gettext 0.23.1 cakgj4ntln		       py-beniget 0.4.2.post1 g6mqwbrfiy
gettext 0.23.1 xxxc56npfd		       py-calver 2025.4.17 gnnyu6h5kx
gettext 0.23.1 3f5v73yqnf		       py-cython 3.1.3 apuchub6dg
git 2.48.1 aim32246be			       py-flit-core 3.12.0 55omqj2ucv
glib 2.86.1 arsxi4dynj			       py-gast 0.6.0 xrd6sez3xn
glib-bootstrap 2.86.1 dcark7n2gm	       py-hatch-vcs 0.5.0 44uhp2gd53
glibc 2.35 vjnqll7uhr			       py-hatchling 1.27.0 qb7x6jyrjb
glibc 2.35 qg7qyazcn2			       py-meson-python 0.18.0 o6u3um7xjp
gmake 4.4.1 ufbescpw7r			       py-numpy 2.3.4 ucqh5kunp6
gmake 4.4.1 vsq3oi374t			       py-packaging 25.0 j3t7cjg7qd
gmake 4.4.1 4obn7cgqfw			       py-pathspec 0.12.1 wvr3fheyxt
gmp 6.3.0 mj4re3lp3b			       py-pip 25.1.1 hjc6yfd2ld
gobject-introspection 1.86.0 zktpj7scqi	       py-pip 25.1.1 qwew5rgzuu
hdf5 1.14.6 7mm6knnnjx			       py-pluggy 1.6.0 2c2amjcima
hdf5 1.14.6 vllfzb7aaz			       py-ply 3.11 xgx3hu4hlc
hdf5 1.14.6 gdk3ghc3gh			       py-pybind11 3.0.1 b7sx7ll7um
hwloc 2.12.2 4hos3725ny			       py-pyproject-metadata 0.9.1 sypchiv5ka
hwloc 2.12.2 pmdeyoykxz			       py-pythran 0.18.0 w4fhoiyknp
hwloc 2.12.2 wkj6z6mxka			       py-scikit-build-core 0.11.5 5r47zypnbt
intel-tbb 2022.3.0 rbf737wu37		       py-scipy 1.16.3 igjijidvsq
json-c 0.18 5viqezvbwe			       py-setuptools 80.9.0 lnndrgestz
json-cwx 0.12 xb3rsbkkz6		       py-setuptools 80.9.0 urwrpfosir
kokkos 4.5.01 k2pkvic65l		       py-setuptools-scm 8.2.1 xcq4maxl4u
kokkos-kernels 4.5.01 qknxuyj5vb	       py-trove-classifiers 2025.9.11.17 cwn5qw7ibd
krb5 1.21.3 hnmy4fwwly			       py-wheel 0.45.1 hpfn7k7fkr
krb5 1.21.3 onmo4bvd2j			       py-wheel 0.45.1 vs4xbx3m62
libbsd 0.12.2 ucrhsywwal		       python 3.11.14 rbf2x4id5w
libbsd 0.12.2 zb37a5t5np		       python 3.14.0 pazbuxsobo
libdwarf 2.1.0 l4t7fu7gmh		       python 3.14.0 yvs3szqzvv
libedit 3.1-20240808 hdzcfgipuk		       python-venv 1.0 essoyki3al
libedit 3.1-20240808 c5o6gvvaov		       python-venv 1.0 nrdm37joni
libevent 2.1.12 lprginh6np		       re2c 3.1 axmwicsvae
libevent 2.1.12 fn26hildna		       readline 8.3 c6d2zljdkl
libfabric 2.3.1 n2l4ckstgt		       readline 8.3 eyfbskp7ur
libfabric 2.3.1 7imejdfjny		       scr 2.0.0 xuzclql7mx
libffi 3.5.2 zgro4twqg3			       scr 2.0.0 f57s4noxfk
libffi 3.5.2 s56v4gfjyu			       silo 4.11.1 vsguusygbz
libgcrypt 1.11.2 4j7zjzsf5h		       slurm 25-05-1-1 tc55ttmeph
libgpg-error 1.55 jerwahojdg		       sqlite 3.50.4 elvodybm3e
libiberty 2.41 wz2npe3qsm		       sqlite 3.50.4 a4zeurpolt
libiconv 1.18 qtepnkrdvq		       sqlite 3.50.4 plzt2ndvbq
libiconv 1.18 g3zxoz6huo		       tar 1.35 lfgvgvawnp
libidn2 2.3.7 gntupl34gn		       tar 1.35 e3ajmyqkyi
libmd 1.1.0 gth3ii5boe			       tar 1.35 wfarfthvpw
libmd 1.1.0 cq4lpjpcxc			       tcl 8.6.17 x74vmgrwo7
libpciaccess 0.17 txbwcin227		       tcl 8.6.17 tsq4fjjw2p
libpciaccess 0.17 p7g23cjqjl		       texinfo 7.2 k6ygirswvb
libsigsegv 2.14 bs5ujst3rr		       trilinos 16.1.0 tj433utqwn
libssh2 1.11.1 txa2olxuox		       trilinos 16.1.0 k3ozjlurkc
libtool 2.4.7 yt7ajy4c3z		       unzip 6.0 5gxay2amcy
libunistring 1.2 x5b7oeto7b		       util-linux-uuid 2.41 n45otd3wy4
libxcrypt 4.4.38 yiij42powr		       util-linux-uuid 2.41 b4npzn6ixc
libxcrypt 4.4.38 drjmmlw4cw		       util-macros 1.20.1 hwxnwvmn44
libxml2 2.13.5 5trxrsws5d		       xz 5.6.3 yzaocbs7ge
libxml2 2.13.5 lguldtjks3		       xz 5.6.3 643vviqh75
libxml2 2.13.5 idam6zhjw4		       yaksa 0.4 zuutzfx4db
libyogrt 1.35 ylblit5vwt		       yaksa 0.4 7jn3vivsnj
llvm 14.0.0 qr2e4rdcbj			       zlib-ng 2.0.7 qlavhjbsgq
lmod 8.7.18 gbpxgjwwyp			       zlib-ng 2.2.4 zk6keshnph
lua 5.4.6 hdxlqejf6f			       zlib-ng 2.2.4 4tglvhvqq7
lua-luafilesystem 1.8.0 nsymk62hqa	       zlib-ng 2.2.4 ct2r7xmldp
lua-luaposix 36.1 bubs6lunhi		       zstd 1.5.7 foiizhdg2m
lwgrp 1.0.6 bms4pwx7wh			       zstd 1.5.7 fj5u5hmzff
lz4 1.10.0 7m7ijnovaf

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 the format string.

spack find --format can be combined with typical command line tools like sort or uniq to retrieve information relevant to specific workflows.

spack find --json

Alternatively, we can get a serialized version of Spec objects in the JSON format using the --json option.

For example, to get attributes for all installations of zlib-ng:

$ spack find --json zlib-ng

This command provides complete information about any spec of interest in a structured format. The output of spack find --json can be piped to JSON filtering tools like jq to extract specific information.

Visit 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 an interpreter with Spack’s Python modules available to import. The underlying Python instance is used for all other commands. We can write scripts to:

  • run Spack commands

  • explore abstract and concretized specs

  • directly access other internal components of Spack

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

$ spack python
exit()
Spack version 1.1.0
Python 3.11.11, Linux x86_64
>>> exit()

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

Accessing the Spec object

Let’s take a look at the internal representation of the Spack Spec. As previously mentioned, specs can be either abstract or concrete. The specs we’ve seen in package.py files (e.g., in the install() method) have been concrete, or fully specified. Specs 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
  >>> zlib = Spec('zlib target=x86_64_v3')
  >>> zlib.concrete
  False
  >>> zlib.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
  >>> zlib.versions
  [:]
  >>> str(zlib.architecture)
  None-None-x86_64_v3

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

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

  • there are no associated versions

  • the spec’s operating system is None

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

  >>> from spack.concretize import concretize_one
  >>> zlib_concrete = spack.concretize.concretize_one(zlib)
  >>> zlib_concrete.concrete
  True
  >>> zlib_concrete.version
  Version('1.3.1')
  >>> zlib_concrete.versions
  [Version('1.3.1')]
  >>> str(zlib_concrete.architecture)
  linux-ubuntu22.04-x86_64_v3

Notice that the concretized spec now:

  • has a version

  • has a single entry in its versions list

  • the operating system is now ubuntu22.04

Querying the Spack database

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(query_spec: Union[str, ForwardRef('spack.spec.Spec'), NoneType] = None, *, predicate_fn: Optional[Callable[[spack.database.InstallRecord], bool]] = None, installed: Union[bool, spack.enums.InstallRecordStatus] = True, explicit: Optional[bool] = None, start_date: Optional[datetime.datetime] = None, end_date: Optional[datetime.datetime] = None, in_buildcache: Optional[bool] = None, hashes: Optional[List[str]] = None, origin: Optional[str] = None, install_tree: str = 'all') -> List[ForwardRef('spack.spec.Spec')] method of spack.database.Database instance
    Queries the Spack database including all upstream databases.

    Args:
        query_spec:  if query_spec is ``None``, match all specs in the database.
            If it is a spec, return all specs matching ``spec.satisfies(query_spec)``.

        predicate_fn: optional predicate taking an InstallRecord as argument, and returning
            whether that record is selected for the query. It can be used to craft criteria
            that need some data for selection not provided by the Database itself.

        installed: 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 matches at least
            one of the InstallStatus.

        explicit: 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: if set considers only specs installed from the starting date.

        end_date: if set considers only specs installed until the ending date.

        in_buildcache: specs that are marked in this database as part of an associated binary
            cache are ``in_buildcache``. All other specs are not. This field is used for
            querying mirror indices. By default, it does not check this status.

        hashes: list of hashes used to restrict the search

        install_tree: query 'all' (default), 'local', 'upstream', or upstream path

        origin: origin of the spec
(END)

We’ll primarily make use of the query_spec argument.

Recall that spack find is limited to queries of attributes with matching values. It cannot be used to find packages that do not meet a specific condition.

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 = [spec for spec in gcc_specs if not spec.satisfies('^mpich')]
  >>> 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.

Exit the interpreter to return to the command line:

>>> exit()

before generalizing the functionality for reuse.

Using scripts

Next, the script can be updated to accept arguments from the command line. By generalizing the script to take include and exclude specs as arguments, it becomes a flexible, general-purpose query tool.

Open a file called find_exclude.py in a text 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 = [spec for spec in all_included if not spec.satisfies(exclude_spec)]

spack.cmd.display_specs(result)

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 / %c,cxx,fortran=gcc@11.4.0 ------
mpich@4.3.2	 openmpi@5.0.8	scr@2.0.0    trilinos@16.1.0
openblas@0.3.30	 scr@2.0.0	silo@4.11.1

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx,fortran=gcc@12.3.0 ------
mpich@4.3.2  openblas@0.3.30  openmpi@5.0.8  py-scikit-build-core@0.11.5  py-scipy@1.16.3

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx=gcc@11.4.0 --------------
adept-utils@1.0.1    expat@2.7.3	    libdwarf@2.1.0	  openssl@3.6.0
berkeley-db@18.1.40  flex@2.6.3		    libffi@3.5.2	  openssl@3.6.0
bison@3.8.2	     gcc@12.3.0		    libiberty@2.41	  python@3.11.14
boost@1.72.0	     gettext@0.23.1	    lua@5.4.6		  python@3.14.0
callpath@1.0.4	     gettext@0.23.1	    lz4@1.10.0		  re2c@3.1
cmake@3.31.9	     glib@2.86.1	    m4@1.4.20		  tcl@8.6.17
cmake@3.31.9	     glib-bootstrap@2.86.1  macsio@1.1		  tcl@8.6.17
curl@8.15.0	     gmp@6.3.0		    macsio@1.1		  texinfo@7.2
curl@8.15.0	     hwloc@2.12.2	    ncurses@6.5-20250705  zlib-ng@2.0.7
dyninst@13.0.0	     hwloc@2.12.2	    nghttp2@1.48.0	  zlib-ng@2.2.4
ed@1.4		     intel-tbb@2022.3.0	    ninja@1.13.0	  zstd@1.5.7
elfutils@0.193	     krb5@1.21.3	    openssh@9.9p1

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx=gcc@12.3.0 --------------
cmake@3.31.9  gettext@0.23.1  libffi@3.5.2	    openssl@3.6.0    python@3.14.0
curl@8.15.0   hwloc@2.12.2    ncurses@6.5-20250705  py-cython@3.1.3  zlib-ng@2.2.4
expat@2.7.3   krb5@1.21.3     openssh@9.9p1	    py-numpy@2.3.4   zstd@1.5.7

-- linux-ubuntu22.04-x86_64_v3 / %c,fortran=gcc@11.4.0 ----------
libyogrt@1.35

-- linux-ubuntu22.04-x86_64_v3 / %c,fortran=gcc@12.3.0 ----------
netlib-lapack@3.12.1  netlib-scalapack@2.2.2  netlib-scalapack@2.2.2

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@10.5.0 ------------------
gmake@4.4.1

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@11.4.0 ------------------
automake@1.16.5		      libgcrypt@1.11.2	       pcre2@10.44
automake@1.16.5		      libgpg-error@1.55	       pdsh@2.31
bc@1.07.1		      libiconv@1.18	       perl@5.42.0
bzip2@1.0.8		      libmd@1.1.0	       perl@5.42.0
diffutils@3.12		      libpciaccess@0.17	       pigz@2.8
dtcmp@1.1.5		      libsigsegv@2.14	       pigz@2.8
findutils@4.10.0	      libssh2@1.11.1	       pkgconf@2.5.1
gawk@5.3.1		      libtool@2.4.7	       pmix@6.0.0
gdbm@1.25		      libxcrypt@4.4.38	       prrte@4.0.0
gmake@4.4.1		      libxml2@2.13.5	       readline@8.3
gmake@4.4.1		      libxml2@2.13.5	       slurm@25-05-1-1
gobject-introspection@1.86.0  lmod@8.7.18	       sqlite@3.50.4
hdf5@1.14.6		      lua-luafilesystem@1.8.0  sqlite@3.50.4
hdf5@1.14.6		      lua-luaposix@36.1	       tar@1.35
json-c@0.18		      lwgrp@1.0.6	       tar@1.35
json-cwx@0.12		      mbedtls@2.28.9	       unzip@6.0
libbsd@0.12.2		      mpc@1.3.1		       util-linux-uuid@2.41
libedit@3.1-20240808	      mpfr@4.2.1	       xz@5.6.3
libevent@2.1.12		      munge@0.5.15	       yaksa@0.4
libfabric@2.3.1		      numactl@2.0.18

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@12.3.0 ------------------
automake@1.16.5	      libevent@2.1.12	 libxcrypt@4.4.38  py-meson-python@0.18.0
bzip2@1.0.8	      libfabric@2.3.1	 libxml2@2.13.5	   readline@8.3
diffutils@3.12	      libiconv@1.18	 numactl@2.0.18	   sqlite@3.50.4
gdbm@1.25	      libidn2@2.3.7	 perl@5.42.0	   tar@1.35
git@2.48.1	      libmd@1.1.0	 pigz@2.8	   util-linux-uuid@2.41
libbsd@0.12.2	      libpciaccess@0.17	 pmix@6.0.0	   xz@5.6.3
libedit@3.1-20240808  libunistring@1.2	 prrte@4.0.0	   yaksa@0.4

-- linux-ubuntu22.04-x86_64_v3 / %cxx=gcc@11.4.0 ----------------
kokkos@4.5.01  kokkos-kernels@4.5.01

-- linux-ubuntu22.04-x86_64_v3 / %cxx=gcc@12.3.0 ----------------
py-pybind11@3.0.1  py-pythran@0.18.0

-- linux-ubuntu22.04-x86_64_v3 / no compilers -------------------
gcc-runtime@10.5.0  gcc-runtime@11.4.0	gcc-runtime@11.4.0  gcc-runtime@12.3.0

This works well, as long as we remember to use Spack’s python command to run it.

Using the spack-python executable

What if the script needs to be shared with others, without requiring them to remember to use spack python?

This can be done by adding a shebang line as the first line of the Python script, which allows it to be run as an executable. However, there is an important limitation to be aware of, as shown in the next example.

Open the find_exclude.py script we created above 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 = [spec for spec in all_included if not spec.satisfies(exclude_spec)]

spack.cmd.display_specs(result)

Exit the 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’: Permission denied

If we’re lucky, it ran successfully, but there’s no guarantee this will work for every system. 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 the 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 = [spec for spec in all_included if not spec.satisfies(exclude_spec)]

spack.cmd.display_specs(result)

Exit the editor and run the script again:

$ ./find_exclude.py %gcc ^mpich
-- linux-ubuntu22.04-x86_64_v3 / %c,cxx,fortran=gcc@11.4.0 ------
mpich@4.3.2	 openmpi@5.0.8	scr@2.0.0    trilinos@16.1.0
openblas@0.3.30	 scr@2.0.0	silo@4.11.1

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx,fortran=gcc@12.3.0 ------
mpich@4.3.2  openblas@0.3.30  openmpi@5.0.8  py-scikit-build-core@0.11.5  py-scipy@1.16.3

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx=gcc@11.4.0 --------------
adept-utils@1.0.1    expat@2.7.3	    libdwarf@2.1.0	  openssl@3.6.0
berkeley-db@18.1.40  flex@2.6.3		    libffi@3.5.2	  openssl@3.6.0
bison@3.8.2	     gcc@12.3.0		    libiberty@2.41	  python@3.11.14
boost@1.72.0	     gettext@0.23.1	    lua@5.4.6		  python@3.14.0
callpath@1.0.4	     gettext@0.23.1	    lz4@1.10.0		  re2c@3.1
cmake@3.31.9	     glib@2.86.1	    m4@1.4.20		  tcl@8.6.17
cmake@3.31.9	     glib-bootstrap@2.86.1  macsio@1.1		  tcl@8.6.17
curl@8.15.0	     gmp@6.3.0		    macsio@1.1		  texinfo@7.2
curl@8.15.0	     hwloc@2.12.2	    ncurses@6.5-20250705  zlib-ng@2.0.7
dyninst@13.0.0	     hwloc@2.12.2	    nghttp2@1.48.0	  zlib-ng@2.2.4
ed@1.4		     intel-tbb@2022.3.0	    ninja@1.13.0	  zstd@1.5.7
elfutils@0.193	     krb5@1.21.3	    openssh@9.9p1

-- linux-ubuntu22.04-x86_64_v3 / %c,cxx=gcc@12.3.0 --------------
cmake@3.31.9  gettext@0.23.1  libffi@3.5.2	    openssl@3.6.0    python@3.14.0
curl@8.15.0   hwloc@2.12.2    ncurses@6.5-20250705  py-cython@3.1.3  zlib-ng@2.2.4
expat@2.7.3   krb5@1.21.3     openssh@9.9p1	    py-numpy@2.3.4   zstd@1.5.7

-- linux-ubuntu22.04-x86_64_v3 / %c,fortran=gcc@11.4.0 ----------
libyogrt@1.35

-- linux-ubuntu22.04-x86_64_v3 / %c,fortran=gcc@12.3.0 ----------
netlib-lapack@3.12.1  netlib-scalapack@2.2.2  netlib-scalapack@2.2.2

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@10.5.0 ------------------
gmake@4.4.1

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@11.4.0 ------------------
automake@1.16.5		      libgcrypt@1.11.2	       pcre2@10.44
automake@1.16.5		      libgpg-error@1.55	       pdsh@2.31
bc@1.07.1		      libiconv@1.18	       perl@5.42.0
bzip2@1.0.8		      libmd@1.1.0	       perl@5.42.0
diffutils@3.12		      libpciaccess@0.17	       pigz@2.8
dtcmp@1.1.5		      libsigsegv@2.14	       pigz@2.8
findutils@4.10.0	      libssh2@1.11.1	       pkgconf@2.5.1
gawk@5.3.1		      libtool@2.4.7	       pmix@6.0.0
gdbm@1.25		      libxcrypt@4.4.38	       prrte@4.0.0
gmake@4.4.1		      libxml2@2.13.5	       readline@8.3
gmake@4.4.1		      libxml2@2.13.5	       slurm@25-05-1-1
gobject-introspection@1.86.0  lmod@8.7.18	       sqlite@3.50.4
hdf5@1.14.6		      lua-luafilesystem@1.8.0  sqlite@3.50.4
hdf5@1.14.6		      lua-luaposix@36.1	       tar@1.35
json-c@0.18		      lwgrp@1.0.6	       tar@1.35
json-cwx@0.12		      mbedtls@2.28.9	       unzip@6.0
libbsd@0.12.2		      mpc@1.3.1		       util-linux-uuid@2.41
libedit@3.1-20240808	      mpfr@4.2.1	       xz@5.6.3
libevent@2.1.12		      munge@0.5.15	       yaksa@0.4
libfabric@2.3.1		      numactl@2.0.18

-- linux-ubuntu22.04-x86_64_v3 / %c=gcc@12.3.0 ------------------
automake@1.16.5	      libevent@2.1.12	 libxcrypt@4.4.38  py-meson-python@0.18.0
bzip2@1.0.8	      libfabric@2.3.1	 libxml2@2.13.5	   readline@8.3
diffutils@3.12	      libiconv@1.18	 numactl@2.0.18	   sqlite@3.50.4
gdbm@1.25	      libidn2@2.3.7	 perl@5.42.0	   tar@1.35
git@2.48.1	      libmd@1.1.0	 pigz@2.8	   util-linux-uuid@2.41
libbsd@0.12.2	      libpciaccess@0.17	 pmix@6.0.0	   xz@5.6.3
libedit@3.1-20240808  libunistring@1.2	 prrte@4.0.0	   yaksa@0.4

-- linux-ubuntu22.04-x86_64_v3 / %cxx=gcc@11.4.0 ----------------
kokkos@4.5.01  kokkos-kernels@4.5.01

-- linux-ubuntu22.04-x86_64_v3 / %cxx=gcc@12.3.0 ----------------
py-pybind11@3.0.1  py-pythran@0.18.0

-- linux-ubuntu22.04-x86_64_v3 / no compilers -------------------
gcc-runtime@10.5.0  gcc-runtime@11.4.0	gcc-runtime@11.4.0  gcc-runtime@12.3.0

It will now work on any system with Spack installed.

With these tools, we can create custom Spack queries and prototype new ideas. Contributions that improve or extend common Spack workflows are always welcome in the community.