blob: f4cf5403b900ecc4a0cbfd96fab701cb28c5af49 [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:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200234 if len(path) > 0 and path[0] == '@':
235 # this is the name of another file, include it and parse it
236 path = path[1:]
237 if os.path.exists(path):
238 for line in open(path):
239 if len(line) > 0 and line[-1] == '\n':
240 line = line[:-1]
241 walk_source_files([line],callback,args,excludes)
242 continue
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800243 if not os.path.isdir(path):
244 callback(path,args)
245 else:
246 for root, dirs, files in os.walk(path):
247 #print "w-- %s (ex: %s)" % (repr((root,dirs)), repr(excludes))
248 if len(excludes):
249 for d in dirs[:]:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200250 if os.path.join(root,d) in excludes:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800251 dirs.remove(d)
252 for f in files:
253 r, ext = os.path.splitext(f)
254 if ext in [ ".h", ".c", ".cpp", ".S" ]:
255 callback( "%s/%s" % (root,f), args )
256
257def cleanup_dir(path):
258 """create a directory if needed, and ensure that it is totally empty
259 by removing any existing content in it"""
260 if not os.path.exists(path):
261 os.mkdir(path)
262 else:
263 for root, dirs, files in os.walk(path, topdown=False):
264 if root.endswith("kernel_headers/"):
265 # skip 'kernel_headers'
266 continue
267 for name in files:
268 os.remove(os.path.join(root, name))
269 for name in dirs:
270 os.rmdir(os.path.join(root, name))
271
272def update_file( path, newdata ):
273 """update a file on disk, only if its content has changed"""
274 if os.path.exists( path ):
275 try:
276 f = open( path, "r" )
277 olddata = f.read()
278 f.close()
279 except:
280 D("update_file: cannot read existing file '%s'" % path)
281 return 0
282
283 if oldata == newdata:
284 D2("update_file: no change to file '%s'" % path )
285 return 0
286
287 update = 1
288 else:
289 try:
290 create_file_path(path)
291 except:
292 D("update_file: cannot create path to '%s'" % path)
293 return 0
294
295 f = open( path, "w" )
296 f.write( newdata )
297 f.close()
298
299 return 1
300
301
302class BatchFileUpdater:
303 """a class used to edit several files at once"""
304 def __init__(self):
305 self.old_files = set()
306 self.new_files = set()
307 self.new_data = {}
308
309 def readFile(self,path):
310 #path = os.path.realpath(path)
311 if os.path.exists(path):
312 self.old_files.add(path)
313
314 def readDir(self,path):
315 #path = os.path.realpath(path)
316 for root, dirs, files in os.walk(path):
317 for f in files:
318 dst = "%s/%s" % (root,f)
319 self.old_files.add(dst)
320
321 def editFile(self,dst,data):
322 """edit a destination file. if the file is not mapped from a source,
323 it will be added. return 0 if the file content wasn't changed,
324 1 if it was edited, or 2 if the file is new"""
325 #dst = os.path.realpath(dst)
326 result = 1
327 if os.path.exists(dst):
328 f = open(dst, "r")
329 olddata = f.read()
330 f.close()
331 if olddata == data:
332 self.old_files.remove(dst)
333 return 0
334 else:
335 result = 2
336
337 self.new_data[dst] = data
338 self.new_files.add(dst)
339 return result
340
341 def getChanges(self):
342 """determine changes, returns (adds, deletes, edits)"""
343 adds = set()
344 edits = set()
345 deletes = set()
346
347 for dst in self.new_files:
348 if not (dst in self.old_files):
349 adds.add(dst)
350 else:
351 edits.add(dst)
352
353 for dst in self.old_files:
354 if not dst in self.new_files:
355 deletes.add(dst)
356
357 return (adds, deletes, edits)
358
359 def _writeFile(self,dst,data=None):
360 if not os.path.exists(os.path.dirname(dst)):
361 create_file_path(dst)
362 if data == None:
363 data = self.new_data[dst]
364 f = open(dst, "w")
365 f.write(self.new_data[dst])
366 f.close()
367
368 def updateFiles(self):
369 adds, deletes, edits = self.getChanges()
370
371 for dst in sorted(adds):
372 self._writeFile(dst)
373
374 for dst in sorted(edits):
375 self._writeFile(dst)
376
377 for dst in sorted(deletes):
378 os.remove(dst)
379
380 def updateP4Files(self):
381 adds, deletes, edits = self.getChanges()
382
383 if len(adds):
384 files = string.join(sorted(adds)," ")
385 D( "%d new files will be p4 add-ed" % len(adds) )
386 for dst in adds:
387 self._writeFile(dst)
388 D2("P4 ADDS: %s" % files)
389 o = commands.getoutput( "p4 add " + files )
390 D2( o )
391
392 if len(edits):
393 files = string.join(sorted(edits)," ")
394 D( "%d files will be p4 edit-ed" % len(edits) )
395 D2("P4 EDITS: %s" % files)
396 o = commands.getoutput( "p4 edit " + files )
397 D2( o )
398 for dst in edits:
399 self._writeFile(dst)
400
401 if len(deletes):
402 files = string.join(sorted(deletes)," ")
403 D( "%d files will be p4 delete-d" % len(deletes) )
404 D2("P4 DELETES: %s" % files)
405 o = commands.getoutput( "p4 delete " + files )
406 D2( o )
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200407
408 def updateGitFiles(self):
409 adds, deletes, edits = self.getChanges()
410
411 if adds:
412 for dst in sorted(adds):
413 self._writeFile(dst)
414 commands.getoutput("git add " + " ".join(adds))
415
416 if deletes:
417 commands.getoutput("git rm " + " ".join(deletes))
418
419 if edits:
420 for dst in sorted(edits):
421 self._writeFile(dst)
422 commands.getoutput("git add " + " ".join(edits))