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

FSL - Improve configuration choice #1498

Merged
merged 9 commits into from
Sep 20, 2018

Conversation

vanzod
Copy link
Member

@vanzod vanzod commented Sep 4, 2018

FSL build relies on an internal check and a script to identify the appropriate configuration to use. Since the available configurations are for relatively old compilers, the current easyblock uses difflib.get_close_matches to identify the closest configuration and copies the directory containing it into a new one named after the configuration targeted by FSL. In certain cases the guessed closest configuration is not the same as the one at which required patches are applied, resulting in a build failure.
This easyblock change uses the patch files as first source of information to guess the target directory and rolls back to difflib.get_close_matches only if no target configuration is found in the patches.


for patch in self.patches:
try:
for line in open(patch['path'], 'r'):
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod Please use the read_file function from filetools

for patch in self.patches:
try:
for line in open(patch['path'], 'r'):
if all(regex.match(line) for regex in [systype_regex, diff_regex]):
Copy link
Member

Choose a reason for hiding this comment

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

Since there are only two, this is probably clearer:

if systype_regex.match(line) and diff_regex.match(line):

But, is there a specific reason why both regex's can't be joined in a single regex?

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason they are separate is because I use systype_regex twice and only once in conjunction with diff_regex. Hence I preferred to split them like that instead of having systype_regex being a subset of diff_regex (i.e. avoid copy/paste).

try:
for line in open(patch['path'], 'r'):
if all(regex.match(line) for regex in [systype_regex, diff_regex]):
matches = [m.group(0) for l in line.split('/') for m in [systype_regex.search(l)] if m]
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, why use a list if you're only considering systype_regex? Ah, because you need a handle for the match (m)... It looks a bit weird like this, probably better roll this out for the sake of clarity:

for part in line.split('/'):
    res = systype_regex.search(part)
    if res:
        patched_cfgs.append(res.group(0))

matches = [m.group(0) for l in line.split('/') for m in [systype_regex.search(l)] if m]
patched_cfgs.extend(matches)
except OSError, err:
raise EasyBuildError("Unable to open patch file: %s", patch['path'])
Copy link
Member

Choose a reason for hiding this comment

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

No more need for the the try/except when you use read_file

cfgdir = os.path.join(self.fsldir, "config")
try:
if not best_cfg:
Copy link
Member

Choose a reason for hiding this comment

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

The try is still relevant here, os.listdir may throw an OSError

cfgs = os.listdir(cfgdir)
best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]

self.log.debug("Best matching config dir for %s is %s" % (fslmachtype, best_cfg))

# Prepare config
# Either use patched config or copy closest match
try:
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod Please use copy_file or copy_dir below (and then there's no need for the try here

raise EasyBuildError("Unable to open patch file: %s", patch['path'])

if patched_cfgs:
if patched_cfgs[1:] == patched_cfgs[:-1]:
Copy link
Member

Choose a reason for hiding this comment

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

Why not check for len(patched_cfgs) == 1?

Copy link
Member Author

Choose a reason for hiding this comment

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

That would work in the first if statement. In the second I am checking that all strings in patched_cfgs are the same, which means that the patch targets only one configuration.

Copy link
Member

Choose a reason for hiding this comment

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

OK, then please clarify with a comment above this line, that's not clear at all :)

if patched_cfgs:
if patched_cfgs[1:] == patched_cfgs[:-1]:
best_cfg = patched_cfgs[0]
self.log.debug("Found patched config dir: %s" % (best_cfg))
Copy link
Member

Choose a reason for hiding this comment

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

self.log.debug("...", best_cfg)

self.log.debug("Found patched config dir: %s" % (best_cfg))
else:
dirs = ', '.join(patched_cfgs)
raise EasyBuildError("Patch files are editing multiple config dirs: %s", dirs)
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 OK to pass the raw patched_cfgs in the error message imho

@boegel
Copy link
Member

boegel commented Sep 17, 2018

@vanzod I quickly ran into trouble when trying this with an old FSL easyconfig (FSL-4.1.9-goolf-1.4.10.eb):

Patch files are editing multiple config dirs: apple-darwin10-gcc4.2, apple-darwin10-gcc4.2, apple-darwin7-gcc3.1, apple-darwin7-gcc3.1, apple-darwin7-gcc3.3, apple-darwin7-gcc3.3, apple-darwin8-gcc4.0, apple-darwin8-gcc4.0, apple-darwin9-gcc4.0, apple-darwin9-gcc4.0, i686-pc-cygwin-gcc3.2, i686-pc-c

So this will need a version check...

@boegel
Copy link
Member

boegel commented Sep 17, 2018

@vanzod It looks like this can only work with FSL 5.0.10 & newer (works with FSL-5.0.10-intel-2017a.eb and FSL-5.0.11-foss-2018b.eb

cfgs = os.listdir(cfgdir)
best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]
self.log.debug("Best matching config dir for %s is %s" % (fslmachtype, best_cfg))
except OSError, err:

Choose a reason for hiding this comment

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

SyntaxError: invalid syntax

Copy link
Member

Choose a reason for hiding this comment

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

This should probably be except OSError as err:

systype_regex = re.compile("^diff.*config\/(.*(apple|gnu|i686|linux|spark)(?:(?!\/).)*)", re.M)

patched_cfgs = []

Choose a reason for hiding this comment

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

blank line contains whitespace

Copy link
Member

Choose a reason for hiding this comment

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

@vanzod The hound is merciless...

Copy link
Member Author

Choose a reason for hiding this comment

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

I like that

systype_regex = re.compile("^diff.*config\/(.*(apple|gnu|i686|linux|spark)(?:(?!\/).)*)", re.M)

patched_cfgs = []

Copy link
Member

Choose a reason for hiding this comment

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

@vanzod The hound is merciless...

cfgs = os.listdir(cfgdir)
best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]
self.log.debug("Best matching config dir for %s is %s" % (fslmachtype, best_cfg))
except OSError, err:
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be except OSError as err:

patched_cfgs.extend([i[0] for i in res])

# Check that at least one config has been found
if len(patched_cfgs) != 0:
Copy link
Member

Choose a reason for hiding this comment

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

if patched_cfgs: is fine here

# Check that at least one config has been found
if len(patched_cfgs) != 0:
# Check that a single config has been patched
if patched_cfgs[1:] == patched_cfgs[:-1]:
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod This hurts my brain... How about using nub from vsc.utils.missing and change this to:

if len(nub(patched_cfgs)) == 1:

Copy link
Member Author

Choose a reason for hiding this comment

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

I like that. I did not know about nub

best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]
self.log.debug("Best matching config dir for %s is %s" % (fslmachtype, best_cfg))
except OSError, err:
raise EasyBuildError("Unable to access configurations directory: %s", err)
Copy link
Member

Choose a reason for hiding this comment

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

include value of cfgdir in error message please

best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]
self.log.debug("Best matching config dir for %s is %s" % (fslmachtype, best_cfg))
except OSError as err:
raise EasyBuildError("Unable to access configuration directory: %s", cfgdir, err)
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod You have a %s missing here, this is going to result in a crash rather than an error message, should be:

raise EasyBuildError("Unable to access configuration directory %s: %s", cfgdir, err)

self.log.debug("Copied %s to %s" % (srcdir, tgtdir))
except OSError, err:
raise EasyBuildError("Failed to copy closest matching config dir: %s", err)
if not best_cfg:
Copy link
Member

Choose a reason for hiding this comment

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

I'm hitting this now:

  File "/tmp/eb-EdQY9x/included-easyblocks/easybuild/easyblocks/fsl.py", line 95, in configure_step
    if not best_cfg:
UnboundLocalError: local variable 'best_cfg' referenced before assignment

Copy link
Member Author

Choose a reason for hiding this comment

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

It looks like I have accidentally deleted the definition of best_cfg during the refactoring

@boegel boegel added this to the 3.7.0 milestone Sep 18, 2018

patched_cfgs = []
best_cfg = None

Choose a reason for hiding this comment

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

blank line contains whitespace

systype_regex = re.compile("^diff.*config\/(.*(apple|gnu|i686|linux|spark)(?:(?!\/).)*)", re.M)

patched_cfgs = []
best_cfg = None
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod This needs to go outside of this if clause to ensure it's always defined for old FSL versions?

Copy link
Member Author

Choose a reason for hiding this comment

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

True. Sloppy brain

@boegel
Copy link
Member

boegel commented Sep 19, 2018

Tests with existing easyconfigs are under way...

raise EasyBuildError("Failed to copy closest matching config dir: %s", err)
if not best_cfg:
cfgs = os.listdir(cfgdir)
best_cfg = difflib.get_close_matches(fslmachtype, cfgs)[0]
Copy link
Member

Choose a reason for hiding this comment

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

@vanzod This whole block is now below the if for FSL 5.0.10 or newer, while it wasn't before. That makes this change backward incompatible, resulting in breaking the installation of FSL-5.0.9-intel-2016a.eb for example...

Copy link
Member

Choose a reason for hiding this comment

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

Wait, no, it's not... But there still seems to be a problem with FSL-5.0.9-intel-2016a.eb (will re-check).

@boegel
Copy link
Member

boegel commented Sep 20, 2018

Good to go, thanks @vanzod!

@boegel boegel merged commit b8fbddc into easybuilders:develop Sep 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants