Roll src/third_party/skia 93b255b:a71aee6
[chromium-blink-merge.git] / ppapi / generators / idl_outfile.py
blob61b678c18af3c2bd73406149c00c46cbcb9398a9
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 """ Output file objects for generator. """
8 import difflib
9 import os
10 import time
11 import sys
13 from idl_log import ErrOut, InfoOut, WarnOut
14 from idl_option import GetOption, Option, ParseOptions
15 from stat import *
17 Option('diff', 'Generate a DIFF when saving the file.')
21 # IDLOutFile
23 # IDLOutFile provides a temporary output file. By default, the object will
24 # not write the output if the file already exists, and matches what will be
25 # written. This prevents the timestamp from changing to optimize cases where
26 # the output files are used by a timestamp dependent build system
28 class IDLOutFile(object):
29 def __init__(self, filename, always_write = False, create_dir = True):
30 self.filename = filename
31 self.always_write = always_write
32 self.create_dir = create_dir
33 self.outlist = []
34 self.open = True
36 # Compare the old text to the current list of output lines.
37 def IsEquivalent_(self, oldtext):
38 if not oldtext: return False
40 oldlines = oldtext.split('\n')
41 curlines = (''.join(self.outlist)).split('\n')
43 # If number of lines don't match, it's a mismatch
44 if len(oldlines) != len(curlines):
45 return False
47 for index in range(len(oldlines)):
48 oldline = oldlines[index]
49 curline = curlines[index]
51 if oldline == curline: continue
53 curwords = curline.split()
54 oldwords = oldline.split()
56 # Unmatched lines must be the same length
57 if len(curwords) != len(oldwords):
58 return False
60 # If it's not a comment then it's a mismatch
61 if curwords[0] not in ['*', '/*', '//']:
62 return False
64 # Ignore changes to the Copyright year which is autogenerated
65 # /* Copyright (c) 2011 The Chromium Authors. All rights reserved.
66 if len(curwords) > 4 and curwords[1] == 'Copyright':
67 if curwords[4:] == oldwords[4:]: continue
69 # Ignore changes to auto generation timestamp when line unwrapped
70 # // From FILENAME.idl modified DAY MON DATE TIME YEAR.
71 # /* From FILENAME.idl modified DAY MON DATE TIME YEAR. */
72 if len(curwords) > 8 and curwords[1] == 'From':
73 if curwords[0:4] == oldwords[0:4]: continue
75 # Ignore changes to auto generation timestamp when line is wrapped
76 # * modified DAY MON DATE TIME YEAR.
77 if len(curwords) > 6 and curwords[1] == 'modified':
78 continue
80 return False
81 return True
83 # Return the file name
84 def Filename(self):
85 return self.filename
87 # Append to the output if the file is still open
88 def Write(self, string):
89 if not self.open:
90 raise RuntimeError('Could not write to closed file %s.' % self.filename)
91 self.outlist.append(string)
93 # Close the file, flushing it to disk
94 def Close(self):
95 filename = os.path.realpath(self.filename)
96 self.open = False
97 outtext = ''.join(self.outlist)
98 oldtext = ''
100 if not self.always_write:
101 if os.path.isfile(filename):
102 oldtext = open(filename, 'rb').read()
103 if self.IsEquivalent_(oldtext):
104 if GetOption('verbose'):
105 InfoOut.Log('Output %s unchanged.' % self.filename)
106 return False
108 if GetOption('diff'):
109 for line in difflib.unified_diff(oldtext.split('\n'), outtext.split('\n'),
110 'OLD ' + self.filename,
111 'NEW ' + self.filename,
112 n=1, lineterm=''):
113 ErrOut.Log(line)
115 try:
116 # If the directory does not exit, try to create it, if we fail, we
117 # still get the exception when the file is openned.
118 basepath, leafname = os.path.split(filename)
119 if basepath and not os.path.isdir(basepath) and self.create_dir:
120 InfoOut.Log('Creating directory: %s\n' % basepath)
121 os.makedirs(basepath)
123 if not GetOption('test'):
124 outfile = open(filename, 'wb')
125 outfile.write(outtext)
126 InfoOut.Log('Output %s written.' % self.filename)
127 return True
129 except IOError as (errno, strerror):
130 ErrOut.Log("I/O error(%d): %s" % (errno, strerror))
131 except:
132 ErrOut.Log("Unexpected error: %s" % sys.exc_info()[0])
133 raise
135 return False
138 def TestFile(name, stringlist, force, update):
139 errors = 0
141 # Get the old timestamp
142 if os.path.exists(name):
143 old_time = os.stat(filename)[ST_MTIME]
144 else:
145 old_time = 'NONE'
147 # Create the file and write to it
148 out = IDLOutFile(filename, force)
149 for item in stringlist:
150 out.Write(item)
152 # We wait for flush to force the timestamp to change
153 time.sleep(2)
155 wrote = out.Close()
156 cur_time = os.stat(filename)[ST_MTIME]
157 if update:
158 if not wrote:
159 ErrOut.Log('Failed to write output %s.' % filename)
160 return 1
161 if cur_time == old_time:
162 ErrOut.Log('Failed to update timestamp for %s.' % filename)
163 return 1
164 else:
165 if wrote:
166 ErrOut.Log('Should not have writen output %s.' % filename)
167 return 1
168 if cur_time != old_time:
169 ErrOut.Log('Should not have modified timestamp for %s.' % filename)
170 return 1
171 return 0
174 def main():
175 errors = 0
176 stringlist = ['Test', 'Testing\n', 'Test']
177 filename = 'outtest.txt'
179 # Test forcibly writing a file
180 errors += TestFile(filename, stringlist, force=True, update=True)
182 # Test conditionally writing the file skipping
183 errors += TestFile(filename, stringlist, force=False, update=False)
185 # Test conditionally writing the file updating
186 errors += TestFile(filename, stringlist + ['X'], force=False, update=True)
188 # Clean up file
189 os.remove(filename)
190 if not errors: InfoOut.Log('All tests pass.')
191 return errors
194 if __name__ == '__main__':
195 sys.exit(main())