Skip to content

Commit e3ab8df

Browse files
authored
Merge pull request #5 from boegel/sources_from_git
minor cleanup in get_source_tarball_from_git + add & use remove_dir/remove function + add tests
2 parents 6af5a0c + 9c578cd commit e3ab8df

88 files changed

Lines changed: 1955 additions & 402 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.travis.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ python: 2.6
33
env:
44
matrix:
55
# purposely specifying slowest builds first, to gain time overall
6-
- LMOD_VERSION=5.8
7-
- LMOD_VERSION=5.8 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
6+
- LMOD_VERSION=6.6.3
7+
- LMOD_VERSION=6.6.3 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
88
- LMOD_VERSION=7.7.16
99
- LMOD_VERSION=7.7.16 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
1010
- ENV_MOD_VERSION=3.2.10 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesC TEST_EASYBUILD_MODULE_SYNTAX=Tcl
@@ -19,7 +19,7 @@ matrix:
1919
include:
2020
# also test default configuration with Python 2.7
2121
- python: 2.7
22-
env: LMOD_VERSION=5.8
22+
env: LMOD_VERSION=6.6.3
2323
addons:
2424
apt:
2525
packages:
@@ -51,7 +51,7 @@ before_install:
5151
# autopep8 1.3.4 is last one to support Python 2.6
5252
- if [ "x$TRAVIS_PYTHON_VERSION" == 'x2.6' ]; then pip install 'autopep8<1.3.5'; else pip install autopep8; fi
5353
# optional Python packages for EasyBuild
54-
- pip install GC3Pie pycodestyle python-graph-dot python-hglib PyYAML
54+
- pip install GC3Pie pycodestyle python-graph-dot python-hglib PyYAML requests
5555
# git config is required to make actual git commits (cfr. tests for GitRepository)
5656
- git config --global user.name "Travis CI"
5757
- git config --global user.email "travis@travis-ci.org"
@@ -100,7 +100,7 @@ script:
100100
- EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
101101
- EB_BOOTSTRAP_SHA256SUM=$(sha256sum $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
102102
- EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
103-
- EB_BOOTSTRAP_EXPECTED="20180531.01 21d9704055c4fcf2cd42fe9825dd5925fa508268e4c7c423cb5958a0903d7c2e"
103+
- EB_BOOTSTRAP_EXPECTED="20180916.01 7e7563787a8bab8c30efdbdf95df6ec8ed63230ebcd361fee7b9574cbe9e74ed"
104104
- test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
105105
# test bootstrap script
106106
- python $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py /tmp/$TRAVIS_JOB_ID/eb_bootstrap

easybuild/framework/easyblock.py

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,13 @@
7272
from easybuild.tools.filetools import CHECKSUM_TYPE_MD5, CHECKSUM_TYPE_SHA256
7373
from easybuild.tools.filetools import adjust_permissions, apply_patch, back_up_file, change_dir, convert_name
7474
from easybuild.tools.filetools import compute_checksum, copy_file, derive_alt_pypi_url, diff_files, download_file
75-
from easybuild.tools.filetools import encode_class_name, extract_file, is_alt_pypi_url, is_sha256_checksum
76-
from easybuild.tools.filetools import get_source_tarball_from_git, mkdir, move_logs, read_file, remove_file, rmtree2
75+
from easybuild.tools.filetools import encode_class_name, extract_file, get_source_tarball_from_git, is_alt_pypi_url
76+
from easybuild.tools.filetools import is_sha256_checksum, mkdir, move_logs, read_file, remove_file, rmtree2
7777
from easybuild.tools.filetools import verify_checksum, weld_paths, write_file
7878
from easybuild.tools.hooks import BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, FETCH_STEP, INSTALL_STEP
7979
from easybuild.tools.hooks import MODULE_STEP, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTPROC_STEP, PREPARE_STEP
80-
from easybuild.tools.hooks import READY_STEP, SANITYCHECK_STEP, SOURCE_STEP, TEST_STEP, TESTCASES_STEP, run_hook
80+
from easybuild.tools.hooks import READY_STEP, SANITYCHECK_STEP, SOURCE_STEP, TEST_STEP, TESTCASES_STEP
81+
from easybuild.tools.hooks import load_hooks, run_hook
8182
from easybuild.tools.run import run_cmd
8283
from easybuild.tools.jenkins import write_to_xml
8384
from easybuild.tools.module_generator import ModuleGeneratorLua, ModuleGeneratorTcl, module_generator, dependencies_for
@@ -124,7 +125,7 @@ def extra_options(extra=None):
124125
#
125126
# INIT
126127
#
127-
def __init__(self, ec, hooks=None):
128+
def __init__(self, ec):
128129
"""
129130
Initialize the EasyBlock instance.
130131
:param ec: a parsed easyconfig file (EasyConfig instance)
@@ -134,7 +135,7 @@ def __init__(self, ec, hooks=None):
134135
self.orig_workdir = os.getcwd()
135136

136137
# list of pre- and post-step hooks
137-
self.hooks = hooks or []
138+
self.hooks = load_hooks(build_option('hooks'))
138139

139140
# list of patch/source files, along with checksums
140141
self.patches = []
@@ -492,7 +493,7 @@ def fetch_extension_sources(self, skip_checksums=False):
492493
'options': ext_options,
493494
}
494495

495-
checksums = ext_options.get('checksums', None)
496+
checksums = ext_options.get('checksums', [])
496497

497498
if ext_options.get('source_tmpl', None):
498499
fn = resolve_template(ext_options['source_tmpl'], ext_src)
@@ -515,14 +516,15 @@ def fetch_extension_sources(self, skip_checksums=False):
515516
src_checksum = compute_checksum(src_fn, checksum_type=checksum_type)
516517
self.log.info("%s checksum for %s: %s", checksum_type, src_fn, src_checksum)
517518

518-
if checksums:
519-
fn_checksum = self.get_checksum_for(checksums, filename=src_fn, index=0)
520-
if verify_checksum(src_fn, fn_checksum):
521-
self.log.info('Checksum for extension source %s verified', fn)
522-
elif build_option('ignore_checksums'):
523-
print_warning("Ignoring failing checksum verification for %s" % fn)
524-
else:
525-
raise EasyBuildError('Checksum verification for extension source %s failed', fn)
519+
# verify checksum (if provided)
520+
self.log.debug('Verifying checksums for extension source...')
521+
fn_checksum = self.get_checksum_for(checksums, filename=src_fn, index=0)
522+
if verify_checksum(src_fn, fn_checksum):
523+
self.log.info('Checksum for extension source %s verified', fn)
524+
elif build_option('ignore_checksums'):
525+
print_warning("Ignoring failing checksum verification for %s" % fn)
526+
else:
527+
raise EasyBuildError('Checksum verification for extension source %s failed', fn)
526528

527529
ext_patches = self.fetch_patches(patch_specs=ext_options.get('patches', []), extension=True)
528530
if ext_patches:
@@ -537,16 +539,16 @@ def fetch_extension_sources(self, skip_checksums=False):
537539
checksum = compute_checksum(patch, checksum_type=checksum_type)
538540
self.log.info("%s checksum for %s: %s", checksum_type, patch, checksum)
539541

540-
if checksums:
541-
self.log.debug('Verifying checksums for extension patches...')
542-
for idx, patch in enumerate(ext_patches):
543-
checksum = self.get_checksum_for(checksums[1:], filename=patch, index=idx)
544-
if verify_checksum(patch, checksum):
545-
self.log.info('Checksum for extension patch %s verified', patch)
546-
elif build_option('ignore_checksums'):
547-
print_warning("Ignoring failing checksum verification for %s" % patch)
548-
else:
549-
raise EasyBuildError('Checksum for extension patch %s failed', patch)
542+
# verify checksum (if provided)
543+
self.log.debug('Verifying checksums for extension patches...')
544+
for idx, patch in enumerate(ext_patches):
545+
checksum = self.get_checksum_for(checksums[1:], filename=patch, index=idx)
546+
if verify_checksum(patch, checksum):
547+
self.log.info('Checksum for extension patch %s verified', patch)
548+
elif build_option('ignore_checksums'):
549+
print_warning("Ignoring failing checksum verification for %s" % patch)
550+
else:
551+
raise EasyBuildError('Checksum for extension patch %s failed', patch)
550552
else:
551553
self.log.debug('No patches found for extension %s.' % ext_name)
552554

@@ -1687,6 +1689,63 @@ def checksum_step(self):
16871689
else:
16881690
raise EasyBuildError("Checksum verification for %s using %s failed.", fil['path'], fil['checksum'])
16891691

1692+
def check_checksums_for(self, ent, sub='', source_cnt=None):
1693+
"""
1694+
Utility method: check whether checksums for all sources/patches are available, for given entity
1695+
"""
1696+
ec_fn = os.path.basename(self.cfg.path)
1697+
checksum_issues = []
1698+
1699+
sources = ent.get('sources', [])
1700+
patches = ent.get('patches', [])
1701+
checksums = ent.get('checksums', [])
1702+
1703+
if source_cnt is None:
1704+
source_cnt = len(sources)
1705+
patch_cnt, checksum_cnt = len(patches), len(checksums)
1706+
1707+
if (source_cnt + patch_cnt) != checksum_cnt:
1708+
if sub:
1709+
sub = "%s in %s" % (sub, ec_fn)
1710+
else:
1711+
sub = "in %s" % ec_fn
1712+
msg = "Checksums missing for one or more sources/patches %s: " % sub
1713+
msg += "found %d sources + %d patches " % (source_cnt, patch_cnt)
1714+
msg += "vs %d checksums" % checksum_cnt
1715+
checksum_issues.append(msg)
1716+
1717+
for fn, checksum in zip(sources + patches, checksums):
1718+
if not is_sha256_checksum(checksum):
1719+
msg = "Non-SHA256 checksum found for %s: %s" % (fn, checksum)
1720+
checksum_issues.append(msg)
1721+
1722+
return checksum_issues
1723+
1724+
def check_checksums(self):
1725+
"""
1726+
Check whether a SHA256 checksum is available for all sources & patches (incl. extensions).
1727+
1728+
:return: list of strings describing checksum issues (missing checksums, wrong checksum type, etc.)
1729+
"""
1730+
checksum_issues = []
1731+
1732+
# check whether a checksum if available for every source + patch
1733+
checksum_issues.extend(self.check_checksums_for(self.cfg))
1734+
1735+
# also check checksums for extensions
1736+
for ext in self.cfg['exts_list']:
1737+
# just skip extensions for which only a name is specified
1738+
# those are just there to check for things that are in the "standard library"
1739+
if not isinstance(ext, basestring):
1740+
ext_name = ext[0]
1741+
# take into account that extension may be a 2-tuple with just name/version
1742+
ext_opts = ext[2] if len(ext) == 3 else {}
1743+
# only a single source per extension is supported (see source_tmpl)
1744+
res = self.check_checksums_for(ext_opts, sub="of extension %s" % ext_name, source_cnt=1)
1745+
checksum_issues.extend(res)
1746+
1747+
return checksum_issues
1748+
16901749
def extract_step(self):
16911750
"""
16921751
Unpack the source files.
@@ -2346,6 +2405,17 @@ def cleanup_step(self):
23462405

23472406
self.restore_iterate_opts()
23482407

2408+
def invalidate_module_caches(self, modpath):
2409+
"""Helper method to invalidate module caches for specified module path."""
2410+
# invalidate relevant 'module avail'/'module show' cache entries
2411+
# consider both paths: for short module name, and subdir indicated by long module name
2412+
paths = [modpath]
2413+
if self.mod_subdir:
2414+
paths.append(os.path.join(modpath, self.mod_subdir))
2415+
2416+
for path in paths:
2417+
invalidate_module_caches_for(path)
2418+
23492419
def make_module_step(self, fake=False):
23502420
"""
23512421
Generate module file
@@ -2395,14 +2465,7 @@ def make_module_step(self, fake=False):
23952465
self.log.info(diff_msg)
23962466
print_msg(diff_msg, log=self.log)
23972467

2398-
# invalidate relevant 'module avail'/'module show' cache entries
2399-
# consider both paths: for short module name, and subdir indicated by long module name
2400-
paths = [modpath]
2401-
if self.mod_subdir:
2402-
paths.append(os.path.join(modpath, self.mod_subdir))
2403-
2404-
for path in paths:
2405-
invalidate_module_caches_for(path)
2468+
self.invalidate_module_caches(modpath)
24062469

24072470
# only update after generating final module file
24082471
if not fake:
@@ -2507,8 +2570,11 @@ def _skip_step(self, step, skippable):
25072570
force = build_option('force') or build_option('rebuild')
25082571
skip = False
25092572

2510-
# skip step if specified as individual (skippable) step
2511-
if skippable and (self.skip or step in self.cfg['skipsteps']):
2573+
# under --skip, sanity check is not skipped
2574+
cli_skip = self.skip and step != SANITYCHECK_STEP
2575+
2576+
# skip step if specified as individual (skippable) step, or if --skip is used
2577+
if skippable and (cli_skip or step in self.cfg['skipsteps']):
25122578
self.log.info("Skipping %s step (skip: %s, skipsteps: %s)", step, self.skip, self.cfg['skipsteps'])
25132579
skip = True
25142580

@@ -2654,7 +2720,7 @@ def install_step_spec(initial):
26542720
steps_part3 = [
26552721
(EXTENSIONS_STEP, 'taking care of extensions', [lambda x: x.extensions_step], False),
26562722
(POSTPROC_STEP, 'postprocessing', [lambda x: x.post_install_step], True),
2657-
(SANITYCHECK_STEP, 'sanity checking', [lambda x: x.sanity_check_step], False),
2723+
(SANITYCHECK_STEP, 'sanity checking', [lambda x: x.sanity_check_step], True),
26582724
(CLEANUP_STEP, 'cleaning up', [lambda x: x.cleanup_step], False),
26592725
(MODULE_STEP, 'creating module', [lambda x: x.make_module_step], False),
26602726
(PERMISSIONS_STEP, 'permissions', [lambda x: x.permissions_step], False),
@@ -2717,7 +2783,7 @@ def print_dry_run_note(loc, silent=True):
27172783
dry_run_msg(msg, silent=silent)
27182784

27192785

2720-
def build_and_install_one(ecdict, init_env, hooks=None):
2786+
def build_and_install_one(ecdict, init_env):
27212787
"""
27222788
Build the software
27232789
:param ecdict: dictionary contaning parsed easyconfig + metadata
@@ -2755,7 +2821,7 @@ def build_and_install_one(ecdict, init_env, hooks=None):
27552821
try:
27562822
app_class = get_easyblock_class(easyblock, name=name)
27572823

2758-
app = app_class(ecdict['ec'], hooks=hooks)
2824+
app = app_class(ecdict['ec'])
27592825
_log.info("Obtained application instance of for %s (easyblock: %s)" % (name, easyblock))
27602826
except EasyBuildError, err:
27612827
print_error("Failed to get application instance for %s (easyblock: %s): %s" % (name, easyblock, err.msg),
@@ -2816,13 +2882,14 @@ def build_and_install_one(ecdict, init_env, hooks=None):
28162882
buildstats = get_build_stats(app, start_time, build_option('command_line'))
28172883
_log.info("Build stats: %s" % buildstats)
28182884

2819-
if build_option("minimal_toolchains"):
2820-
# for reproducability we dump out the parsed easyconfig since the contents are affected when
2821-
# --minimal-toolchains (and --use-existing-modules) is used
2822-
# TODO --try-toolchain needs to be fixed so this doesn't play havoc with it's usability
2823-
reprod_spec = os.path.join(new_log_dir, 'reprod', ec_filename)
2885+
# for reproducability we dump out the fully processed easyconfig since the contents can be affected
2886+
# by subtoolchain resolution (and related options) and/or hooks
2887+
reprod_spec = os.path.join(new_log_dir, 'reprod', ec_filename)
2888+
try:
28242889
app.cfg.dump(reprod_spec)
2825-
_log.debug("Dumped easyconfig tweaked via --minimal-toolchains to %s", reprod_spec)
2890+
_log.info("Dumped fully processed easyconfig to %s", reprod_spec)
2891+
except NotImplementedError as err:
2892+
_log.warn("Unable to dumped fully processed easyconfig to %s: %s", reprod_spec, err)
28262893

28272894
try:
28282895
# upload easyconfig (and patch files) to central repository

easybuild/framework/easyconfig/default.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
'easyblock': [None, "EasyBlock to use for building; if set to None, an easyblock is selected "
9191
"based on the software name", BUILD],
9292
'easybuild_version': [None, "EasyBuild-version this spec-file was written for", BUILD],
93-
'github_account': [None, "GitHub account name to be used to resolve template values in source URLs", BUILD],
93+
'github_account': ['%(namelower)s', "GitHub account name to be used to resolve template values in source URLs",
94+
BUILD],
9495
'hidden': [False, "Install module file as 'hidden' by prefixing its version with '.'", BUILD],
9596
'installopts': ['', 'Extra options for installation', BUILD],
9697
'maxparallel': [None, 'Max degree of parallelism', BUILD],

0 commit comments

Comments
 (0)