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

consider potential of multiple subtoolchains in get_toolchain_hierarchy #2466

Merged

Conversation

bartoldeman
Copy link
Contributor

@bartoldeman bartoldeman commented Apr 4, 2018

This PR replaces #2234, and depends on #2464 and #2465.
subtoolchains are now searched using a breadth-first-search, which
allows multiple subtoolchains per toolchain.

Some subtoolchains such as golf and golfc are now marked OPTIONAL
since they do not have to exist as modules, and can be skipped.

Since those optional subtoolchains cannot usually be found via a
dependency search (as toolchain easyconfigs do not typically have
subtoolchains as dependencies)
try to search for subtoolchain/version with the same version as
the parent. E.g. goolfc/2.6.10 searches for golfc/2.6.10, and
golfc/2.6.10 searches for golf/2.6.10 and gcccuda/2.6.10.

This PR replaces easybuilders#2234.
subtoolchains are now searched using a breadth-first-search, which
allows multiple subtoolchains per toolchain.

Some subtoolchains such as golf and golfc are now marked OPTIONAL
since they do not have to exist as modules, and can be skipped.

Since those optional subtoolchains cannot usually be found via a
dependency search (as toolchain easyconfigs do not typically have
subtoolchains as dependencies)
try to search for subtoolchain/version with the same version as
the parent. E.g. goolfc/2.6.10 searches for golfc/2.6.10, and
golfc/2.6.10 searches for golf/2.6.10 and gcccuda/2.6.10.
from easybuild.toolchains.fft.fftw import Fftw
from easybuild.toolchains.linalg.openblas import OpenBLAS

class Golfc(GccCUDA, Golf, OpenBLAS, Fftw):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

from easybuild.toolchains.fft.fftw import Fftw
from easybuild.toolchains.linalg.openblas import OpenBLAS

class Golfc(GccCUDA, Golf, OpenBLAS, Fftw):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

@boegel boegel added this to the 3.7.0 milestone Aug 27, 2018
@easybuilders easybuilders deleted a comment from boegelbot Aug 27, 2018
@akesandgren
Copy link
Contributor

Now a bit of merging of conflicts is in order here.

@bartoldeman
Copy link
Contributor Author

Just a merge, no conflicts.

@bartoldeman
Copy link
Contributor Author

Reopening since travis still has not reported after 19 hours.

@bartoldeman bartoldeman reopened this Aug 29, 2018
@boegel boegel changed the title Multiple subtoolchains consider potential of multiple subtoolchains in get_toolchain_hierarchy Aug 29, 2018
@akesandgren
Copy link
Contributor

This looks sane to me. How much testing have you done?

@bartoldeman
Copy link
Contributor Author

I've had a simpler variant (from #2234) of this in Compute Canada for ages (since May 2017), but not this exact code yet. Will do a few tests here.

@bartoldeman
Copy link
Contributor Author

I did a test with this code on CP2K-iomkl now which in our setup depends on libxsmm-iimkl using this subtoolchain mechanism. It works.

I did promise in last week's conf call however to add a comment with the dependency tree for the most complex case (goolfc). So I will do that.

@bartoldeman
Copy link
Contributor Author

One more thing, this approach should be applied to many other toolchains to be complete, at least the following:
foss.py
gmkl.py
gmklc.py
iimkl.py
iimklc.py
intel.py
iomkl.py
pmkl.py

should I submit a seperate PR for this fairly boring but noisy diff?

@akesandgren
Copy link
Contributor

I think a separate PR for that would be easier to handle? @boegel ??

@bartoldeman
Copy link
Contributor Author

travis now fails with File "/tmp/422121558/lib/python2.6/site-packages/easybuild_framework-3.6.3.dev0-py2.6.egg/easybuild/tools/job/gc3pie.py", line 94, in _check_version version_str = gc3libs.core.__version__ AttributeError: 'module' object has no attribute '__version__'
which looks unrelated.

@bartoldeman
Copy link
Contributor Author

Related to Travis issue:
#2204

@boegel
Copy link
Member

boegel commented Aug 29, 2018

travis now fails with File "/tmp/422121558/lib/python2.6/site-packages/easybuild_framework-3.6.3.dev0-py2.6.egg/easybuild/tools/job/gc3pie.py", line 94, in _check_version version_str = gc3libs.core.__version__ AttributeError: 'module' object has no attribute '__version__'
which looks unrelated.

@riccardomurri Can this somehow be caused by the GC3Pie release? The latest version of GC3Pie gets installed automagically, it seems like 2.5.0 includes a backwards incompatible change?!

@riccardomurri
Copy link
Contributor

The __version__ fields were removed from the GC3Pie source files, to keep the release version in one place only (setup.py).

The pkg_resources API should be used to check the package version -- I'll add to the GC3Pie 2.5.0 compatibility PR.

@boegel
Copy link
Member

boegel commented Aug 29, 2018

@riccardomurri Well, ok, I can see the motivation, but this is a backward-incompatible change, so anyone who will be trying to use an existing EasyBuild release with GC3Pie 2.5.0 will run into this... :)

Very good reason to get #2474 ready & merged to be included in EasyBuild v3.7.0 ASAP... :)

Copy link
Member

@boegel boegel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartoldeman Do you mind adding a dedicated test for det_subtoolchain_version? In test/framework/easyconfig.py would make sense, but put it wherever it's most convenient (get_toolchain_hierarchy is tested in test/framework/robot.py)

# skip dummy if add_dummy_to_minimal_toolchains is not set
return None

return {'name': subtoolchain_name, 'version': subtoolchain_version}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartoldeman I would just return subtoolchain_version here? I don't really see the point in returning a dict value here...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't because of the change of name from "GCCcore" to "dummy" for legacy toolchains. I did not want to remove that code.

I suspect the idea is that older toolchains had GCC directly above dummy, but the gcc toolchain has GCCcore as the subtoolchain which does not exist in those situations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, OK, I figured there must have been a good reason.

Then: i) mention that in a comment, explain why you need to return the subtoolchain name too, ii) just make this a tuple (subtoolchain_name, subtoolchain_version).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some ways around this of course, but e.g. moving the GCCcore logic back to the main function needs another check for dummy there too then, which is awkward...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's fine like this, just needs a comment since it doesn't make sense unless you take the whole function into account.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the end I found a more elegant way by declaring the gcccore toolchain as optional. It still needs some special logic in the main loop, but det_subtoolchain_version does exactly that now and nothing more.

Next will be tests for that but probably on Monday :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only read this after your earlier comment, looks good to me in its current state, it doesn't hurt that the fallback to dummy for GCCcore stands out a bit.

subtoolchain_name, subtoolchain_version = subtoolchains[current_tc_name], None
toolchain_hierarchy.insert(0, {'name': current_tc_name, 'version': current_tc_version})
for subtoolchain_name in subtoolchain_names:
tc = det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains, cands)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the return type of det_subtoolchain_version, I'd go with subtc_ver (but I guess subtoolchain_version is better for consistency with subtoolchain_name)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the above in mind:

subtc_name, subtc_ver = det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains, cands)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That won't work since the function can also return None.
I tried to implement your suggestion but I don't think it looks any better:

@@ -185,7 +185,7 @@ def det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains,
         # skip dummy if add_dummy_to_minimal_toolchains is not set
         return None

-    return {'name': subtoolchain_name, 'version': subtoolchain_version}
+    return subtoolchain_name, subtoolchain_version


 @toolchain_hierarchy_cache
@@ -278,12 +278,15 @@ def get_toolchain_hierarchy(parent_toolchain):
         cands = [c for c in cands if c['name'] in subtoolchain_names]

         for subtoolchain_name in subtoolchain_names:
-            tc = det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains, cands)
+            subtc = det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains, cands)
             # add to hierarchy and move to next
-            if tc is not None and tc['name'] not in visited:
-                toolchain_hierarchy.insert(0, tc)
-                bfs_queue.insert(0, tc)
-                visited.add(tc['name'])
+            if subtc is not None:
+                subtc_name, subtc_ver = subtc
+                if subtc_name not in visited:
+                    tc = {'name': subtc_name, 'version': subtc_ver}
+                    toolchain_hierarchy.insert(0, tc)
+                    bfs_queue.insert(0, tc)
+                    visited.add(subtc_name)

     _log.info("Found toolchain hierarchy for toolchain %s: %s", parent_toolchain, toolchain_hierarchy)
     return toolchain_hierarchy

unless I am missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I overlooked the fact that you need tc as a dict anyway... You're right, keep it as is. :)

for subtoolchain_name in subtoolchain_names:
tc = det_subtoolchain_version(current_tc, subtoolchain_name, optional_toolchains, cands)
# add to hierarchy and move to next
if tc is not None and tc['name'] not in visited:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the tc['name'] that makes this a bit awkward, just use subtoolchain_name?

Legacy where gcc/iccifort/etc have dummy directly as subtoolchain,
and GCCcore does not exist.
@boegel
Copy link
Member

boegel commented Sep 1, 2018

@bartoldeman Gave this another review, looks good, just missing the test for det_subtoolchain_version...

@akesandgren
Copy link
Contributor

The OPTIONAL flag in the toolchain definitions, that's used in a toolchain that is optional, not in toolchains that have optional sub-toolchains?
Was trying to look at the toolchain changes and want to make sure...

@bartoldeman
Copy link
Contributor Author

It is the toolchain itself that is optional. However I hit an issue by marking gcccore optional it marked almost all others optional too through subclassing...will correct tomorrow. Today we are all off in Canada (labour day)

By allowing both GCCcore and dummy as allowed subtoolchains for
compilers we no longer need the special logic in easyconfig.py.
However I did need to unconditionally return '' as version for the
dummy toolchain to avoid inconsistencies with 'dummy','dummy',
which resolves to the same dependency anyway.
This is now done in the candidate search instead of
det_toolchain_version, and applies to all subtoolchains that are
composite, so they can have the same version as the parent.
@@ -2078,6 +2080,52 @@ def test_resolve_template(self):
# '%(name)' is not a correct template spec (missing trailing 's')
self.assertEqual(resolve_template('%(name)', tmpl_dict), '%(name)')


def test_det_subtoolchain_version(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many blank lines (2)

@bartoldeman
Copy link
Contributor Author

ok, I have been able to simplify the code quite a bit by giving the gcc, iccifort and pgi toolchains multiple subtoolchains (GCCcore and dummy). This way the data can do the talking which looks much more elegant than special case code.

Test case added for det_subtoolchain_version too.


class GccToolchain(GCCcore):
"""Simple toolchain with just the GCC compilers."""
NAME = 'GCC'
COMPILER_MODULE_NAME = [NAME]
SUBTOOLCHAIN = GCCcore.NAME
SUBTOOLCHAIN = [GCCcore.NAME, DUMMY_TOOLCHAIN_NAME]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartoldeman How does this relate to --add-dummy-to-minimal-toolchains?

dummy should only be considered a subtoolchain when --add-dummy-to-minimal-toolchains is enabled?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it's probably OK, since dummy was already a subtoolchain for GCCcore (see below)..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly. the bfs tree has dummy twice here:

       gcc
     /     \
gcccore    dummy
    |
  dummy

which actually corresponds to pre-existing behaviour since the legacy gcc toolchains do not have gcccore. det_toolchain_version then explicitly filters out dummy if --add-dummy-to-minimal-toolchains is not set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, should adjust the ascii-art diagram comment then!

Copy link
Member

@boegel boegel Sep 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, indeed :)

It's basically this:

       gcc
       /  \
      /    \
gcccore    /
     \    /
      \  /
       dummy

(I need more practice in this ASCII art thing, ugh...)

@@ -37,3 +37,4 @@ class GCCcore(Gcc):
# Replace the default compiler module name with our own
COMPILER_MODULE_NAME = [NAME]
SUBTOOLCHAIN = DUMMY_TOOLCHAIN_NAME
OPTIONAL = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartoldeman Can you clarify why GCCcore is optional?

It's the base of every toolchain, or am I overlooking something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not the base of the older toolchains with GCC 4.8 / intel 2015.2 and older.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I overlooked that...

Please clarify with a comment above:

# GCCcore is only guaranteed to be present in recent toolchains
# for old versions of some toolchains (GCC, intel) it is not part of the hierarchy and hence optional

boegel
boegel previously approved these changes Sep 4, 2018
@akesandgren
Copy link
Contributor

Apart from the missing toolchains (esp intelcuda and friends), this looks good.

@boegel
Copy link
Member

boegel commented Sep 4, 2018

@akesandgren Isn't intelcuda handled via iimklc.py which is updated here?

@boegel
Copy link
Member

boegel commented Sep 4, 2018

nvm, changes in 345c515 make sense...

Anything else @akesandgren?

@bartoldeman
Copy link
Contributor Author

No @boegel, @akesandgren is right. I missed intelcuda and intel-para as well.

Some others could have multiple subtoolchains except they do not exist yet. I skipped them for now:
cgmpolf/cgmvolf/cgoolf => cgolf (all Clang based toolchain easyconfigs are archived)
gmacml => gacml (not done yet, also ACML looks no longer current)
goalf => galf (not done yet, atlas is also mostly archived)
goblf => gblf (not done yet, on my plate for later)
iqacml => iacml (see above; QLogic MPI is also pretty dead)

@akesandgren
Copy link
Contributor

I'm a bit tired so this is probably wrong^3, but why doesn't iccifort have both icc and ifort listed as subtoolchains

@boegel
Copy link
Member

boegel commented Sep 4, 2018

@akesandgren Neither icc nor ifort are toolchains by themselves currently. We can consider refactoring that, but that should be done in a separate PR.

@akesandgren
Copy link
Contributor

Going in, thanks @bartoldeman

@akesandgren akesandgren merged commit 811f3a3 into easybuilders:develop Sep 5, 2018
elif len(uniq_subtc_versions) == 0:
if subtoolchain_name not in optional_toolchains:
# raise error if the subtoolchain considered now is not optional
raise EasyBuildError("No version found for subtoolchain %s in dependencies of %s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartoldeman I'm hitting this error with our intel-para toolchain but I don't understand why since Iimkl is an optional toolchain

== 2018-09-21 14:42:03,912 build_log.py:158 ERROR EasyBuild crashed with an error (at ?:124 in __init__): No version found for subtoolchain <class 'easybuild.toolchains.iimkl.Iimkl'> in dependencies of intel-para (at easybuild/framework/easyconfig/easyconfig.py:162 in det_subtoolchain_version)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind PR on the way

elif len(uniq_subtc_versions) == 0:
if subtoolchain_name not in optional_toolchains:
# raise error if the subtoolchain considered now is not optional
raise EasyBuildError("No version found for subtoolchain %s in dependencies of %s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind PR on the way

easybuild/toolchains/intel-para.py Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants