summaryrefslogtreecommitdiff
path: root/lib/spack/spack/schema/packages.py
blob: e24f610c74f81162d677db19304dbf13526d785d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Schema for packages.yaml configuration files.

.. literalinclude:: _spack_root/lib/spack/spack/schema/packages.py
   :lines: 13-
"""


def deprecate_paths_and_modules(instance, deprecated_properties):
    """Function to produce warning/error messages if "paths" and "modules" are
    found in "packages.yaml"

    Args:
        instance: instance of the configuration file
        deprecated_properties: deprecated properties in instance

    Returns:
        Warning/Error message to be printed
    """
    import copy
    import os.path
    import llnl.util.tty
    import spack.util.spack_yaml as syaml
    # Copy the instance to remove default attributes that are not related
    # to the part that needs to be reported
    instance_copy = copy.copy(instance)

    # Check if this configuration comes from an environment or not
    absolute_path = instance_copy._end_mark.name
    command_to_suggest = '$ spack config update packages'
    if os.path.basename(absolute_path) == 'spack.yaml':
        command_to_suggest = '$ spack env update <environment>'

    # Retrieve the relevant part of the configuration as YAML
    keys_to_be_removed = [
        x for x in instance_copy if x not in deprecated_properties
    ]
    for key in keys_to_be_removed:
        instance_copy.pop(key)
    yaml_as_str = syaml.dump_config(instance_copy, blame=True)

    if llnl.util.tty.is_debug():
        msg = 'OUTDATED CONFIGURATION FILE [file={0}]\n{1}'
        llnl.util.tty.debug(msg.format(absolute_path, yaml_as_str))

    msg = ('detected deprecated properties in {0}\nActivate the debug '
           'flag to have more information on the deprecated parts or '
           'run:\n\n\t{2}\n\nto update the file to the new format\n')
    return msg.format(
        absolute_path, yaml_as_str, command_to_suggest
    )


#: Properties for inclusion in other schemas
properties = {
    'packages': {
        'type': 'object',
        'default': {},
        'additionalProperties': False,
        'patternProperties': {
            r'\w[\w-]*': {  # package name
                'type': 'object',
                'default': {},
                'additionalProperties': False,
                'properties': {
                    'version': {
                        'type': 'array',
                        'default': [],
                        # version strings
                        'items': {'anyOf': [{'type': 'string'},
                                            {'type': 'number'}]}},
                    'target': {
                        'type': 'array',
                        'default': [],
                        # target names
                        'items': {'type': 'string'},
                    },
                    'compiler': {
                        'type': 'array',
                        'default': [],
                        'items': {'type': 'string'}},  # compiler specs
                    'buildable': {
                        'type':  'boolean',
                        'default': True,
                    },
                    'permissions': {
                        'type': 'object',
                        'additionalProperties': False,
                        'properties': {
                            'read': {
                                'type':  'string',
                                'enum': ['user', 'group', 'world'],
                            },
                            'write': {
                                'type':  'string',
                                'enum': ['user', 'group', 'world'],
                            },
                            'group': {
                                'type':  'string',
                            },
                        },
                    },
                    'providers': {
                        'type':  'object',
                        'default': {},
                        'additionalProperties': False,
                        'patternProperties': {
                            r'\w[\w-]*': {
                                'type': 'array',
                                'default': [],
                                'items': {'type': 'string'}, }, }, },
                    'variants': {
                        'oneOf': [
                            {'type': 'string'},
                            {'type': 'array',
                             'items': {'type': 'string'}}],
                    },
                    'externals': {
                        'type': 'array',
                        'items': {
                            'type': 'object',
                            'properties': {
                                'spec': {'type': 'string'},
                                'prefix': {'type': 'string'},
                                'modules': {'type': 'array',
                                            'items': {'type': 'string'}},
                                'extra_attributes': {'type': 'object'}
                            },
                            'additionalProperties': True,
                            'required': ['spec']
                        }
                    },
                    # Deprecated properties, will trigger an error with a
                    # message telling how to update.
                    'paths': {'type': 'object'},
                    'modules': {'type': 'object'},
                },
                'deprecatedProperties': {
                    'properties': ['modules', 'paths'],
                    'message': deprecate_paths_and_modules,
                    'error': False
                }
            },
        },
    },
}


#: Full schema with metadata
schema = {
    '$schema': 'http://json-schema.org/schema#',
    'title': 'Spack package configuration file schema',
    'type': 'object',
    'additionalProperties': False,
    'properties': properties,
}


def update(data):
    """Update the data in place to remove deprecated properties.

    Args:
        data (dict): dictionary to be updated

    Returns:
        True if data was changed, False otherwise
    """
    changed = False
    for cfg_object in data.values():
        externals = []

        # If we don't have these deprecated attributes, continue
        if not any(x in cfg_object for x in ('paths', 'modules')):
            continue

        # If we arrive here we need to make some changes i.e.
        # we need to remove and eventually convert some attributes
        changed = True
        paths = cfg_object.pop('paths', {})
        for spec, prefix in paths.items():
            externals.append({
                'spec': str(spec),
                'prefix': str(prefix)
            })
        modules = cfg_object.pop('modules', {})
        for spec, module in modules.items():
            externals.append({
                'spec': str(spec),
                'modules': [str(module)]
            })
        if externals:
            cfg_object['externals'] = externals

    return changed