From f92a2d688de53b5dd38891bb6546286bc7151236 Mon Sep 17 00:00:00 2001 From: "Mark W. Krentel" Date: Thu, 11 Nov 2021 19:04:53 -0600 Subject: [PATCH 01/28] elfutils: add version 0.186 (#27345) --- var/spack/repos/builtin/packages/elfutils/package.py | 1 + 1 file changed, 1 insertion(+) diff --git a/var/spack/repos/builtin/packages/elfutils/package.py b/var/spack/repos/builtin/packages/elfutils/package.py index e32b8fcbc1c..731c7f69a75 100644 --- a/var/spack/repos/builtin/packages/elfutils/package.py +++ b/var/spack/repos/builtin/packages/elfutils/package.py @@ -22,6 +22,7 @@ class Elfutils(AutotoolsPackage, SourcewarePackage): list_url = "https://sourceware.org/elfutils/ftp" list_depth = 1 + version('0.186', sha256='7f6fb9149b1673d38d9178a0d3e0fb8a1ec4f53a9f4c2ff89469609879641177') version('0.185', sha256='dc8d3e74ab209465e7f568e1b3bb9a5a142f8656e2b57d10049a73da2ae6b5a6') version('0.184', sha256='87e7d1d7f0333815dd1f62135d047a4dc4082068f361452f357997c11360644b') version('0.183', sha256='c3637c208d309d58714a51e61e63f1958808fead882e9b607506a29e5474f2c5') From 544826c8259edef6d9a3b67435e5a3b07f283ba7 Mon Sep 17 00:00:00 2001 From: Dylan Simon Date: Tue, 9 Nov 2021 05:52:08 -0500 Subject: [PATCH 02/28] make --enable-locks actually enable locks (#24675) --- lib/spack/spack/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py index 31a4bfe4ed5..86f5c760020 100644 --- a/lib/spack/spack/main.py +++ b/lib/spack/spack/main.py @@ -491,8 +491,9 @@ def setup_main_options(args): # override lock configuration if passed on command line if args.locks is not None: - spack.util.lock.check_lock_safety(spack.paths.prefix) - spack.config.set('config:locks', False, scope='command_line') + if args.locks is False: + spack.util.lock.check_lock_safety(spack.paths.prefix) + spack.config.set('config:locks', args.locks, scope='command_line') if args.mock: rp = spack.repo.RepoPath(spack.paths.mock_packages_path) From abd418cc315fff968da242522c6bf403ea841161 Mon Sep 17 00:00:00 2001 From: Robert Underwood Date: Tue, 9 Nov 2021 05:58:14 -0500 Subject: [PATCH 03/28] openmpi fix external find for 0.17 (#27255) --- var/spack/repos/builtin/packages/openmpi/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/var/spack/repos/builtin/packages/openmpi/package.py b/var/spack/repos/builtin/packages/openmpi/package.py index 6d3e12d6e5c..0d48cfc523b 100644 --- a/var/spack/repos/builtin/packages/openmpi/package.py +++ b/var/spack/repos/builtin/packages/openmpi/package.py @@ -1058,7 +1058,7 @@ def is_enabled(text): # This code gets all the fabric names from the variants list # Idea taken from the AutotoolsPackage source. def get_options_from_variant(self, name): - values = self.variants[name].values + values = self.variants[name][0].values if getattr(values, 'feature_values', None): values = values.feature_values return values From 7c6b253d89cb4e91c2683d150e46da00a6085695 Mon Sep 17 00:00:00 2001 From: Jordan Galby <67924449+Jordan474@users.noreply.github.com> Date: Tue, 9 Nov 2021 16:47:32 +0100 Subject: [PATCH 04/28] Fix log-format reporter ignoring install errors (#25961) When running `spack install --log-format junit|cdash ...`, install errors were ignored. This made spack continue building dependents of failed install, ignoring `--fail-fast`, and exit 0 at the end. --- lib/spack/spack/report.py | 34 +++++++++++++------------ lib/spack/spack/test/cmd/install.py | 39 +++++++++++++++++++---------- lib/spack/spack/test/cmd/test.py | 6 +++-- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/lib/spack/spack/report.py b/lib/spack/spack/report.py index 4685321bc88..83bb89e27f1 100644 --- a/lib/spack/spack/report.py +++ b/lib/spack/spack/report.py @@ -151,6 +151,22 @@ def wrapper(instance, *args, **kwargs): 'installed_from_binary_cache': False } + # Append the package to the correct spec report. In some + # cases it may happen that a spec that is asked to be + # installed explicitly will also be installed as a + # dependency of another spec. In this case append to both + # spec reports. + for s in llnl.util.lang.dedupe([pkg.spec.root, pkg.spec]): + name = name_fmt.format(s.name, s.dag_hash(length=7)) + try: + item = next(( + x for x in self.specs + if x['name'] == name + )) + item['packages'].append(package) + except StopIteration: + pass + start_time = time.time() value = None try: @@ -170,6 +186,7 @@ def wrapper(instance, *args, **kwargs): package['stdout'] = fetch_log(pkg, do_fn, self.dir) package['stdout'] += package['message'] package['exception'] = e.traceback + raise except (Exception, BaseException) as e: # Everything else is an error (the installation @@ -178,26 +195,11 @@ def wrapper(instance, *args, **kwargs): package['stdout'] = fetch_log(pkg, do_fn, self.dir) package['message'] = str(e) or 'Unknown error' package['exception'] = traceback.format_exc() + raise finally: package['elapsed_time'] = time.time() - start_time - # Append the package to the correct spec report. In some - # cases it may happen that a spec that is asked to be - # installed explicitly will also be installed as a - # dependency of another spec. In this case append to both - # spec reports. - for s in llnl.util.lang.dedupe([pkg.spec.root, pkg.spec]): - name = name_fmt.format(s.name, s.dag_hash(length=7)) - try: - item = next(( - x for x in self.specs - if x['name'] == name - )) - item['packages'].append(package) - except StopIteration: - pass - return value return wrapper diff --git a/lib/spack/spack/test/cmd/install.py b/lib/spack/spack/test/cmd/install.py index b0b10d12877..4fa022d168f 100644 --- a/lib/spack/spack/test/cmd/install.py +++ b/lib/spack/spack/test/cmd/install.py @@ -393,9 +393,14 @@ def test_junit_output_with_failures(tmpdir, exc_typename, msg): '--log-format=junit', '--log-file=test.xml', 'raiser', 'exc_type={0}'.format(exc_typename), - 'msg="{0}"'.format(msg) + 'msg="{0}"'.format(msg), + fail_on_error=False, ) + assert isinstance(install.error, spack.build_environment.ChildError) + assert install.error.name == exc_typename + assert install.error.pkg.name == 'raiser' + files = tmpdir.listdir() filename = tmpdir.join('test.xml') assert filename in files @@ -407,18 +412,22 @@ def test_junit_output_with_failures(tmpdir, exc_typename, msg): assert 'failures="1"' in content assert 'errors="0"' in content + # Nothing should have succeeded + assert 'tests="0"' not in content + assert 'failures="0"' not in content + # We want to have both stdout and stderr assert '' in content assert msg in content @pytest.mark.disable_clean_stage_check -@pytest.mark.parametrize('exc_typename,msg', [ - ('RuntimeError', 'something weird happened'), - ('KeyboardInterrupt', 'Ctrl-C strikes again') +@pytest.mark.parametrize('exc_typename,expected_exc,msg', [ + ('RuntimeError', spack.installer.InstallError, 'something weird happened'), + ('KeyboardInterrupt', KeyboardInterrupt, 'Ctrl-C strikes again') ]) def test_junit_output_with_errors( - exc_typename, msg, + exc_typename, expected_exc, msg, mock_packages, mock_archive, mock_fetch, install_mockery, config, tmpdir, monkeypatch): @@ -429,11 +438,11 @@ def just_throw(*args, **kwargs): monkeypatch.setattr(spack.installer.PackageInstaller, '_install_task', just_throw) - # TODO: Why does junit output capture appear to swallow the exception - # TODO: as evidenced by the two failing packages getting tagged as - # TODO: installed? with tmpdir.as_cwd(): - install('--log-format=junit', '--log-file=test.xml', 'libdwarf') + install('--log-format=junit', '--log-file=test.xml', 'libdwarf', + fail_on_error=False) + + assert isinstance(install.error, expected_exc) files = tmpdir.listdir() filename = tmpdir.join('test.xml') @@ -441,10 +450,14 @@ def just_throw(*args, **kwargs): content = filename.open().read() - # Count failures and errors correctly: libdwarf _and_ libelf - assert 'tests="2"' in content + # Only libelf error is reported (through libdwarf root spec). libdwarf + # install is skipped and it is not an error. + assert 'tests="1"' in content assert 'failures="0"' in content - assert 'errors="2"' in content + assert 'errors="1"' in content + + # Nothing should have succeeded + assert 'errors="0"' not in content # We want to have both stdout and stderr assert '' in content @@ -877,7 +890,7 @@ def test_install_help_cdash(capsys): @pytest.mark.disable_clean_stage_check -def test_cdash_auth_token(tmpdir, install_mockery, capfd): +def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, capfd): # capfd interferes with Spack's capturing with tmpdir.as_cwd(): with capfd.disabled(): diff --git a/lib/spack/spack/test/cmd/test.py b/lib/spack/spack/test/cmd/test.py index df9fb35db1c..9ee117b281f 100644 --- a/lib/spack/spack/test/cmd/test.py +++ b/lib/spack/spack/test/cmd/test.py @@ -133,7 +133,8 @@ def test_junit_output_with_failures(tmpdir, mock_test_stage, pkg_name, msgs): with tmpdir.as_cwd(): spack_test('run', '--log-format=junit', '--log-file=test.xml', - pkg_name) + pkg_name, + fail_on_error=False) files = tmpdir.listdir() filename = tmpdir.join('test.xml') @@ -160,7 +161,8 @@ def test_cdash_output_test_error( spack_test('run', '--log-format=cdash', '--log-file=cdash_reports', - 'test-error') + 'test-error', + fail_on_error=False) report_dir = tmpdir.join('cdash_reports') print(tmpdir.listdir()) assert report_dir in tmpdir.listdir() From d862507bcf41a80ecb98493aef5d5bcd7d2a0518 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Fri, 12 Nov 2021 08:34:18 +0100 Subject: [PATCH 05/28] Fix overloaded argparse keys (#27379) Commands should not reuse option names defined in main. --- lib/spack/spack/cmd/env.py | 16 +++++++--------- lib/spack/spack/cmd/install.py | 6 ++++-- lib/spack/spack/cmd/python.py | 4 ++-- lib/spack/spack/reporters/cdash.py | 1 - lib/spack/spack/test/cmd/env.py | 2 +- share/spack/spack-completion.bash | 7 +------ 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/spack/spack/cmd/env.py b/lib/spack/spack/cmd/env.py index b88e1281bc8..2f94bbc4459 100644 --- a/lib/spack/spack/cmd/env.py +++ b/lib/spack/spack/cmd/env.py @@ -411,8 +411,6 @@ def env_status(args): # def env_loads_setup_parser(subparser): """list modules for an installed environment '(see spack module loads)'""" - subparser.add_argument( - 'env', nargs='?', help='name of env to generate loads file for') subparser.add_argument( '-n', '--module-set-name', default='default', help='module set for which to generate load operations') @@ -448,19 +446,19 @@ def env_loads(args): def env_update_setup_parser(subparser): """update environments to the latest format""" subparser.add_argument( - metavar='env', dest='env', + metavar='env', dest='update_env', help='name or directory of the environment to activate' ) spack.cmd.common.arguments.add_common_arguments(subparser, ['yes_to_all']) def env_update(args): - manifest_file = ev.manifest_file(args.env) + manifest_file = ev.manifest_file(args.update_env) backup_file = manifest_file + ".bkp" needs_update = not ev.is_latest_format(manifest_file) if not needs_update: - tty.msg('No update needed for the environment "{0}"'.format(args.env)) + tty.msg('No update needed for the environment "{0}"'.format(args.update_env)) return proceed = True @@ -470,7 +468,7 @@ def env_update(args): 'Spack that are older than this version may not be able to ' 'read it. Spack stores backups of the updated environment ' 'which can be retrieved with "spack env revert"') - tty.msg(msg.format(args.env)) + tty.msg(msg.format(args.update_env)) proceed = tty.get_yes_or_no('Do you want to proceed?', default=False) if not proceed: @@ -478,20 +476,20 @@ def env_update(args): ev.update_yaml(manifest_file, backup_file=backup_file) msg = 'Environment "{0}" has been updated [backup={1}]' - tty.msg(msg.format(args.env, backup_file)) + tty.msg(msg.format(args.update_env, backup_file)) def env_revert_setup_parser(subparser): """restore environments to their state before update""" subparser.add_argument( - metavar='env', dest='env', + metavar='env', dest='revert_env', help='name or directory of the environment to activate' ) spack.cmd.common.arguments.add_common_arguments(subparser, ['yes_to_all']) def env_revert(args): - manifest_file = ev.manifest_file(args.env) + manifest_file = ev.manifest_file(args.revert_env) backup_file = manifest_file + ".bkp" # Check that both the spack.yaml and the backup exist, the inform user diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py index c04fb23ea31..95b195bc536 100644 --- a/lib/spack/spack/cmd/install.py +++ b/lib/spack/spack/cmd/install.py @@ -38,7 +38,7 @@ def update_kwargs_from_args(args, kwargs): 'keep_stage': args.keep_stage, 'restage': not args.dont_restage, 'install_source': args.install_source, - 'verbose': args.verbose, + 'verbose': args.verbose or args.install_verbose, 'fake': args.fake, 'dirty': args.dirty, 'use_cache': args.use_cache, @@ -130,7 +130,7 @@ def setup_parser(subparser): help="install source files in prefix") arguments.add_common_arguments(subparser, ['no_checksum', 'deprecated']) subparser.add_argument( - '-v', '--verbose', action='store_true', + '-v', '--verbose', action='store_true', dest='install_verbose', help="display verbose build output while installing") subparser.add_argument( '--fake', action='store_true', @@ -285,6 +285,8 @@ def install_specs(cli_args, kwargs, specs): def install(parser, args, **kwargs): + # TODO: unify args.verbose? + tty.set_verbose(args.verbose or args.install_verbose) if args.help_cdash: parser = argparse.ArgumentParser( diff --git a/lib/spack/spack/cmd/python.py b/lib/spack/spack/cmd/python.py index 156e01b4bae..a7b998a8dae 100644 --- a/lib/spack/spack/cmd/python.py +++ b/lib/spack/spack/cmd/python.py @@ -23,7 +23,7 @@ def setup_parser(subparser): subparser.add_argument( - '-V', '--version', action='store_true', + '-V', '--version', action='store_true', dest='python_version', help='print the Python version number and exit') subparser.add_argument( '-c', dest='python_command', help='command to execute') @@ -42,7 +42,7 @@ def setup_parser(subparser): def python(parser, args, unknown_args): - if args.version: + if args.python_version: print('Python', platform.python_version()) return diff --git a/lib/spack/spack/reporters/cdash.py b/lib/spack/spack/reporters/cdash.py index cb9539b2d73..19721fc6768 100644 --- a/lib/spack/spack/reporters/cdash.py +++ b/lib/spack/spack/reporters/cdash.py @@ -62,7 +62,6 @@ class CDash(Reporter): def __init__(self, args): Reporter.__init__(self, args) - tty.set_verbose(args.verbose) self.success = True self.template_dir = os.path.join('reports', 'cdash') self.cdash_upload_url = args.cdash_upload_url diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 553d80c5628..d7e712d3d08 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -868,7 +868,7 @@ def test_env_loads(install_mockery, mock_fetch): install('--fake') with ev.read('test'): - env('loads', 'test') + env('loads') e = ev.read('test') diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash index e9022d47bbd..fa3ebd3c3ee 100755 --- a/share/spack/spack-completion.bash +++ b/share/spack/spack-completion.bash @@ -969,12 +969,7 @@ _spack_env_st() { } _spack_env_loads() { - if $list_options - then - SPACK_COMPREPLY="-h --help -n --module-set-name -m --module-type --input-only -p --prefix -x --exclude -r --dependencies" - else - _environments - fi + SPACK_COMPREPLY="-h --help -n --module-set-name -m --module-type --input-only -p --prefix -x --exclude -r --dependencies" } _spack_env_view() { From c8daa7218de7904151e61f2d9a1458e63b0e3363 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Mon, 15 Nov 2021 11:21:37 +0100 Subject: [PATCH 06/28] Turn some verbose messages into debug messages (#27408) --- lib/spack/spack/installer.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py index 798f63cb815..010632076c8 100644 --- a/lib/spack/spack/installer.py +++ b/lib/spack/spack/installer.py @@ -1022,7 +1022,7 @@ def _ensure_locked(self, lock_type, pkg): tty.debug(msg.format('Upgrading to', desc, pkg_id, timeout)) op = 'upgrade to' lock.upgrade_read_to_write(timeout) - tty.verbose('{0} is now {1} locked'.format(pkg_id, lock_type)) + tty.debug('{0} is now {1} locked'.format(pkg_id, lock_type)) except (lk.LockDowngradeError, lk.LockTimeoutError) as exc: tty.debug(err.format(op, desc, pkg_id, exc.__class__.__name__, @@ -1254,7 +1254,7 @@ def _push_task(self, task): # Remove any associated build task since its sequence will change self._remove_task(task.pkg_id) desc = 'Queueing' if task.attempts == 0 else 'Requeueing' - tty.verbose(msg.format(desc, task.pkg_id, task.status)) + tty.debug(msg.format(desc, task.pkg_id, task.status)) # Now add the new task to the queue with a new sequence number to # ensure it is the last entry popped with the same priority. This @@ -1276,7 +1276,7 @@ def _release_lock(self, pkg_id): ltype, lock = self.locks[pkg_id] if lock is not None: try: - tty.verbose(msg.format(ltype, pkg_id)) + tty.debug(msg.format(ltype, pkg_id)) if ltype == 'read': lock.release_read() else: @@ -1296,8 +1296,8 @@ def _remove_task(self, pkg_id): pkg_id (str): identifier for the package to be removed """ if pkg_id in self.build_tasks: - tty.verbose('Removing build task for {0} from list' - .format(pkg_id)) + tty.debug('Removing build task for {0} from list' + .format(pkg_id)) task = self.build_tasks.pop(pkg_id) task.status = STATUS_REMOVED return task @@ -1381,8 +1381,8 @@ def _update_failed(self, task, mark=False, exc=None): self._update_failed(dep_task, mark) self._remove_task(dep_id) else: - tty.verbose('No build task for {0} to skip since {1} failed' - .format(dep_id, pkg_id)) + tty.debug('No build task for {0} to skip since {1} failed' + .format(dep_id, pkg_id)) def _update_installed(self, task): """ @@ -1511,7 +1511,7 @@ def install(self): pkg, pkg_id, spec = task.pkg, task.pkg_id, task.pkg.spec term_title.set('Processing {0}'.format(pkg.name)) - tty.verbose('Processing {0}: task={1}'.format(pkg_id, task)) + tty.debug('Processing {0}: task={1}'.format(pkg_id, task)) # Ensure that the current spec has NO uninstalled dependencies, # which is assumed to be reflected directly in its priority. # From 654f6839ebe33d64710b5e7f3c388539e84f8e35 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Thu, 18 Nov 2021 13:00:39 +0100 Subject: [PATCH 07/28] ci: run style unit tests only if we target develop (#27472) Some tests assume the base branch is develop, but this branch may not have been checked out. --- .github/workflows/setup_git.sh | 9 ++++----- .github/workflows/unit_tests.yaml | 2 ++ lib/spack/spack/test/cmd/style.py | 13 ++++++++++++- lib/spack/spack/test/package_sanity.py | 6 +++++- share/spack/qa/run-style-tests | 10 +++++----- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/.github/workflows/setup_git.sh b/.github/workflows/setup_git.sh index bd79daf268a..e319e07512e 100755 --- a/.github/workflows/setup_git.sh +++ b/.github/workflows/setup_git.sh @@ -1,9 +1,8 @@ #!/usr/bin/env sh git config --global user.email "spack@example.com" git config --global user.name "Test User" -# With fetch-depth: 0 we have a remote develop -# but not a local branch. Don't do this on develop -if [ "$(git branch --show-current)" != "develop" ] -then - git branch develop origin/develop + +# create a local pr base branch +if [[ -n $GITHUB_BASE_REF ]]; then + git fetch origin "${GITHUB_BASE_REF}:${GITHUB_BASE_REF}" fi diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml index 35722b9137e..764a9cdcf04 100644 --- a/.github/workflows/unit_tests.yaml +++ b/.github/workflows/unit_tests.yaml @@ -211,6 +211,7 @@ jobs: git clone "${{ github.server_url }}/${{ github.repository }}.git" && cd spack git fetch origin "${{ github.ref }}:test-branch" git checkout test-branch + . .github/workflows/setup_git.sh bin/spack unit-test -x - name: Run unit tests (only package tests) if: ${{ needs.changes.outputs.with_coverage == 'false' }} @@ -223,6 +224,7 @@ jobs: git clone "${{ github.server_url }}/${{ github.repository }}.git" && cd spack git fetch origin "${{ github.ref }}:test-branch" git checkout test-branch + . .github/workflows/setup_git.sh bin/spack unit-test -x -m "not maybeslow" -k "package_sanity" # Test RHEL8 UBI with platform Python. This job is run diff --git a/lib/spack/spack/test/cmd/style.py b/lib/spack/spack/test/cmd/style.py index af0fc47d242..29cde14400b 100644 --- a/lib/spack/spack/test/cmd/style.py +++ b/lib/spack/spack/test/cmd/style.py @@ -24,8 +24,19 @@ style = spack.main.SpackCommand("style") + +def has_develop_branch(): + git = which('git') + if not git: + return False + git("show-ref", "--verify", "--quiet", + "refs/heads/develop", fail_on_error=False) + return git.returncode == 0 + + # spack style requires git to run -- skip the tests if it's not there -pytestmark = pytest.mark.skipif(not which('git'), reason='requires git') +pytestmark = pytest.mark.skipif(not has_develop_branch(), + reason='requires git with develop branch') # The style tools have requirements to use newer Python versions. We simplify by # requiring Python 3.6 or higher to run spack style. diff --git a/lib/spack/spack/test/package_sanity.py b/lib/spack/spack/test/package_sanity.py index 35840d85dab..dd2f4b425fb 100644 --- a/lib/spack/spack/test/package_sanity.py +++ b/lib/spack/spack/test/package_sanity.py @@ -206,8 +206,12 @@ def test_prs_update_old_api(): """Ensures that every package modified in a PR doesn't contain deprecated calls to any method. """ + ref = os.getenv("GITHUB_BASE_REF") + if not ref: + pytest.skip("No base ref found") + changed_package_files = [ - x for x in style.changed_files() if style.is_package(x) + x for x in style.changed_files(base=ref) if style.is_package(x) ] failing = [] for file in changed_package_files: diff --git a/share/spack/qa/run-style-tests b/share/spack/qa/run-style-tests index 72c47ff0555..9f0cbdb266f 100755 --- a/share/spack/qa/run-style-tests +++ b/share/spack/qa/run-style-tests @@ -14,15 +14,15 @@ # Usage: # run-flake8-tests # -. "$(dirname $0)/setup.sh" +. "$(dirname "$0")/setup.sh" -BASE="" -if [ -n "$GITHUB_BASE_REF" ]; then - BASE="--base ${GITHUB_BASE_REF}" +args=() +if [[ -n $GITHUB_BASE_REF ]]; then + args+=("--base" "${GITHUB_BASE_REF}") fi # verify that the code style is correct -spack style --root-relative $BASE +spack style --root-relative "${args[@]}" # verify that the license headers are present spack license verify From 8f98f1d1820970a72b64000cc5003006f0a9b945 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Fri, 26 Nov 2021 19:03:05 +0100 Subject: [PATCH 08/28] Use bash in setup_git.sh (#27676) --- .github/workflows/setup_git.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_git.sh b/.github/workflows/setup_git.sh index e319e07512e..4eb416720be 100755 --- a/.github/workflows/setup_git.sh +++ b/.github/workflows/setup_git.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/bash -e git config --global user.email "spack@example.com" git config --global user.name "Test User" From 9d4291e590b304164598f32f63a8afbd6a2f2ec0 Mon Sep 17 00:00:00 2001 From: Paul Ferrell <51765748+Paul-Ferrell@users.noreply.github.com> Date: Mon, 29 Nov 2021 05:27:02 -0700 Subject: [PATCH 09/28] Handle byte sequences which are not encoded as UTF8 while logging. (#21447) Fix builds which produce a lines with non-UTF8 output while logging The alternative is to read in binary mode, and then decode while ignoring errors. --- lib/spack/llnl/util/tty/log.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/spack/llnl/util/tty/log.py b/lib/spack/llnl/util/tty/log.py index efcd487f23f..a7a4637ba9a 100644 --- a/lib/spack/llnl/util/tty/log.py +++ b/lib/spack/llnl/util/tty/log.py @@ -780,7 +780,12 @@ def _writer_daemon(stdin_multiprocess_fd, read_multiprocess_fd, write_fd, echo, try: while line_count < 100: # Handle output from the calling process. - line = _retry(in_pipe.readline)() + try: + line = _retry(in_pipe.readline)() + except UnicodeDecodeError: + # installs like --test=root gpgme produce non-UTF8 logs + line = '\n' + if not line: return line_count += 1 From e9f7fb03c9f92e78637480cbda7e5ecdc3e28dda Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Tue, 30 Nov 2021 14:59:55 +0100 Subject: [PATCH 10/28] spack audit: fix API calls (#27713) This broke in #24858 --- lib/spack/spack/audit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/spack/spack/audit.py b/lib/spack/spack/audit.py index 47894383a56..f6ef90e0b8f 100644 --- a/lib/spack/spack/audit.py +++ b/lib/spack/spack/audit.py @@ -389,9 +389,8 @@ def _unknown_variants_in_dependencies(pkgs, error_cls): dependency_variants = dependency_edge.spec.variants for name, value in dependency_variants.items(): try: - dependency_pkg.variants[name].validate_or_raise( - value, pkg=dependency_pkg - ) + v, _ = dependency_pkg.variants[name] + v.validate_or_raise(value, pkg=dependency_pkg) except Exception as e: summary = (pkg_name + ": wrong variant used for a " "dependency in a 'depends_on' directive") @@ -419,7 +418,8 @@ def _analyze_variants_in_directive(pkg, constraint, directive, error_cls): errors = [] for name, v in constraint.variants.items(): try: - pkg.variants[name].validate_or_raise(v, pkg=pkg) + variant, _ = pkg.variants[name] + variant.validate_or_raise(v, pkg=pkg) except variant_exceptions as e: summary = pkg.name + ': wrong variant in "{0}" directive' summary = summary.format(directive) From 7e5de95a3025237247fda6552eea24cd406a1ded Mon Sep 17 00:00:00 2001 From: Greg Becker Date: Fri, 10 Dec 2021 01:49:33 -0800 Subject: [PATCH 11/28] Improve debug info from concretizer (#27707) --- lib/spack/spack/solver/asp.py | 24 ++++++++++++++++++++++-- lib/spack/spack/test/concretize.py | 11 +++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index c1c3cb33a9b..4ad05cd9bf7 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -366,6 +366,19 @@ def format_minimal_cores(self): string_list.extend(self.format_core(core)) return string_list + def format_cores(self): + """List of facts for each core + + Separate cores are separated by an empty line + Cores are not minimized + """ + string_list = [] + for core in self.cores: + if string_list: + string_list.append('\n') + string_list.extend(self.format_core(core)) + return string_list + def raise_if_unsat(self): """ Raise an appropriate error if the result is unsatisfiable. @@ -379,7 +392,9 @@ def raise_if_unsat(self): constraints = self.abstract_specs if len(constraints) == 1: constraints = constraints[0] - conflicts = self.format_minimal_cores() + + debug = spack.config.get('config:debug', False) + conflicts = self.format_cores() if debug else self.format_minimal_cores() raise spack.error.UnsatisfiableSpecError(constraints, conflicts=conflicts) @@ -496,7 +511,12 @@ def fact(self, head, assumption=False): self.out.write("%s.\n" % str(symbol)) atom = self.backend.add_atom(symbol) - choice = self.cores and assumption + + # in debug mode, make all facts choices/assumptions + # otherwise, only if we're generating cores and assumption=True + debug = spack.config.get('config:debug', False) + choice = debug or (self.cores and assumption) + self.backend.add_rule([atom], [], choice=choice) if choice: self.assumptions.append(atom) diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 54d886a55b0..5e9188b888f 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -557,6 +557,17 @@ def test_conflicts_in_spec(self, conflict_spec): with pytest.raises(spack.error.SpackError): s.concretize() + def test_conflicts_new_concretizer_debug(self, conflict_spec, mutable_config): + if spack.config.get('config:concretizer') == 'original': + pytest.skip('Testing debug statements specific to new concretizer') + + spack.config.set('config:debug', True) + s = Spec(conflict_spec) + with pytest.raises(spack.error.SpackError) as e: + s.concretize() + + assert "conflict_trigger(" in e.value.message + def test_conflict_in_all_directives_true(self): s = Spec('when-directives-true') with pytest.raises(spack.error.SpackError): From 20ad47f9e19e7650491a43aa4376d2b7095eba10 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Mon, 13 Dec 2021 11:45:31 +0100 Subject: [PATCH 12/28] Install dir creation message demoted to "debug" level (#27911) --- lib/spack/spack/installer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py index 010632076c8..196858830ab 100644 --- a/lib/spack/spack/installer.py +++ b/lib/spack/spack/installer.py @@ -1328,8 +1328,7 @@ def _setup_install_dir(self, pkg): pkg (spack.package.Package): the package to be built and installed """ if not os.path.exists(pkg.spec.prefix): - tty.verbose('Creating the installation directory {0}' - .format(pkg.spec.prefix)) + tty.debug('Creating the installation directory {0}'.format(pkg.spec.prefix)) spack.store.layout.create_install_directory(pkg.spec) else: # Set the proper group for the prefix From adf4e91658cce82592500337d86e6444bbde1817 Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Wed, 10 Nov 2021 16:48:02 -0500 Subject: [PATCH 13/28] upcxx: Update the UPC++ package to 2021.9.0 (#26996) * upcxx: Update the UPC++ package to 2021.9.0 * Add the new release, and a missing older one. * Remove the spack package cruft for supporting the obsolete build system that was present in older versions that are no longer supported. * General cleanups. Support for library versions older than 2020.3.0 is officially retired, for two reasons: 1. Releases prior to 2020.3.0 had a required dependency on Python 2, which is [officially EOL](https://www.python.org/doc/sunset-python-2/) as of Jan 1 2020, and is no longer considered secure. 2. (Most importantly) The UPC++ development team is unable/unwilling to support releases more than two years old. UPC++ provides robust backwards-compatibility to earlier releases of UPC++ v1.0, with very rare well-documented/well-motivated exceptions. Users are strongly encouraged to update to a current version of UPC++. NOTE: Most of the lines changed in this commit are simply re-indentation, and thus might be best reviewed in a diff that ignores whitespace. * upcxx: Detect Cray XC more explicitly This change is necessary to prevent false matches occuring on new Cray Shasta systems, which do not use the aries network but were incorrectly being treated as a Cray XC + aries platform. UPC++ has not yet deployed official native support for Cray Shasta, but this change is sufficient to allow building the portable backends there. --- .../repos/builtin/packages/upcxx/package.py | 194 ++++++++---------- 1 file changed, 83 insertions(+), 111 deletions(-) diff --git a/var/spack/repos/builtin/packages/upcxx/package.py b/var/spack/repos/builtin/packages/upcxx/package.py index 2661be42831..61011a35ace 100644 --- a/var/spack/repos/builtin/packages/upcxx/package.py +++ b/var/spack/repos/builtin/packages/upcxx/package.py @@ -3,11 +3,18 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import os + from spack import * +def is_CrayXC(): + return (spack.platforms.host().name == 'cray') and \ + (os.environ.get('CRAYPE_NETWORK_TARGET') == "aries") + + def cross_detect(): - if spack.platforms.host().name == 'cray': + if is_CrayXC(): if which('srun'): return 'cray-aries-slurm' if which('aprun'): @@ -24,6 +31,7 @@ class Upcxx(Package): homepage = "https://upcxx.lbl.gov" maintainers = ['bonachea'] + url = "https://bitbucket.org/berkeleylab/upcxx/downloads/upcxx-2021.3.0.tar.gz" git = 'https://bitbucket.org/berkeleylab/upcxx.git' tags = ['e4s'] @@ -31,13 +39,15 @@ class Upcxx(Package): version('develop', branch='develop') version('master', branch='master') + version('2021.9.0', sha256='9299e17602bcc8c05542cdc339897a9c2dba5b5c3838d6ef2df7a02250f42177') version('2021.3.0', sha256='3433714cd4162ffd8aad9a727c12dbf1c207b7d6664879fc41259a4b351595b7') version('2020.11.0', sha256='f6f212760a485a9f346ca11bb4751e7095bbe748b8e5b2389ff9238e9e321317', url='https://bitbucket.org/berkeleylab/upcxx/downloads/upcxx-2020.11.0-memory_kinds_prototype.tar.gz') version('2020.10.0', sha256='623e074b512bf8cad770a04040272e1cc660d2749760398b311f9bcc9d381a37') + version('2020.3.2', sha256='978adc315d21089c739d5efda764b77fc9a2a7c5860f169fe5cd2ca1d840620f') version('2020.3.0', sha256='01be35bef4c0cfd24e9b3d50c88866521b9cac3ad4cbb5b1fc97aea55078810f') - version('2019.9.0', sha256='7d67ccbeeefb59de9f403acc719f52127a30801a2c2b9774a1df03f850f8f1d4') - version('2019.3.2', sha256='dcb0b337c05a0feb2ed5386f5da6c60342412b49cab10f282f461e74411018ad') + # Do NOT add older versions here. + # UPC++ releases over 2 years old are not supported. variant('mpi', default=False, description='Enables MPI-based spawners and mpi-conduit') @@ -48,8 +58,8 @@ class Upcxx(Package): variant('cross', default=cross_detect(), description="UPC++ cross-compile target (autodetect by default)") - conflicts('cross=none', when='platform=cray', - msg='cross=none is unacceptable on Cray.' + + conflicts('cross=none', when=is_CrayXC(), + msg='cross=none is unacceptable on Cray XC.' + 'Please specify an appropriate "cross" value') # UPC++ always relies on GASNet-EX. @@ -61,47 +71,15 @@ class Upcxx(Package): depends_on('mpi', when='+mpi') depends_on('cuda', when='+cuda') - # Require Python2 2.7.5+ up to v2019.9.0 - depends_on('python@2.7.5:2', - type=("build", "run"), when='@:2019.9.0') - # v2020.3.0 and later also permit Python3 - depends_on('python@2.7.5:', type=("build", "run"), when='@2020.3.0:') + depends_on('python@2.7.5:', type=("build", "run")) # All flags should be passed to the build-env in autoconf-like vars flag_handler = env_flags - def url_for_version(self, version): - if version > Version('2019.3.2'): - url = "https://bitbucket.org/berkeleylab/upcxx/downloads/upcxx-{0}.tar.gz" - else: - url = "https://bitbucket.org/berkeleylab/upcxx/downloads/upcxx-{0}-offline.tar.gz" - return url.format(version) - - def setup_build_environment(self, env): - # ensure we use the correct python - env.set('UPCXX_PYTHON', self.spec['python'].command.path) - - if '+mpi' in self.spec: - env.set('GASNET_CONFIGURE_ARGS', - '--enable-mpi --enable-mpi-compat') - else: - env.set('GASNET_CONFIGURE_ARGS', '--without-mpicc') - - if 'cross=none' not in self.spec: - env.set('CROSS', self.spec.variants['cross'].value) - - if '+cuda' in self.spec: - env.set('UPCXX_CUDA', '1') - env.set('UPCXX_CUDA_NVCC', self.spec['cuda'].prefix.bin.nvcc) - def setup_run_environment(self, env): - # ensure we use the correct python - env.set('UPCXX_PYTHON', self.spec['python'].command.path) - env.set('UPCXX_INSTALL', self.prefix) env.set('UPCXX', self.prefix.bin.upcxx) - if 'platform=cray' in self.spec: - env.set('UPCXX_GASNET_CONDUIT', 'aries') + if is_CrayXC(): env.set('UPCXX_NETWORK', 'aries') def setup_dependent_package(self, module, dep_spec): @@ -110,97 +88,91 @@ def setup_dependent_package(self, module, dep_spec): def setup_dependent_build_environment(self, env, dependent_spec): env.set('UPCXX_INSTALL', self.prefix) env.set('UPCXX', self.prefix.bin.upcxx) - if 'platform=cray' in self.spec: - env.set('UPCXX_GASNET_CONDUIT', 'aries') + if is_CrayXC(): env.set('UPCXX_NETWORK', 'aries') def install(self, spec, prefix): + env = os.environ # UPC++ follows autoconf naming convention for LDLIBS, which is 'LIBS' if (env.get('LDLIBS')): env['LIBS'] = env['LDLIBS'] - if spec.version <= Version('2019.9.0'): - env['CC'] = self.compiler.cc - if '+mpi' in self.spec: - if 'platform=cray' in self.spec: - env['GASNET_CONFIGURE_ARGS'] += \ - " --with-mpicc=" + self.compiler.cc - else: - env['CXX'] = spec['mpi'].mpicxx - else: - env['CXX'] = self.compiler.cxx - if '+gasnet' in self.spec: - env['GASNET'] = spec['gasnet'].prefix.src - installsh = Executable("./install") - installsh(prefix) + options = ["--prefix=%s" % prefix] + + if 'cross=none' in spec: + options.append('--without-cross') else: - if 'platform=cray' in self.spec: - # Spack loads the cray-libsci module incorrectly on ALCF theta, - # breaking the Cray compiler wrappers - # cray-libsci is irrelevant to our build, so disable it - for var in ['PE_PKGCONFIG_PRODUCTS', 'PE_PKGCONFIG_LIBS']: - env[var] = ":".join( - filter(lambda x: "libsci" not in x.lower(), - env[var].split(":"))) - # Undo spack compiler wrappers: - # the C/C++ compilers must work post-install - # hack above no longer works after the fix to UPC++ issue #287 - real_cc = join_path(env['CRAYPE_DIR'], 'bin', 'cc') - real_cxx = join_path(env['CRAYPE_DIR'], 'bin', 'CC') - # workaround a bug in the UPC++ installer: (issue #346) - env['GASNET_CONFIGURE_ARGS'] += \ - " --with-cc=" + real_cc + " --with-cxx=" + real_cxx - if '+mpi' in self.spec: - env['GASNET_CONFIGURE_ARGS'] += " --with-mpicc=" + real_cc - else: - real_cc = self.compiler.cc - real_cxx = self.compiler.cxx - if '+mpi' in self.spec: - real_cxx = spec['mpi'].mpicxx + options.append('--with-cross=' + spec.variants['cross'].value) - env['CC'] = real_cc - env['CXX'] = real_cxx + if is_CrayXC(): + # Spack loads the cray-libsci module incorrectly on ALCF theta, + # breaking the Cray compiler wrappers + # cray-libsci is irrelevant to our build, so disable it + for var in ['PE_PKGCONFIG_PRODUCTS', 'PE_PKGCONFIG_LIBS']: + env[var] = ":".join( + filter(lambda x: "libsci" not in x.lower(), + env[var].split(":"))) + # Undo spack compiler wrappers: + # the C/C++ compilers must work post-install + real_cc = join_path(env['CRAYPE_DIR'], 'bin', 'cc') + real_cxx = join_path(env['CRAYPE_DIR'], 'bin', 'CC') + # workaround a bug in the UPC++ installer: (issue #346) + if (env.get('GASNET_CONFIGURE_ARGS') is None): + env['GASNET_CONFIGURE_ARGS'] = '' + env['GASNET_CONFIGURE_ARGS'] += \ + " --with-cc=" + real_cc + " --with-cxx=" + real_cxx + if '+mpi' in spec: + env['GASNET_CONFIGURE_ARGS'] += " --with-mpicc=" + real_cc + else: + real_cc = self.compiler.cc + real_cxx = self.compiler.cxx + if '+mpi' in spec: + real_cxx = spec['mpi'].mpicxx - options = ["--prefix=%s" % prefix] + options.append('--with-cc=' + real_cc) + options.append('--with-cxx=' + real_cxx) - if '+gasnet' in self.spec: - options.append('--with-gasnet=' + spec['gasnet'].prefix.src) + if '+gasnet' in spec: + options.append('--with-gasnet=' + spec['gasnet'].prefix.src) - configure(*options) + options.append('--with-python=' + spec['python'].command.path) - make() + if '+mpi' in spec: + options.append('--enable-mpi') + options.append('--enable-mpi-compat') + else: + options.append('--without-mpicc') - make('install') + if '+cuda' in spec: + options.append('--with-cuda') + options.append('--with-nvcc=' + spec['cuda'].prefix.bin.nvcc) + + configure(*options) + + make() + + make('install') install_tree('example', prefix.example) @run_after('install') @on_package_attributes(run_tests=True) def test_install(self): - if self.spec.version <= Version('2019.9.0'): - spack.main.send_warning_to_tty( - "run_tests not supported in UPC++ version " + - self.spec.version.string + " -- SKIPPED") - else: - # enable testing of unofficial conduits (mpi) - test_networks = 'NETWORKS=$(CONDUITS)' - # build hello world against installed tree in all configurations - make('test_install', test_networks) - make('tests-clean') # cleanup - # build all tests for all networks in debug mode - make('tests', test_networks) - if 'cross=none' in self.spec: - make('run-tests', 'NETWORKS=smp') # runs tests for smp backend - make('tests-clean') # cleanup + # enable testing of unofficial conduits (mpi) + test_networks = 'NETWORKS=$(CONDUITS)' + # build hello world against installed tree in all configurations + make('test_install', test_networks) + make('tests-clean') # cleanup + # build all tests for all networks in debug mode + make('tests', test_networks) + if 'cross=none' in self.spec: + make('run-tests', 'NETWORKS=smp') # runs tests for smp backend + make('tests-clean') # cleanup def test(self): - if self.spec.version <= Version('2019.9.0'): - spack.main.send_warning_to_tty( - "post-install tests not supported in UPC++ version " + - self.spec.version.string + " -- SKIPPED") - else: # run post-install smoke test: - test_install = join_path(self.prefix.bin, 'test-upcxx-install.sh') - self.run_test(test_install, expected=['SUCCESS'], status=0, - installed=True, - purpose='Checking UPC++ compile+link ' + - 'for all installed backends') + # run post-install smoke test: + test_install = join_path(self.prefix.bin, 'test-upcxx-install.sh') + self.run_test(test_install, expected=['SUCCESS'], status=0, + installed=True, + purpose='Checking UPC++ compile+link ' + + 'for all installed backends') From 9345bf81b95641a4e9728664f87777ed05f56b8f Mon Sep 17 00:00:00 2001 From: Greg Becker Date: Tue, 14 Dec 2021 23:52:53 -0800 Subject: [PATCH 14/28] Add option to minimize full debug cores. include warning message about performance (#27970) Co-authored-by: Harmen Stoppels --- lib/spack/spack/error.py | 17 +++------- lib/spack/spack/main.py | 18 ++++++++++ lib/spack/spack/solver/asp.py | 53 ++++++++++++++++++++++++++---- lib/spack/spack/test/concretize.py | 6 ++-- share/spack/spack-completion.bash | 2 +- 5 files changed, 74 insertions(+), 22 deletions(-) diff --git a/lib/spack/spack/error.py b/lib/spack/spack/error.py index be49656c070..700f4ea1d8d 100644 --- a/lib/spack/spack/error.py +++ b/lib/spack/spack/error.py @@ -123,18 +123,11 @@ class UnsatisfiableSpecError(SpecError): For original concretizer, provide the requirement that was violated when raising. """ - def __init__(self, provided, required=None, constraint_type=None, conflicts=None): - # required is only set by the original concretizer. - # clingo concretizer handles error messages differently. - if required is not None: - assert not conflicts # can't mix formats - super(UnsatisfiableSpecError, self).__init__( - "%s does not satisfy %s" % (provided, required)) - else: - indented = [' %s\n' % conflict for conflict in conflicts] - conflict_msg = ''.join(indented) - msg = '%s is unsatisfiable, conflicts are:\n%s' % (provided, conflict_msg) - super(UnsatisfiableSpecError, self).__init__(msg) + def __init__(self, provided, required, constraint_type): + # This is only the entrypoint for old concretizer errors + super(UnsatisfiableSpecError, self).__init__( + "%s does not satisfy %s" % (provided, required)) + self.provided = provided self.required = required self.constraint_type = constraint_type diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py index 86f5c760020..f41423fa1e2 100644 --- a/lib/spack/spack/main.py +++ b/lib/spack/spack/main.py @@ -41,6 +41,7 @@ import spack.paths import spack.platforms import spack.repo +import spack.solver.asp import spack.spec import spack.store import spack.util.debug @@ -380,6 +381,13 @@ def make_argument_parser(**kwargs): # stat names in groups of 7, for nice wrapping. stat_lines = list(zip(*(iter(stat_names),) * 7)) + # help message for --show-cores + show_cores_help = 'provide additional information on concretization failures\n' + show_cores_help += 'off (default): show only the violated rule\n' + show_cores_help += 'full: show raw unsat cores from clingo\n' + show_cores_help += 'minimized: show subset-minimal unsat cores ' + show_cores_help += '(Warning: this may take hours for some specs)' + parser.add_argument( '-h', '--help', dest='help', action='store_const', const='short', default=None, @@ -403,6 +411,9 @@ def make_argument_parser(**kwargs): '-d', '--debug', action='count', default=0, help="write out debug messages " "(more d's for more verbosity: -d, -dd, -ddd, etc.)") + parser.add_argument( + '--show-cores', choices=["off", "full", "minimized"], default="off", + help=show_cores_help) parser.add_argument( '--timestamp', action='store_true', help="Add a timestamp to tty output") @@ -486,6 +497,13 @@ def setup_main_options(args): spack.config.set('config:debug', True, scope='command_line') spack.util.environment.tracing_enabled = True + if args.show_cores != "off": + # minimize_cores defaults to true, turn it off if we're showing full core + # but don't want to wait to minimize it. + spack.solver.asp.full_cores = True + if args.show_cores == 'full': + spack.solver.asp.minimize_cores = False + if args.timestamp: tty.set_timestamp(True) diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 4ad05cd9bf7..957f363960c 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -59,6 +59,14 @@ parse_files = None +#: whether we should write ASP unsat cores quickly in debug mode when the cores +#: may be very large or take the time (sometimes hours) to minimize them +minimize_cores = True + +#: whether we should include all facts in the unsat cores or only error messages +full_cores = False + + # backward compatibility functions for clingo ASTs def ast_getter(*names): def getter(node): @@ -393,10 +401,12 @@ def raise_if_unsat(self): if len(constraints) == 1: constraints = constraints[0] - debug = spack.config.get('config:debug', False) - conflicts = self.format_cores() if debug else self.format_minimal_cores() + if minimize_cores: + conflicts = self.format_minimal_cores() + else: + conflicts = self.format_cores() - raise spack.error.UnsatisfiableSpecError(constraints, conflicts=conflicts) + raise UnsatisfiableSpecError(constraints, conflicts=conflicts) @property def specs(self): @@ -512,10 +522,9 @@ def fact(self, head, assumption=False): atom = self.backend.add_atom(symbol) - # in debug mode, make all facts choices/assumptions - # otherwise, only if we're generating cores and assumption=True - debug = spack.config.get('config:debug', False) - choice = debug or (self.cores and assumption) + # with `--show-cores=full or --show-cores=minimized, make all facts + # choices/assumptions, otherwise only if assumption=True + choice = self.cores and (full_cores or assumption) self.backend.add_rule([atom], [], choice=choice) if choice: @@ -2044,3 +2053,33 @@ def solve(specs, dump=(), models=0, timers=False, stats=False, tests=False, return driver.solve( setup, specs, dump, models, timers, stats, tests, reuse ) + + +class UnsatisfiableSpecError(spack.error.UnsatisfiableSpecError): + """ + Subclass for new constructor signature for new concretizer + """ + def __init__(self, provided, conflicts): + indented = [' %s\n' % conflict for conflict in conflicts] + conflict_msg = ''.join(indented) + issue = 'conflicts' if full_cores else 'errors' + msg = '%s is unsatisfiable, %s are:\n%s' % (provided, issue, conflict_msg) + + newline_indent = '\n ' + if not full_cores: + msg += newline_indent + 'To see full clingo unsat cores, ' + msg += 're-run with `spack --show-cores=full`' + if not minimize_cores or not full_cores: + # not solver.minimalize_cores and not solver.full_cores impossible + msg += newline_indent + 'For full, subset-minimal unsat cores, ' + msg += 're-run with `spack --show-cores=minimized' + msg += newline_indent + msg += 'Warning: This may take (up to) hours for some specs' + + super(spack.error.UnsatisfiableSpecError, self).__init__(msg) + + self.provided = provided + + # Add attribute expected of the superclass interface + self.required = None + self.constraint_type = None diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 5e9188b888f..179a4e26d81 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -557,11 +557,13 @@ def test_conflicts_in_spec(self, conflict_spec): with pytest.raises(spack.error.SpackError): s.concretize() - def test_conflicts_new_concretizer_debug(self, conflict_spec, mutable_config): + def test_conflicts_show_cores(self, conflict_spec, monkeypatch): if spack.config.get('config:concretizer') == 'original': pytest.skip('Testing debug statements specific to new concretizer') - spack.config.set('config:debug', True) + monkeypatch.setattr(spack.solver.asp, 'full_cores', True) + monkeypatch.setattr(spack.solver.asp, 'minimize_cores', False) + s = Spec(conflict_spec) with pytest.raises(spack.error.SpackError) as e: s.concretize() diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash index fa3ebd3c3ee..c7e4d101977 100755 --- a/share/spack/spack-completion.bash +++ b/share/spack/spack-completion.bash @@ -335,7 +335,7 @@ _spacktivate() { _spack() { if $list_options then - SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars" + SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --show-cores --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars" else SPACK_COMPREPLY="activate add analyze arch audit blame bootstrap build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config containerize create deactivate debug dependencies dependents deprecate dev-build develop diff docs edit env extensions external fetch find flake8 gc gpg graph help info install license list load location log-parse maintainers mark mirror module monitor patch pkg providers pydoc python reindex remove rm repo resource restage solve spec stage style tags test test-env tutorial undevelop uninstall unit-test unload url verify versions view" fi From 314867e6358377d6a46de442bb04314f75959e25 Mon Sep 17 00:00:00 2001 From: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com> Date: Wed, 15 Dec 2021 22:56:54 -0800 Subject: [PATCH 15/28] Provide meaningful message for empty environment installs (#28031) * Provide a meaningful failure message for installation of an empty environment * Allow regenerating view per offline discussion --- lib/spack/spack/cmd/install.py | 23 ++++++++++++++--------- lib/spack/spack/test/cmd/install.py | 12 ++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py index 95b195bc536..f4a4644312a 100644 --- a/lib/spack/spack/cmd/install.py +++ b/lib/spack/spack/cmd/install.py @@ -348,17 +348,22 @@ def get_tests(specs): env.write(regenerate=False) specs = env.all_specs() - if not args.log_file and not reporter.filename: - reporter.filename = default_log_file(specs[0]) - reporter.specs = specs + if specs: + if not args.log_file and not reporter.filename: + reporter.filename = default_log_file(specs[0]) + reporter.specs = specs - # Tell the monitor about the specs - if args.use_monitor and specs: - monitor.new_configuration(specs) + # Tell the monitor about the specs + if args.use_monitor and specs: + monitor.new_configuration(specs) - tty.msg("Installing environment {0}".format(env.name)) - with reporter('build'): - env.install_all(**kwargs) + tty.msg("Installing environment {0}".format(env.name)) + with reporter('build'): + env.install_all(**kwargs) + + else: + msg = '{0} environment has no specs to install'.format(env.name) + tty.msg(msg) tty.debug("Regenerating environment views for {0}" .format(env.name)) diff --git a/lib/spack/spack/test/cmd/install.py b/lib/spack/spack/test/cmd/install.py index 4fa022d168f..2c79880088a 100644 --- a/lib/spack/spack/test/cmd/install.py +++ b/lib/spack/spack/test/cmd/install.py @@ -1099,3 +1099,15 @@ def test_install_env_with_tests_root(tmpdir, mock_packages, mock_fetch, add('depb') install('--test', 'root') assert not os.path.exists(test_dep.prefix) + + +def test_install_empty_env(tmpdir, mock_packages, mock_fetch, + install_mockery, mutable_mock_env_path): + env_name = 'empty' + env('create', env_name) + with ev.read(env_name): + out = install(fail_on_error=False) + + assert env_name in out + assert 'environment' in out + assert 'no specs to install' in out From 37fbe30c4a5b6b9c453078270f7511cc5a1eb146 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 16 Dec 2021 10:47:15 +0100 Subject: [PATCH 16/28] Added opensuse/leap:15 to spack containerize (#27837) Co-authored-by: Massimiliano Culpo --- lib/spack/docs/containers.rst | 3 +++ lib/spack/spack/container/images.json | 18 ++++++++++++++++ share/spack/docker/leap-15.dockerfile | 21 ++++++++++++++----- .../templates/container/leap-15.dockerfile | 21 +++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 share/spack/templates/container/leap-15.dockerfile diff --git a/lib/spack/docs/containers.rst b/lib/spack/docs/containers.rst index 3d32de0841c..57a9cad1899 100644 --- a/lib/spack/docs/containers.rst +++ b/lib/spack/docs/containers.rst @@ -129,6 +129,9 @@ are currently supported are summarized in the table below: * - CentOS 7 - ``centos:7`` - ``spack/centos7`` + * - openSUSE Leap + - ``opensuse/leap`` + - ``spack/leap15`` All the images are tagged with the corresponding release of Spack: diff --git a/lib/spack/spack/container/images.json b/lib/spack/spack/container/images.json index ee4e5a2caa5..b82596f20cc 100644 --- a/lib/spack/spack/container/images.json +++ b/lib/spack/spack/container/images.json @@ -28,6 +28,19 @@ "develop": "latest" } }, + "opensuse/leap:15": { + "bootstrap": { + "template": "container/leap-15.dockerfile" + }, + "os_package_manager": "zypper", + "build": "spack/leap15", + "build_tags": { + "develop": "latest" + }, + "final": { + "image": "opensuse/leap:latest" + } + }, "nvidia/cuda:11.2.1": { "bootstrap": { "template": "container/cuda_11_2_1.dockerfile", @@ -85,6 +98,11 @@ "update": "yum update -y && amazon-linux-extras install epel -y", "install": "yum install -y", "clean": "rm -rf /var/cache/yum && yum clean all" + }, + "zypper": { + "update": "zypper update -y", + "install": "zypper install -y", + "clean": "rm -rf /var/cache/zypp && zypper clean -a" } } } diff --git a/share/spack/docker/leap-15.dockerfile b/share/spack/docker/leap-15.dockerfile index 65375c359b7..1bf41c6149a 100644 --- a/share/spack/docker/leap-15.dockerfile +++ b/share/spack/docker/leap-15.dockerfile @@ -2,7 +2,7 @@ FROM opensuse/leap:15.3 MAINTAINER Christian Goll ENV DOCKERFILE_BASE=opensuse \ - DOCKERFILE_DISTRO=opensuse_leap \ + DOCKERFILE_DISTRO=leap \ DOCKERFILE_DISTRO_VERSION=15.3 \ SPACK_ROOT=/opt/spack \ DEBIAN_FRONTEND=noninteractive \ @@ -11,10 +11,21 @@ ENV DOCKERFILE_BASE=opensuse \ RUN zypper ref && \ zypper up -y && \ - zypper in -y python3-base python3-boto3 \ - xz gzip tar bzip2 curl patch patchelf file \ - gcc-c++ gcc-fortran make cmake automake && \ - zypper clean + zypper in -y \ + bzip2\ + curl\ + file\ + gcc-c++\ + gcc-fortran\ + make\ + gzip\ + patch\ + patchelf\ + python3-base \ + python3-boto3\ + tar\ + xz\ +&& zypper clean # clean up manpages RUN rm -rf /var/cache/zypp/* \ diff --git a/share/spack/templates/container/leap-15.dockerfile b/share/spack/templates/container/leap-15.dockerfile new file mode 100644 index 00000000000..38e4f1c0096 --- /dev/null +++ b/share/spack/templates/container/leap-15.dockerfile @@ -0,0 +1,21 @@ +{% extends "container/bootstrap-base.dockerfile" %} +{% block install_os_packages %} +RUN zypper ref && \ + zypper up -y && \ + zypper in -y \ + bzip2\ + curl\ + file\ + gcc-c++\ + gcc-fortran\ + make\ + git\ + gzip\ + patch\ + patchelf\ + python3-base \ + python3-boto3\ + tar\ + xz\ +&& zypper clean +{% endblock %} From 8f3b025b5502120c99c483b619ae83d2ead07c7f Mon Sep 17 00:00:00 2001 From: Andrew W Elble Date: Thu, 16 Dec 2021 05:54:35 -0500 Subject: [PATCH 17/28] MANPATH needs a trailing ':' to utilize system defaults (#21682) otherwise spack breaks using system man pages by default. Co-authored-by: Harmen Stoppels --- lib/spack/spack/test/cmd/load.py | 19 +++++++++++++++++++ lib/spack/spack/util/environment.py | 4 +++- .../builtin.mock/packages/mpileaks/package.py | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/spack/spack/test/cmd/load.py b/lib/spack/spack/test/cmd/load.py index ef014418c79..51867b548ca 100644 --- a/lib/spack/spack/test/cmd/load.py +++ b/lib/spack/spack/test/cmd/load.py @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) import os +import re import pytest @@ -16,6 +17,24 @@ location = SpackCommand('location') +def test_manpath_trailing_colon(install_mockery, mock_fetch, mock_archive, + mock_packages, working_env): + """Test that the commands generated by load add the MANPATH prefix + inspections. Also test that Spack correctly preserves the default/existing + manpath search path via a trailing colon""" + install('mpileaks') + + sh_out = load('--sh', '--only', 'package', 'mpileaks') + lines = sh_out.split('\n') + assert any(re.match(r'export MANPATH=.*:;', ln) for ln in lines) + + os.environ['MANPATH'] = '/tmp/man:' + + sh_out = load('--sh', '--only', 'package', 'mpileaks') + lines = sh_out.split('\n') + assert any(re.match(r'export MANPATH=.*:/tmp/man:;', ln) for ln in lines) + + def test_load(install_mockery, mock_fetch, mock_archive, mock_packages): """Test that the commands generated by load add the specified prefix inspections. Also test that Spack records loaded specs by hash in the diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py index a0a6c4a3aa0..f0457c27b82 100644 --- a/lib/spack/spack/util/environment.py +++ b/lib/spack/spack/util/environment.py @@ -611,7 +611,6 @@ def apply_modifications(self, env=None): def shell_modifications(self, shell='sh', explicit=False, env=None): """Return shell code to apply the modifications and clears the list.""" modifications = self.group_by_name() - new_env = os.environ.copy() if env is None: env = os.environ @@ -622,6 +621,9 @@ def shell_modifications(self, shell='sh', explicit=False, env=None): for x in actions: x.execute(new_env) + if 'MANPATH' in new_env and not new_env.get('MANPATH').endswith(':'): + new_env['MANPATH'] += ':' + cmds = '' for name in sorted(set(modifications)): diff --git a/var/spack/repos/builtin.mock/packages/mpileaks/package.py b/var/spack/repos/builtin.mock/packages/mpileaks/package.py index e0bff0673b0..36c073ccf0e 100644 --- a/var/spack/repos/builtin.mock/packages/mpileaks/package.py +++ b/var/spack/repos/builtin.mock/packages/mpileaks/package.py @@ -28,6 +28,7 @@ class Mpileaks(Package): def install(self, spec, prefix): touch(prefix.mpileaks) + mkdirp(prefix.man) def setup_environment(self, senv, renv): renv.set('FOOBAR', self.name) From b2694013d4f3a564a0d1e4a9b02feaab15a5abef Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Wed, 15 Dec 2021 17:56:03 +0100 Subject: [PATCH 18/28] Revert "patches: make re-applied patches idempotent (#26784)" (#27625) This reverts commit c5ca0db27fce5d772dc8a4fcffec3b62bb0bf1f3. --- lib/spack/spack/patch.py | 36 ++--------- lib/spack/spack/test/patch.py | 97 +++++------------------------- lib/spack/spack/util/crypto.py | 5 -- lib/spack/spack/util/executable.py | 24 +++----- 4 files changed, 25 insertions(+), 137 deletions(-) diff --git a/lib/spack/spack/patch.py b/lib/spack/spack/patch.py index cdd9ad03879..a273514636d 100644 --- a/lib/spack/spack/patch.py +++ b/lib/spack/spack/patch.py @@ -7,7 +7,6 @@ import inspect import os import os.path -import sys import llnl.util.filesystem import llnl.util.lang @@ -26,12 +25,6 @@ def apply_patch(stage, patch_path, level=1, working_dir='.'): """Apply the patch at patch_path to code in the stage. - Spack runs ``patch`` with ``-N`` so that it does not reject already-applied - patches. This is useful for develop specs, so that the build does not fail - due to repeated application of patches, and for easing requirements on patch - specifications in packages -- packages won't stop working when patches we - previously had to apply land in upstream. - Args: stage (spack.stage.Stage): stage with code that will be patched patch_path (str): filesystem location for the patch to apply @@ -41,31 +34,10 @@ def apply_patch(stage, patch_path, level=1, working_dir='.'): """ patch = which("patch", required=True) with llnl.util.filesystem.working_dir(stage.source_path): - output = patch( - '-N', # don't reject already-applied patches - '-p', str(level), # patch level (directory depth) - '-i', patch_path, # input source is the patch file - '-d', working_dir, # patch chdir's to here before patching - output=str, - fail_on_error=False, - ) - - if patch.returncode != 0: - # `patch` returns 1 both: - # a) when an error applying a patch, and - # b) when -N is supplied and the patch has already been applied - # - # It returns > 1 if there's something more serious wrong. - # - # So, the best we can do is to look for return code 1, look for output - # indicating that the patch was already applied, and ignore the error - # if we see it. Most implementations (BSD and GNU) seem to have the - # same messages, so we expect these checks to be reliable. - if patch.returncode > 1 or not any( - s in output for s in ("Skipping patch", "ignored") - ): - sys.stderr.write(output) - raise patch.error + patch('-s', + '-p', str(level), + '-i', patch_path, + '-d', working_dir) class Patch(object): diff --git a/lib/spack/spack/test/patch.py b/lib/spack/spack/test/patch.py index 5285be8ba96..83831004409 100644 --- a/lib/spack/spack/test/patch.py +++ b/lib/spack/spack/test/patch.py @@ -15,9 +15,8 @@ import spack.paths import spack.repo import spack.util.compression -import spack.util.crypto from spack.spec import Spec -from spack.stage import DIYStage, Stage +from spack.stage import Stage from spack.util.executable import Executable # various sha256 sums (using variables for legibility) @@ -34,43 +33,6 @@ url2_archive_sha256 = 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd' -# some simple files for patch tests -file_to_patch = """\ -first line -second line -""" - -patch_file = """\ -diff a/foo.txt b/foo-expected.txt ---- a/foo.txt -+++ b/foo-expected.txt -@@ -1,2 +1,3 @@ -+zeroth line - first line --second line -+third line -""" - -expected_patch_result = """\ -zeroth line -first line -third line -""" - -file_patch_cant_apply_to = """\ -this file -is completely different -from anything in the files -or patch above -""" - - -def write_file(filename, contents): - """Helper function for setting up tests.""" - with open(filename, 'w') as f: - f.write(contents) - - @pytest.fixture() def mock_patch_stage(tmpdir_factory, monkeypatch): # Don't disrupt the spack install directory with tests. @@ -105,9 +67,19 @@ def test_url_patch(mock_patch_stage, filename, sha256, archive_sha256): mkdirp(stage.source_path) with working_dir(stage.source_path): - write_file("foo.txt", file_to_patch) - write_file("foo-expected.txt", expected_patch_result) - + # write a file to be patched + with open('foo.txt', 'w') as f: + f.write("""\ +first line +second line +""") + # write the expected result of patching. + with open('foo-expected.txt', 'w') as f: + f.write("""\ +zeroth line +first line +third line +""") # apply the patch and compare files patch.fetch() patch.apply(stage) @@ -117,47 +89,6 @@ def test_url_patch(mock_patch_stage, filename, sha256, archive_sha256): assert filecmp.cmp('foo.txt', 'foo-expected.txt') -def test_apply_patch_twice(mock_patch_stage, tmpdir): - """Ensure that patch doesn't fail if applied twice.""" - - stage = DIYStage(str(tmpdir)) - with tmpdir.as_cwd(): - write_file("foo.txt", file_to_patch) - write_file("foo-expected.txt", expected_patch_result) - write_file("foo.patch", patch_file) - - FakePackage = collections.namedtuple( - 'FakePackage', ['name', 'namespace', 'fullname']) - fake_pkg = FakePackage('fake-package', 'test', 'fake-package') - - def make_patch(filename): - path = os.path.realpath(str(tmpdir.join(filename))) - url = 'file://' + path - sha256 = spack.util.crypto.checksum("sha256", path) - return spack.patch.UrlPatch(fake_pkg, url, sha256=sha256) - - # apply the first time - patch = make_patch('foo.patch') - patch.fetch() - - patch.apply(stage) - with working_dir(stage.source_path): - assert filecmp.cmp('foo.txt', 'foo-expected.txt') - - # ensure apply() is idempotent - patch.apply(stage) - with working_dir(stage.source_path): - assert filecmp.cmp('foo.txt', 'foo-expected.txt') - - # now write a file that can't be patched - with working_dir(stage.source_path): - write_file("foo.txt", file_patch_cant_apply_to) - - # this application should fail with a real error - with pytest.raises(spack.util.executable.ProcessError): - patch.apply(stage) - - def test_patch_in_spec(mock_packages, config): """Test whether patches in a package appear in the spec.""" spec = Spec('patch') diff --git a/lib/spack/spack/util/crypto.py b/lib/spack/spack/util/crypto.py index 8e055126fe5..549216a4c6d 100644 --- a/lib/spack/spack/util/crypto.py +++ b/lib/spack/spack/util/crypto.py @@ -92,11 +92,6 @@ def checksum(hashlib_algo, filename, **kwargs): """Returns a hex digest of the filename generated using an algorithm from hashlib. """ - if isinstance(hashlib_algo, str): - if hashlib_algo not in hashes: - raise ValueError("Invalid hash algorithm: ", hashlib_algo) - hashlib_algo = hash_fun_for_algo(hashlib_algo) - block_size = kwargs.get('block_size', 2**20) hasher = hashlib_algo() with open(filename, 'rb') as file: diff --git a/lib/spack/spack/util/executable.py b/lib/spack/spack/util/executable.py index ded429c2e6b..e615ccdcfd8 100644 --- a/lib/spack/spack/util/executable.py +++ b/lib/spack/spack/util/executable.py @@ -27,7 +27,6 @@ def __init__(self, name): from spack.util.environment import EnvironmentModifications # no cycle self.default_envmod = EnvironmentModifications() self.returncode = None - self.error = None # saved ProcessError when fail_on_error if not self.exe: raise ProcessError("Cannot construct executable for '%s'" % name) @@ -91,8 +90,7 @@ def __call__(self, *args, **kwargs): the environment (neither requires nor precludes env) fail_on_error (bool): Raise an exception if the subprocess returns an error. Default is True. The return code is available as - ``exe.returncode``, and a saved ``ProcessError`` that would - have been raised is in ``exe.error``. + ``exe.returncode`` ignore_errors (int or list): A list of error codes to ignore. If these codes are returned, this process will not raise an exception even if ``fail_on_error`` is set to ``True`` @@ -215,7 +213,7 @@ def streamify(arg, mode): sys.stderr.write(errstr) rc = self.returncode = proc.returncode - if rc != 0: + if fail_on_error and rc != 0 and (rc not in ignore_errors): long_msg = cmd_line_string if result: # If the output is not captured in the result, it will have @@ -224,11 +222,8 @@ def streamify(arg, mode): # stdout/stderr (e.g. if 'output' is not specified) long_msg += '\n' + result - self.error = ProcessError( - 'Command exited with status %d:' % proc.returncode, long_msg - ) - if fail_on_error and (rc not in ignore_errors): - raise self.error + raise ProcessError('Command exited with status %d:' % + proc.returncode, long_msg) return result @@ -237,15 +232,10 @@ def streamify(arg, mode): '%s: %s' % (self.exe[0], e.strerror), 'Command: ' + cmd_line_string) except subprocess.CalledProcessError as e: - self.error = ProcessError( - str(e), - '\nExit status %d when invoking command: %s' % ( - proc.returncode, - cmd_line_string, - ), - ) if fail_on_error: - raise self.error + raise ProcessError( + str(e), '\nExit status %d when invoking command: %s' % + (proc.returncode, cmd_line_string)) finally: if close_ostream: From 13e36c5457422120105c1fdaf2f9945723f2bcf9 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Thu, 16 Dec 2021 13:13:12 +0100 Subject: [PATCH 19/28] Fix table formatting (#28037) --- lib/spack/docs/packaging_guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index e2e3e297fd2..e974924e7a7 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -2943,7 +2943,7 @@ The package base class, usually specialized for a given build system, determines actual set of entities available for overriding. The classes that are currently provided by Spack are: -+-------------------------=--------------------------------+----------------------------------+ ++----------------------------------------------------------+----------------------------------+ | **Base Class** | **Purpose** | +==========================================================+==================================+ | :class:`~spack.package.Package` | General base class not | From 79fd1c5114e8aa7fb2d44a3f17c7b3dc1547cbcd Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Thu, 16 Dec 2021 13:50:20 +0100 Subject: [PATCH 20/28] Set backup=False by default in filter_file (#28036) --- lib/spack/llnl/util/filesystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py index 4c4ea1d5b8f..753129b78e4 100644 --- a/lib/spack/llnl/util/filesystem.py +++ b/lib/spack/llnl/util/filesystem.py @@ -141,7 +141,7 @@ def filter_file(regex, repl, *filenames, **kwargs): file. """ string = kwargs.get('string', False) - backup = kwargs.get('backup', True) + backup = kwargs.get('backup', False) ignore_absent = kwargs.get('ignore_absent', False) stop_at = kwargs.get('stop_at', None) From 17edf1ae90a9b56eb50ec002027a424bef58d97d Mon Sep 17 00:00:00 2001 From: victorusu Date: Fri, 17 Dec 2021 10:05:32 +0100 Subject: [PATCH 21/28] Add setdefault option to tcl module (#14686) This commit introduces the command spack module tcl setdefault similar to the one already available for lmod Co-authored-by: Massimiliano Culpo --- lib/spack/spack/cmd/modules/lmod.py | 30 +++++++++++++--------- lib/spack/spack/cmd/modules/tcl.py | 40 ++++++++++++++++++++++++++--- lib/spack/spack/modules/common.py | 3 +++ lib/spack/spack/test/cmd/module.py | 14 +++++++--- share/spack/spack-completion.bash | 11 +++++++- 5 files changed, 79 insertions(+), 19 deletions(-) diff --git a/lib/spack/spack/cmd/modules/lmod.py b/lib/spack/spack/cmd/modules/lmod.py index d28ccb3eaf8..1920f8ec66b 100644 --- a/lib/spack/spack/cmd/modules/lmod.py +++ b/lib/spack/spack/cmd/modules/lmod.py @@ -4,12 +4,11 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) import functools -import os - -import llnl.util.filesystem import spack.cmd.common.arguments import spack.cmd.modules +import spack.config +import spack.modules.lmod def add_command(parser, command_dict): @@ -41,12 +40,19 @@ def setdefault(module_type, specs, args): # https://lmod.readthedocs.io/en/latest/060_locating.html#marking-a-version-as-default # spack.cmd.modules.one_spec_or_raise(specs) - writer = spack.modules.module_types['lmod']( - specs[0], args.module_set_name) - - module_folder = os.path.dirname(writer.layout.filename) - module_basename = os.path.basename(writer.layout.filename) - with llnl.util.filesystem.working_dir(module_folder): - if os.path.exists('default') and os.path.islink('default'): - os.remove('default') - os.symlink(module_basename, 'default') + spec = specs[0] + data = { + 'modules': { + args.module_set_name: { + 'lmod': { + 'defaults': [str(spec)] + } + } + } + } + # Need to clear the cache if a SpackCommand is called during scripting + spack.modules.lmod.configuration_registry = {} + scope = spack.config.InternalConfigScope('lmod-setdefault', data) + with spack.config.override(scope): + writer = spack.modules.module_types['lmod'](spec, args.module_set_name) + writer.update_module_defaults() diff --git a/lib/spack/spack/cmd/modules/tcl.py b/lib/spack/spack/cmd/modules/tcl.py index a9486b9de45..5a29bc6df5b 100644 --- a/lib/spack/spack/cmd/modules/tcl.py +++ b/lib/spack/spack/cmd/modules/tcl.py @@ -2,18 +2,52 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) - import functools +import spack.cmd.common.arguments import spack.cmd.modules +import spack.config +import spack.modules.tcl def add_command(parser, command_dict): tcl_parser = parser.add_parser( 'tcl', help='manipulate non-hierarchical module files' ) - spack.cmd.modules.setup_parser(tcl_parser) + sp = spack.cmd.modules.setup_parser(tcl_parser) + + # Set default module file for a package + setdefault_parser = sp.add_parser( + 'setdefault', help='set the default module file for a package' + ) + spack.cmd.common.arguments.add_common_arguments( + setdefault_parser, ['constraint'] + ) + + callbacks = dict(spack.cmd.modules.callbacks.items()) + callbacks['setdefault'] = setdefault command_dict['tcl'] = functools.partial( - spack.cmd.modules.modules_cmd, module_type='tcl' + spack.cmd.modules.modules_cmd, module_type='tcl', callbacks=callbacks ) + + +def setdefault(module_type, specs, args): + """Set the default module file, when multiple are present""" + # Currently, accepts only a single matching spec + spack.cmd.modules.one_spec_or_raise(specs) + spec = specs[0] + data = { + 'modules': { + args.module_set_name: { + 'tcl': { + 'defaults': [str(spec)] + } + } + } + } + spack.modules.tcl.configuration_registry = {} + scope = spack.config.InternalConfigScope('tcl-setdefault', data) + with spack.config.override(scope): + writer = spack.modules.module_types['tcl'](spec, args.module_set_name) + writer.update_module_defaults() diff --git a/lib/spack/spack/modules/common.py b/lib/spack/spack/modules/common.py index 8855e57e644..aee1c1cc17c 100644 --- a/lib/spack/spack/modules/common.py +++ b/lib/spack/spack/modules/common.py @@ -906,6 +906,9 @@ def write(self, overwrite=False): fp.set_permissions_by_spec(self.layout.filename, self.spec) # Symlink defaults if needed + self.update_module_defaults() + + def update_module_defaults(self): if any(self.spec.satisfies(default) for default in self.conf.defaults): # This spec matches a default, it needs to be symlinked to default # Symlink to a tmp location first and move, so that existing diff --git a/lib/spack/spack/test/cmd/module.py b/lib/spack/spack/test/cmd/module.py index 3bced1d335f..b157e1be6b3 100644 --- a/lib/spack/spack/test/cmd/module.py +++ b/lib/spack/spack/test/cmd/module.py @@ -178,10 +178,18 @@ def test_loads_recursive_blacklisted(database, module_configuration): @pytest.mark.db def test_setdefault_command( - mutable_database, module_configuration + mutable_database, mutable_config ): - module_configuration('autoload_direct') - + data = { + 'default': { + 'enable': ['lmod'], + 'lmod': { + 'core_compilers': ['clang@3.3'], + 'hierarchy': ['mpi'] + } + } + } + spack.config.set('modules', data) # Install two different versions of a package other_spec, preferred = 'a@1.0', 'a@2.0' diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash index c7e4d101977..b41a21946c7 100755 --- a/share/spack/spack-completion.bash +++ b/share/spack/spack-completion.bash @@ -1382,7 +1382,7 @@ _spack_module_tcl() { then SPACK_COMPREPLY="-h --help -n --name" else - SPACK_COMPREPLY="refresh find rm loads" + SPACK_COMPREPLY="refresh find rm loads setdefault" fi } @@ -1422,6 +1422,15 @@ _spack_module_tcl_loads() { fi } +_spack_module_tcl_setdefault() { + if $list_options + then + SPACK_COMPREPLY="-h --help" + else + _installed_packages + fi +} + _spack_monitor() { SPACK_COMPREPLY="-h --help --monitor --monitor-save-local --monitor-no-auth --monitor-tags --monitor-keep-going --monitor-host --monitor-prefix" } From e1cc28a30a21da9d17967bc21e93d94c7fa03466 Mon Sep 17 00:00:00 2001 From: Paul Spencer <32964672+pwablito@users.noreply.github.com> Date: Sat, 18 Dec 2021 23:07:20 -0700 Subject: [PATCH 22/28] sbang: respect package permissive package permissions for sbang (#25764) Co-authored-by: Todd Gamblin Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com> --- lib/spack/spack/hooks/sbang.py | 45 ++++++++++++++++++++++-- lib/spack/spack/test/sbang.py | 63 +++++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/lib/spack/spack/hooks/sbang.py b/lib/spack/spack/hooks/sbang.py index ce2cf3435d2..179416f03c4 100644 --- a/lib/spack/spack/hooks/sbang.py +++ b/lib/spack/spack/hooks/sbang.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) import filecmp +import grp import os import re import shutil @@ -14,7 +15,9 @@ import llnl.util.filesystem as fs import llnl.util.tty as tty +import spack.package_prefs import spack.paths +import spack.spec import spack.store #: OS-imposed character limit for shebang line: 127 for Linux; 511 for Mac. @@ -187,11 +190,47 @@ def install_sbang(): spack.paths.sbang_script, sbang_path): return - # make $install_tree/bin and copy in a new version of sbang if needed + # make $install_tree/bin sbang_bin_dir = os.path.dirname(sbang_path) fs.mkdirp(sbang_bin_dir) - fs.install(spack.paths.sbang_script, sbang_path) - fs.set_install_permissions(sbang_bin_dir) + + # get permissions for bin dir from configuration files + group_name = spack.package_prefs.get_package_group(spack.spec.Spec("all")) + config_mode = spack.package_prefs.get_package_dir_permissions( + spack.spec.Spec("all") + ) + + if group_name: + os.chmod(sbang_bin_dir, config_mode) # Use package directory permissions + else: + fs.set_install_permissions(sbang_bin_dir) + + # set group on sbang_bin_dir if not already set (only if set in configuration) + if group_name and grp.getgrgid(os.stat(sbang_bin_dir).st_gid).gr_name != group_name: + os.chown( + sbang_bin_dir, + os.stat(sbang_bin_dir).st_uid, + grp.getgrnam(group_name).gr_gid + ) + + # copy over the fresh copy of `sbang` + sbang_tmp_path = os.path.join( + os.path.dirname(sbang_path), + ".%s.tmp" % os.path.basename(sbang_path), + ) + shutil.copy(spack.paths.sbang_script, sbang_tmp_path) + + # set permissions on `sbang` (including group if set in configuration) + os.chmod(sbang_tmp_path, config_mode) + if group_name: + os.chown( + sbang_tmp_path, + os.stat(sbang_tmp_path).st_uid, + grp.getgrnam(group_name).gr_gid + ) + + # Finally, move the new `sbang` into place atomically + os.rename(sbang_tmp_path, sbang_path) def post_install(spec): diff --git a/lib/spack/spack/test/sbang.py b/lib/spack/spack/test/sbang.py index 6d26bd1ba87..6e0d00eba08 100644 --- a/lib/spack/spack/test/sbang.py +++ b/lib/spack/spack/test/sbang.py @@ -7,6 +7,7 @@ Test that Spack's shebang filtering works correctly. """ import filecmp +import grp import os import shutil import stat @@ -19,6 +20,7 @@ import spack.hooks.sbang as sbang import spack.paths import spack.store +import spack.util.spack_yaml as syaml from spack.util.executable import which too_long = sbang.system_shebang_limit + 1 @@ -256,7 +258,34 @@ def test_shebang_handles_non_writable_files(script_dir, sbang_line): assert oct(not_writable_mode) == oct(st.st_mode) -def check_sbang_installation(): +@pytest.fixture(scope='function') +def configure_group_perms(): + conf = syaml.load_config("""\ +all: + permissions: + read: world + write: group + group: {0} +""".format(grp.getgrgid(os.getegid()).gr_name)) + spack.config.set('packages', conf, scope='user') + + yield + + +@pytest.fixture(scope='function') +def configure_user_perms(): + conf = syaml.load_config("""\ +all: + permissions: + read: world + write: user +""") + spack.config.set('packages', conf, scope='user') + + yield + + +def check_sbang_installation(group=False): sbang_path = sbang.sbang_install_path() sbang_bin_dir = os.path.dirname(sbang_path) assert sbang_path.startswith(spack.store.store.unpadded_root) @@ -264,14 +293,22 @@ def check_sbang_installation(): assert os.path.exists(sbang_path) assert fs.is_exe(sbang_path) - status = os.stat(sbang_path) - assert (status.st_mode & 0o777) == 0o755 - status = os.stat(sbang_bin_dir) - assert (status.st_mode & 0o777) == 0o755 + mode = (status.st_mode & 0o777) + if group: + assert mode == 0o775, 'Unexpected {0}'.format(oct(mode)) + else: + assert mode == 0o755, 'Unexpected {0}'.format(oct(mode)) + + status = os.stat(sbang_path) + mode = (status.st_mode & 0o777) + if group: + assert mode == 0o775, 'Unexpected {0}'.format(oct(mode)) + else: + assert mode == 0o755, 'Unexpected {0}'.format(oct(mode)) -def test_install_sbang(install_mockery): +def run_test_install_sbang(group): sbang_path = sbang.sbang_install_path() sbang_bin_dir = os.path.dirname(sbang_path) @@ -279,7 +316,7 @@ def test_install_sbang(install_mockery): assert not os.path.exists(sbang_bin_dir) sbang.install_sbang() - check_sbang_installation() + check_sbang_installation(group) # put an invalid file in for sbang fs.mkdirp(sbang_bin_dir) @@ -287,11 +324,19 @@ def test_install_sbang(install_mockery): f.write("foo") sbang.install_sbang() - check_sbang_installation() + check_sbang_installation(group) # install again and make sure sbang is still fine sbang.install_sbang() - check_sbang_installation() + check_sbang_installation(group) + + +def test_install_group_sbang(install_mockery, configure_group_perms): + run_test_install_sbang(True) + + +def test_install_user_sbang(install_mockery, configure_user_perms): + run_test_install_sbang(False) def test_install_sbang_too_long(tmpdir): From fa5e186d4a25996f0d5ef4cff363117ee423513f Mon Sep 17 00:00:00 2001 From: Morten Kristensen Date: Sat, 18 Dec 2021 19:17:02 +0100 Subject: [PATCH 23/28] py-vermin: add latest version 1.3.2 (#28072) * py-vermin: add latest version 1.3.2 * [vermin] Fixed usages of super() without arguments (v3+) --- var/spack/repos/builtin/packages/openloops/get-process-list.py | 2 +- var/spack/repos/builtin/packages/py-charm4py/package.py | 2 +- var/spack/repos/builtin/packages/py-vermin/package.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/var/spack/repos/builtin/packages/openloops/get-process-list.py b/var/spack/repos/builtin/packages/openloops/get-process-list.py index 6481865eeff..e9a8d8104f7 100644 --- a/var/spack/repos/builtin/packages/openloops/get-process-list.py +++ b/var/spack/repos/builtin/packages/openloops/get-process-list.py @@ -8,7 +8,7 @@ def error(self, message): pass def __init__(self): - super().__init__() + super(HTMLParser, self).__init__() self.state = 0 self.processes = [] diff --git a/var/spack/repos/builtin/packages/py-charm4py/package.py b/var/spack/repos/builtin/packages/py-charm4py/package.py index ebe181bceaa..e2f93215bea 100644 --- a/var/spack/repos/builtin/packages/py-charm4py/package.py +++ b/var/spack/repos/builtin/packages/py-charm4py/package.py @@ -66,7 +66,7 @@ def setup_build_environment(self, env): def install_args(self, spec, prefix): # Have the parent class version set prefix - args = super().install_args(spec, prefix) + args = super(PythonPackage, self).install_args(spec, prefix) if '+mpi' in spec: args.append('--mpi') return args diff --git a/var/spack/repos/builtin/packages/py-vermin/package.py b/var/spack/repos/builtin/packages/py-vermin/package.py index b7962716cf8..c592b78493f 100644 --- a/var/spack/repos/builtin/packages/py-vermin/package.py +++ b/var/spack/repos/builtin/packages/py-vermin/package.py @@ -8,10 +8,11 @@ class PyVermin(PythonPackage): """Concurrently detect the minimum Python versions needed to run code.""" homepage = "https://github.com/netromdk/vermin" - url = "https://github.com/netromdk/vermin/archive/v1.3.1.tar.gz" + url = "https://github.com/netromdk/vermin/archive/v1.3.2.tar.gz" maintainers = ['netromdk'] + version('1.3.2', sha256='2818eaea24c5be5dae1f374ddb2377e9cfaad04d0a3372ad129cffc46cec5404') version('1.3.1', sha256='ddcdaad5a708a483af192075f5d2eaaaf3aa4661b5101ddafa40d7837eeb5368') version('1.3.0', sha256='adf2b6ea34c01c3a81fc4fa78c2e5fa6c8dd6d35327a8e5a4caeeaef7ec21668') version('1.2.2', sha256='d0343b2a78d7e4de67dfd2d882eeaf8b241db724f7e67f83bdd4111edb97f1e2') From 87abda4cdd689bf9dff66960558dc63268794d87 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Mon, 20 Dec 2021 09:05:06 +0100 Subject: [PATCH 24/28] py-pandas: fix issue reported by vermin --- var/spack/repos/builtin/packages/py-pandas/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/var/spack/repos/builtin/packages/py-pandas/package.py b/var/spack/repos/builtin/packages/py-pandas/package.py index 9d5fa53c7cd..9496908dd47 100644 --- a/var/spack/repos/builtin/packages/py-pandas/package.py +++ b/var/spack/repos/builtin/packages/py-pandas/package.py @@ -91,7 +91,7 @@ class PyPandas(PythonPackage): @property def import_modules(self): - modules = super().import_modules + modules = super(PyPandas, self).import_modules() ignored_imports = ["pandas.tests", "pandas.plotting._matplotlib"] From 5daf023aecf3d72943cff0a010dfafd95edfdf3b Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Tue, 21 Dec 2021 18:41:12 +0100 Subject: [PATCH 25/28] Regenerate views when specs already installed (#28113) With this commit: ``` $ spack env activate --temp $ spack install zlib ==> All of the packages are already installed ==> Updating view at /tmp/spack-faiirgmt/.spack-env/view $ spack install zlib ==> All of the packages are already installed ``` Before this PR: ``` $ spack env activate --temp $ spack install zlib ==> All of the packages are already installed $ spack install zlib ==> All of the packages are already installed ``` No view was generated --- lib/spack/spack/environment/environment.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py index a6be5d1c500..3114f55638e 100644 --- a/lib/spack/spack/environment/environment.py +++ b/lib/spack/spack/environment/environment.py @@ -1500,10 +1500,8 @@ def install_specs(self, specs=None, **install_args): if not specs_to_install: tty.msg('All of the packages are already installed') - return - - tty.debug('Processing {0} uninstalled specs'.format( - len(specs_to_install))) + else: + tty.debug('Processing {0} uninstalled specs'.format(len(specs_to_install))) install_args['overwrite'] = install_args.get( 'overwrite', []) + self._get_overwrite_specs() From 8e659f512e00534c6af427ef3832db68fcd2f2d3 Mon Sep 17 00:00:00 2001 From: Tom Scogland Date: Wed, 22 Dec 2021 07:25:05 -0800 Subject: [PATCH 26/28] locks: allow locks to work under high contention (#27846) * locks: allow locks to work under high contention This is a bug found by Harshitha Menon. The `lock=None` line shouldn't be a release but should be ``` return (lock_type, None) ``` to inform the caller it couldn't get the lock type requested without disturbing the existing lock object in the database. There were also a couple of bugs due to taking write locks at the beginning without any checking or release, and not releasing read locks before requeueing. This version no longer gives me read upgrade to write errors, even running 200 instances on one box. * Change lock in check_deps_status to read, release if not installed, not sure why this was ever write, but read definitely is more appropriate here, and the read lock is only held out of the scope if the package is installed. * Release read lock before requeueing to reduce chance of livelock, the timeout that caused the original issue now happens in roughly 3 of 200 workers instead of 199 on average. --- lib/spack/spack/installer.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py index 196858830ab..df1d704cd70 100644 --- a/lib/spack/spack/installer.py +++ b/lib/spack/spack/installer.py @@ -794,10 +794,10 @@ def _check_deps_status(self, request): .format(dep_id, action) raise InstallError(err.format(request.pkg_id, msg)) - # Attempt to get a write lock to ensure another process does not + # Attempt to get a read lock to ensure another process does not # uninstall the dependency while the requested spec is being # installed - ltype, lock = self._ensure_locked('write', dep_pkg) + ltype, lock = self._ensure_locked('read', dep_pkg) if lock is None: msg = '{0} is write locked by another process'.format(dep_id) raise InstallError(err.format(request.pkg_id, msg)) @@ -816,6 +816,8 @@ def _check_deps_status(self, request): tty.debug('Flagging {0} as installed per the database' .format(dep_id)) self._flag_installed(dep_pkg) + else: + lock.release_read() def _prepare_for_install(self, task): """ @@ -1027,7 +1029,7 @@ def _ensure_locked(self, lock_type, pkg): except (lk.LockDowngradeError, lk.LockTimeoutError) as exc: tty.debug(err.format(op, desc, pkg_id, exc.__class__.__name__, str(exc))) - lock = None + return (lock_type, None) except (Exception, KeyboardInterrupt, SystemExit) as exc: tty.error(err.format(op, desc, pkg_id, exc.__class__.__name__, @@ -1626,6 +1628,7 @@ def install(self): # established by the other process -- failed, installed, or # uninstalled -- on the next pass. if ltype == 'read': + lock.release_read() self._requeue_task(task) continue From 69b8cddb1b5bca729d8983450fe0b0e1ac1a2f46 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Thu, 23 Dec 2021 13:56:58 +0100 Subject: [PATCH 27/28] Bump version and update CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ lib/spack/spack/__init__.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9bcb548eec..44123606dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# v0.17.1 (2021-12-23) + +### Spack Bugfixes +* Allow locks to work under high contention (#27846) +* Improve errors messages from clingo (#27707 #27970) +* Respect package permissions for sbang (#25764) +* Fix --enable-locks behavior (#24675) +* Fix log-format reporter ignoring install errors (#25961) +* Fix overloaded argparse keys (#27379) +* Allow style commands to run with targets other than "develop" (#27472) +* Log lock messages to debug level, instead of verbose level (#27408) +* Handle invalid unicode while logging (#21447) +* spack audit: fix API calls to variants (#27713) +* Provide meaningful message for empty environment installs (#28031) +* Added opensuse leap containers to spack containerize (#27837) +* Revert "patches: make re-applied patches idempotent" (#27625) +* MANPATH can use system defaults (#21682) +* Add "setdefault" subcommand to `spack module tcl` (#14686) +* Regenerate views when specs already installed (#28113) + +### Package bugfixes +* Fix external package detection for OpenMPI (#27255) +* Update the UPC++ package to 2021.9.0 (#26996) +* Added py-vermin v1.3.2 (#28072) + # v0.17.0 (2021-11-05) `v0.17.0` is a major feature release. diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 5686f27a6dd..e0c472dfb92 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -4,7 +4,7 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) #: major, minor, patch version for Spack, in a tuple -spack_version_info = (0, 17, 0) +spack_version_info = (0, 17, 1) #: String containing Spack version joined with .'s spack_version = '.'.join(str(v) for v in spack_version_info) From e974b44e8673ded449ce4a3ed5844398b68e81a2 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Thu, 23 Dec 2021 16:34:31 +0100 Subject: [PATCH 28/28] Fix execution of style tests --- share/spack/qa/run-style-tests | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/spack/qa/run-style-tests b/share/spack/qa/run-style-tests index 9f0cbdb266f..df3d441c755 100755 --- a/share/spack/qa/run-style-tests +++ b/share/spack/qa/run-style-tests @@ -19,6 +19,8 @@ args=() if [[ -n $GITHUB_BASE_REF ]]; then args+=("--base" "${GITHUB_BASE_REF}") +else + args+=("--base" "${GITHUB_REF_NAME}") fi # verify that the code style is correct