Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
119 changes: 119 additions & 0 deletions easybuild/easyblocks/generic/_config_guess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# This file contains constants and functions required to find and update
# config.guess files in a source tree.

import os
import re
from datetime import datetime

from easybuild.framework.easyblock import EasyBlock
from easybuild.tools.config import source_paths
from easybuild.tools.filetools import read_file, remove_file, verify_checksum
from easybuild.easyblocks import VERSION as EASYBLOCKS_VERSION
from easybuild.tools.filetools import CHECKSUM_TYPE_SHA256, adjust_permissions, compute_checksum, download_file

# download location & SHA256 for config.guess script
# note: if this is updated, don't forget to trash the cached download from generic/Configure/<eb_version>/!
CONFIG_GUESS_VERSION = '2018-08-29'
CONFIG_GUESS_URL_STUB = "https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb="
CONFIG_GUESS_COMMIT_ID = "59e2ce0e6b46bb47ef81b68b600ed087e14fdaad"
CONFIG_GUESS_SHA256 = "c02eb9cc55c86cfd1e9a794e548d25db5c9539e7b2154beb649bc6e2cbffc74c"

class ConfigGuessUpdater(EasyBlock):
def __init__(self, *args, **kwargs):
"""Initialize easyblock."""
super(ConfigGuessUpdater, self).__init__(*args, **kwargs)

self.config_guess = None

def fetch_step(self, *args, **kwargs):
"""Custom fetch step for ConfigGuessUpdater so we use an updated config.guess."""
super(ConfigGuessUpdater, self).fetch_step(*args, **kwargs)

# Use an updated config.guess from a global location (if possible)
self.config_guess = self.obtain_config_guess()

def obtain_config_guess(self, download_source_path=None, search_source_paths=None):
"""
Locate or download an up-to-date config.guess for use with ConfigureMake

:param download_source_path: Path to download config.guess to
:param search_source_paths: Paths to search for config.guess
:return: Path to config.guess or None
"""
eb_source_paths = source_paths()
if download_source_path is None:
download_source_path = eb_source_paths[0]
if search_source_paths is None:
search_source_paths = eb_source_paths

config_guess = 'config.guess'
sourcepath_subdir = os.path.join('generic', 'eb_v%s' % EASYBLOCKS_VERSION, 'ConfigureMake')

config_guess_path = None

# check if config.guess has already been downloaded to source path
for path in eb_source_paths:
cand_config_guess_path = os.path.join(path, sourcepath_subdir, config_guess)
if os.path.isfile(cand_config_guess_path):
config_guess_path = cand_config_guess_path
self.log.info("Found recent %s at %s, using it if required", config_guess, config_guess_path)
break

# if not found, try to download it
if config_guess_path is None:
cand_config_guess_path = os.path.join(download_source_path, sourcepath_subdir, config_guess)
config_guess_url = CONFIG_GUESS_URL_STUB + CONFIG_GUESS_COMMIT_ID
downloaded_path = download_file(config_guess, config_guess_url, cand_config_guess_path)
if downloaded_path is not None:
# verify SHA256 checksum of download to avoid using a corrupted download
if verify_checksum(downloaded_path, CONFIG_GUESS_SHA256):
config_guess_path = downloaded_path
# add execute permissions
adjust_permissions(downloaded_path, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH, add=True)
self.log.info("Downloaded recent %s to %s, using it if required", config_guess, config_guess_path)
else:
self.log.warning("Checksum failed for downloaded file %s, not using it!", downloaded_path)
remove_file(downloaded_path)
else:
self.log.warning("Failed to download recent %s to %s for use with ConfigureMake easyblock (if needed)",
config_guess, cand_config_guess_path)

return config_guess_path

def check_config_guess(self):
"""
Check timestamp & SHA256 checksum of config.guess script.

Returns True if ok (or there is no config.guess for this package) and False if it's too old
or doesn't match the checksum.
"""
# log version, timestamp & SHA256 checksum of config.guess that was found (if any)
if self.config_guess:
# config.guess includes a "timestamp='...'" indicating the version
config_guess_version = None
version_regex = re.compile("^timestamp='(.*)'", re.M)
res = version_regex.search(read_file(self.config_guess))
if res:
config_guess_version = res.group(1)

config_guess_checksum = compute_checksum(self.config_guess, checksum_type=CHECKSUM_TYPE_SHA256)
try:
config_guess_timestamp = datetime.fromtimestamp(os.stat(self.config_guess).st_mtime).isoformat()
except OSError as err:
self.log.warning("Failed to determine timestamp of %s: %s", self.config_guess, err)
config_guess_timestamp = None

self.log.info("config.guess version: %s (last updated: %s, SHA256 checksum: %s)",
config_guess_version, config_guess_timestamp, config_guess_checksum)

if config_guess_version != CONFIG_GUESS_VERSION:
tup = (self.config_guess, config_guess_version, CONFIG_GUESS_VERSION)
self.log.warning("config.guess version at %s does not match expected version: %s vs %s" % tup)
return False

if config_guess_checksum != CONFIG_GUESS_SHA256:
tup = (self.config_guess, config_guess_checksum, CONFIG_GUESS_SHA256)
self.log.warning("SHA256 checksum of config.guess at %s does not match expected checksum: %s vs %s" % tup)
return False

return True
112 changes: 5 additions & 107 deletions easybuild/easyblocks/generic/configuremake.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
##
# Copyright 2009-2020 Ghent University
# Copyright 2009-2019 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
Expand Down Expand Up @@ -36,38 +36,26 @@
@author: Alan O'Cais (Juelich Supercomputing Centre)
"""
import os
import re
import stat
from datetime import datetime

from easybuild.easyblocks import VERSION as EASYBLOCKS_VERSION
from easybuild.framework.easyblock import EasyBlock
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import print_warning
from easybuild.tools.config import source_paths
from easybuild.tools.filetools import CHECKSUM_TYPE_SHA256, adjust_permissions, compute_checksum, download_file
from easybuild.tools.filetools import read_file, remove_file, verify_checksum
from easybuild.tools.filetools import read_file
from easybuild.tools.run import run_cmd
from easybuild.easyblocks.generic._config_guess import ConfigGuessUpdater

# string that indicates that a configure script was generated by Autoconf
# note: bytes string since this constant is used to check the contents of 'configure' which is read as bytes
# (mainly important when EasyBuild is using Python 3)
AUTOCONF_GENERATED_MSG = b"Generated by GNU Autoconf"

# download location & SHA256 for config.guess script
# note: if this is updated, don't forget to trash the cached download from generic/Configure/<eb_version>/!
CONFIG_GUESS_VERSION = '2018-08-29'
CONFIG_GUESS_URL_STUB = "https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb="
CONFIG_GUESS_COMMIT_ID = "59e2ce0e6b46bb47ef81b68b600ed087e14fdaad"
CONFIG_GUESS_SHA256 = "c02eb9cc55c86cfd1e9a794e548d25db5c9539e7b2154beb649bc6e2cbffc74c"


DEFAULT_CONFIGURE_CMD = './configure'
DEFAULT_BUILD_CMD = 'make'
DEFAULT_INSTALL_CMD = 'make install'


class ConfigureMake(EasyBlock):
class ConfigureMake(ConfigGuessUpdater):
"""
Support for building and installing applications with configure/make/make install
"""
Expand All @@ -92,96 +80,6 @@ def extra_options(extra_vars=None):
})
return extra_vars

def __init__(self, *args, **kwargs):
"""Initialize easyblock."""
super(ConfigureMake, self).__init__(*args, **kwargs)

self.config_guess = None

def obtain_config_guess(self, download_source_path=None, search_source_paths=None):
"""
Locate or download an up-to-date config.guess for use with ConfigureMake

:param download_source_path: Path to download config.guess to
:param search_source_paths: Paths to search for config.guess
:return: Path to config.guess or None
"""
eb_source_paths = source_paths()
if download_source_path is None:
download_source_path = eb_source_paths[0]
if search_source_paths is None:
search_source_paths = eb_source_paths

config_guess = 'config.guess'
sourcepath_subdir = os.path.join('generic', 'eb_v%s' % EASYBLOCKS_VERSION, 'ConfigureMake')

config_guess_path = None

# check if config.guess has already been downloaded to source path
for path in eb_source_paths:
cand_config_guess_path = os.path.join(path, sourcepath_subdir, config_guess)
if os.path.isfile(cand_config_guess_path):
config_guess_path = cand_config_guess_path
self.log.info("Found recent %s at %s, using it if required", config_guess, config_guess_path)
break

# if not found, try to download it
if config_guess_path is None:
cand_config_guess_path = os.path.join(download_source_path, sourcepath_subdir, config_guess)
config_guess_url = CONFIG_GUESS_URL_STUB + CONFIG_GUESS_COMMIT_ID
downloaded_path = download_file(config_guess, config_guess_url, cand_config_guess_path)
if downloaded_path is not None:
# verify SHA256 checksum of download to avoid using a corrupted download
if verify_checksum(downloaded_path, CONFIG_GUESS_SHA256):
config_guess_path = downloaded_path
# add execute permissions
adjust_permissions(downloaded_path, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH, add=True)
self.log.info("Downloaded recent %s to %s, using it if required", config_guess, config_guess_path)
else:
self.log.warning("Checksum failed for downloaded file %s, not using it!", downloaded_path)
remove_file(downloaded_path)
else:
self.log.warning("Failed to download recent %s to %s for use with ConfigureMake easyblock (if needed)",
config_guess, cand_config_guess_path)

return config_guess_path

def check_config_guess(self):
"""Check timestamp & SHA256 checksum of config.guess script."""
# log version, timestamp & SHA256 checksum of config.guess that was found (if any)
if self.config_guess:
# config.guess includes a "timestamp='...'" indicating the version
config_guess_version = None
version_regex = re.compile("^timestamp='(.*)'", re.M)
res = version_regex.search(read_file(self.config_guess))
if res:
config_guess_version = res.group(1)

config_guess_checksum = compute_checksum(self.config_guess, checksum_type=CHECKSUM_TYPE_SHA256)
try:
config_guess_timestamp = datetime.fromtimestamp(os.stat(self.config_guess).st_mtime).isoformat()
except OSError as err:
self.log.warning("Failed to determine timestamp of %s: %s", self.config_guess, err)
config_guess_timestamp = None

self.log.info("config.guess version: %s (last updated: %s, SHA256 checksum: %s)",
config_guess_version, config_guess_timestamp, config_guess_checksum)

if config_guess_version != CONFIG_GUESS_VERSION:
tup = (self.config_guess, config_guess_version, CONFIG_GUESS_VERSION)
print_warning("config.guess version at %s does not match expected version: %s vs %s" % tup)

if config_guess_checksum != CONFIG_GUESS_SHA256:
tup = (self.config_guess, config_guess_checksum, CONFIG_GUESS_SHA256)
print_warning("SHA256 checksum of config.guess at %s does not match expected checksum: %s vs %s" % tup)

def fetch_step(self, *args, **kwargs):
"""Custom fetch step for ConfigureMake so we use an updated config.guess."""
super(ConfigureMake, self).fetch_step(*args, **kwargs)

# Use an updated config.guess from a global location (if possible)
self.config_guess = self.obtain_config_guess()

def configure_step(self, cmd_prefix=''):
"""
Configure step
Expand Down Expand Up @@ -229,7 +127,7 @@ def configure_step(self, cmd_prefix=''):

if build_type is None or host_type is None:

# config.guess script may not be obtained yet despite the call in fetch_step,
# config.guess script may not be obtained yet despite the call in fetch_step in ConfigGuessUpdater,
# for example when installing a Bundle component with ConfigureMake
if self.config_guess is None:
self.config_guess = self.obtain_config_guess()
Expand Down
20 changes: 16 additions & 4 deletions easybuild/easyblocks/generic/rpackage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
##
# Copyright 2009-2020 Ghent University
# Copyright 2009-2019 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
Expand Down Expand Up @@ -39,8 +39,9 @@
from easybuild.framework.easyconfig import CUSTOM
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.filetools import mkdir
from easybuild.tools.filetools import mkdir, copy_file
from easybuild.tools.run import run_cmd, parse_log_for_error
from easybuild.easyblocks.generic._config_guess import ConfigGuessUpdater


def make_R_install_option(opt, values, cmdline=False):
Expand Down Expand Up @@ -74,12 +75,12 @@ def extra_options(extra_vars=None):
extra_vars.update({
'exts_subdir': ['', "Subdirectory where R extensions should be installed info", CUSTOM],
'unpack_sources': [False, "Unpack sources before installation", CUSTOM],
'update_config_guess': [False, "Update the config.guess file(s) in this package", CUSTOM],
})
return extra_vars

def __init__(self, *args, **kwargs):
"""Initliaze RPackage-specific class variables."""

super(RPackage, self).__init__(*args, **kwargs)

self.configurevars = []
Expand Down Expand Up @@ -215,10 +216,21 @@ def run(self):
lib_install_prefix = os.path.join(self.installdir, self.cfg['exts_subdir'])
mkdir(lib_install_prefix, parents=True)

if self.patches:
if self.patches or self.cfg['update_config_guess']:
super(RPackage, self).run(unpack_src=True)
else:
super(RPackage, self).run()

if self.cfg['update_config_guess']:
cgu = ConfigGuessUpdater(self.cfg)
for root, dirs, files in os.walk(self.builddir):
for name in files:
if name == 'config.guess':
cgu.config_guess = os.path.join(root, name)
if not cgu.check_config_guess():
updated = cgu.obtain_config_guess()
self.log.warning("Using updated config.guess for %s", cgu.config_guess)
copy_file(updated, cgu.config_guess)

if self.src:
self.ext_src = self.src
Expand Down