Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions easybuild/tools/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@
from easybuild.tools.module_naming_scheme.easybuild_mns import EasyBuildMNS
from easybuild.tools.module_naming_scheme.utilities import det_full_ec_version


_log = fancylogger.getLogger('tools.robot', fname=False)


def det_robot_path(robot_paths_option, tweaked_ecs_paths, pr_path, auto_robot=False):
"""Determine robot path."""
robot_path = robot_paths_option[:]
Expand Down Expand Up @@ -91,14 +93,35 @@ def mk_key(spec):

return (spec['name'], det_full_ec_version(spec))

# determine whether any 'wrappers' are involved
wrapper_deps = {}
for ec in ordered_ecs:
# easyconfigs using ModuleRC install a 'wrapper' for their dependency
# these need to be filtered out to avoid reporting false conflicts...
if ec['ec']['easyblock'] == 'ModuleRC':
wrapper_deps[mk_key(ec)] = mk_key(ec['ec']['dependencies'][0])

def mk_dep_keys(deps):
"""Create keys for given list of dependencies."""
res = []
for dep in deps:
# filter out dependencies marked as external module
if not dep.get('external_module', False):
key = mk_key(dep)
# replace 'wrapper' dependencies with the dependency they're wrapping
if key in wrapper_deps:
key = wrapper_deps[key]
res.append(key)
return res

# construct a dictionary: (name, installver) tuple to (build) dependencies
deps_for, dep_of = {}, {}
for node in ordered_ecs:
node_key = mk_key(node)

# exclude external modules, since we can't check conflicts on them (we don't even know the software name)
build_deps = [mk_key(d) for d in node['builddependencies'] if not d.get('external_module', False)]
deps = [mk_key(d) for d in node['ec'].all_dependencies if not d.get('external_module', False)]
build_deps = mk_dep_keys(node['builddependencies'])
deps = mk_dep_keys(node['ec'].all_dependencies)

# separate runtime deps from build deps
runtime_deps = [d for d in deps if d not in build_deps]
Expand All @@ -111,8 +134,10 @@ def mk_key(spec):

if check_inter_ec_conflicts:
# add ghost entry that depends on each of the specified easyconfigs,
# since we want to check for conflicts between specified easyconfigs too
deps_for[(None, None)] = ([], [mk_key(e) for e in easyconfigs])
# since we want to check for conflicts between specified easyconfigs too;
# 'wrapper' easyconfigs are not included to avoid false conflicts being reported
ec_keys = [k for k in [mk_key(e) for e in easyconfigs] if k not in wrapper_deps]
deps_for[(None, None)] = ([], ec_keys)

# iteratively expand list of dependencies
last_deps_for = None
Expand Down
1 change: 1 addition & 0 deletions test/framework/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ def test_list_easyblocks(self):
r'\| \|-- DummyExtension',
r'\| \|-- EB_toy',
r'\| \|-- Toy_Extension',
r'\|-- ModuleRC',
r'\|-- Toolchain',
r'Extension',
r'\|-- ExtensionEasyBlock',
Expand Down
25 changes: 25 additions & 0 deletions test/framework/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,31 @@ def test_check_conflicts(self):
# test use of check_inter_ec_conflicts
self.assertFalse(check_conflicts(ecs, self.modtool, check_inter_ec_conflicts=False), "No conflicts found")

def test_check_conflicts_wrapper_deps(self):
"""Test check_conflicts when dependency 'wrappers' are involved."""
test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
toy_ec = os.path.join(test_easyconfigs, 't', 'toy', 'toy-0.0.eb')

wrapper_ec_txt = '\n'.join([
"easyblock = 'ModuleRC'",
"name = 'toy'",
"version = '0'",
"homepage = 'https://example.com'",
"description = 'Just A Wrapper'",
"toolchain = {'name': 'dummy', 'version': ''}",
"dependencies = [('toy', '0.0')]",
])
wrapper_ec = os.path.join(self.test_prefix, 'toy-0.eb')
write_file(wrapper_ec, wrapper_ec_txt)

ecs, _ = parse_easyconfigs([(toy_ec, False), (wrapper_ec, False)])
self.mock_stderr(True)
res = check_conflicts(ecs, self.modtool)
stderr = self.get_stderr()
self.mock_stderr(False)
self.assertEqual(stderr, '')
self.assertFalse(res)

def test_robot_archived_easyconfigs(self):
"""Test whether robot can pick up archived easyconfigs when asked."""
test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
Expand Down
46 changes: 46 additions & 0 deletions test/framework/sandbox/easybuild/easyblocks/generic/modulerc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
##
# Copyright 2009-2018 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/>.
##
"""
Dummy ModuleRC easyblock.

@author: Kenneth Hoste (Ghent University)
"""
from easybuild.framework.easyblock import EasyBlock


class ModuleRC(EasyBlock):
"""Dummy implementation of generic easyblock that generates .modulerc files."""

def configure_step(self):
pass

def build_step(self):
pass

def install_step(self):
pass

def sanity_check_step(self):
pass