diff --git a/easyblocks/d/dirac.py b/easyblocks/d/dirac.py new file mode 100644 index 0000000000000000000000000000000000000000..18857f3e1d3b3508ebb0d0c98f6b49f40c278966 --- /dev/null +++ b/easyblocks/d/dirac.py @@ -0,0 +1,133 @@ +## +# Copyright 2009-2019 Ghent University +# +# This file is part of EasyBuild, +# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), +# with support of Ghent University (http://ugent.be/hpc), +# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), +# Flemish Research Foundation (FWO) (http://www.fwo.be/en) +# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). +# +# https://github.com/easybuilders/easybuild +# +# EasyBuild 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 v2. +# +# EasyBuild 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 +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EasyBuild. If not, see <http://www.gnu.org/licenses/>. +## +""" +EasyBuild support for building and installing DIRAC, implemented as an easyblock +""" +import os +import re +import shutil +import tempfile + +import easybuild.tools.environment as env +import easybuild.tools.toolchain as toolchain +from easybuild.easyblocks.generic.cmakemake import CMakeMake +from easybuild.framework.easyconfig import CUSTOM, MANDATORY +from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.config import build_option +from easybuild.tools.run import run_cmd + + +class EB_DIRAC(CMakeMake): + """Support for building/installing DIRAC.""" + + def configure_step(self): + """Custom configuration procedure for DIRAC.""" + + # make very sure the install directory isn't there yet, since it may cause problems if it used (forced rebuild) + if os.path.exists(self.installdir): + self.log.warning("Found existing install directory %s, removing it to avoid problems", self.installdir) + try: + shutil.rmtree(self.installdir) + except OSError as err: + raise EasyBuildError("Failed to remove existing install directory %s: %s", self.installdir, err) + + self.cfg['separate_build_dir'] = True + #self.cfg.update('configopts', "-DENABLE_MPI=ON -DCMAKE_BUILD_TYPE=release") + self.cfg.update('configopts', "--fc=mpiifort --mpi --cc=mpiicc --cxx=mpicxx") + + # complete configuration with configure_method of parent + super(EB_DIRAC, self).configure_step() + + def test_step(self): + """Custom built-in test procedure for DIRAC.""" + if self.cfg['runtest']: + + if not build_option('mpi_tests'): + self.log.info("Skipping testing of DIRAC since MPI testing is disabled") + return + + # set up test environment + # see http://diracprogram.org/doc/release-14/installation/testing.html + env.setvar('DIRAC_TMPDIR', tempfile.mkdtemp(prefix='dirac-test-')) + env.setvar('DIRAC_MPI_COMMAND', self.toolchain.mpi_cmd_for('', self.cfg['parallel'])) + + # run tests (may take a while, especially if some tests take a while to time out) + self.log.info("Running tests may take a while, especially if some tests timeout (default timeout is 1500s)") + cmd = "make test" + out, ec = run_cmd(cmd, simple=False, log_all=False, log_ok=False) + + # check that majority of tests pass + # some may fail due to timeout, but that's acceptable + # cfr. https://groups.google.com/forum/#!msg/dirac-users/zEd5-xflBnY/OQ1pSbuX810J + + # over 90% of tests should pass + passed_regex = re.compile('^(9|10)[0-9.]+% tests passed', re.M) + if not passed_regex.search(out) and not self.dry_run: + raise EasyBuildError("Too many failed tests; '%s' not found in test output: %s", + passed_regex.pattern, out) + + # extract test results + test_result_regex = re.compile(r'^\s*[0-9]+/[0-9]+ Test \s*#[0-9]+: .*', re.M) + test_results = test_result_regex.findall(out) + if test_results: + self.log.info("Found %d test results: %s", len(test_results), test_results) + elif self.dry_run: + # dummy test result + test_results = ["1/1 Test #1: dft_alda_xcfun ............................. Passed 72.29 sec"] + else: + raise EasyBuildError("Couldn't find *any* test results?") + + test_count_regex = re.compile(r'^\s*[0-9]+/([0-9]+)') + res = test_count_regex.search(test_results[0]) + if res: + test_count = int(res.group(1)) + elif self.dry_run: + # a single dummy test result + test_count = 1 + else: + raise EasyBuildError("Failed to determine total test count from %s using regex '%s'", + test_results[0], test_count_regex.pattern) + + if len(test_results) != test_count: + raise EasyBuildError("Expected to find %s test results, but found %s", test_count, len(test_results)) + + # check test results, only 'Passed' or 'Timeout' are acceptable outcomes + faulty_tests = [] + for test_result in test_results: + if ' Passed ' not in test_result: + self.log.warning("Found failed test: %s", test_result) + if '***Timeout' not in test_result: + faulty_tests.append(test_result) + + if faulty_tests: + raise EasyBuildError("Found tests failing due to something else than timeout: %s", faulty_tests) + + def sanity_check_step(self): + """Custom sanity check for DIRAC.""" + custom_paths = { + 'files': ['bin/pam-dirac'], + 'dirs': ['share/dirac'], + } + super(EB_DIRAC, self).sanity_check_step(custom_paths=custom_paths)