idl_outfile.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#!/usr/bin/env python 20a08460599eed603e469e3e16d0cf6aa33b8ba93Chandler Carruth# Copyright (c) 2012 The Chromium Authors. All rights reserved. 30a08460599eed603e469e3e16d0cf6aa33b8ba93Chandler Carruth# Use of this source code is governed by a BSD-style license that can be 40a08460599eed603e469e3e16d0cf6aa33b8ba93Chandler Carruth# found in the LICENSE file. 50a08460599eed603e469e3e16d0cf6aa33b8ba93Chandler Carruth 6e3ba15c794839abe076e3e2bdf6c626396a19d4dWill Dietz""" Output file objects for generator. """ 731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarimport difflib 94ca7e09b7c1e41535f2a1bd86915375d023daf27Chandler Carruthimport os 1031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarimport time 1131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarimport sys 1231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 1331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarfrom idl_log import ErrOut, InfoOut, WarnOut 1431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarfrom idl_option import GetOption, Option, ParseOptions 1531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarfrom stat import * 1631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 1731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick TryzelaarOption('diff', 'Generate a DIFF when saving the file.') 1831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 1931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaardef IsEquivelent(intext, outtext): 2031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not intext: return False 2131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar inlines = intext.split('\n') 2231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outlines = outtext.split('\n') 2331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 2431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # If number of lines don't match, it's a mismatch 2531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if len(inlines) != len(outlines): return False 2631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 2731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar for index in range(len(inlines)): 2831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar inline = inlines[index] 2931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outline = outlines[index] 3031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 3131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inline == outline: continue 3231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 3331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # If the line is not an exact match, check for comment deltas 3431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar inwords = inline.split() 3531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outwords = outline.split() 3631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 3731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not inwords or not outwords: return False 3831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inwords[0] != outwords[0] or inwords[0] not in ('/*', '*'): return False 3931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 4031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Neither the year, nor the modified date need an exact match 4131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inwords[1] == 'Copyright': 4231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inwords[4:] == outwords[4:]: continue 4331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar elif inwords[1] == 'From': # Un-wrapped modified date. 4431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inwords[0:4] == outwords[0:4]: continue 4531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar elif inwords[1] == 'modified': # Wrapped modified date. 4631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if inwords[0:2] == outwords[0:2]: continue 4731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return False 4831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return True 4931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 5031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 5131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# 5231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# IDLOutFile 5331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# 5431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# IDLOutFile provides a temporary output file. By default, the object will 5531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# not write the output if the file already exists, and matches what will be 5631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# written. This prevents the timestamp from changing to optimize cases where 5731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# the output files are used by a timestamp dependent build system 5831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar# 5931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarclass IDLOutFile(object): 6031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar def __init__(self, filename, always_write = False, create_dir = True): 6131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.filename = filename 6231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.always_write = always_write 6331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.create_dir = create_dir 6431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.outlist = [] 6531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.open = True 6631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 6731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Return the file name 6831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar def Filename(self): 6931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return self.filename 7031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 7131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Append to the output if the file is still open 7231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar def Write(self, string): 7331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not self.open: 7431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar raise RuntimeError('Could not write to closed file %s.' % self.filename) 7531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.outlist.append(string) 7631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 7731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Close the file 7831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar def Close(self): 7931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar filename = os.path.realpath(self.filename) 8031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar self.open = False 8131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outtext = ''.join(self.outlist) 8231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not self.always_write: 839d7c776d32c8a4d64b37a91c2d627629cf1498efBill Wendling if os.path.isfile(filename): 8431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar intext = open(filename, 'rb').read() 8531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar else: 8631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar intext = '' 879d7c776d32c8a4d64b37a91c2d627629cf1498efBill Wendling 8831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if IsEquivelent(intext, outtext): 8931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if GetOption('verbose'): 9031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar InfoOut.Log('Output %s unchanged.' % self.filename) 9131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return False 9231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 9331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if GetOption('diff'): 9431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar for line in difflib.unified_diff(intext.split('\n'), outtext.split('\n'), 9531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 'OLD ' + self.filename, 9631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 'NEW ' + self.filename, 9731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar n=1, lineterm=''): 9831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log(line) 9931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 10031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar try: 10131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # If the directory does not exit, try to create it, if we fail, we 10231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # still get the exception when the file is openned. 10331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar basepath, leafname = os.path.split(filename) 10431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if basepath and not os.path.isdir(basepath) and self.create_dir: 10531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar InfoOut.Log('Creating directory: %s\n' % basepath) 10631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar os.makedirs(basepath) 10731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 10831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not GetOption('test'): 10931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outfile = open(filename, 'wb') 11031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar outfile.write(outtext) 11131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar InfoOut.Log('Output %s written.' % self.filename) 11231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return True 11331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 11431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar except IOError as (errno, strerror): 11531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log("I/O error(%d): %s" % (errno, strerror)) 11631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar except: 11731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log("Unexpected error: %s" % sys.exc_info()[0]) 11831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar raise 11931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 12031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return False 12131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 12231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 12331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaardef TestFile(name, stringlist, force, update): 12431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar errors = 0 12531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 12631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Get the old timestamp 12731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if os.path.exists(name): 12831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar old_time = os.stat(filename)[ST_MTIME] 12931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar else: 13031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar old_time = 'NONE' 13131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 13231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Create the file and write to it 13331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar out = IDLOutFile(filename, force) 13431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar for item in stringlist: 13531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar out.Write(item) 13631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 13731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # We wait for flush to force the timestamp to change 13831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar time.sleep(2) 13931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 14031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar wrote = out.Close() 14131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar cur_time = os.stat(filename)[ST_MTIME] 14231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if update: 14331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not wrote: 14431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log('Failed to write output %s.' % filename) 14531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return 1 14631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if cur_time == old_time: 14731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log('Failed to update timestamp for %s.' % filename) 14831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return 1 14931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar else: 1509d7c776d32c8a4d64b37a91c2d627629cf1498efBill Wendling if wrote: 15131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log('Should not have writen output %s.' % filename) 15231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return 1 15331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if cur_time != old_time: 15431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar ErrOut.Log('Should not have modified timestamp for %s.' % filename) 15531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return 1 15631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return 0 15731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 15831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 15931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaardef main(): 16031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar errors = 0 16131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar stringlist = ['Test', 'Testing\n', 'Test'] 16231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar filename = 'outtest.txt' 16331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 16431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Test forcibly writing a file 16531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar errors += TestFile(filename, stringlist, force=True, update=True) 16631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 16731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Test conditionally writing the file skipping 16831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar errors += TestFile(filename, stringlist, force=False, update=False) 16931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 17031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Test conditionally writing the file updating 17131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar errors += TestFile(filename, stringlist + ['X'], force=False, update=True) 17231c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 17331c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar # Clean up file 17431c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar os.remove(filename) 17531c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar if not errors: InfoOut.Log('All tests pass.') 17631c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar return errors 17731c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 17831c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar 17931c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaarif __name__ == '__main__': 18031c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar sys.exit(main()) 18131c6c5d58a6d2254063e8a18fd32b851a06e2ddfErick Tryzelaar