Skip to content
Open
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
1 change: 1 addition & 0 deletions easybuild/tools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
'amdgcn_capabilities',
'backup_modules',
'banned_linked_shared_libs',
'breakpoints',
'checksum_priority',
'container_config',
'container_image_format',
Expand Down
52 changes: 47 additions & 5 deletions easybuild/tools/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,34 @@ def find_hook(label, hooks, pre_step_hook=False, post_step_hook=False):
return res


def _bash_breakpoint(*args, **kwargs):
"""Simple breakpoint hook that opens a bash shell."""
old_ps1 = os.environ.get('PS1', '')
old_promptcmd = os.environ.get('PROMPT_COMMAND', '')
os.environ['PROMPT_COMMAND'] = ''
os.environ['PS1'] = 'easybuild-breakpoint> '
os.system('bash --norc --noprofile')
os.environ['PS1'] = old_ps1
os.environ['PROMPT_COMMAND'] = old_promptcmd


def _python_breakpoint(*args, **kwargs):
"""Simple breakpoint hook that opens a Python shell."""
print('Python breakpoint reached, entering pdb shell...')
print('You can inspect/modify the state of the program from here and use `continue` to proceed.')
print('Arguments passed to this hook (will contain EasyBlock object to inspect/modify if available):')
print(' args = %s' % (args,))
print(' kwargs = %s' % (kwargs,))
import pdb
pdb.set_trace()


breakpoint_types = {
'bash': _bash_breakpoint,
'python': _python_breakpoint,
}


def run_hook(label, hooks, pre_step_hook=False, post_step_hook=False, args=None, kwargs=None, msg=None):
"""
Run hook with specified label and return result of calling the hook or None.
Expand All @@ -231,14 +259,28 @@ def run_hook(label, hooks, pre_step_hook=False, post_step_hook=False, args=None,
:param args: arguments to pass to hook function
:param msg: custom message that is printed when hook is called
"""
args = args or []
kwargs = kwargs or {}

breakpoints = build_option('breakpoints')
bk_hooks = {}
if breakpoints:
for bk in breakpoints:
bk_type, bk_label = (['bash'] + bk.split(':', 1))[-2:]
if bk_type not in breakpoint_types:
raise EasyBuildError("Unknown breakpoint type '%s' specified for breakpoint '%s'", bk_type, bk)
hook_func = breakpoint_types.get(bk_type)
if not bk_label.endswith(HOOK_SUFF):
bk_label += HOOK_SUFF
bk_hooks[bk_label] = hook_func

bp_hook = find_hook(label, bk_hooks, pre_step_hook=pre_step_hook, post_step_hook=post_step_hook)
if bp_hook:
bp_hook(*args, **kwargs)

hook = find_hook(label, hooks, pre_step_hook=pre_step_hook, post_step_hook=post_step_hook)
res = None
if hook:
if args is None:
args = []
if kwargs is None:
kwargs = {}

if pre_step_hook:
label = 'pre-' + label
elif post_step_hook:
Expand Down
4 changes: 4 additions & 0 deletions easybuild/tools/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,10 @@ def config_options(self):
None, "store_true", False,),
'avail-repositories': ("Show all repository types (incl. non-usable)",
None, "store_true", False,),
'breakpoints': (
"Drop into an interactive shell on the specified steps (use same names as for hooks comma separated)",
'strlist', 'store', None
),
'buildpath': ("Temporary build path", None, 'store', mk_full_default_path('buildpath')),
'containerpath': ("Location where container recipe & image will be stored", None, 'store',
mk_full_default_path('containerpath')),
Expand Down
Loading