Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ppapi / generators / idl_release.py
blobff4aa01db04e8fc87152fc11143c40ad64397547
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """
7 IDLRelease for PPAPI
9 This file defines the behavior of the AST namespace which allows for resolving
10 a symbol as one or more AST nodes given a Release or range of Releases.
11 """
13 import sys
15 from idl_log import ErrOut, InfoOut, WarnOut
16 from idl_option import GetOption, Option, ParseOptions
18 Option('release_debug', 'Debug Release data')
19 Option('wgap', 'Ignore Release gap warning')
23 # Module level functions and data used for testing.
25 error = None
26 warning = None
27 def ReportReleaseError(msg):
28 global error
29 error = msg
31 def ReportReleaseWarning(msg):
32 global warning
33 warning = msg
35 def ReportClear():
36 global error, warning
37 error = None
38 warning = None
41 # IDLRelease
43 # IDLRelease is an object which stores the association of a given symbol
44 # name, with an AST node for a range of Releases for that object.
46 # A vmin value of None indicates that the object begins at the earliest
47 # available Release number. The value of vmin is always inclusive.
49 # A vmax value of None indicates that the object is never deprecated, so
50 # it exists until it is overloaded or until the latest available Release.
51 # The value of vmax is always exclusive, representing the first Release
52 # on which the object is no longer valid.
53 class IDLRelease(object):
54 def __init__(self, rmin, rmax):
55 self.rmin = rmin
56 self.rmax = rmax
58 def __str__(self):
59 if not self.rmin:
60 rmin = '0'
61 else:
62 rmin = str(self.rmin)
63 if not self.rmax:
64 rmax = '+oo'
65 else:
66 rmax = str(self.rmax)
67 return '[%s,%s)' % (rmin, rmax)
69 def SetReleaseRange(self, rmin, rmax):
70 self.rmin = rmin
71 self.rmax = rmax
73 # True, if Release falls within the interval [self.vmin, self.vmax)
74 def IsRelease(self, release):
75 if self.rmax and self.rmax <= release:
76 return False
77 if self.rmin and self.rmin > release:
78 return False
79 if GetOption('release_debug'):
80 InfoOut.Log('%f is in %s' % (release, self))
81 return True
83 # True, if Release falls within the interval [self.vmin, self.vmax)
84 def InReleases(self, releases):
85 if not releases: return False
87 # Check last release first, since InRange does not match last item
88 if self.IsRelease(releases[-1]): return True
89 if len(releases) > 1:
90 return self.InRange(releases[0], releases[-1])
91 return False
93 # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax)
94 def InRange(self, rmin, rmax):
95 assert (rmin == None) or rmin < rmax
97 # An min of None always passes a min bound test
98 # An max of None always passes a max bound test
99 if rmin is not None and self.rmax is not None:
100 if self.rmax <= rmin:
101 return False
102 if rmax is not None and self.rmin is not None:
103 if self.rmin >= rmax:
104 return False
106 if GetOption('release_debug'):
107 InfoOut.Log('%f to %f is in %s' % (rmin, rmax, self))
108 return True
110 def GetMinMax(self, releases = None):
111 if not releases:
112 return self.rmin, self.rmax
114 if not self.rmin:
115 rmin = releases[0]
116 else:
117 rmin = str(self.rmin)
118 if not self.rmax:
119 rmax = releases[-1]
120 else:
121 rmax = str(self.rmax)
122 return (rmin, rmax)
124 def SetMin(self, release):
125 assert not self.rmin
126 self.rmin = release
128 def Error(self, msg):
129 ReportReleaseError(msg)
131 def Warn(self, msg):
132 ReportReleaseWarning(msg)
136 # IDLReleaseList
138 # IDLReleaseList is a list based container for holding IDLRelease
139 # objects in order. The IDLReleaseList can be added to, and searched by
140 # range. Objects are stored in order, and must be added in order.
142 class IDLReleaseList(object):
143 def __init__(self):
144 self._nodes = []
146 def GetReleases(self):
147 return self._nodes
149 def FindRelease(self, release):
150 for node in self._nodes:
151 if node.IsRelease(release):
152 return node
153 return None
155 def FindRange(self, rmin, rmax):
156 assert (rmin == None) or rmin != rmax
158 out = []
159 for node in self._nodes:
160 if node.InRange(rmin, rmax):
161 out.append(node)
162 return out
164 def AddNode(self, node):
165 if GetOption('release_debug'):
166 InfoOut.Log('\nAdding %s %s' % (node.Location(), node))
167 last = None
169 # Check current releases in that namespace
170 for cver in self._nodes:
171 if GetOption('release_debug'): InfoOut.Log(' Checking %s' % cver)
173 # We should only be missing a 'release' tag for the first item.
174 if not node.rmin:
175 node.Error('Missing release on overload of previous %s.' %
176 cver.Location())
177 return False
179 # If the node has no max, then set it to this one
180 if not cver.rmax:
181 cver.rmax = node.rmin
182 if GetOption('release_debug'): InfoOut.Log(' Update %s' % cver)
184 # if the max and min overlap, than's an error
185 if cver.rmax > node.rmin:
186 if node.rmax and cver.rmin >= node.rmax:
187 node.Error('Declarations out of order.')
188 else:
189 node.Error('Overlap in releases: %s vs %s when adding %s' %
190 (cver.rmax, node.rmin, node))
191 return False
192 last = cver
194 # Otherwise, the previous max and current min should match
195 # unless this is the unlikely case of something being only
196 # temporarily deprecated.
197 if last and last.rmax != node.rmin:
198 node.Warn('Gap in release numbers.')
200 # If we made it here, this new node must be the 'newest'
201 # and does not overlap with anything previously added, so
202 # we can add it to the end of the list.
203 if GetOption('release_debug'): InfoOut.Log('Done %s' % node)
204 self._nodes.append(node)
205 return True
208 # IDLReleaseMap
210 # A release map, can map from an float interface release, to a global
211 # release string.
213 class IDLReleaseMap(object):
214 def __init__(self, release_info):
215 self.version_to_release = {}
216 self.release_to_version = {}
217 self.release_to_channel = {}
218 for release, version, channel in release_info:
219 self.version_to_release[version] = release
220 self.release_to_version[release] = version
221 self.release_to_channel[release] = channel
222 self.releases = sorted(self.release_to_version.keys())
223 self.versions = sorted(self.version_to_release.keys())
225 def GetVersion(self, release):
226 return self.release_to_version.get(release, None)
228 def GetVersions(self):
229 return self.versions
231 def GetRelease(self, version):
232 return self.version_to_release.get(version, None)
234 def GetReleases(self):
235 return self.releases
237 def GetReleaseRange(self):
238 return (self.releases[0], self.releases[-1])
240 def GetVersionRange(self):
241 return (self.versions[0], self.version[-1])
243 def GetChannel(self, release):
244 return self.release_to_channel.get(release, None)
247 # Test Code
249 def TestReleaseNode():
250 FooXX = IDLRelease(None, None)
251 Foo1X = IDLRelease('M14', None)
252 Foo23 = IDLRelease('M15', 'M16')
254 assert FooXX.IsRelease('M13')
255 assert FooXX.IsRelease('M14')
256 assert FooXX.InRange('M13', 'M13A')
257 assert FooXX.InRange('M14','M15')
259 assert not Foo1X.IsRelease('M13')
260 assert Foo1X.IsRelease('M14')
261 assert Foo1X.IsRelease('M15')
263 assert not Foo1X.InRange('M13', 'M14')
264 assert not Foo1X.InRange('M13A', 'M14')
265 assert Foo1X.InRange('M14', 'M15')
266 assert Foo1X.InRange('M15', 'M16')
268 assert not Foo23.InRange('M13', 'M14')
269 assert not Foo23.InRange('M13A', 'M14')
270 assert not Foo23.InRange('M14', 'M15')
271 assert Foo23.InRange('M15', 'M16')
272 assert Foo23.InRange('M14', 'M15A')
273 assert Foo23.InRange('M15B', 'M17')
274 assert not Foo23.InRange('M16', 'M17')
275 print "TestReleaseNode - Passed"
278 def TestReleaseListWarning():
279 FooXX = IDLRelease(None, None)
280 Foo1X = IDLRelease('M14', None)
281 Foo23 = IDLRelease('M15', 'M16')
282 Foo45 = IDLRelease('M17', 'M18')
284 # Add nodes out of order should fail
285 ReportClear()
286 releases = IDLReleaseList()
287 assert releases.AddNode(Foo23)
288 assert releases.AddNode(Foo45)
289 assert warning
290 print "TestReleaseListWarning - Passed"
293 def TestReleaseListError():
294 FooXX = IDLRelease(None, None)
295 Foo1X = IDLRelease('M14', None)
296 Foo23 = IDLRelease('M15', 'M16')
297 Foo45 = IDLRelease('M17', 'M18')
299 # Add nodes out of order should fail
300 ReportClear()
301 releases = IDLReleaseList()
302 assert releases.AddNode(FooXX)
303 assert releases.AddNode(Foo23)
304 assert not releases.AddNode(Foo1X)
305 assert error
306 print "TestReleaseListError - Passed"
309 def TestReleaseListOK():
310 FooXX = IDLRelease(None, None)
311 Foo1X = IDLRelease('M14', None)
312 Foo23 = IDLRelease('M15', 'M16')
313 Foo45 = IDLRelease('M17', 'M18')
315 # Add nodes in order should work
316 ReportClear()
317 releases = IDLReleaseList()
318 assert releases.AddNode(FooXX)
319 assert releases.AddNode(Foo1X)
320 assert releases.AddNode(Foo23)
321 assert not error and not warning
322 assert releases.AddNode(Foo45)
323 assert warning
325 assert releases.FindRelease('M13') == FooXX
326 assert releases.FindRelease('M14') == Foo1X
327 assert releases.FindRelease('M15') == Foo23
328 assert releases.FindRelease('M16') == None
329 assert releases.FindRelease('M17') == Foo45
330 assert releases.FindRelease('M18') == None
332 assert releases.FindRange('M13','M14') == [FooXX]
333 assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23]
334 assert releases.FindRange('M16','M17') == []
335 assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45]
337 # Verify we can find the correct versions
338 print "TestReleaseListOK - Passed"
341 def TestReleaseMap():
342 print "TestReleaseMap- Passed"
345 def Main(args):
346 TestReleaseNode()
347 TestReleaseListWarning()
348 TestReleaseListError()
349 TestReleaseListOK()
350 print "Passed"
351 return 0
354 if __name__ == '__main__':
355 sys.exit(Main(sys.argv[1:]))