Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using SystemCompiler easyblock for wrapper around system GCC fails if an Intel license is not defined #2815

Open
branfosj opened this issue Oct 25, 2022 · 5 comments · May be fixed by #3402

Comments

@branfosj
Copy link
Member

Building GCCcore-system.eb fails if a suitable Intel license definition is not set

== 2022-10-25 19:38:56,495 filetools.py:2353 INFO Considering INTEL_LICENSE_FILE to find FlexLM license specs: []
== 2022-10-25 19:38:56,495 filetools.py:2353 INFO Considering LM_LICENSE_FILE to find FlexLM license specs: []
== 2022-10-25 19:38:56,495 filetools.py:2353 INFO Considering None to find FlexLM license specs: []
== 2022-10-25 19:38:56,496 filetools.py:2390 INFO Found valid license specs via provided license spec: []
== 2022-10-25 19:38:56,582 build_log.py:169 ERROR EasyBuild crashed with an error (at easybuild/src/easybuild-framework/easybuild/base/exceptions.py:124 in __init__): No viable license specifications found; specify 'license_file', or define $INTEL_LICENSE_FILE or $LM_LICENSE_FILE (at easybuild/src/easybuild-easyblocks/easybuild/easyblocks/generic/intelbase.py:287 in prepare_step)

Following this through:

  • prepare_step in systemcompiler.py, at EB_GCC.prepare_step(self, *args, **kwargs)
  • super(EB_GCC, self).prepare_step(*args, **kwargs) in gcc.py
  • hit the check for Intel license in intelbase.py

Any of the following avoid the issue:

  • setting requires_runtime_license = False in GCCcore-system.eb
  • changing class SystemCompiler(Bundle, EB_GCC, EB_ifort) to be class SystemCompiler(Bundle, EB_ifort, EB_GCC) in systemcompiler.py
  • setting fake Intel license information

The second of these makes me believe that the correct fix is making sure the inheritance order triggers the right super in gcc.py.

@branfosj branfosj added this to the next release (4.6.3?) milestone Oct 25, 2022
@branfosj branfosj changed the title Using systemcompiler easyblock for building GCCcore fails if an Intel license is not available Using systemcompiler easyblock for building GCCcore fails if an Intel license is not defined Oct 25, 2022
@boegel
Copy link
Member

boegel commented Oct 26, 2022

Since the license check in intelbase.py is a part of prepare_step, this is weird: we're explicitly calling EB_GCC.prepare_step, so we're not calling super(SystemCompiler, self).prepare_step, and hence we should never hit EB_ifort.prepare_step (which is probably what's triggering IntelBase.prepare_step...

Changing the order in the class definition is "cheating" in some sense, and may cause trouble further down the installation process (especially when wrapping a system installation of the Intel compilers).

@boegel boegel changed the title Using systemcompiler easyblock for building GCCcore fails if an Intel license is not defined using SystemCompiler easyblock for wrapper around system GCC fails if an Intel license is not defined Oct 26, 2022
@boegel boegel modified the milestones: 4.7.3, release after 4.7.3 Jul 6, 2023
@Micket
Copy link
Contributor

Micket commented Jul 18, 2023

Just to demystify this, as I encountered the same issue with another diamond inheritance graph for my CargoPythonBundle.

The SystemCompiler.__mro__ will be constructed based on the class hierarchy.
We have GCC -> (ConfigureMake ->) EasyBlock -> object
and EB_ifort -> (EB_icc ->) IntelBase -> EasyBlock -> object

Python will determine the mro as follows

<class 'easybuild.easyblocks.generic.systemcompiler.SystemCompiler'>
<class 'easybuild.easyblocks.generic.bundle.Bundle'>  # First direct inclusion
<class 'easybuild.easyblocks.gcc.EB_GCC'>                # Second direct inclusion
<class 'easybuild.easyblocks.generic.configuremake.ConfigureMake'> # Direct dependency of EB_GCC
<class 'easybuild.easyblocks.ifort.EB_ifort'>
<class 'easybuild.easyblocks.icc.EB_icc'>
<class 'easybuild.easyblocks.generic.intelbase.IntelBase'>
<class 'easybuild.framework.easyblock.EasyBlock'>    # First common dependency; the second intel must be squeezed in before here.
<class 'object'>

So while SystemCompiler.prepare_step intentionally calls EB_GCC.prepare_step, EB_GCC.prepare_step itself calls:

    def prepare_step(self, *args, **kwargs):
        """
        Prepare build environment, track currently active build stage
        """
        super(EB_GCC, self).prepare_step(*args, **kwargs)

        # Set the current build stage to the specified stage based on the iteration index
        self.current_stage = self.build_stages[self.iter_idx]

so while one might think it should look up it's own statically determine class hierarchy (regardless of subclasses like SystemCompiler), that isn't what it does. It basically looks for the next method down the mro list.
ConfigureMake doesn't overload prepare step, so next up is EB_ifort.prepare_step or EB_icc.prepare_step or IntelBase.prepare_step.

@bartoldeman
Copy link
Contributor

@Micket would a better solution then be for ConfigureMake to overload the prepare step?

@Micket
Copy link
Contributor

Micket commented Jul 18, 2023

@bartoldeman If the first "chain" of classes continue to call super() up to the shared base class (which would eventually be the EasyBlock class) it wouldn't make any difference.

Here is a small example to play around with:

class A:
    def __init__(self):
        super(A, self).__init__()
        print("A")

class B(A):
    def __init__(self):
        super(B, self).__init__()
        print("B")

class C(B):
    def __init__(self):
        super(C, self).__init__()
        print("C")

class D(A):
    def __init__(self):
        super(D, self).__init__()
        print("D")

class Diamond(C, D):
    def __init__(self):
        C.__init__(self)  # You might expect this to go C,B,A, but it won't.

print("Just C")
C()

print("Diamond!")
Diamond()

My Diamond class doesn't even want to initialize according to D. One would probably assume that this would print C, B, A; but;

Just C
A
B
C
Diamond!
A
D
B
C

That's just what super() is intended to do.
https://stackoverflow.com/questions/42665175/diamond-inheritance-and-the-mro

@Micket
Copy link
Contributor

Micket commented Jul 18, 2023

I'm also getting hit by this type of "bug" with my attempt at CargoPythonBundle #2964, where I'm not able to change order since my new init is automatically called from Bundle's super-init.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants