idl_release.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
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. 5 6""" 7IDLRelease for PPAPI 8 9This file defines the behavior of the AST namespace which allows for resolving 10a symbol as one or more AST nodes given a Release or range of Releases. 11""" 12 13import sys 14 15from idl_log import ErrOut, InfoOut, WarnOut 16from idl_option import GetOption, Option, ParseOptions 17 18Option('release_debug', 'Debug Release data') 19Option('wgap', 'Ignore Release gap warning') 20 21 22# 23# Module level functions and data used for testing. 24# 25error = None 26warning = None 27def ReportReleaseError(msg): 28 global error 29 error = msg 30 31def ReportReleaseWarning(msg): 32 global warning 33 warning = msg 34 35def ReportClear(): 36 global error, warning 37 error = None 38 warning = None 39 40# 41# IDLRelease 42# 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. 45# 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. 48 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. 53class IDLRelease(object): 54 def __init__(self, rmin, rmax): 55 self.rmin = rmin 56 self.rmax = rmax 57 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) 68 69 def SetReleaseRange(self, rmin, rmax): 70 self.rmin = rmin 71 self.rmax = rmax 72 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 82 83 # True, if Release falls within the interval [self.vmin, self.vmax) 84 def InReleases(self, releases): 85 if not releases: return False 86 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 92 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 96 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 105 106 if GetOption('release_debug'): 107 InfoOut.Log('%f to %f is in %s' % (rmin, rmax, self)) 108 return True 109 110 def GetMinMax(self, releases = None): 111 if not releases: 112 return self.rmin, self.rmax 113 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) 123 124 def SetMin(self, release): 125 assert not self.rmin 126 self.rmin = release 127 128 def Error(self, msg): 129 ReportReleaseError(msg) 130 131 def Warn(self, msg): 132 ReportReleaseWarning(msg) 133 134 135# 136# IDLReleaseList 137# 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. 141# 142class IDLReleaseList(object): 143 def __init__(self): 144 self._nodes = [] 145 146 def GetReleases(self): 147 return self._nodes 148 149 def FindRelease(self, release): 150 for node in self._nodes: 151 if node.IsRelease(release): 152 return node 153 return None 154 155 def FindRange(self, rmin, rmax): 156 assert (rmin == None) or rmin != rmax 157 158 out = [] 159 for node in self._nodes: 160 if node.InRange(rmin, rmax): 161 out.append(node) 162 return out 163 164 def AddNode(self, node): 165 if GetOption('release_debug'): 166 InfoOut.Log('\nAdding %s %s' % (node.Location(), node)) 167 last = None 168 169 # Check current releases in that namespace 170 for cver in self._nodes: 171 if GetOption('release_debug'): InfoOut.Log(' Checking %s' % cver) 172 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 178 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) 183 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 193 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.') 199 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 206 207# 208# IDLReleaseMap 209# 210# A release map, can map from an float interface release, to a global 211# release string. 212# 213class IDLReleaseMap(object): 214 def __init__(self, release_info): 215 self.version_to_release = {} 216 self.release_to_version = {} 217 for release, version in release_info: 218 self.version_to_release[version] = release 219 self.release_to_version[release] = version 220 self.releases = sorted(self.release_to_version.keys()) 221 self.versions = sorted(self.version_to_release.keys()) 222 223 def GetVersion(self, release): 224 return self.release_to_version.get(release, None) 225 226 def GetVersions(self): 227 return self.versions 228 229 def GetRelease(self, version): 230 return self.version_to_release.get(version, None) 231 232 def GetReleases(self): 233 return self.releases 234 235 def GetReleaseRange(self): 236 return (self.releases[0], self.releases[-1]) 237 238 def GetVersionRange(self): 239 return (self.versions[0], self.version[-1]) 240 241# 242# Test Code 243# 244def TestReleaseNode(): 245 FooXX = IDLRelease(None, None) 246 Foo1X = IDLRelease('M14', None) 247 Foo23 = IDLRelease('M15', 'M16') 248 249 assert FooXX.IsRelease('M13') 250 assert FooXX.IsRelease('M14') 251 assert FooXX.InRange('M13', 'M13A') 252 assert FooXX.InRange('M14','M15') 253 254 assert not Foo1X.IsRelease('M13') 255 assert Foo1X.IsRelease('M14') 256 assert Foo1X.IsRelease('M15') 257 258 assert not Foo1X.InRange('M13', 'M14') 259 assert not Foo1X.InRange('M13A', 'M14') 260 assert Foo1X.InRange('M14', 'M15') 261 assert Foo1X.InRange('M15', 'M16') 262 263 assert not Foo23.InRange('M13', 'M14') 264 assert not Foo23.InRange('M13A', 'M14') 265 assert not Foo23.InRange('M14', 'M15') 266 assert Foo23.InRange('M15', 'M16') 267 assert Foo23.InRange('M14', 'M15A') 268 assert Foo23.InRange('M15B', 'M17') 269 assert not Foo23.InRange('M16', 'M17') 270 print "TestReleaseNode - Passed" 271 272 273def TestReleaseListWarning(): 274 FooXX = IDLRelease(None, None) 275 Foo1X = IDLRelease('M14', None) 276 Foo23 = IDLRelease('M15', 'M16') 277 Foo45 = IDLRelease('M17', 'M18') 278 279 # Add nodes out of order should fail 280 ReportClear() 281 releases = IDLReleaseList() 282 assert releases.AddNode(Foo23) 283 assert releases.AddNode(Foo45) 284 assert warning 285 print "TestReleaseListWarning - Passed" 286 287 288def TestReleaseListError(): 289 FooXX = IDLRelease(None, None) 290 Foo1X = IDLRelease('M14', None) 291 Foo23 = IDLRelease('M15', 'M16') 292 Foo45 = IDLRelease('M17', 'M18') 293 294 # Add nodes out of order should fail 295 ReportClear() 296 releases = IDLReleaseList() 297 assert releases.AddNode(FooXX) 298 assert releases.AddNode(Foo23) 299 assert not releases.AddNode(Foo1X) 300 assert error 301 print "TestReleaseListError - Passed" 302 303 304def TestReleaseListOK(): 305 FooXX = IDLRelease(None, None) 306 Foo1X = IDLRelease('M14', None) 307 Foo23 = IDLRelease('M15', 'M16') 308 Foo45 = IDLRelease('M17', 'M18') 309 310 # Add nodes in order should work 311 ReportClear() 312 releases = IDLReleaseList() 313 assert releases.AddNode(FooXX) 314 assert releases.AddNode(Foo1X) 315 assert releases.AddNode(Foo23) 316 assert not error and not warning 317 assert releases.AddNode(Foo45) 318 assert warning 319 320 assert releases.FindRelease('M13') == FooXX 321 assert releases.FindRelease('M14') == Foo1X 322 assert releases.FindRelease('M15') == Foo23 323 assert releases.FindRelease('M16') == None 324 assert releases.FindRelease('M17') == Foo45 325 assert releases.FindRelease('M18') == None 326 327 assert releases.FindRange('M13','M14') == [FooXX] 328 assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23] 329 assert releases.FindRange('M16','M17') == [] 330 assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45] 331 332 # Verify we can find the correct versions 333 print "TestReleaseListOK - Passed" 334 335 336def TestReleaseMap(): 337 print "TestReleaseMap- Passed" 338 339 340def Main(args): 341 TestReleaseNode() 342 TestReleaseListWarning() 343 TestReleaseListError() 344 TestReleaseListOK() 345 print "Passed" 346 return 0 347 348 349if __name__ == '__main__': 350 sys.exit(Main(sys.argv[1:])) 351 352