updatefile.py
Go to the documentation of this file.
00001 #! /usr/bin/env python 00002 00003 # ATTENTION: DO NOT use the tokens used by the file update anywhere within 00004 # this file. Write < basis-custom > instead, for example. 00005 00006 ## 00007 # @file updatefile.py 00008 # @brief Update file from template file while preserving custom sections (deprecated). 00009 # 00010 # This script is used by the BasisUpdate.cmake module. This module is used to 00011 # update files of a project instantiated from a particular revision of the 00012 # BASIS project template during the configure step of CMake. This way, 00013 # projects pull the changes of the compatible template automatically. 00014 # Sections in the original file which are enclosed by the tokens 00015 # <basis-custom> and </basis-custom> or <basis-license> and 00016 # </basis-license> (without trailing spaces) are preserved while all other 00017 # content is replaced by the template file. The customized sections are 00018 # inserted into the template in the order they appear in the original file 00019 # and the template file. If more custom sections are present in the original 00020 # file than in the template file, these custom sections are appended at the 00021 # end of the resulting file. 00022 # 00023 # See the documentation of BasisUpdate.cmake for further details. 00024 # 00025 # Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br /> 00026 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file. 00027 # 00028 # Contact: SBIA Group <sbia-software at uphs.upenn.edu> 00029 # 00030 # @ingroup CMakeHelpers 00031 00032 # modules 00033 import os 00034 import sys 00035 import getopt 00036 00037 # constants 00038 customTag = "basis-custom" 00039 licenseTag = "basis-license" 00040 00041 tokenCustomStart = "<" + customTag + ">" 00042 tokenCustomEnd = "</" + customTag + ">" 00043 tokenLicenseStart = "<" + licenseTag + ">" 00044 tokenLicenseEnd = "</" + licenseTag + ">" 00045 tokenKeepTemplate = "REMOVE_THIS_STRING_IF_YOU_WANT_TO_KEEP_YOUR_CHANGES" 00046 00047 # **************************************************************************** 00048 def version (progName): 00049 """Print version information.""" 00050 print progName + "1.0.0" 00051 00052 # **************************************************************************** 00053 def usage (progName): 00054 print "Usage:" 00055 print " " + progName + " [options]" 00056 print 00057 print "Required options:" 00058 print " [-i --in] : Filename of original file" 00059 print " [-t --template] : Filename of template file" 00060 print 00061 print "Options:" 00062 print " [-o --out] Filename of output file. If this option is not given," 00063 print " changes are not applied and the exit code can be used" 00064 print " to check whether changes would have been applied." 00065 print " [-f --force] Force overwrite of output file. Otherwise ask user." 00066 print 00067 print "Return value:" 00068 print " 0 Merged output differs from input file and output file was" 00069 print " written successfully if option -o or --out was given" 00070 print " 1 Failed to read or write file" 00071 print " 2 Nothing changed, input file not overwritten" 00072 print " 3 Merged output differs from input file but user chose not" 00073 print " to overwrite input file" 00074 print "Example:" 00075 print " " + progName + " -i CMakeLists.txt -t CMakeLists.txt.template -o CMakeLists.txt" 00076 print 00077 print "Contact:" 00078 print " SBIA Group <sbia-software at uphs.upenn.edu>" 00079 00080 # **************************************************************************** 00081 def help (progName): 00082 usage (progName) 00083 00084 # **************************************************************************** 00085 def extractCustomizedSections (input): 00086 custom = [] 00087 start = 0 00088 while True: 00089 start = input.find (tokenCustomStart, start) 00090 if start == -1: 00091 break 00092 start += len (tokenCustomStart) 00093 next = input.find (tokenCustomStart, start) 00094 end = input.find (tokenCustomEnd, start) 00095 if end == -1 or (next != -1 and end > next): 00096 print "WARNING: Found begin of customized section without end token '" + tokenCustomEnd + "'" 00097 print "WARNING: Will keep template section instead" 00098 custom.append (tokenKeepTemplate) 00099 else: 00100 custom.append (input [start:end]) 00101 start = next 00102 return custom 00103 00104 # **************************************************************************** 00105 def replaceCustomizedSections (input, custom): 00106 start = 0 00107 end = -1 00108 result = input 00109 for section in custom: 00110 if start != -1: 00111 start = result.find (tokenCustomStart, start) 00112 end = -1 00113 if start != -1: 00114 start += len (tokenCustomStart) 00115 end = result.find (tokenCustomEnd, start) 00116 if start == -1 or end == -1: 00117 if section != tokenKeepTemplate: 00118 result = result + '\n' 00119 result = result + '# ' + tokenCustomStart 00120 result = result + section 00121 result = result + tokenCustomEnd 00122 else: 00123 if section != tokenKeepTemplate: 00124 result = result [:start] + section + result [end:] 00125 end = start + len (section) 00126 if end != -1: 00127 start = end + len (tokenCustomEnd) 00128 return result 00129 00130 # **************************************************************************** 00131 def extractLicenseSections (input): 00132 license = [] 00133 start = 0 00134 while True: 00135 start = input.find (tokenLicenseStart, start) 00136 if start == -1: 00137 break 00138 start += len (tokenLicenseStart) 00139 next = input.find (tokenLicenseStart, start) 00140 end = input.find (tokenLicenseEnd, start) 00141 if end == -1 or (next != -1 and end > next): 00142 print "WARNING: Found begin of license section without end token '" + tokenLicenseEnd + "'" 00143 print "WARNING: Will keep template section instead" 00144 license.append (tokenKeepTemplate) 00145 else: 00146 license.append (input [start:end]) 00147 start = next 00148 return license 00149 00150 # **************************************************************************** 00151 def replaceLicenseSections (input, license): 00152 start = 0 00153 end = -1 00154 result = input 00155 for section in license: 00156 if start != -1: 00157 start = result.find (tokenLicenseStart, start) 00158 if start != -1: 00159 start += len (tokenLicenseStart) 00160 end = result.find (tokenLicenseEnd, start) 00161 if start != -1 and end != -1: 00162 if section != tokenKeepTemplate: 00163 result = result [:start] + section + result [end:] 00164 start = end + len (tokenLicenseEnd) 00165 return result 00166 00167 00168 # **************************************************************************** 00169 def run (inputFile, templateFile, outputFile, force): 00170 # open input and output files 00171 try: 00172 fIn = open (inputFile, 'r') 00173 except IOError: 00174 sys.stderr.write ("Failed to open file '" + inputFile + "'\n") 00175 return 1 00176 try: 00177 fTem = open (templateFile, 'r') 00178 except IOError: 00179 sys.stderr.write ("Failed to open file '" + templateFile + "'\n") 00180 fIn.close () 00181 return 1 00182 # read files 00183 input = fIn.read () 00184 fIn.close () 00185 result = fTem.read () 00186 fTem.close () 00187 # extract custom sections from input and substitute them in template 00188 sections = extractCustomizedSections (input) 00189 result = replaceCustomizedSections (result, sections) 00190 # extract license sections from input and substitute them in template 00191 sections = extractLicenseSections (input) 00192 result = replaceLicenseSections (result, sections) 00193 # check whether input file equals output file 00194 if inputFile == outputFile: 00195 # check if content changed 00196 if result == input: 00197 return 2 00198 # query user if file should be overwritten 00199 if not force: 00200 try: 00201 sys.stdout.write ("Template of file '" + inputFile + "' has been modified.\n") 00202 sys.stdout.write ("Do you want to apply the changes (basis-custom sections remain unchanged)? ") 00203 sys.stdout.flush () 00204 apply = raw_input () 00205 if apply != "y" and apply != "yes": 00206 return 3 00207 except EOFError: 00208 return 1 00209 # write result to output file if -o option given 00210 if outputFile != "": 00211 try: 00212 fOut = open (outputFile, 'w') 00213 except IOError: 00214 sys.stderr.write ("Failed to open file '" + outputFile + "'\n") 00215 return 1 00216 fOut.write (result) 00217 fOut.close () 00218 # done 00219 if result == input: 00220 return 2 00221 return 0 00222 00223 # **************************************************************************** 00224 if __name__ == "__main__": 00225 progName = os.path.basename (sys.argv [0]) 00226 # options 00227 verbosity = 0 00228 inputFile = "" 00229 templateFile = "" 00230 outputFile = "" 00231 force = False 00232 # get options 00233 try: 00234 opts, files = getopt.gnu_getopt (sys.argv [1:], "uhvVfi:t:o:", 00235 ["usage","help","version","verbose","force","in=","template=","out="]) 00236 except getopt.GetoptError, err: 00237 usage (progName) 00238 print str(err) 00239 sys.exit(1) 00240 # parse command line options 00241 for o, a in opts: 00242 if o in ["-V", "--verbose"]: 00243 verbosity += 1 00244 elif o in ["-h", "--help","-u","--usage"]: 00245 help (progName) 00246 sys.exit(0) 00247 elif o in ["-v", "--version", "--Version"]: 00248 version (progName) 00249 sys.exit(0) 00250 elif o in ["-f", "--force"]: 00251 force = True 00252 elif o in ["-i", "--in"]: 00253 inputFile = os.path.realpath(a) 00254 elif o in ["-t", "--template"]: 00255 templateFile = os.path.realpath(a) 00256 elif o in ["-o", "--out"]: 00257 outputFile = os.path.realpath(a) 00258 else: 00259 assert False, "unhandled option " + o 00260 # all required inputs specified ? 00261 if inputFile == "" or templateFile == "": 00262 usage (progName) 00263 sys.exit (1) 00264 # run 00265 sys.exit (run (inputFile, templateFile, outputFile, force)) 00266