Skip to content

Commit 58d6a8d

Browse files
committed
add spec highlighting
1 parent c1f75b1 commit 58d6a8d

11 files changed

Lines changed: 174 additions & 94 deletions

_static/css/custom.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ div.version-switch select:hover {
4343
.toc-tree li.scroll-current>.reference {
4444
font-weight: normal;
4545
}
46+
47+
.highlight .go {
48+
color: #333;
49+
}

conf.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
from sphinx.domains.python import PythonDomain
2222

23+
from pygments.lexer import RegexLexer, default
24+
from pygments.token import *
25+
26+
2327
# -- Spack customizations -----------------------------------------------------
2428
# Add the Spack bin directory to the path so that we can use its output in docs.
2529
os.environ["SPACK_ROOT"] = os.path.abspath("_spack_root")
@@ -29,9 +33,80 @@
2933
os.environ["COLIFY_SIZE"] = "25x120"
3034
os.environ["COLUMNS"] = "120"
3135

36+
sys.path.insert(0, os.path.abspath("_spack_root/lib/spack/"))
37+
3238
# Enable todo items
3339
todo_include_todos = True
3440

41+
from spack.spec_parser import SpecTokens
42+
43+
44+
class SpecLexer(RegexLexer):
45+
"""A custom lexer for Spack spec strings and spack commands."""
46+
47+
name = "Spack spec"
48+
aliases = ["spec"]
49+
filenames = []
50+
tokens = {
51+
"root": [
52+
# Looks for `$ command`, which may need spec highlighting.
53+
(r"^\$\s+", Generic.Prompt, "command"),
54+
(r"#.*?\n", Comment.Single),
55+
# Alternatively, we just get a literal spec string, so we move to spec mode. We just
56+
# look ahead here, without consuming the spec string.
57+
(r"(?=\S+)", Generic.Prompt, "spec"),
58+
],
59+
"command": [
60+
# A spack install command is followed by a spec string, which we highlight.
61+
(
62+
r"spack(?:\s+(?:-[eC]\s+\S+|--?\S+))*\s+(?:install|uninstall|spec|load|unload|find|info|list|versions|providers|mark|diff|add|develop)(?: +(?:--?\S+)?)*",
63+
Text,
64+
"spec",
65+
),
66+
# Comment
67+
(r"\s+#.*?\n", Comment.Single, "command_output"),
68+
# Escaped newline should leave us in this mode
69+
(r".*?\\\n", Text),
70+
# Otherwise, it's the end of the command
71+
(r".*?\n", Text, "command_output"),
72+
],
73+
"command_output": [
74+
(r"^\$\s+", Generic.Prompt, "#pop"), # new command
75+
(r"#.*?\n", Comment.Single), # comments
76+
(r".*?\n", Generic.Output), # command output
77+
],
78+
"spec": [
79+
# New line terminates the spec string
80+
(r"\s*?$", Text, "#pop"),
81+
# Dependency, with optional virtual assignment specifier
82+
(SpecTokens.START_EDGE_PROPERTIES.regex, Name.Variable, "edge_properties"),
83+
(SpecTokens.DEPENDENCY.regex, Name.Variable),
84+
# versions
85+
(SpecTokens.VERSION_HASH_PAIR.regex, Keyword.Pseudo),
86+
(SpecTokens.GIT_VERSION.regex, Keyword.Pseudo),
87+
(SpecTokens.VERSION.regex, Keyword.Pseudo),
88+
# variants
89+
(SpecTokens.PROPAGATED_BOOL_VARIANT.regex, Name.Function),
90+
(SpecTokens.BOOL_VARIANT.regex, Name.Function),
91+
(SpecTokens.PROPAGATED_KEY_VALUE_PAIR.regex, Name.Function),
92+
(SpecTokens.KEY_VALUE_PAIR.regex, Name.Function),
93+
# filename
94+
(SpecTokens.FILENAME.regex, Text),
95+
# Package name
96+
(SpecTokens.FULLY_QUALIFIED_PACKAGE_NAME.regex, Name.Class),
97+
(SpecTokens.UNQUALIFIED_PACKAGE_NAME.regex, Name.Class),
98+
# DAG hash
99+
(SpecTokens.DAG_HASH.regex, Text),
100+
(SpecTokens.WS.regex, Text),
101+
# Also stop at unrecognized tokens (without consuming them)
102+
default("#pop"),
103+
],
104+
"edge_properties": [
105+
(SpecTokens.KEY_VALUE_PAIR.regex, Name.Function),
106+
(SpecTokens.END_EDGE_PROPERTIES.regex, Name.Variable, "#pop"),
107+
],
108+
}
109+
35110

36111
#
37112
# Disable duplicate cross-reference warnings.
@@ -47,6 +122,7 @@ def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
47122

48123
def setup(sphinx):
49124
sphinx.add_domain(PatchedPythonDomain, override=True)
125+
sphinx.add_lexer("spec", SpecLexer)
50126

51127

52128
# -- General configuration -----------------------------------------------------
@@ -143,6 +219,8 @@ def setup(sphinx):
143219
# The theme to use for HTML and HTML Help pages. See the documentation for
144220
# a list of builtin themes.
145221
html_theme = "furo"
222+
pygments_style = "default"
223+
pygments_dark_style = "monokai"
146224

147225
# Add any paths that contain custom themes here, relative to this directory.
148226
# html_theme_path = ["_themes"]

tutorial_basics.rst

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ To install a software package, type:
7575
Let's go ahead and install ``gmake``,
7676

7777
.. literalinclude:: outputs/basics/gmake.out
78-
:language: console
78+
:language: spec
7979

8080
You will see Spack installed ``gmake``, ``gcc``, ``gcc-runtime``, and ``glibc``.
8181
The ``glibc`` and ``gcc-runtime`` packages are automatically tracked by Spack to manage consistency requirements among compiler runtimes.
@@ -108,7 +108,7 @@ The ``%`` sigil is used to specify direct dependencies like a package's compiler
108108
For example, we can install zlib (a commonly used compression library), but instead of building it with the GCC compiler as we did for gmake previously, we'll install it with ``%clang`` to build it with the clang compiler.
109109

110110
.. literalinclude:: outputs/basics/zlib-clang.out
111-
:language: console
111+
:language: spec
112112

113113
Notice that this installation is located separately from the previous one.
114114
We'll explore this concept in more detail later, but this separation is fundamental to how Spack supports multiple configurations and versions of software packages simultaneously.
@@ -118,36 +118,36 @@ Before we install additional versions, we can check the versions available to us
118118
Let's check what versions of zlib-ng are available, and then we'll install a different version to demonstrate Spack's flexibility in managing multiple package versions.
119119

120120
.. literalinclude:: outputs/basics/versions-zlib.out
121-
:language: console
121+
:language: spec
122122

123123
The ``@`` sigil is used to specify versions.
124124

125125
.. literalinclude:: outputs/basics/zlib-2.0.7.out
126-
:language: console
126+
:language: spec
127127

128128
The spec syntax is recursive -- any syntax we can specify for the "root" package (``zlib-ng``) we can also use for a dependency.
129129

130130
.. literalinclude:: outputs/basics/zlib-gcc-10.out
131-
:language: console
131+
:language: spec
132132

133133
The spec syntax in Spack also supports compiler flags.
134134
We can specify parameters such as ``cppflags``, ``cflags``, ``cxxflags``, ``fflags``, ``ldflags``, and ``ldlibs``.
135135
If any of these values contain spaces, we'll need to enclose them in quotes on the command line.
136136
Spack’s compiler wrappers will automatically inject these flags into the appropriate compilation commands.
137137

138138
.. literalinclude:: outputs/basics/zlib-O3.out
139-
:language: console
139+
:language: spec
140140

141141
After installing packages, we can use the ``spack find`` command to query which packages are installed.
142142
Notice that by default, some installed packages appear identical in the output.
143143
To help distinguish between them, we can add the ``-l`` flag to display each package’s unique hash.
144144
Additionally, if we include the ``-f`` flag, Spack will show any non-empty compiler flags that were used during installation.
145145

146146
.. literalinclude:: outputs/basics/find.out
147-
:language: console
147+
:language: spec
148148

149149
.. literalinclude:: outputs/basics/find-lf.out
150-
:language: console
150+
:language: spec
151151

152152
Spack generates a unique hash for each spec.
153153
This hash reflects the complete provenance of the package, so any change to the spec—such as compiler version, build options, or dependencies—will result in a different hash.
@@ -158,14 +158,14 @@ By default, Spack prioritizes reusing installations that already exist, whether
158158
This approach helps us avoid unnecessary rebuilds of common dependencies, which is especially valuable if we update Spack frequently.
159159

160160
.. literalinclude:: outputs/basics/tcl.out
161-
:language: console
161+
:language: spec
162162

163163
Sometimes it is simpler to specify dependencies without caring whether they are direct or transitive dependencies.
164164
To do that, use the ``^`` sigil.
165165
Note that a dependency specified by ``^`` is always applied to the root package, whereas a direct dependency specified by ``%`` is applied to either the root or any intervening dependency specified by ``^``.
166166

167167
.. literalinclude:: outputs/basics/tcl-zlib-clang.out
168-
:language: console
168+
:language: spec
169169

170170
We can also refer to packages from the command line by their package hash.
171171
Earlier, when we used the ``spack find -lf`` command, we saw that the hash for our optimized installation of zlib-ng (with ``cflags="-O3"``) began with ``umrbkwv``.
@@ -175,20 +175,20 @@ Similar to tools like Git, we do not need to enter the entire hash on the comman
175175
If the prefix we provide matches more than one installed package, Spack will report an error and prompt us to be more specific.
176176

177177
.. literalinclude:: outputs/basics/tcl-zlib-hash.out
178-
:language: console
178+
:language: spec
179179

180180
The ``spack find`` command can also take a ``-d`` flag, which can show dependency information.
181181
Note that each package has a top-level entry, even if it also appears as a dependency.
182182

183183
.. literalinclude:: outputs/basics/find-ldf.out
184-
:language: console
184+
:language: spec
185185

186186
Spack models the dependencies of packages as a directed acyclic graph (DAG).
187187
The ``spack find -d`` command shows the tree representation of that graph, which loses some dependency relationship information.
188188
We can also use the ``spack graph`` command to view the entire DAG as a graph.
189189

190190
.. literalinclude:: outputs/basics/graph-tcl.out
191-
:language: console
191+
:language: spec
192192

193193
Let's move on to slightly more complicated packages.
194194
HDF5 is a good example of a more complicated package, with an MPI dependency.
@@ -197,12 +197,12 @@ We can check the install plan in advance to ensure it's what we want to install
197197
The ``spack spec`` command accepts the same spec syntax.
198198

199199
.. literalinclude:: outputs/basics/hdf5-spec.out
200-
:language: console
200+
:language: spec
201201

202202
Assuming we're happy with that configuration, we will now install it.
203203

204204
.. literalinclude:: outputs/basics/hdf5.out
205-
:language: console
205+
:language: spec
206206

207207
Spack packages can also have build options, called variants.
208208
Boolean variants can be specified using the ``+`` (enable) and ``~`` or ``-``
@@ -212,7 +212,7 @@ Variants (boolean or otherwise) can also be specified using the same syntax as c
212212
Here we can install HDF5 without MPI support.
213213

214214
.. literalinclude:: outputs/basics/hdf5-no-mpi.out
215-
:language: console
215+
:language: spec
216216

217217
We might also want to install HDF5 with a different MPI implementation.
218218
While ``mpi`` itself is a virtual package representing an interface, other packages can depend on such abstract interfaces.
@@ -231,7 +231,7 @@ We call this "virtual assignment", and can be specified by ``%virtual=provider``
231231

232232
For example if we wanted to install hdf5 using GCC for the C and C++ components but Intel OneAPI for the Fortran compiler we could write:
233233

234-
.. code-block:: none
234+
.. code-block:: spec
235235
236236
hdf5 %c,cxx=gcc %fortran=oneapi
237237
@@ -240,7 +240,7 @@ We could use the same syntax for ``^mpi=mpich``, but there's no need because the
240240
This is also why we didn't care to specify which virtuals ``gcc`` and ``clang`` provided earlier when building simpler packages.
241241

242242
.. literalinclude:: outputs/basics/hdf5-hl-mpi.out
243-
:language: console
243+
:language: spec
244244

245245
.. note::
246246

@@ -252,13 +252,13 @@ This is also why we didn't care to specify which virtuals ``gcc`` and ``clang``
252252
We'll do a quick check in on what we have installed so far.
253253

254254
.. literalinclude:: outputs/basics/find-ldf-2.out
255-
:language: console
255+
:language: spec
256256

257257
HDF5 is more complicated than our basic example of zlib-ng and Tcl, but it's still within the realm of software that an experienced HPC user could reasonably expect to manually install given a bit of time.
258258
Now let's look at an even more complicated package.
259259

260260
.. literalinclude:: outputs/basics/trilinos.out
261-
:language: console
261+
:language: spec
262262

263263
Now we're starting to see the power of Spack.
264264
Depending on the spec, Trilinos can have over 30 direct dependencies, many of which have dependencies of their own.
@@ -270,19 +270,19 @@ Every MPI dependency will be satisfied by the same configuration of MPI, etc.
270270
If we install Trilinos again specifying a dependency on our previous HDF5 built with MPICH:
271271

272272
.. literalinclude:: outputs/basics/trilinos-hdf5.out
273-
:language: console
273+
:language: spec
274274

275275
We see that every package in the Trilinos DAG that depends on MPI now uses MPICH.
276276

277277
.. literalinclude:: outputs/basics/find-d-trilinos.out
278-
:language: console
278+
:language: spec
279279

280280
As we discussed before, the ``spack find -d`` command shows the dependency information as a tree.
281281
While that is often sufficient, many complicated packages, including Trilinos, have dependencies that cannot be fully represented as a tree.
282282
Again, the ``spack graph`` command shows the full DAG of the dependency information.
283283

284284
.. literalinclude:: outputs/basics/graph-trilinos.out
285-
:language: console
285+
:language: spec
286286

287287
You can control how the output is displayed with a number of options.
288288

@@ -303,18 +303,18 @@ Earlier we installed many configurations each of zlib-ng and Tcl.
303303
Now we will go through and uninstall some of those packages that we didn't really need.
304304

305305
.. literalinclude:: outputs/basics/find-d-tcl.out
306-
:language: console
306+
:language: spec
307307

308308
.. literalinclude:: outputs/basics/find-zlib.out
309-
:language: console
309+
:language: spec
310310

311311
We can uninstall packages by spec using the same syntax as install.
312312

313313
.. literalinclude:: outputs/basics/uninstall-zlib.out
314-
:language: console
314+
:language: spec
315315

316316
.. literalinclude:: outputs/basics/find-lf-zlib.out
317-
:language: console
317+
:language: spec
318318

319319
We can also uninstall packages by referring only to their hash.
320320

@@ -323,19 +323,19 @@ Use ``--force`` to remove just the specified package, leaving dependents broken.
323323
Use ``--dependents`` to remove the specified package and all of its dependents.
324324

325325
.. literalinclude:: outputs/basics/uninstall-needed.out
326-
:language: console
326+
:language: spec
327327

328328
.. literalinclude:: outputs/basics/uninstall-r-needed.out
329-
:language: console
329+
:language: spec
330330

331331
Spack will not uninstall packages that are not sufficiently specified (i.e., if the spec is ambiguous and matches multiple installed packages).
332332
The ``--all`` (or ``-a``) flag can be used to uninstall all packages matching an ambiguous spec.
333333

334334
.. literalinclude:: outputs/basics/uninstall-ambiguous.out
335-
:language: console
335+
:language: spec
336336

337337
.. literalinclude:: outputs/basics/uninstall-specific.out
338-
:language: console
338+
:language: spec
339339

340340
-----------------------------
341341
Advanced ``spack find`` Usage
@@ -348,17 +348,17 @@ The ``spack find`` command can accept what we call "anonymous specs." These are
348348
For example, ``spack find ^mpich`` will return every installed package that depends on MPICH, and ``spack find cflags="-O3"`` will return every package which was built with ``cflags="-O3"``.
349349

350350
.. literalinclude:: outputs/basics/find-dep-mpich.out
351-
:language: console
351+
:language: spec
352352

353353
.. literalinclude:: outputs/basics/find-O3.out
354-
:language: console
354+
:language: spec
355355

356356
The ``find`` command can also show which packages were installed explicitly (rather than pulled in as a dependency) using the lowercase ``-x`` flag.
357357
The uppercase ``-X`` flag shows implicit installs only.
358358
The ``find`` command can also show the path to which a Spack package was installed using the ``-p`` flag.
359359

360360
.. literalinclude:: outputs/basics/find-px.out
361-
:language: console
361+
:language: spec
362362

363363
---------------------
364364
Customizing Compilers
@@ -375,22 +375,22 @@ Later in the tutorial we will discuss how to configure external compilers by han
375375
Spack can also use compilers built by Spack to compile later packages.
376376

377377
.. literalinclude:: outputs/basics/install-gcc-12.1.0.out
378-
:language: console
378+
:language: spec
379379

380380
.. literalinclude:: outputs/basics/compilers-2.out
381-
:language: console
381+
:language: spec
382382

383383
Because this compiler is a newer version than the external compilers Spack knows about, it will be the new default compiler.
384384
We will discuss changing these defaults in a later section.
385385
We can check that this compiler is preferred by looking at the install plan for a package that isn't being reused from binary.
386386

387387
.. literalinclude:: outputs/basics/spec-zziplib
388-
:language: console
388+
:language: spec
389389

390390
For the test of the tutorial we will sometimes use this new compiler, and sometimes we want to demonstrate things without it. For now, we will uninstall it to avoid using it in the next section.
391391

392392
.. literalinclude:: outputs/basics/compiler-uninstall.out
393-
:language: console
393+
:language: spec
394394

395395
.. note::
396396

0 commit comments

Comments
 (0)