- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / generators / idl_outfile.py
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 """ Output file objects for generator. """
7
8 import difflib
9 import os
10 import time
11 import sys
12
13 from idl_log import ErrOut, InfoOut, WarnOut
14 from idl_option import GetOption, Option, ParseOptions
15 from stat import *
16
17 Option('diff', 'Generate a DIFF when saving the file.')
18
19
20 #
21 # IDLOutFile
22 #
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
27 #
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
35
36   # Compare the old text to the current list of output lines.
37   def IsEquivalent_(self, oldtext):
38     if not oldtext: return False
39
40     oldlines = oldtext.split('\n')
41     curlines = (''.join(self.outlist)).split('\n')
42
43     # If number of lines don't match, it's a mismatch
44     if len(oldlines) != len(curlines):
45       return False
46
47     for index in range(len(oldlines)):
48       oldline = oldlines[index]
49       curline = curlines[index]
50
51       if oldline == curline: continue
52
53       curwords = curline.split()
54       oldwords = oldline.split()
55
56       # Unmatched lines must be the same length
57       if len(curwords) != len(oldwords):
58         return False
59
60       # If it's not a comment then it's a mismatch
61       if curwords[0] not in ['*', '/*', '//']:
62         return False
63
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
68
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
74
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
79
80       return False
81     return True
82
83   # Return the file name
84   def Filename(self):
85     return self.filename
86
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)
92
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 = ''
99
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
107
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)
114
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)
122
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
128
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
134
135     return False
136
137
138 def TestFile(name, stringlist, force, update):
139   errors = 0
140
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'
146
147   # Create the file and write to it
148   out = IDLOutFile(filename, force)
149   for item in stringlist:
150     out.Write(item)
151
152   # We wait for flush to force the timestamp to change
153   time.sleep(2)
154
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
172
173
174 def main():
175   errors = 0
176   stringlist = ['Test', 'Testing\n', 'Test']
177   filename = 'outtest.txt'
178
179   # Test forcibly writing a file
180   errors += TestFile(filename, stringlist, force=True, update=True)
181
182   # Test conditionally writing the file skipping
183   errors += TestFile(filename, stringlist, force=False, update=False)
184
185   # Test conditionally writing the file updating
186   errors += TestFile(filename, stringlist + ['X'], force=False, update=True)
187
188   # Clean up file
189   os.remove(filename)
190   if not errors: InfoOut.Log('All tests pass.')
191   return errors
192
193
194 if __name__ == '__main__':
195   sys.exit(main())