From f8657e3fd9f1dba6f8360f50e89fe81a2978207b Mon Sep 17 00:00:00 2001 From: becker33 Date: Wed, 21 Mar 2018 18:14:01 -0700 Subject: [PATCH] Propagate architecture information during concretization (#7412) This updates architecture concretization to * Search for the nearest parent in the DAG for architecture information rather than defaulting to the root of the DAG * Propagate architecture settings transitively, such that if for example the target is set at the root of the dag it will set the same target on indirect dependencies (assuming no intermediate dependency specifies a separate target). Previously this occurred in general but under some conditions did not, for example if an intermediate dependency specified some subset of architecture properties. --- lib/spack/spack/concretize.py | 34 +++++++++++++------------ lib/spack/spack/test/concretize.py | 40 +++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py index 4d1ddf7ac9..c722a5506c 100644 --- a/lib/spack/spack/concretize.py +++ b/lib/spack/spack/concretize.py @@ -222,27 +222,29 @@ def concretize_architecture(self, spec): DAG has an architecture, then use the root otherwise use the defaults on the platform. """ - root_arch = spec.root.architecture - sys_arch = spack.spec.ArchSpec(spack.architecture.sys_type()) + try: + # Get the nearest architecture with any fields set + nearest = next(p for p in spec.traverse(direction='parents') + if (p.architecture and p is not spec)) + nearest_arch = nearest.architecture + except StopIteration: + # Default to the system architecture if nothing set + nearest_arch = spack.spec.ArchSpec(spack.architecture.sys_type()) + spec_changed = False + # ensure type safety for the architecture if spec.architecture is None: - spec.architecture = spack.spec.ArchSpec(sys_arch) + spec.architecture = spack.spec.ArchSpec() spec_changed = True - default_archs = list(x for x in [root_arch, sys_arch] if x) - for arch in default_archs: - if spec.architecture.concrete: - break - - replacement_fields = [k for k, v in iteritems(arch.to_cmp_dict()) - if v and not getattr(spec.architecture, k)] - for field in replacement_fields: - setattr(spec.architecture, field, getattr(arch, field)) - spec_changed = True - - if not spec.architecture.concrete: - raise InsufficientArchitectureInfoError(spec, default_archs) + # replace each of the fields (platform, os, target) separately + nearest_dict = nearest_arch.to_cmp_dict() + replacement_fields = [k for k, v in iteritems(nearest_dict) + if v and not getattr(spec.architecture, k)] + for field in replacement_fields: + setattr(spec.architecture, field, getattr(nearest_arch, field)) + spec_changed = True return spec_changed diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 7885ca279f..87a4e03b4d 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -1,4 +1,4 @@ -############################################################################## +############################################################################# # Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. # Produced at the Lawrence Livermore National Laboratory. # @@ -31,6 +31,7 @@ from spack.spec import Spec, CompilerSpec from spack.spec import ConflictsInSpecError, SpecError from spack.version import ver +from spack.test.conftest import MockPackage, MockPackageMultiRepo def check_spec(abstract, concrete): @@ -204,6 +205,42 @@ def test_different_compilers_get_different_flags(self): assert set(client.compiler_flags['fflags']) == set(['-O0']) assert not set(cmake.compiler_flags['fflags']) + def test_architecture_inheritance(self): + """test_architecture_inheritance is likely to fail with an + UnavailableCompilerVersionError if the architecture is concretized + incorrectly. + """ + spec = Spec('cmake-client %gcc@4.7.2 os=fe ^ cmake') + spec.concretize() + assert spec['cmake'].architecture == spec.architecture + + def test_architecture_deep_inheritance(self): + """Make sure that indirect dependencies receive architecture + information from the root even when partial architecture information + is provided by an intermediate dependency. + """ + saved_repo = spack.repo + + default_dep = ('link', 'build') + + bazpkg = MockPackage('bazpkg', [], []) + barpkg = MockPackage('barpkg', [bazpkg], [default_dep]) + foopkg = MockPackage('foopkg', [barpkg], [default_dep]) + mock_repo = MockPackageMultiRepo([foopkg, barpkg, bazpkg]) + + spack.repo = mock_repo + + try: + spec = Spec('foopkg %clang@3.3 os=CNL target=footar' + + ' ^barpkg os=SuSE11 ^bazpkg os=be') + spec.concretize() + + for s in spec.traverse(root=False): + assert s.architecture.target == spec.architecture.target + + finally: + spack.repo = saved_repo + def test_compiler_flags_from_user_are_grouped(self): spec = Spec('a%gcc cflags="-O -foo-flag foo-val" platform=test') spec.concretize() @@ -216,6 +253,7 @@ def concretize_multi_provider(self): assert s['mpi'].version == ver('1.10.3') def test_concretize_two_virtuals(self): + """Test a package with multiple virtual dependencies.""" Spec('hypre').concretize()