X Tutup
Skip to content

Commit e727f56

Browse files
scheibelpbecker33
authored andcommitted
Features/compiler config consistency (spack#2999)
* default scope for config command is made consistent with cmd/__init__ default * dont specify a scope when looking for compilers with a matching spec (since compiler concretization is scope-independent) * config edit should default to platform-specific file only for compilers * when duplicate compiler specs are detected, the exception raised now points the user to the files where the duplicates appear * updated error message to emphasize that a spec is duplicated (since multiple specs can reference the same compiler) * 'spack compilers' is now also broken down into sections by os and target * Added tests for new compiler methods
1 parent 68f5b9a commit e727f56

File tree

6 files changed

+181
-70
lines changed

6 files changed

+181
-70
lines changed

lib/spack/spack/cmd/compiler.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ def compiler_find(args):
9696
for c in compilers:
9797
arch_spec = ArchSpec(None, c.operating_system, c.target)
9898
same_specs = spack.compilers.compilers_for_spec(c.spec,
99-
arch_spec,
100-
args.scope)
99+
arch_spec)
101100

102101
if not same_specs:
103102
new_compilers.append(c)
@@ -165,14 +164,18 @@ def compiler_info(args):
165164

166165
def compiler_list(args):
167166
tty.msg("Available compilers")
168-
index = index_by(spack.compilers.all_compilers(scope=args.scope), 'name')
169-
for i, (name, compilers) in enumerate(index.items()):
167+
index = index_by(spack.compilers.all_compilers(scope=args.scope),
168+
lambda c: (c.spec.name, c.operating_system, c.target))
169+
for i, (key, compilers) in enumerate(index.items()):
170170
if i >= 1:
171171
print
172-
173-
cname = "%s{%s}" % (spack.spec.compiler_color, name)
172+
name, os, target = key
173+
os_str = os
174+
if target:
175+
os_str += "-%s" % target
176+
cname = "%s{%s} %s" % (spack.spec.compiler_color, name, os_str)
174177
tty.hline(colorize(cname), char='-')
175-
colify(reversed(sorted(compilers)))
178+
colify(reversed(sorted(c.spec for c in compilers)))
176179

177180

178181
def compiler(parser, args):

lib/spack/spack/cmd/config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ def config_get(args):
5555

5656
def config_edit(args):
5757
if not args.scope:
58-
args.scope = 'user'
58+
if args.section == 'compilers':
59+
args.scope = spack.cmd.default_modify_scope
60+
else:
61+
args.scope = 'user'
5962
if not args.section:
6063
args.section = None
6164
config_file = spack.config.get_config_filename(args.scope, args.section)

lib/spack/spack/compilers/__init__.py

Lines changed: 115 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def all_compilers_config(scope=None, init_config=True):
164164
return _cache_config_file
165165

166166

167-
def all_compilers(scope=None, init_config=True):
167+
def all_compiler_specs(scope=None, init_config=True):
168168
# Return compiler specs from the merged config.
169169
return [spack.spec.CompilerSpec(s['compiler']['spec'])
170170
for s in all_compilers_config(scope, init_config)]
@@ -203,72 +203,93 @@ def supported(compiler_spec):
203203
def find(compiler_spec, scope=None):
204204
"""Return specs of available compilers that match the supplied
205205
compiler spec. Return an empty list if nothing found."""
206-
return [c for c in all_compilers(scope) if c.satisfies(compiler_spec)]
206+
return [c for c in all_compiler_specs(scope) if c.satisfies(compiler_spec)]
207+
208+
209+
def all_compilers(scope=None):
210+
config = get_compiler_config(scope)
211+
compilers = list()
212+
for items in config:
213+
items = items['compiler']
214+
compilers.append(compiler_from_config_entry(items))
215+
return compilers
207216

208217

209218
@_auto_compiler_spec
210-
def compilers_for_spec(compiler_spec, arch_spec=None, scope=None):
219+
def compilers_for_spec(compiler_spec, arch_spec=None, scope=None,
220+
use_cache=True):
211221
"""This gets all compilers that satisfy the supplied CompilerSpec.
212222
Returns an empty list if none are found.
213223
"""
214-
config = all_compilers_config(scope)
215-
216-
def get_compilers(cspec):
217-
compilers = []
218-
219-
for items in config:
220-
items = items['compiler']
221-
if items['spec'] != str(cspec):
222-
continue
223-
224-
# If an arch spec is given, confirm that this compiler
225-
# is for the given operating system
226-
os = items.get('operating_system', None)
227-
if arch_spec and os != arch_spec.platform_os:
228-
continue
229-
230-
# If an arch spec is given, confirm that this compiler
231-
# is for the given target. If the target is 'any', match
232-
# any given arch spec. If the compiler has no assigned
233-
# target this is an old compiler config file, skip this logic.
234-
target = items.get('target', None)
235-
if arch_spec and target and (target != arch_spec.target and
236-
target != 'any'):
237-
continue
238-
239-
if not ('paths' in items and
240-
all(n in items['paths'] for n in _path_instance_vars)):
241-
raise InvalidCompilerConfigurationError(cspec)
242-
243-
cls = class_for_compiler_name(cspec.name)
244-
245-
compiler_paths = []
246-
for c in _path_instance_vars:
247-
compiler_path = items['paths'][c]
248-
if compiler_path != 'None':
249-
compiler_paths.append(compiler_path)
250-
else:
251-
compiler_paths.append(None)
252-
253-
mods = items.get('modules')
254-
if mods == 'None':
255-
mods = []
256-
257-
alias = items.get('alias', None)
258-
compiler_flags = items.get('flags', {})
259-
environment = items.get('environment', {})
260-
extra_rpaths = items.get('extra_rpaths', [])
261-
262-
compilers.append(
263-
cls(cspec, os, target, compiler_paths, mods, alias,
264-
environment, extra_rpaths, **compiler_flags))
265-
266-
return compilers
224+
if use_cache:
225+
config = all_compilers_config(scope)
226+
else:
227+
config = get_compiler_config(scope)
267228

268229
matches = set(find(compiler_spec, scope))
269230
compilers = []
270231
for cspec in matches:
271-
compilers.extend(get_compilers(cspec))
232+
compilers.extend(get_compilers(cspec, config, arch_spec))
233+
return compilers
234+
235+
236+
def compiler_from_config_entry(items):
237+
cspec = spack.spec.CompilerSpec(items['spec'])
238+
os = items.get('operating_system', None)
239+
target = items.get('target', None)
240+
241+
if not ('paths' in items and
242+
all(n in items['paths'] for n in _path_instance_vars)):
243+
raise InvalidCompilerConfigurationError(cspec)
244+
245+
cls = class_for_compiler_name(cspec.name)
246+
247+
compiler_paths = []
248+
for c in _path_instance_vars:
249+
compiler_path = items['paths'][c]
250+
if compiler_path != 'None':
251+
compiler_paths.append(compiler_path)
252+
else:
253+
compiler_paths.append(None)
254+
255+
mods = items.get('modules')
256+
if mods == 'None':
257+
mods = []
258+
259+
alias = items.get('alias', None)
260+
compiler_flags = items.get('flags', {})
261+
environment = items.get('environment', {})
262+
extra_rpaths = items.get('extra_rpaths', [])
263+
264+
return cls(cspec, os, target, compiler_paths, mods, alias,
265+
environment, extra_rpaths, **compiler_flags)
266+
267+
268+
def get_compilers(cspec, config, arch_spec=None):
269+
compilers = []
270+
271+
for items in config:
272+
items = items['compiler']
273+
if items['spec'] != str(cspec):
274+
continue
275+
276+
# If an arch spec is given, confirm that this compiler
277+
# is for the given operating system
278+
os = items.get('operating_system', None)
279+
if arch_spec and os != arch_spec.platform_os:
280+
continue
281+
282+
# If an arch spec is given, confirm that this compiler
283+
# is for the given target. If the target is 'any', match
284+
# any given arch spec. If the compiler has no assigned
285+
# target this is an old compiler config file, skip this logic.
286+
target = items.get('target', None)
287+
if arch_spec and target and (target != arch_spec.target and
288+
target != 'any'):
289+
continue
290+
291+
compilers.append(compiler_from_config_entry(items))
292+
272293
return compilers
273294

274295

@@ -283,10 +304,28 @@ def compiler_for_spec(compiler_spec, arch_spec):
283304
if len(compilers) < 1:
284305
raise NoCompilerForSpecError(compiler_spec, arch_spec.platform_os)
285306
if len(compilers) > 1:
286-
raise CompilerSpecInsufficientlySpecificError(compiler_spec)
307+
raise CompilerDuplicateError(compiler_spec, arch_spec)
287308
return compilers[0]
288309

289310

311+
@_auto_compiler_spec
312+
def get_compiler_duplicates(compiler_spec, arch_spec):
313+
config_scopes = spack.config.config_scopes
314+
scope_to_compilers = dict()
315+
for scope in config_scopes:
316+
compilers = compilers_for_spec(compiler_spec, arch_spec=arch_spec,
317+
scope=scope, use_cache=False)
318+
if compilers:
319+
scope_to_compilers[scope] = compilers
320+
321+
cfg_file_to_duplicates = dict()
322+
for scope, compilers in scope_to_compilers.iteritems():
323+
config_file = config_scopes[scope].get_section_filename('compilers')
324+
cfg_file_to_duplicates[config_file] = compilers
325+
326+
return cfg_file_to_duplicates
327+
328+
290329
def class_for_compiler_name(compiler_name):
291330
"""Given a compiler module name, get the corresponding Compiler class."""
292331
assert(supported(compiler_name))
@@ -341,6 +380,24 @@ def __init__(self, compiler_spec, target):
341380
% (target, compiler_spec))
342381

343382

383+
class CompilerDuplicateError(spack.error.SpackError):
384+
def __init__(self, compiler_spec, arch_spec):
385+
config_file_to_duplicates = get_compiler_duplicates(
386+
compiler_spec, arch_spec)
387+
duplicate_table = list(
388+
(x, len(y)) for x, y in config_file_to_duplicates.iteritems())
389+
descriptor = lambda num: 'time' if num == 1 else 'times'
390+
duplicate_msg = (
391+
lambda cfgfile, count: "{0}: {1} {2}".format(
392+
cfgfile, str(count), descriptor(count)))
393+
msg = (
394+
"Compiler configuration contains entries with duplicate" +
395+
" specification ({0}, {1})".format(compiler_spec, arch_spec) +
396+
" in the following files:\n\t" +
397+
'\n\t'.join(duplicate_msg(x, y) for x, y in duplicate_table))
398+
super(CompilerDuplicateError, self).__init__(msg)
399+
400+
344401
class CompilerSpecInsufficientlySpecificError(spack.error.SpackError):
345402
def __init__(self, compiler_spec):
346403
super(CompilerSpecInsufficientlySpecificError, self).__init__(

lib/spack/spack/concretize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def concretize_compiler(self, spec):
315315
def _proper_compiler_style(cspec, aspec):
316316
return spack.compilers.compilers_for_spec(cspec, arch_spec=aspec)
317317

318-
all_compilers = spack.compilers.all_compilers()
318+
all_compilers = spack.compilers.all_compiler_specs()
319319

320320
if (spec.compiler and
321321
spec.compiler.concrete and

lib/spack/spack/test/cmd/test_compiler_cmd.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ def test_compiler_remove(self):
7171
all=True, compiler_spec='gcc@4.5.0', add_paths=[], scope=None
7272
)
7373
spack.cmd.compiler.compiler_remove(args)
74-
compilers = spack.compilers.all_compilers()
74+
compilers = spack.compilers.all_compiler_specs()
7575
assert spack.spec.CompilerSpec("gcc@4.5.0") not in compilers
7676

7777
def test_compiler_add(self, mock_compiler_dir):
7878
# Compilers available by default.
79-
old_compilers = set(spack.compilers.all_compilers())
79+
old_compilers = set(spack.compilers.all_compiler_specs())
8080

8181
args = spack.util.pattern.Bunch(
8282
all=None,
@@ -87,7 +87,7 @@ def test_compiler_add(self, mock_compiler_dir):
8787
spack.cmd.compiler.compiler_find(args)
8888

8989
# Ensure new compiler is in there
90-
new_compilers = set(spack.compilers.all_compilers())
90+
new_compilers = set(spack.compilers.all_compiler_specs())
9191
new_compiler = new_compilers - old_compilers
9292
assert new_compiler
9393
c = new_compiler.pop()

lib/spack/spack/test/compilers.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
##############################################################################
2+
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
3+
# Produced at the Lawrence Livermore National Laboratory.
4+
#
5+
# This file is part of Spack.
6+
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
7+
# LLNL-CODE-647188
8+
#
9+
# For details, see https://github.com/llnl/spack
10+
# Please also see the LICENSE file for our notice and the LGPL.
11+
#
12+
# This program is free software; you can redistribute it and/or modify
13+
# it under the terms of the GNU Lesser General Public License (as
14+
# published by the Free Software Foundation) version 2.1, February 1999.
15+
#
16+
# This program is distributed in the hope that it will be useful, but
17+
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
19+
# conditions of the GNU Lesser General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU Lesser General Public
22+
# License along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24+
##############################################################################
25+
import pytest
26+
27+
import spack.spec
28+
import spack.compilers as compilers
29+
30+
31+
@pytest.mark.usefixtures('config')
32+
class TestCompilers(object):
33+
34+
def test_get_compiler_duplicates(self):
35+
# In this case there is only one instance of the specified compiler in
36+
# the test configuration (so it is not actually a duplicate), but the
37+
# method behaves the same.
38+
cfg_file_to_duplicates = compilers.get_compiler_duplicates(
39+
'gcc@4.5.0', spack.spec.ArchSpec('cray-CNL-xeon'))
40+
assert len(cfg_file_to_duplicates) == 1
41+
cfg_file, duplicates = cfg_file_to_duplicates.iteritems().next()
42+
assert len(duplicates) == 1
43+
44+
def test_all_compilers(self):
45+
all_compilers = compilers.all_compilers()
46+
filtered = list(x for x in all_compilers if str(x.spec) == 'clang@3.3')
47+
filtered = list(x for x in filtered if x.operating_system == 'SuSE11')
48+
assert len(filtered) == 1

0 commit comments

Comments
 (0)
X Tutup