| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """ | 7 """ |
| 8 IDLVersion for PPAPI | 8 IDLRelease for PPAPI |
| 9 | 9 |
| 10 This file defines the behavior of the AST namespace which allows for resolving | 10 This file defines the behavior of the AST namespace which allows for resolving |
| 11 a symbol as one or more AST nodes given a version or range of versions. | 11 a symbol as one or more AST nodes given a Release or range of Releases. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 import sys | 14 import sys |
| 15 | 15 |
| 16 from idl_log import ErrOut, InfoOut, WarnOut | 16 from idl_log import ErrOut, InfoOut, WarnOut |
| 17 from idl_option import GetOption, Option, ParseOptions | 17 from idl_option import GetOption, Option, ParseOptions |
| 18 | 18 |
| 19 Option('version_debug', 'Debug version data') | 19 Option('release_debug', 'Debug Release data') |
| 20 Option('wgap', 'Ignore version gap warning') | 20 Option('wgap', 'Ignore Release gap warning') |
| 21 | 21 |
| 22 | 22 |
| 23 # | 23 # |
| 24 # Module level functions and data used for testing. | 24 # Module level functions and data used for testing. |
| 25 # | 25 # |
| 26 error = None | 26 error = None |
| 27 warning = None | 27 warning = None |
| 28 def ReportVersionError(msg): | 28 def ReportReleaseError(msg): |
| 29 global error | 29 global error |
| 30 error = msg | 30 error = msg |
| 31 | 31 |
| 32 def ReportVersionWarning(msg): | 32 def ReportReleaseWarning(msg): |
| 33 global warning | 33 global warning |
| 34 warning = msg | 34 warning = msg |
| 35 | 35 |
| 36 def ReportClear(): | 36 def ReportClear(): |
| 37 global error, warning | 37 global error, warning |
| 38 error = None | 38 error = None |
| 39 warning = None | 39 warning = None |
| 40 | 40 |
| 41 # | 41 # |
| 42 # IDLVersion | 42 # IDLRelease |
| 43 # | 43 # |
| 44 # IDLVersion is an object which stores the association of a given symbol | 44 # IDLRelease is an object which stores the association of a given symbol |
| 45 # name, with an AST node for a range of versions for that object. | 45 # name, with an AST node for a range of Releases for that object. |
| 46 # | 46 # |
| 47 # A vmin value of None indicates that the object begins at the earliest | 47 # A vmin value of None indicates that the object begins at the earliest |
| 48 # available version number. The value of vmin is always inclusive. | 48 # available Release number. The value of vmin is always inclusive. |
| 49 | 49 |
| 50 # A vmax value of None indicates that the object is never deprecated, so | 50 # A vmax value of None indicates that the object is never deprecated, so |
| 51 # it exists until it is overloaded or until the latest available version. | 51 # it exists until it is overloaded or until the latest available Release. |
| 52 # The value of vmax is always exclusive, representing the first version | 52 # The value of vmax is always exclusive, representing the first Release |
| 53 # on which the object is no longer valid. | 53 # on which the object is no longer valid. |
| 54 class IDLVersion(object): | 54 class IDLRelease(object): |
| 55 def __init__(self, vmin, vmax): | 55 def __init__(self, rmin, rmax): |
| 56 self.vmin = vmin | 56 self.rmin = rmin |
| 57 self.vmax = vmax | 57 self.rmax = rmax |
| 58 | 58 |
| 59 def __str__(self): | 59 def __str__(self): |
| 60 if not self.vmin: | 60 if not self.rmin: |
| 61 vmin = '0' | 61 rmin = '0' |
| 62 else: | 62 else: |
| 63 vmin = str(self.vmin) | 63 rmin = str(self.rmin) |
| 64 if not self.vmax: | 64 if not self.rmax: |
| 65 vmax = '+oo' | 65 rmax = '+oo' |
| 66 else: | 66 else: |
| 67 vmax = str(self.vmax) | 67 rmax = str(self.rmax) |
| 68 return '[%s,%s)' % (vmin, vmax) | 68 return '[%s,%s)' % (rmin, rmax) |
| 69 | 69 |
| 70 def SetVersionRange(self, vmin, vmax): | 70 def SetReleaseRange(self, rmin, rmax): |
| 71 if vmin is not None: vmin = float(vmin) | 71 self.rmin = rmin |
| 72 if vmax is not None: vmax = float(vmax) | 72 self.rmax = rmax |
| 73 self.vmin = vmin | 73 |
| 74 self.vmax = vmax | 74 # True, if Release falls within the interval [self.vmin, self.vmax) |
| 75 | 75 def IsRelease(self, release): |
| 76 # True, if version falls within the interval [self.vmin, self.vmax) | 76 if self.rmax and self.rmax <= release: |
| 77 def IsVersion(self, version): | |
| 78 assert type(version) == float | |
| 79 | |
| 80 if self.vmax and self.vmax <= version: | |
| 81 return False | 77 return False |
| 82 if self.vmin and self.vmin > version: | 78 if self.rmin and self.rmin > release: |
| 83 return False | 79 return False |
| 84 if GetOption('version_debug'): | 80 if GetOption('release_debug'): |
| 85 InfoOut.Log('%f is in %s' % (version, self)) | 81 InfoOut.Log('%f is in %s' % (release, self)) |
| 86 return True | 82 return True |
| 87 | 83 |
| 84 # True, if Release falls within the interval [self.vmin, self.vmax) |
| 85 def InReleases(self, releases): |
| 86 if not releases: return False |
| 87 |
| 88 # Check last release first, since InRange does not match last item |
| 89 if self.IsRelease(releases[-1]): return True |
| 90 if len(releases) > 1: |
| 91 return self.InRange(releases[0], releases[-1]) |
| 92 return False |
| 93 |
| 88 # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax) | 94 # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax) |
| 89 def InRange(self, vmin, vmax): | 95 def InRange(self, rmin, rmax): |
| 90 assert type(vmin) == float | 96 assert (rmin == None) or rmin < rmax |
| 91 assert type(vmax) == float | 97 |
| 92 assert vmin != vmax | 98 # An min of None always passes a min bound test |
| 93 | 99 # An max of None always passes a max bound test |
| 94 if self.vmax and self.vmax <= vmin: | 100 if rmin is not None and self.rmax is not None: |
| 95 return False | 101 if self.rmax <= rmin: |
| 96 if self.vmin and self.vmin >= vmax: | 102 return False |
| 97 return False | 103 if rmax is not None and self.rmin is not None: |
| 98 | 104 if self.rmin >= rmax: |
| 99 if GetOption('version_debug'): | 105 return False |
| 100 InfoOut.Log('%f to %f is in %s' % (vmin, vmax, self)) | 106 |
| 107 if GetOption('release_debug'): |
| 108 InfoOut.Log('%f to %f is in %s' % (rmin, rmax, self)) |
| 101 return True | 109 return True |
| 102 | 110 |
| 103 def Error(self, msg): | 111 def Error(self, msg): |
| 104 ReportVersionError(msg) | 112 ReportReleaseError(msg) |
| 105 | 113 |
| 106 def Warning(self, msg): | 114 def Warn(self, msg): |
| 107 ReportVersionWarning(msg) | 115 ReportReleaseWarning(msg) |
| 108 | 116 |
| 117 |
| 118 # |
| 119 # IDLReleaseList |
| 120 # |
| 121 # IDLReleaseList is a list based container for holding IDLRelease |
| 122 # objects in order. The IDLReleaseList can be added to, and searched by |
| 123 # range. Objects are stored in order, and must be added in order. |
| 124 # |
| 125 class IDLReleaseList(object): |
| 126 def __init__(self): |
| 127 self.nodes = [] |
| 128 |
| 129 def FindRelease(self, release): |
| 130 for node in self.nodes: |
| 131 if node.IsRelease(release): |
| 132 return node |
| 133 return None |
| 134 |
| 135 def FindRange(self, rmin, rmax): |
| 136 assert (rmin == None) or rmin != rmax |
| 137 |
| 138 out = [] |
| 139 for node in self.nodes: |
| 140 if node.InRange(rmin, rmax): |
| 141 out.append(node) |
| 142 return out |
| 143 |
| 144 def AddNode(self, node): |
| 145 if GetOption('release_debug'): |
| 146 InfoOut.Log('\nAdding %s %s' % (node.Location(), node)) |
| 147 last = None |
| 148 |
| 149 # Check current releases in that namespace |
| 150 for cver in self.nodes: |
| 151 if GetOption('release_debug'): InfoOut.Log(' Checking %s' % cver) |
| 152 |
| 153 # We should only be missing a 'release' tag for the first item. |
| 154 if not node.rmin: |
| 155 node.Error('Missing release on overload of previous %s.' % |
| 156 cver.Location()) |
| 157 return False |
| 158 |
| 159 # If the node has no max, then set it to this one |
| 160 if not cver.rmax: |
| 161 cver.rmax = node.rmin |
| 162 if GetOption('release_debug'): InfoOut.Log(' Update %s' % cver) |
| 163 |
| 164 # if the max and min overlap, than's an error |
| 165 if cver.rmax > node.rmin: |
| 166 if node.rmax and cver.rmin >= node.rmax: |
| 167 node.Error('Declarations out of order.') |
| 168 else: |
| 169 node.Error('Overlap in releases: %s vs %s when adding %s' % |
| 170 (cver.rmax, node.rmin, node)) |
| 171 return False |
| 172 last = cver |
| 173 |
| 174 # Otherwise, the previous max and current min should match |
| 175 # unless this is the unlikely case of something being only |
| 176 # temporarily deprecated. |
| 177 if last and last.rmax != node.rmin: |
| 178 node.Warn('Gap in release numbers.') |
| 179 |
| 180 # If we made it here, this new node must be the 'newest' |
| 181 # and does not overlap with anything previously added, so |
| 182 # we can add it to the end of the list. |
| 183 if GetOption('release_debug'): InfoOut.Log('Done %s' % node) |
| 184 self.nodes.append(node) |
| 185 return True |
| 186 |
| 187 # |
| 188 # IDLReleaseMap |
| 189 # |
| 190 # A release map, can map from an float interface release, to a global |
| 191 # release string. |
| 192 # |
| 193 class IDLReleaseMap(object): |
| 194 def __init__(self, release_info): |
| 195 self.version_to_release = {} |
| 196 self.release_to_version = {} |
| 197 for release, version in release_info: |
| 198 self.version_to_release[version] = release |
| 199 self.release_to_version[release] = version |
| 200 self.releases = sorted(self.release_to_version.keys()) |
| 201 self.versions = sorted(self.version_to_release.keys()) |
| 202 |
| 203 def GetVersion(self, release): |
| 204 return self.release_to_version.get(release, None) |
| 205 |
| 206 def GetVersions(self): |
| 207 return self.versions |
| 208 |
| 209 def GetRelease(self, version): |
| 210 return self.version_to_release.get(version, None) |
| 211 |
| 212 def GetReleases(self): |
| 213 return self.releases |
| 109 | 214 |
| 110 # | 215 # |
| 111 # Test Code | 216 # Test Code |
| 112 # | 217 # |
| 218 def TestReleaseNode(): |
| 219 FooXX = IDLRelease(None, None) |
| 220 Foo1X = IDLRelease('M14', None) |
| 221 Foo23 = IDLRelease('M15', 'M16') |
| 222 |
| 223 assert FooXX.IsRelease('M13') |
| 224 assert FooXX.IsRelease('M14') |
| 225 assert FooXX.InRange('M13', 'M13A') |
| 226 assert FooXX.InRange('M14','M15') |
| 227 |
| 228 assert not Foo1X.IsRelease('M13') |
| 229 assert Foo1X.IsRelease('M14') |
| 230 assert Foo1X.IsRelease('M15') |
| 231 |
| 232 assert not Foo1X.InRange('M13', 'M14') |
| 233 assert not Foo1X.InRange('M13A', 'M14') |
| 234 assert Foo1X.InRange('M14', 'M15') |
| 235 assert Foo1X.InRange('M15', 'M16') |
| 236 |
| 237 assert not Foo23.InRange('M13', 'M14') |
| 238 assert not Foo23.InRange('M13A', 'M14') |
| 239 assert not Foo23.InRange('M14', 'M15') |
| 240 assert Foo23.InRange('M15', 'M16') |
| 241 assert Foo23.InRange('M14', 'M15A') |
| 242 assert Foo23.InRange('M15B', 'M17') |
| 243 assert not Foo23.InRange('M16', 'M17') |
| 244 print "TestReleaseNode - Passed" |
| 245 |
| 246 def TestReleaseListWarning(): |
| 247 FooXX = IDLRelease(None, None) |
| 248 Foo1X = IDLRelease('M14', None) |
| 249 Foo23 = IDLRelease('M15', 'M16') |
| 250 Foo45 = IDLRelease('M17', 'M18') |
| 251 |
| 252 # Add nodes out of order should fail |
| 253 ReportClear() |
| 254 releases = IDLReleaseList() |
| 255 assert releases.AddNode(Foo23) |
| 256 assert releases.AddNode(Foo45) |
| 257 assert warning |
| 258 print "TestReleaseListWarning - Passed" |
| 259 |
| 260 def TestReleaseListError(): |
| 261 FooXX = IDLRelease(None, None) |
| 262 Foo1X = IDLRelease('M14', None) |
| 263 Foo23 = IDLRelease('M15', 'M16') |
| 264 Foo45 = IDLRelease('M17', 'M18') |
| 265 |
| 266 # Add nodes out of order should fail |
| 267 ReportClear() |
| 268 releases = IDLReleaseList() |
| 269 assert releases.AddNode(FooXX) |
| 270 assert releases.AddNode(Foo23) |
| 271 assert not releases.AddNode(Foo1X) |
| 272 assert error |
| 273 print "TestReleaseListError - Passed" |
| 274 |
| 275 def TestReleaseListOK(): |
| 276 FooXX = IDLRelease(None, None) |
| 277 Foo1X = IDLRelease('M14', None) |
| 278 Foo23 = IDLRelease('M15', 'M16') |
| 279 Foo45 = IDLRelease('M17', 'M18') |
| 280 |
| 281 # Add nodes in order should work |
| 282 ReportClear() |
| 283 releases = IDLReleaseList() |
| 284 assert releases.AddNode(FooXX) |
| 285 assert releases.AddNode(Foo1X) |
| 286 assert releases.AddNode(Foo23) |
| 287 assert not error and not warning |
| 288 assert releases.AddNode(Foo45) |
| 289 assert warning |
| 290 |
| 291 assert releases.FindRelease('M13') == FooXX |
| 292 assert releases.FindRelease('M14') == Foo1X |
| 293 assert releases.FindRelease('M15') == Foo23 |
| 294 assert releases.FindRelease('M16') == None |
| 295 assert releases.FindRelease('M17') == Foo45 |
| 296 assert releases.FindRelease('M18') == None |
| 297 |
| 298 assert releases.FindRange('M13','M14') == [FooXX] |
| 299 assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23] |
| 300 assert releases.FindRange('M16','M17') == [] |
| 301 assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45] |
| 302 |
| 303 # Verify we can find the correct versions |
| 304 print "TestReleaseListOK - Passed" |
| 305 |
| 306 |
| 307 def TestReleaseMap(): |
| 308 print "TestReleaseMap- Passed" |
| 309 |
| 113 def Main(args): | 310 def Main(args): |
| 114 global errors | 311 TestReleaseNode() |
| 115 | 312 TestReleaseListWarning() |
| 116 FooXX = IDLVersion(None, None) | 313 TestReleaseListError() |
| 117 Foo1X = IDLVersion(1.0, None) | 314 TestReleaseListOK() |
| 118 Foo23 = IDLVersion(2.0, 3.0) | |
| 119 | |
| 120 assert FooXX.IsVersion(0.0) | |
| 121 assert FooXX.IsVersion(1.0) | |
| 122 assert FooXX.InRange(0.0, 0.1) | |
| 123 assert FooXX.InRange(1.0,2.0) | |
| 124 | |
| 125 assert not Foo1X.IsVersion(0.0) | |
| 126 assert Foo1X.IsVersion(1.0) | |
| 127 assert Foo1X.IsVersion(2.0) | |
| 128 | |
| 129 assert not Foo1X.InRange(0.0, 1.0) | |
| 130 assert not Foo1X.InRange(0.5, 1.0) | |
| 131 assert Foo1X.InRange(1.0, 2.0) | |
| 132 assert Foo1X.InRange(2.0, 3.0) | |
| 133 | |
| 134 assert not Foo23.InRange(0.0, 1.0) | |
| 135 assert not Foo23.InRange(0.5, 1.0) | |
| 136 assert not Foo23.InRange(1.0, 2.0) | |
| 137 assert Foo23.InRange(2.0, 3.0) | |
| 138 assert Foo23.InRange(1.0, 2.1) | |
| 139 assert Foo23.InRange(2.9, 4.0) | |
| 140 assert not Foo23.InRange(3.0, 4.0) | |
| 141 | |
| 142 print "Passed" | 315 print "Passed" |
| 143 return 0 | 316 return 0 |
| 144 | 317 |
| 145 if __name__ == '__main__': | 318 if __name__ == '__main__': |
| 146 sys.exit(Main(sys.argv[1:])) | 319 sys.exit(Main(sys.argv[1:])) |
| 147 | 320 |
| OLD | NEW |