Skip to content

Commit 9fad84c

Browse files
author
Vasileios Karakasis
authored
Merge pull request #1989 from rsarm/tutorial/eb
[doc] Add tutorial to showcase the use of build automation tools through ReFrame
2 parents 285d22f + 78d0414 commit 9fad84c

File tree

8 files changed

+253
-6
lines changed

8 files changed

+253
-6
lines changed

ci-scripts/ci-runner.bash

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ checked_exec()
4141

4242
run_tutorial_checks()
4343
{
44-
cmd="./bin/reframe -C tutorials/config/settings.py -J account=jenscscs \
45-
--save-log-files --flex-alloc-nodes=2 -r -x HelloThreadedExtendedTest $@"
44+
cmd="./bin/reframe -vv -C tutorials/config/settings.py -J account=jenscscs \
45+
--save-log-files --flex-alloc-nodes=2 -r -x HelloThreadedExtendedTest|BZip2.*Check $@"
4646
echo "[INFO] Running tutorial checks with \`$cmd'"
4747
checked_exec $cmd
4848
}

docs/tutorial_build_automation.rst

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
==========================================================
2+
Tutorial 5: Using Build Automation Tools As a Build System
3+
==========================================================
4+
5+
In this tutorial we will present how to use `Easybuild <https://easybuild.io/>`__ and `Spack <https://spack.io/>`__ as a build system for a ReFrame test.
6+
The example uses the configuration file presented in :doc:`tutorial_basics`, which you can find in ``tutorials/config/settings.py``.
7+
We also assume that the reader is already familiar with the concepts presented in the basic tutorial and has a working knowledge of EasyBuild and Spack.
8+
Finally, to avoid specifying the tutorial configuration file each time you run the test, make sure to export it here:
9+
10+
.. code:: bash
11+
12+
export RFM_CONFIG_FILE=$(pwd)/tutorials/config/mysettings.py
13+
14+
15+
16+
Using EasyBuild to Build the Test Code
17+
--------------------------------------
18+
19+
.. versionadded:: 3.5.0
20+
21+
22+
Let's consider a simple ReFrame test that installs ``bzip2-1.0.6`` given the easyconfig `bzip2-1.0.6.eb <https://github.com/eth-cscs/production/blob/master/easybuild/easyconfigs/b/bzip2/bzip2-1.0.6.eb>`__ and checks that the installed version is correct.
23+
The following code block shows the check, highlighting the lines specific to this tutorial:
24+
25+
.. literalinclude:: ../tutorials/build_systems/easybuild/eb_test.py
26+
:lines: 6-
27+
:emphasize-lines: 12,14-17,19-21
28+
29+
The test looks pretty standard except for the highlighted blocks.
30+
Let's have a look first to the block in the :class:`BZip2Check` class.
31+
32+
The first thing is to specify that the EasyBuild build system will be used.
33+
This is done by setting :attr:`~reframe.core.pipeline.RegressionTest.build_system` to ``'EasyBuild'``.
34+
Then, the software to be installed is passed as a list to :attr:`~reframe.core.buildsystems.EasyBuild.easyconfigs`.
35+
Here only one easyconfig is given, but more than one can be passed.
36+
Finally, through :attr:`~reframe.core.buildsystems.EasyBuild.options`, command line options can be passed to the ``eb`` executable.
37+
In this test we pass ``-f`` to make sure that ``bzip2`` will be built even if the module already exists externally.
38+
39+
For this test, ReFrame generates the following command to build and install the easyconfig:
40+
41+
.. code-block:: console
42+
43+
export EASYBUILD_BUILDPATH={stagedir}/easybuild/build
44+
export EASYBUILD_INSTALLPATH={stagedir}/easybuild
45+
export EASYBUILD_PREFIX={stagedir}/easybuild
46+
export EASYBUILD_SOURCEPATH={stagedir}/easybuild
47+
eb bzip2-1.0.6.eb -f
48+
49+
ReFrame will keep all the files generated by EasyBuild (sources, temporary files, installed software and the corresponding modules) under the test's stage directory.
50+
For this reason it sets the relevant EasyBuild environment variables.
51+
52+
.. tip::
53+
54+
Users may set the EasyBuild prefix to a different location by setting the :attr:`~reframe.core.buildsystems.EasyBuild.prefix` attribute of the build system.
55+
This allows you to have the built software installed upon successful completion of the build phase, but if the test fails in a later stage (sanity, performance), the installed software will not be cleaned up automatically.
56+
57+
.. note::
58+
59+
ReFrame assumes that the ``eb`` executable is available on the system where the compilation is run (typically the local host where ReFrame is executed).
60+
61+
62+
Now that we know everything related to building and installing the code, we can move to the part dealing with running it.
63+
To run the code, the generated modules need to be loaded in order to make the software available.
64+
The modules can be accessed through :attr:`~reframe.core.buildsystems.Easybuild.generated_modules`, however, they are available only after EasyBuild completes the installation.
65+
This means that :attr:`~reframe.core.pipeline.RegressionTest.modules` can be set only after the build phase finishes.
66+
For that, we can set :attr:`~reframe.core.pipeline.RegressionTest.modules` in a class method wrapped by the :py:func:`~reframe.core.RegressionTest.run_before` built-in, specifying the ``run`` phase.
67+
This test will then run the following commands:
68+
69+
.. code-block:: console
70+
71+
module load bzip/1.0.6
72+
bzip2 --help
73+
74+
75+
--------------------------
76+
Packaging the installation
77+
--------------------------
78+
79+
The EasyBuild build system offers a way of packaging the installation via EasyBuild's packaging support.
80+
To use this feature, `the FPM package manager <https://fpm.readthedocs.io/en/latest/intro.html>`__ must be available.
81+
By setting the dictionary :attr:`~reframe.core.buildsystems.Easybuild.package_opts` in the test, ReFrame will pass ``--package-{key}={val}`` to the EasyBuild invocation.
82+
For instance, the following can be set to package the installations as an rpm file:
83+
84+
.. code-block:: python
85+
86+
self.keep_files = ['easybuild/packages']
87+
self.build_system.package_opts = {
88+
'type': 'rpm',
89+
}
90+
91+
The packages are generated by EasyBuild in the stage directory.
92+
To retain them after the test succeeds, :attr:`~reframe.core.pipeline.RegressionTest.keep_files` needs to be set.
93+
94+
95+
96+
Using Spack to Build the Test Code
97+
----------------------------------
98+
99+
100+
.. versionadded:: 3.6.1
101+
102+
103+
This example is the equivalent to the previous one, except that it uses Spack to build ``bzip2``.
104+
Here is the test's code:
105+
106+
.. literalinclude:: ../tutorials/build_systems/spack/spack_test.py
107+
:lines: 6-
108+
:emphasize-lines: 12,14-16
109+
110+
111+
When :attr:`~reframe.core.pipeline.RegressionTest.build_system` is set to ``'Spack'``, ReFrame will leverage Spack environments in order to build the test code.
112+
For this reason, currently, users must specify an environment.
113+
ReFrame treats Spack environments as *test resources* so it expects to find them under the test's :attr:`~reframe.core.pipeline.RegressionTest.sourcesdir`, which defaults to ``'src'``.
114+
Here is the directory structure for the test in this particular example that we show here:
115+
116+
.. code:: console
117+
118+
tutorials/build_systems/spack/
119+
├── spack_test.py
120+
└── src
121+
└── myenv
122+
└── spack.yaml
123+
124+
125+
We could have placed ``spack.yaml`` directly under the ``src/`` directory, in which case we would need to specify ``'.'`` as an environment.
126+
For reference, here are the contents of ``spack.yaml``:
127+
128+
.. literalinclude:: ../tutorials/build_systems/spack/src/myenv/spack.yaml
129+
130+
131+
As with every other test, ReFrame will copy the test's resources to its stage directory before building it.
132+
ReFrame will then activate the environment and install the associated specs as in this case.
133+
Optionally, we can add more specs to the environment by setting the :attr:`~reframe.core.buildsystems.Spack.specs` attribute of the build system.
134+
Here is what ReFrame generates as a build script in this example:
135+
136+
.. code:: bash
137+
138+
. $SPACK_ROOT/share/spack/setup-env.sh
139+
spack env activate -V -d myenv
140+
spack install
141+
142+
Any additional specs specified inside the ReFrame test will be added using the ``spack add`` command.
143+
As you might have noticed ReFrame expects that Spack is already installed on the system.
144+
The packages specified in the environment and the tests will be installed in the test's stage directory, where the environment is copied before building.
145+
Here is the stage directory structure:
146+
147+
.. code:: console
148+
149+
stage/generic/default/builtin/BZip2SpackCheck/
150+
├── myenv
151+
│   ├── spack
152+
│   │   ├── opt
153+
│   │   │   └── spack
154+
│   │   │   ├── bin
155+
│   │   │   └── darwin-catalina-skylake
156+
│   │   └── share
157+
│   │   └── spack
158+
│   │   └── modules
159+
│   ├── spack.lock
160+
│   └── spack.yaml
161+
├── rfm_BZip2SpackCheck_build.err
162+
├── rfm_BZip2SpackCheck_build.out
163+
├── rfm_BZip2SpackCheck_build.sh
164+
├── rfm_BZip2SpackCheck_job.err
165+
├── rfm_BZip2SpackCheck_job.out
166+
└── rfm_BZip2SpackCheck_job.sh
167+
168+
169+
Finally, here is the generated run script that ReFrame uses to run the test, once its build has succeeded:
170+
171+
.. code-block:: bash
172+
173+
#!/bin/bash
174+
. $SPACK_ROOT/share/spack/setup-env.sh
175+
spack env activate -V -d myenv
176+
bzip2 --help
177+
178+
From this point on, sanity and performance checking are exactly identical to any other ReFrame test.
179+
180+
.. tip::
181+
182+
While developing a test using Spack or EasyBuild as a build system, it can be useful to run ReFrame with the :option:`--keep-stage-files` and :option:`--dont-restage` options to prevent ReFrame from removing the test's stage directory upon successful completion of the test.
183+
For this particular type of test, these options will avoid having to rebuild the required package dependencies every time the test is retried.

docs/tutorials.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ReFrame Tutorials
99
tutorial_advanced
1010
tutorial_deps
1111
tutorial_tips_tricks
12+
tutorial_build_automation
1213

1314

1415
Online Tutorials

reframe/core/buildsystems.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ class EasyBuild(BuildSystem):
703703
704704
ReFrame will use EasyBuild to build and install the code in the test's
705705
stage directory by default. ReFrame uses environment variables to
706-
configure EasyBuild for running, so Users can pass additional options to
706+
configure EasyBuild for running, so users can pass additional options to
707707
the ``eb`` command and modify the default behaviour.
708708
709709
.. versionadded:: 3.5.0
@@ -832,7 +832,8 @@ class Spack(BuildSystem):
832832
#: :default: :class:`None`
833833
environment = fields.TypedField(typ.Str[r'\S+'], _UndefinedType)
834834

835-
#: The list of specs to build and install within the given environment.
835+
#: A list of additional specs to build and install within the given
836+
#: environment.
836837
#:
837838
#: ReFrame will add the specs to the active environment by emititing the
838839
#: following command:

reframe/frontend/cli.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,6 @@ def _case_failed(t):
857857
printer.debug(dependencies.format_deps(testgraph))
858858
if options.restore_session is not None:
859859
testgraph, restored_cases = report.restore_dangling(testgraph)
860-
print(dependencies.format_deps(testgraph))
861-
print(restored_cases)
862860

863861
testcases = dependencies.toposort(
864862
testgraph,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2016-2021 Swiss National Supercomputing Centre (CSCS/ETH Zurich)
2+
# ReFrame Project Developers. See the top-level LICENSE file for details.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
import reframe as rfm
7+
import reframe.utility.sanity as sn
8+
9+
10+
@rfm.simple_test
11+
class BZip2EBCheck(rfm.RegressionTest):
12+
descr = 'Demo test using EasyBuild to build the test code'
13+
valid_systems = ['*']
14+
valid_prog_environs = ['builtin']
15+
executable = 'bzip2'
16+
executable_opts = ['--help']
17+
build_system = 'EasyBuild'
18+
19+
@run_before('compile')
20+
def setup_build_system(self):
21+
self.build_system.easyconfigs = ['bzip2-1.0.6.eb']
22+
self.build_system.options = ['-f']
23+
24+
@run_before('run')
25+
def prepare_run(self):
26+
self.modules = self.build_system.generated_modules
27+
28+
@sanity_function
29+
def assert_version(self):
30+
return sn.assert_found(r'Version 1.0.6', self.stderr)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2016-2021 Swiss National Supercomputing Centre (CSCS/ETH Zurich)
2+
# ReFrame Project Developers. See the top-level LICENSE file for details.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
import reframe as rfm
7+
import reframe.utility.sanity as sn
8+
9+
10+
@rfm.simple_test
11+
class BZip2SpackCheck(rfm.RegressionTest):
12+
descr = 'Demo test using Spack to build the test code'
13+
valid_systems = ['*']
14+
valid_prog_environs = ['builtin']
15+
executable = 'bzip2'
16+
executable_opts = ['--help']
17+
build_system = 'Spack'
18+
19+
@run_before('compile')
20+
def setup_build_system(self):
21+
self.build_system.environment = 'myenv'
22+
23+
@sanity_function
24+
def assert_version(self):
25+
return sn.assert_found(r'Version 1.0.6', self.stderr)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
spack:
2+
specs:
3+
4+
concretization: together
5+
config:
6+
install_tree: spack/opt/spack
7+
module_roots:
8+
tcl: spack/share/spack/modules
9+
lmod: spack/share/spack/lmod

0 commit comments

Comments
 (0)