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 |