thirdparty:waf: New files for waf 1.9.10
[vlendec/samba-autobuild/.git] / third_party / waf / waflib / ConfigSet.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2005-2016 (ita)
8
9 """
10
11 ConfigSet: a special dict
12
13 The values put in :py:class:`ConfigSet` must be serializable (dicts, lists, strings)
14 """
15
16 import copy, re, os
17 from waflib import Logs, Utils
18 re_imp = re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re.M)
19
20 class ConfigSet(object):
21         """
22         A copy-on-write dict with human-readable serialized format. The serialization format
23         is human-readable (python-like) and performed by using eval() and repr().
24         For high performance prefer pickle. Do not store functions as they are not serializable.
25
26         The values can be accessed by attributes or by keys::
27
28                 from waflib.ConfigSet import ConfigSet
29                 env = ConfigSet()
30                 env.FOO = 'test'
31                 env['FOO'] = 'test'
32         """
33         __slots__ = ('table', 'parent')
34         def __init__(self, filename=None):
35                 self.table = {}
36                 """
37                 Internal dict holding the object values
38                 """
39                 #self.parent = None
40
41                 if filename:
42                         self.load(filename)
43
44         def __contains__(self, key):
45                 """
46                 Enables the *in* syntax::
47
48                         if 'foo' in env:
49                                 print(env['foo'])
50                 """
51                 if key in self.table: return True
52                 try: return self.parent.__contains__(key)
53                 except AttributeError: return False # parent may not exist
54
55         def keys(self):
56                 """Dict interface"""
57                 keys = set()
58                 cur = self
59                 while cur:
60                         keys.update(cur.table.keys())
61                         cur = getattr(cur, 'parent', None)
62                 keys = list(keys)
63                 keys.sort()
64                 return keys
65
66         def __iter__(self):
67                 return iter(self.keys())
68
69         def __str__(self):
70                 """Text representation of the ConfigSet (for debugging purposes)"""
71                 return "\n".join(["%r %r" % (x, self.__getitem__(x)) for x in self.keys()])
72
73         def __getitem__(self, key):
74                 """
75                 Dictionary interface: get value from key::
76
77                         def configure(conf):
78                                 conf.env['foo'] = {}
79                                 print(env['foo'])
80                 """
81                 try:
82                         while 1:
83                                 x = self.table.get(key)
84                                 if not x is None:
85                                         return x
86                                 self = self.parent
87                 except AttributeError:
88                         return []
89
90         def __setitem__(self, key, value):
91                 """
92                 Dictionary interface: set value for key
93                 """
94                 self.table[key] = value
95
96         def __delitem__(self, key):
97                 """
98                 Dictionary interface: mark the key as missing
99                 """
100                 self[key] = []
101
102         def __getattr__(self, name):
103                 """
104                 Attribute access provided for convenience. The following forms are equivalent::
105
106                         def configure(conf):
107                                 conf.env.value
108                                 conf.env['value']
109                 """
110                 if name in self.__slots__:
111                         return object.__getattr__(self, name)
112                 else:
113                         return self[name]
114
115         def __setattr__(self, name, value):
116                 """
117                 Attribute access provided for convenience. The following forms are equivalent::
118
119                         def configure(conf):
120                                 conf.env.value = x
121                                 env['value'] = x
122                 """
123                 if name in self.__slots__:
124                         object.__setattr__(self, name, value)
125                 else:
126                         self[name] = value
127
128         def __delattr__(self, name):
129                 """
130                 Attribute access provided for convenience. The following forms are equivalent::
131
132                         def configure(conf):
133                                 del env.value
134                                 del env['value']
135                 """
136                 if name in self.__slots__:
137                         object.__delattr__(self, name)
138                 else:
139                         del self[name]
140
141         def derive(self):
142                 """
143                 Returns a new ConfigSet deriving from self. The copy returned
144                 will be a shallow copy::
145
146                         from waflib.ConfigSet import ConfigSet
147                         env = ConfigSet()
148                         env.append_value('CFLAGS', ['-O2'])
149                         child = env.derive()
150                         child.CFLAGS.append('test') # warning! this will modify 'env'
151                         child.CFLAGS = ['-O3'] # new list, ok
152                         child.append_value('CFLAGS', ['-O3']) # ok
153
154                 Use :py:func:`ConfigSet.detach` to detach the child from the parent.
155                 """
156                 newenv = ConfigSet()
157                 newenv.parent = self
158                 return newenv
159
160         def detach(self):
161                 """
162                 Detaches this instance from its parent (if present)
163
164                 Modifying the parent :py:class:`ConfigSet` will not change the current object
165                 Modifying this :py:class:`ConfigSet` will not modify the parent one.
166                 """
167                 tbl = self.get_merged_dict()
168                 try:
169                         delattr(self, 'parent')
170                 except AttributeError:
171                         pass
172                 else:
173                         keys = tbl.keys()
174                         for x in keys:
175                                 tbl[x] = copy.deepcopy(tbl[x])
176                         self.table = tbl
177                 return self
178
179         def get_flat(self, key):
180                 """
181                 Returns a value as a string. If the input is a list, the value returned is space-separated.
182
183                 :param key: key to use
184                 :type key: string
185                 """
186                 s = self[key]
187                 if isinstance(s, str): return s
188                 return ' '.join(s)
189
190         def _get_list_value_for_modification(self, key):
191                 """
192                 Returns a list value for further modification.
193
194                 The list may be modified inplace and there is no need to do this afterwards::
195
196                         self.table[var] = value
197                 """
198                 try:
199                         value = self.table[key]
200                 except KeyError:
201                         try:
202                                 value = self.parent[key]
203                         except AttributeError:
204                                 value = []
205                         else:
206                                 if isinstance(value, list):
207                                         # force a copy
208                                         value = value[:]
209                                 else:
210                                         value = [value]
211                         self.table[key] = value
212                 else:
213                         if not isinstance(value, list):
214                                 self.table[key] = value = [value]
215                 return value
216
217         def append_value(self, var, val):
218                 """
219                 Appends a value to the specified config key::
220
221                         def build(bld):
222                                 bld.env.append_value('CFLAGS', ['-O2'])
223
224                 The value must be a list or a tuple
225                 """
226                 if isinstance(val, str): # if there were string everywhere we could optimize this
227                         val = [val]
228                 current_value = self._get_list_value_for_modification(var)
229                 current_value.extend(val)
230
231         def prepend_value(self, var, val):
232                 """
233                 Prepends a value to the specified item::
234
235                         def configure(conf):
236                                 conf.env.prepend_value('CFLAGS', ['-O2'])
237
238                 The value must be a list or a tuple
239                 """
240                 if isinstance(val, str):
241                         val = [val]
242                 self.table[var] =  val + self._get_list_value_for_modification(var)
243
244         def append_unique(self, var, val):
245                 """
246                 Appends a value to the specified item only if it's not already present::
247
248                         def build(bld):
249                                 bld.env.append_unique('CFLAGS', ['-O2', '-g'])
250
251                 The value must be a list or a tuple
252                 """
253                 if isinstance(val, str):
254                         val = [val]
255                 current_value = self._get_list_value_for_modification(var)
256
257                 for x in val:
258                         if x not in current_value:
259                                 current_value.append(x)
260
261         def get_merged_dict(self):
262                 """
263                 Computes the merged dictionary from the fusion of self and all its parent
264
265                 :rtype: a ConfigSet object
266                 """
267                 table_list = []
268                 env = self
269                 while 1:
270                         table_list.insert(0, env.table)
271                         try: env = env.parent
272                         except AttributeError: break
273                 merged_table = {}
274                 for table in table_list:
275                         merged_table.update(table)
276                 return merged_table
277
278         def store(self, filename):
279                 """
280                 Serializes the :py:class:`ConfigSet` data to a file. See :py:meth:`ConfigSet.load` for reading such files.
281
282                 :param filename: file to use
283                 :type filename: string
284                 """
285                 try:
286                         os.makedirs(os.path.split(filename)[0])
287                 except OSError:
288                         pass
289
290                 buf = []
291                 merged_table = self.get_merged_dict()
292                 keys = list(merged_table.keys())
293                 keys.sort()
294
295                 try:
296                         fun = ascii
297                 except NameError:
298                         fun = repr
299
300                 for k in keys:
301                         if k != 'undo_stack':
302                                 buf.append('%s = %s\n' % (k, fun(merged_table[k])))
303                 Utils.writef(filename, ''.join(buf))
304
305         def load(self, filename):
306                 """
307                 Restores contents from a file (current values are not cleared). Files are written using :py:meth:`ConfigSet.store`.
308
309                 :param filename: file to use
310                 :type filename: string
311                 """
312                 tbl = self.table
313                 code = Utils.readf(filename, m='rU')
314                 for m in re_imp.finditer(code):
315                         g = m.group
316                         tbl[g(2)] = eval(g(3))
317                 Logs.debug('env: %s', self.table)
318
319         def update(self, d):
320                 """
321                 Dictionary interface: replace values with the ones from another dict
322
323                 :param d: object to use the value from
324                 :type d: dict-like object
325                 """
326                 self.table.update(d)
327
328         def stash(self):
329                 """
330                 Stores the object state to provide transactionality semantics::
331
332                         env = ConfigSet()
333                         env.stash()
334                         try:
335                                 env.append_value('CFLAGS', '-O3')
336                                 call_some_method(env)
337                         finally:
338                                 env.revert()
339
340                 The history is kept in a stack, and is lost during the serialization by :py:meth:`ConfigSet.store`
341                 """
342                 orig = self.table
343                 tbl = self.table = self.table.copy()
344                 for x in tbl.keys():
345                         tbl[x] = copy.deepcopy(tbl[x])
346                 self.undo_stack = self.undo_stack + [orig]
347
348         def commit(self):
349                 """
350                 Commits transactional changes. See :py:meth:`ConfigSet.stash`
351                 """
352                 self.undo_stack.pop(-1)
353
354         def revert(self):
355                 """
356                 Reverts the object to a previous state. See :py:meth:`ConfigSet.stash`
357                 """
358                 self.table = self.undo_stack.pop(-1)