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. """
13 from idl_log
import ErrOut
, InfoOut
, WarnOut
14 from idl_option
import GetOption
, Option
, ParseOptions
17 Option('diff', 'Generate a DIFF when saving the file.')
19 def IsEquivalent(intext
, outtext
):
20 if not intext
: return False
21 inlines
= intext
.split('\n')
22 outlines
= outtext
.split('\n')
24 # If number of lines don't match, it's a mismatch
25 if len(inlines
) != len(outlines
): return False
27 for index
in range(len(inlines
)):
28 inline
= inlines
[index
]
29 outline
= outlines
[index
]
31 if inline
== outline
: continue
33 # If the line is not an exact match, check for comment deltas
34 inwords
= inline
.split()
35 outwords
= outline
.split()
37 if not inwords
or not outwords
: return False
38 if inwords
[0] != outwords
[0] or inwords
[0] not in ('/*', '*', '//'):
41 # Neither the year, nor the modified date need an exact match
42 if inwords
[1] == 'Copyright':
43 if inwords
[4:] == outwords
[4:]: continue
44 elif inwords
[1] == 'From': # Un-wrapped modified date.
45 if inwords
[0:4] == outwords
[0:4]: continue
46 elif inwords
[1] == 'modified': # Wrapped modified date.
47 if inwords
[0:2] == outwords
[0:2]: continue
55 # IDLOutFile provides a temporary output file. By default, the object will
56 # not write the output if the file already exists, and matches what will be
57 # written. This prevents the timestamp from changing to optimize cases where
58 # the output files are used by a timestamp dependent build system
60 class IDLOutFile(object):
61 def __init__(self
, filename
, always_write
= False, create_dir
= True):
62 self
.filename
= filename
63 self
.always_write
= always_write
64 self
.create_dir
= create_dir
68 # Return the file name
72 # Append to the output if the file is still open
73 def Write(self
, string
):
75 raise RuntimeError('Could not write to closed file %s.' % self
.filename
)
76 self
.outlist
.append(string
)
80 filename
= os
.path
.realpath(self
.filename
)
82 outtext
= ''.join(self
.outlist
)
83 if not self
.always_write
:
84 if os
.path
.isfile(filename
):
85 intext
= open(filename
, 'rb').read()
89 if IsEquivalent(intext
, outtext
):
90 if GetOption('verbose'):
91 InfoOut
.Log('Output %s unchanged.' % self
.filename
)
95 for line
in difflib
.unified_diff(intext
.split('\n'), outtext
.split('\n'),
96 'OLD ' + self
.filename
,
97 'NEW ' + self
.filename
,
102 # If the directory does not exit, try to create it, if we fail, we
103 # still get the exception when the file is openned.
104 basepath
, leafname
= os
.path
.split(filename
)
105 if basepath
and not os
.path
.isdir(basepath
) and self
.create_dir
:
106 InfoOut
.Log('Creating directory: %s\n' % basepath
)
107 os
.makedirs(basepath
)
109 if not GetOption('test'):
110 outfile
= open(filename
, 'wb')
111 outfile
.write(outtext
)
112 InfoOut
.Log('Output %s written.' % self
.filename
)
115 except IOError as (errno
, strerror
):
116 ErrOut
.Log("I/O error(%d): %s" % (errno
, strerror
))
118 ErrOut
.Log("Unexpected error: %s" % sys
.exc_info()[0])
124 def TestFile(name
, stringlist
, force
, update
):
127 # Get the old timestamp
128 if os
.path
.exists(name
):
129 old_time
= os
.stat(filename
)[ST_MTIME
]
133 # Create the file and write to it
134 out
= IDLOutFile(filename
, force
)
135 for item
in stringlist
:
138 # We wait for flush to force the timestamp to change
142 cur_time
= os
.stat(filename
)[ST_MTIME
]
145 ErrOut
.Log('Failed to write output %s.' % filename
)
147 if cur_time
== old_time
:
148 ErrOut
.Log('Failed to update timestamp for %s.' % filename
)
152 ErrOut
.Log('Should not have writen output %s.' % filename
)
154 if cur_time
!= old_time
:
155 ErrOut
.Log('Should not have modified timestamp for %s.' % filename
)
162 stringlist
= ['Test', 'Testing\n', 'Test']
163 filename
= 'outtest.txt'
165 # Test forcibly writing a file
166 errors
+= TestFile(filename
, stringlist
, force
=True, update
=True)
168 # Test conditionally writing the file skipping
169 errors
+= TestFile(filename
, stringlist
, force
=False, update
=False)
171 # Test conditionally writing the file updating
172 errors
+= TestFile(filename
, stringlist
+ ['X'], force
=False, update
=True)
176 if not errors
: InfoOut
.Log('All tests pass.')
180 if __name__
== '__main__':