Open Flamefire opened 3 months ago
@Flamefire It feels a bit backward to make changes to the GCC
easyblock for this, can't we fix this in the SystemCompiler
easyblock itself?
Ah this was the other MRO issue I mentioned in the confcall.
can't we fix this in the SystemCompiler easyblock itself?
I don't see how. The issue is caused by GCC
calling super()
which is intended if we want to call the methods of all classes in the current hierarchy, which should always be the case for "proper" dependencies. But e.g. SystemCompiler
creates a hierarchy where we don't want that.
I guess other easyblocks where we do the same to e.g. change behavior based on version have similar issues, but here it is very visible as the prepare_step
of IntelBase cannot be called when using GCC
So the trouble here is that inheritance instead of composition is used for code reuse. We would avoid issues like this, if blocks like SystemCompiler
contain an instance of the required easyblock, i.e. either GCC
or Intel
, instead of inheriting from both.
However that means we need to implement all methods and properties of EasyBlock
to direct them to the method of the contained instance.
So no idea besides making sure that any base class in a multi-inheritance scheme doesn't use super()
.
it all looks very brittle to me.
(created using
eb --new-pr
)Python MRO causes trouble here due to multi-inheritance:
SystemCompiler
inherits fromEB_GCC
andEB_ifort
EB_ifort
inherits fromEB_icc
and both fromIntelBase
prepare_step
calls e.g.EB_GCC.prepare_step
super(EB_GCC, self).prepare_step(*args, **kwargs)
prepare_step
inIntelBase
prepare_step
inConfigureMake
IntelBase
) continues, then fails:This is due to the MRO which here is
(<class 'easybuild.easyblocks.generic.systemcompiler.SystemCompiler'>, <class 'easybuild.easyblocks.generic.bundle.Bundle'>, <class 'easybuild.easyblocks.gcc.EB_GCC'>, <class 'easybuild.easyblocks.generic.configuremake.ConfigureMake'>, <class 'easybuild.easyblocks.ifort.EB_ifort'>, <class 'easybuild.easyblocks.icc.EB_icc'>, <class 'easybuild.easyblocks.generic.intelbase.IntelBase'>, <class 'easybuild.framework.easyblock.EasyBlock'>, <class 'object'>)
Only GCC, IntelBase and EasyBlock define a
prepare_step
so going up from GCC the one in IntelBase is called next.If there was one in ConfigureMake that one would be called between GCC and IntelBase.
My solution here is to directly call
ConfigureMake.prepare_step
from GCC which ends inEasyBlock.prepare_step
.This works for now but will cause the same issue if
ConfigureMake.prepare_step
with asuper()
call is added at some point.It also will be a problem if someone inherits (e.g. transitively) from
GCC
and some other class providingprepare_step
and expects both to be called.I can't think of any other easy solution.
Fixes #2815