blob: 763c7d26fd3cf7034b1e507fa2232f2948c26a94 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001# common python utility routines for the Bionic tool scripts
2
3import sys, os, commands, string, commands
4
5# basic debugging trace support
6# call D_setlevel to set the verbosity level
7# and D(), D2(), D3(), D4() to add traces
8#
9verbose = 0
10
11def panic(msg):
12 sys.stderr.write( find_program_name() + ": error: " )
13 sys.stderr.write( msg )
14 sys.exit(1)
15
16def D(msg):
17 global verbose
18 if verbose > 0:
19 print msg
20
21def D2(msg):
22 global verbose
23 if verbose >= 2:
24 print msg
25
26def D3(msg):
27 global verbose
28 if verbose >= 3:
29 print msg
30
31def D4(msg):
32 global verbose
33 if verbose >= 4:
34 print msg
35
36def D_setlevel(level):
37 global verbose
38 verbose = level
39
40
41# other stuff
42#
43#
44def find_program_name():
45 return os.path.basename(sys.argv[0])
46
47def find_program_dir():
48 return os.path.dirname(sys.argv[0])
49
50def find_file_from_upwards(from_path,target_file):
51 """find a file in the current directory or its parents. if 'from_path' is None,
52 seach from the current program's directory"""
53 path = from_path
54 if path == None:
55 path = os.path.realpath(sys.argv[0])
56 path = os.path.dirname(path)
57 D("this script seems to be located in: %s" % path)
58
59 while 1:
60 D("probing "+path)
61 if path == "":
62 file = target_file
63 else:
64 file = path + "/" + target_file
65
66 if os.path.isfile(file):
67 D("found %s in %s" % (target_file, path))
68 return file
69
70 if path == "":
71 return None
72
73 path = os.path.dirname(path)
74
75def find_bionic_root():
76 file = find_file_from_upwards(None, "SYSCALLS.TXT")
77 if file:
78 return os.path.dirname(file)
79 else:
80 return None
81
82def find_kernel_headers():
83 """try to find the directory containing the kernel headers for this machine"""
84 status, version = commands.getstatusoutput( "uname -r" ) # get Linux kernel version
85 if status != 0:
86 D("could not execute 'uname -r' command properly")
87 return None
88
89 # get rid of the "-xenU" suffix that is found in Xen virtual machines
90 if len(version) > 5 and version[-5:] == "-xenU":
91 version = version[:-5]
92
93 path = "/usr/src/linux-headers-" + version
94 D("probing %s for kernel headers" % (path+"/include"))
95 ret = os.path.isdir( path )
96 if ret:
97 D("found kernel headers in: %s" % (path + "/include"))
98 return path
99 return None
100
101
102# parser for the SYSCALLS.TXT file
103#
104class SysCallsTxtParser:
105 def __init__(self):
106 self.syscalls = []
107 self.lineno = 0
108
109 def E(msg):
110 print "%d: %s" % (self.lineno, msg)
111
112 def parse_line(self, line):
113 pos_lparen = line.find('(')
114 E = self.E
115 if pos_lparen < 0:
116 E("missing left parenthesis in '%s'" % line)
117 return
118
119 pos_rparen = line.rfind(')')
120 if pos_rparen < 0 or pos_rparen <= pos_lparen:
121 E("missing or misplaced right parenthesis in '%s'" % line)
122 return
123
124 return_type = line[:pos_lparen].strip().split()
125 if len(return_type) < 2:
126 E("missing return type in '%s'" % line)
127 return
128
129 syscall_func = return_type[-1]
130 return_type = string.join(return_type[:-1],' ')
131
132 pos_colon = syscall_func.find(':')
133 if pos_colon < 0:
134 syscall_name = syscall_func
135 else:
136 if pos_colon == 0 or pos_colon+1 >= len(syscall_func):
137 E("misplaced colon in '%s'" % line)
138 return
139 syscall_name = syscall_func[pos_colon+1:]
140 syscall_func = syscall_func[:pos_colon]
141
142 if pos_rparen > pos_lparen+1:
143 syscall_params = line[pos_lparen+1:pos_rparen].split(',')
144 params = string.join(syscall_params,',')
145 else:
146 syscall_params = []
147 params = "void"
148
149 number = line[pos_rparen+1:].strip()
150 if number == "stub":
151 syscall_id = -1
152 syscall_id2 = -1
153 else:
154 try:
155 if number[0] == '#':
156 number = number[1:].strip()
157 numbers = string.split(number,',')
158 syscall_id = int(numbers[0])
159 syscall_id2 = syscall_id
160 if len(numbers) > 1:
161 syscall_id2 = int(numbers[1])
162 except:
163 E("invalid syscall number in '%s'" % line)
164 return
165
166 t = { "id" : syscall_id,
167 "id2" : syscall_id2,
168 "name" : syscall_name,
169 "func" : syscall_func,
170 "params" : syscall_params,
171 "decl" : "%-15s %s (%s);" % (return_type, syscall_func, params) }
172
173 self.syscalls.append(t)
174
175 def parse_file(self, file_path):
176 fp = open(file_path)
177 for line in fp.xreadlines():
178 self.lineno += 1
179 line = line.strip()
180 if not line: continue
181 if line[0] == '#': continue
182 self.parse_line(line)
183
184 fp.close()
185
186
187class Output:
188 def __init__(self,out=sys.stdout):
189 self.out = out
190
191 def write(self,msg):
192 self.out.write(msg)
193
194 def writeln(self,msg):
195 self.out.write(msg)
196 self.out.write("\n")
197
198class StringOutput:
199 def __init__(self):
200 self.line = ""
201
202 def write(self,msg):
203 self.line += msg
204 D2("write '%s'" % msg)
205
206 def writeln(self,msg):
207 self.line += msg + '\n'
208 D2("write '%s\\n'"% msg)
209
210 def get(self):
211 return self.line
212
213
214def create_file_path(path):
215 dirs = []
216 while 1:
217 parent = os.path.dirname(path)
218 #print "parent: %s <- %s" % (parent, path)
219 if parent == "/" or parent == "":
220 break
221 dirs.append(parent)
222 path = parent
223
224 dirs.reverse()
225 for dir in dirs:
226 #print "dir %s" % dir
227 if os.path.isdir(dir):
228 continue
229 os.mkdir(dir)
230
231def walk_source_files(paths,callback,args,excludes=[]):
232 """recursively walk a list of paths and files, only keeping the source files in directories"""
233 for path in paths:
234 if not os.path.isdir(path):
235 callback(path,args)
236 else:
237 for root, dirs, files in os.walk(path):
238 #print "w-- %s (ex: %s)" % (repr((root,dirs)), repr(excludes))
239 if len(excludes):
240 for d in dirs[:]:
241 if d in excludes:
242 dirs.remove(d)
243 for f in files:
244 r, ext = os.path.splitext(f)
245 if ext in [ ".h", ".c", ".cpp", ".S" ]:
246 callback( "%s/%s" % (root,f), args )
247
248def cleanup_dir(path):
249 """create a directory if needed, and ensure that it is totally empty
250 by removing any existing content in it"""
251 if not os.path.exists(path):
252 os.mkdir(path)
253 else:
254 for root, dirs, files in os.walk(path, topdown=False):
255 if root.endswith("kernel_headers/"):
256 # skip 'kernel_headers'
257 continue
258 for name in files:
259 os.remove(os.path.join(root, name))
260 for name in dirs:
261 os.rmdir(os.path.join(root, name))
262
263def update_file( path, newdata ):
264 """update a file on disk, only if its content has changed"""
265 if os.path.exists( path ):
266 try:
267 f = open( path, "r" )
268 olddata = f.read()
269 f.close()
270 except:
271 D("update_file: cannot read existing file '%s'" % path)
272 return 0
273
274 if oldata == newdata:
275 D2("update_file: no change to file '%s'" % path )
276 return 0
277
278 update = 1
279 else:
280 try:
281 create_file_path(path)
282 except:
283 D("update_file: cannot create path to '%s'" % path)
284 return 0
285
286 f = open( path, "w" )
287 f.write( newdata )
288 f.close()
289
290 return 1
291
292
293class BatchFileUpdater:
294 """a class used to edit several files at once"""
295 def __init__(self):
296 self.old_files = set()
297 self.new_files = set()
298 self.new_data = {}
299
300 def readFile(self,path):
301 #path = os.path.realpath(path)
302 if os.path.exists(path):
303 self.old_files.add(path)
304
305 def readDir(self,path):
306 #path = os.path.realpath(path)
307 for root, dirs, files in os.walk(path):
308 for f in files:
309 dst = "%s/%s" % (root,f)
310 self.old_files.add(dst)
311
312 def editFile(self,dst,data):
313 """edit a destination file. if the file is not mapped from a source,
314 it will be added. return 0 if the file content wasn't changed,
315 1 if it was edited, or 2 if the file is new"""
316 #dst = os.path.realpath(dst)
317 result = 1
318 if os.path.exists(dst):
319 f = open(dst, "r")
320 olddata = f.read()
321 f.close()
322 if olddata == data:
323 self.old_files.remove(dst)
324 return 0
325 else:
326 result = 2
327
328 self.new_data[dst] = data
329 self.new_files.add(dst)
330 return result
331
332 def getChanges(self):
333 """determine changes, returns (adds, deletes, edits)"""
334 adds = set()
335 edits = set()
336 deletes = set()
337
338 for dst in self.new_files:
339 if not (dst in self.old_files):
340 adds.add(dst)
341 else:
342 edits.add(dst)
343
344 for dst in self.old_files:
345 if not dst in self.new_files:
346 deletes.add(dst)
347
348 return (adds, deletes, edits)
349
350 def _writeFile(self,dst,data=None):
351 if not os.path.exists(os.path.dirname(dst)):
352 create_file_path(dst)
353 if data == None:
354 data = self.new_data[dst]
355 f = open(dst, "w")
356 f.write(self.new_data[dst])
357 f.close()
358
359 def updateFiles(self):
360 adds, deletes, edits = self.getChanges()
361
362 for dst in sorted(adds):
363 self._writeFile(dst)
364
365 for dst in sorted(edits):
366 self._writeFile(dst)
367
368 for dst in sorted(deletes):
369 os.remove(dst)
370
371 def updateP4Files(self):
372 adds, deletes, edits = self.getChanges()
373
374 if len(adds):
375 files = string.join(sorted(adds)," ")
376 D( "%d new files will be p4 add-ed" % len(adds) )
377 for dst in adds:
378 self._writeFile(dst)
379 D2("P4 ADDS: %s" % files)
380 o = commands.getoutput( "p4 add " + files )
381 D2( o )
382
383 if len(edits):
384 files = string.join(sorted(edits)," ")
385 D( "%d files will be p4 edit-ed" % len(edits) )
386 D2("P4 EDITS: %s" % files)
387 o = commands.getoutput( "p4 edit " + files )
388 D2( o )
389 for dst in edits:
390 self._writeFile(dst)
391
392 if len(deletes):
393 files = string.join(sorted(deletes)," ")
394 D( "%d files will be p4 delete-d" % len(deletes) )
395 D2("P4 DELETES: %s" % files)
396 o = commands.getoutput( "p4 delete " + files )
397 D2( o )