| OLD | NEW |
| (Empty) |
| 1 #! /usr/bin/python | |
| 2 # Copyright 2010 The Native Client Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can | |
| 4 # be found in the LICENSE file. | |
| 5 | |
| 6 """Script to update .scons rules from the ppapi.gyp file. | |
| 7 | |
| 8 This script reads the .scons files in this directory. It replaces all of the | |
| 9 lines between the starting marker and the ending marker with the corresponding | |
| 10 list of files from the given gyp file. | |
| 11 | |
| 12 The starting marker format is: | |
| 13 # From GYP_FILE_NAME:TARGET:REGEXP | |
| 14 The ending marker format is: | |
| 15 # End GYP_FILE_NAME | |
| 16 | |
| 17 For example, if this exists in the .scons file: | |
| 18 # From ppapi.gyp:ppapi_c:.*\.h | |
| 19 ... | |
| 20 # End ppapi.gyp | |
| 21 | |
| 22 then this script will remove all of the lines between the starting marker and | |
| 23 the ending marker. It will then find the 'ppapi_c' target in the ppapi.gyp | |
| 24 file. It will find all 'sources' for that target that match the regular | |
| 25 expression '.*\.h' and will insert each of those source files in between the | |
| 26 two markers. | |
| 27 | |
| 28 This script performs a similar action for the ppapi include tests. The files | |
| 29 ../../../tests/ppapi/cpp_header_test.cc and | |
| 30 ../../../tests/ppapi/cpp_dev_header_test.cc have their include statements | |
| 31 replaced based on an include marker with format: | |
| 32 // From ppapi.gyp:TARGET:REGEXP | |
| 33 """ | |
| 34 | |
| 35 from optparse import OptionParser | |
| 36 import os | |
| 37 import re | |
| 38 import sys | |
| 39 | |
| 40 # Constants. | |
| 41 PROG = os.path.basename(sys.argv[0]) | |
| 42 NATIVE_CLIENT_SRC_SHARED_PPAPI = os.path.abspath(os.path.dirname(sys.argv[0])) | |
| 43 NATIVE_CLIENT_SRC_SHARED = os.path.dirname(NATIVE_CLIENT_SRC_SHARED_PPAPI) | |
| 44 NATIVE_CLIENT_SRC = os.path.dirname(NATIVE_CLIENT_SRC_SHARED) | |
| 45 NATIVE_CLIENT = os.path.dirname(NATIVE_CLIENT_SRC) | |
| 46 CLIENTDIR = os.path.dirname(NATIVE_CLIENT) | |
| 47 | |
| 48 # Known files names. | |
| 49 PPAPI_GYP_DIR = os.path.join(CLIENTDIR, 'ppapi') | |
| 50 BUILD_SCONS = os.path.join(NATIVE_CLIENT_SRC_SHARED_PPAPI, 'build.scons') | |
| 51 NACL_SCONS = os.path.join(NATIVE_CLIENT_SRC_SHARED_PPAPI, 'nacl.scons') | |
| 52 PPAPI_GYP = os.path.join(NATIVE_CLIENT_SRC_SHARED_PPAPI, 'ppapi.gyp') | |
| 53 CPP_HEADER_TEST = os.path.join( | |
| 54 NATIVE_CLIENT, 'tests', 'ppapi', 'cpp_header_test.cc') | |
| 55 CPP_DEV_HEADER_TEST = os.path.join( | |
| 56 NATIVE_CLIENT, 'tests', 'ppapi', 'cpp_dev_header_test.cc') | |
| 57 | |
| 58 # Regular expressions for the .scons files. | |
| 59 START_PATTERN = re.compile( | |
| 60 '^([ \t]*)#[ \t]*From ([^:]*):([^:]*):(.*) *\n', | |
| 61 re.IGNORECASE) | |
| 62 END_PATTERN = re.compile('^[ \t]*#[ \t]*End ([^:]*) *\n', re.IGNORECASE) | |
| 63 | |
| 64 # Regular expressions for the .cc files. | |
| 65 INCLUDE_PATTERN = re.compile( | |
| 66 '^[ \t]*//[ \t]*From ([^:]*):([^:]*):(.*) *\n', | |
| 67 re.IGNORECASE) | |
| 68 | |
| 69 # Regular expressions for the ppapi.gyp file. | |
| 70 TARGET_PATTERN = re.compile( | |
| 71 '^[ \t]*\'target_name\'[ \t]*:[ \t]*\'([^\']*)\'[ \t]*,') | |
| 72 SOURCES_PATTERN = re.compile('^[ \t]*\'sources\'[ \t]*:[ \t]*[[]') | |
| 73 END_SOURCES_PATTERN = re.compile('^[ \t]*[]]') | |
| 74 | |
| 75 | |
| 76 def ParseCommandLine(): | |
| 77 """Parses options from the command line. | |
| 78 | |
| 79 Parses options from the command line and returns the options and the | |
| 80 arguments to the caller. | |
| 81 | |
| 82 Returns: | |
| 83 options: the options settings from the command line. | |
| 84 args: the arguments (things that were not options). | |
| 85 """ | |
| 86 usage = '%prog [options]' | |
| 87 description = ('Update the .scons files in the "%s" directory with the' | |
| 88 ' current definitions from ppapi.gyp. Also update the #include' | |
| 89 ' test files in the "%s/../../../tests/ppapi" directory' | |
| 90 % (NATIVE_CLIENT_SRC_SHARED_PPAPI, | |
| 91 NATIVE_CLIENT_SRC_SHARED_PPAPI)) | |
| 92 parser = OptionParser(usage=usage, description=description) | |
| 93 (options, args) = parser.parse_args() | |
| 94 return (options, args) | |
| 95 | |
| 96 | |
| 97 def CheckFileIsReadable(filename): | |
| 98 """Check the read accessibility of filename. | |
| 99 | |
| 100 Args: | |
| 101 filename: the file to check. | |
| 102 | |
| 103 Returns: | |
| 104 Exits to the system if the file is not readable. | |
| 105 """ | |
| 106 if not os.access(filename, os.F_OK): | |
| 107 print >> sys.stderr, '%s does not exist.' % (filename) | |
| 108 sys.exit(1) | |
| 109 if not os.access(filename, os.R_OK): | |
| 110 print >> sys.stderr, 'Cannot read from %s' % (filename) | |
| 111 sys.exit(1) | |
| 112 | |
| 113 | |
| 114 def CheckFileIsWritable(filename): | |
| 115 """Check the write accessibility of filename. | |
| 116 | |
| 117 Args: | |
| 118 filename: the file to check. | |
| 119 | |
| 120 Returns: | |
| 121 Exits to the system if the file is not writable. | |
| 122 """ | |
| 123 if os.access(filename, os.F_OK): | |
| 124 # The file already exists. Check if it is writable. | |
| 125 if not os.access(filename, os.W_OK): | |
| 126 print >> sys.stderr, 'Cannot write to %s' % (filename) | |
| 127 sys.exit(1) | |
| 128 else: | |
| 129 # The file does not yet exist. Check the directory. | |
| 130 dirname = os.path.dirname(filename) | |
| 131 if not dirname: | |
| 132 dirname = '.' | |
| 133 if not os.access(dirname, os.W_OK): | |
| 134 print >> sys.stderr, 'Cannot write to directory %s' % (dirname) | |
| 135 sys.exit(1) | |
| 136 | |
| 137 | |
| 138 def BuildTmpFilename(filename): | |
| 139 """Returns the name of a temporary file for filename. | |
| 140 | |
| 141 Args: | |
| 142 filename: the name of a file. | |
| 143 | |
| 144 Returns: | |
| 145 The name of a temporary file. | |
| 146 """ | |
| 147 return filename + '.tmp' | |
| 148 | |
| 149 | |
| 150 def RenameTmpToFile(filename): | |
| 151 """Renames filename.tmp to filename. | |
| 152 | |
| 153 Args: | |
| 154 filename: the final name of the file. | |
| 155 """ | |
| 156 tmp_filename = BuildTmpFilename(filename) | |
| 157 print '%s: Renaming %s back to %s' % (PROG, tmp_filename, filename) | |
| 158 os.remove(filename) | |
| 159 os.rename(tmp_filename, filename) | |
| 160 | |
| 161 | |
| 162 def TransferLinesFromPpapiGyp(marker, write_fp, is_cc_file): | |
| 163 """Copies matching file lines from ppapi.gyp. | |
| 164 | |
| 165 Opens and reads ppapi.gyp to find the 'target' section that matches the | |
| 166 target listed on the marker line. Copies all of the files that match the | |
| 167 marker's regular expression into the output file. | |
| 168 | |
| 169 Note that this function reads and parses the contents of ppapi.gyp each | |
| 170 time it is called. This entire script should take less than 1 second to | |
| 171 run, so the increased complexity involved in pre-caching the contents of | |
| 172 the file and passing it around is unnecessary. | |
| 173 | |
| 174 Args: | |
| 175 marker: The marker line from the file; matches START_PATTERN. | |
| 176 write_fp: The file to write to. | |
| 177 is_cc_file: False if this is a .scons file, True if .cc. | |
| 178 """ | |
| 179 # Pluck off the interesting parts of the marker. | |
| 180 if not is_cc_file: | |
| 181 match = re.match(START_PATTERN, marker) | |
| 182 scons_indentation = match.group(1) | |
| 183 gyp_file_name = match.group(2) | |
| 184 target = match.group(3) | |
| 185 file_regexp = match.group(4) | |
| 186 else: | |
| 187 match = re.match(INCLUDE_PATTERN, marker) | |
| 188 gyp_file_name = match.group(1) | |
| 189 target = match.group(2) | |
| 190 file_regexp = match.group(3) | |
| 191 | |
| 192 # Convert the input file_regexp into a pattern that will match the syntax | |
| 193 # of a source file in the ppapi.gyp file. The .gyp file looks like: | |
| 194 # 'path/some-file.cc', | |
| 195 # Put everything except the leading spaces into match.group(1). | |
| 196 # So if the marker line in the .scons file contains the regexp: | |
| 197 # .*\.cc | |
| 198 # the file_pattern regular expression (for .scons) will look like: | |
| 199 # ^ *('.*\.cc',) | |
| 200 # and the file_pattern regular expression (for .cc) will look like: | |
| 201 # ^ *'(.*\.cc)', | |
| 202 if not is_cc_file: | |
| 203 file_pattern = re.compile('^[ \t]*(\'' + file_regexp + '\',)') | |
| 204 else: | |
| 205 file_pattern = re.compile('^[ \t]*\'(' + file_regexp + ')\',') | |
| 206 | |
| 207 gyp_file = open(os.path.join(PPAPI_GYP_DIR, gyp_file_name), 'r') | |
| 208 found_target = False | |
| 209 found_sources = False | |
| 210 for line in gyp_file: | |
| 211 if not found_target: | |
| 212 # Still looking for: | |
| 213 # 'target_name': 'TARGET' | |
| 214 match = re.match(TARGET_PATTERN, line) | |
| 215 if match: | |
| 216 if match.group(1) == target: | |
| 217 found_target = True | |
| 218 elif not found_sources: | |
| 219 # Looking for the start of the sources section for this target: | |
| 220 # 'sources': [ | |
| 221 if re.match(SOURCES_PATTERN, line): | |
| 222 found_sources = True | |
| 223 elif re.match(END_SOURCES_PATTERN, line): | |
| 224 # Found the ']' at the end of the 'sources': section. | |
| 225 break | |
| 226 else: | |
| 227 # This is a line from the 'sources' section. Does it match the filespec? | |
| 228 match = re.match(file_pattern, line) | |
| 229 if match: | |
| 230 # Change the line's indentation to match the .scons file then write | |
| 231 # the line to the .scons file. | |
| 232 if not is_cc_file: | |
| 233 out_line = scons_indentation + match.group(1) + '\n' | |
| 234 else: | |
| 235 out_line = '#include "ppapi/' + match.group(1) + '"\n' | |
| 236 write_fp.write(out_line) | |
| 237 | |
| 238 gyp_file.close() | |
| 239 | |
| 240 | |
| 241 def UpdateSconsToTmp(filename): | |
| 242 """Updates the input .scons file, writing to filename.tmp. | |
| 243 | |
| 244 Updates all filename lines between the start and end header markers with | |
| 245 the current values from gyp_file. Writes all output to a temporary file. | |
| 246 | |
| 247 Args: | |
| 248 filename: the file to update. | |
| 249 """ | |
| 250 tmp_filename = BuildTmpFilename(filename) | |
| 251 print '%s: Updating %s to %s' % (PROG, filename, tmp_filename) | |
| 252 | |
| 253 read_fp = open(filename, 'r') | |
| 254 write_fp = open(tmp_filename, 'w') | |
| 255 | |
| 256 skipping_lines = False | |
| 257 for line in read_fp: | |
| 258 if not skipping_lines: | |
| 259 # Currently copying all lines until a START_PATTERN line is found. | |
| 260 write_fp.write(line) | |
| 261 if re.match(START_PATTERN, line): | |
| 262 TransferLinesFromPpapiGyp(line, write_fp, False) | |
| 263 skipping_lines = True | |
| 264 else: | |
| 265 # All of the most recent source files have been copied from the | |
| 266 # ppapi.gyp file into the output file. We are now skipping all lines | |
| 267 # until an END_PATTERN line is found. | |
| 268 if re.match(END_PATTERN, line): | |
| 269 write_fp.write(line) | |
| 270 skipping_lines = False | |
| 271 | |
| 272 read_fp.close() | |
| 273 write_fp.close() | |
| 274 | |
| 275 | |
| 276 def UpdateCcToTmp(filename): | |
| 277 """Updates the input .cc file, writing to filename.tmp. | |
| 278 | |
| 279 Updates all #include lines after the inclusion marker with the | |
| 280 current values from gyp_file. Writes all output to a temporary file. | |
| 281 | |
| 282 Args: | |
| 283 filename: the file to update. | |
| 284 """ | |
| 285 tmp_filename = BuildTmpFilename(filename) | |
| 286 print '%s: Updating %s to %s' % (PROG, filename, tmp_filename) | |
| 287 | |
| 288 read_fp = open(filename, 'r') | |
| 289 write_fp = open(tmp_filename, 'w') | |
| 290 | |
| 291 for line in read_fp: | |
| 292 # Currently copying all lines until a INCLUDE_PATTERN line is found. | |
| 293 write_fp.write(line) | |
| 294 if re.match(INCLUDE_PATTERN, line): | |
| 295 TransferLinesFromPpapiGyp(line, write_fp, True) | |
| 296 break | |
| 297 | |
| 298 read_fp.close() | |
| 299 write_fp.close() | |
| 300 | |
| 301 | |
| 302 def main(): | |
| 303 ParseCommandLine() | |
| 304 | |
| 305 # Make sure all of the files are accessible. | |
| 306 CheckFileIsReadable(BUILD_SCONS) | |
| 307 CheckFileIsReadable(NACL_SCONS) | |
| 308 CheckFileIsReadable(PPAPI_GYP) | |
| 309 CheckFileIsReadable(CPP_HEADER_TEST) | |
| 310 CheckFileIsReadable(CPP_DEV_HEADER_TEST) | |
| 311 CheckFileIsWritable(BuildTmpFilename(BUILD_SCONS)) | |
| 312 CheckFileIsWritable(BuildTmpFilename(NACL_SCONS)) | |
| 313 CheckFileIsWritable(BuildTmpFilename(PPAPI_GYP)) | |
| 314 CheckFileIsWritable(BuildTmpFilename(CPP_HEADER_TEST)) | |
| 315 CheckFileIsWritable(BuildTmpFilename(CPP_DEV_HEADER_TEST)) | |
| 316 | |
| 317 # Update each of the .scons files into temporary files. | |
| 318 UpdateSconsToTmp(BUILD_SCONS) | |
| 319 UpdateSconsToTmp(NACL_SCONS) | |
| 320 | |
| 321 # Update the .gyp file into temporary file. | |
| 322 UpdateSconsToTmp(PPAPI_GYP) | |
| 323 | |
| 324 # Update each of the .cc files into temporary files. | |
| 325 UpdateCcToTmp(CPP_HEADER_TEST) | |
| 326 UpdateCcToTmp(CPP_DEV_HEADER_TEST) | |
| 327 | |
| 328 # Copy the temporary files back to the real files. | |
| 329 RenameTmpToFile(BUILD_SCONS) | |
| 330 RenameTmpToFile(NACL_SCONS) | |
| 331 RenameTmpToFile(PPAPI_GYP) | |
| 332 RenameTmpToFile(CPP_HEADER_TEST) | |
| 333 RenameTmpToFile(CPP_DEV_HEADER_TEST) | |
| 334 | |
| 335 return 0 | |
| 336 | |
| 337 | |
| 338 if __name__ == '__main__': | |
| 339 sys.exit(main()) | |
| OLD | NEW |