blob: e2820d1ebac0fec804b1bac8d68a6864448cd133 [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
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800187class StringOutput:
188 def __init__(self):
189 self.line = ""
190
191 def write(self,msg):
192 self.line += msg
193 D2("write '%s'" % msg)
194
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800195 def get(self):
196 return self.line
197
198
199def create_file_path(path):
200 dirs = []
201 while 1:
202 parent = os.path.dirname(path)
203 #print "parent: %s <- %s" % (parent, path)
204 if parent == "/" or parent == "":
205 break
206 dirs.append(parent)
207 path = parent
208
209 dirs.reverse()
210 for dir in dirs:
211 #print "dir %s" % dir
212 if os.path.isdir(dir):
213 continue
214 os.mkdir(dir)
215
216def walk_source_files(paths,callback,args,excludes=[]):
217 """recursively walk a list of paths and files, only keeping the source files in directories"""
218 for path in paths:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200219 if len(path) > 0 and path[0] == '@':
220 # this is the name of another file, include it and parse it
221 path = path[1:]
222 if os.path.exists(path):
223 for line in open(path):
224 if len(line) > 0 and line[-1] == '\n':
225 line = line[:-1]
226 walk_source_files([line],callback,args,excludes)
227 continue
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800228 if not os.path.isdir(path):
229 callback(path,args)
230 else:
231 for root, dirs, files in os.walk(path):
232 #print "w-- %s (ex: %s)" % (repr((root,dirs)), repr(excludes))
233 if len(excludes):
234 for d in dirs[:]:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200235 if os.path.join(root,d) in excludes:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800236 dirs.remove(d)
237 for f in files:
238 r, ext = os.path.splitext(f)
239 if ext in [ ".h", ".c", ".cpp", ".S" ]:
240 callback( "%s/%s" % (root,f), args )
241
242def cleanup_dir(path):
243 """create a directory if needed, and ensure that it is totally empty
244 by removing any existing content in it"""
245 if not os.path.exists(path):
246 os.mkdir(path)
247 else:
248 for root, dirs, files in os.walk(path, topdown=False):
249 if root.endswith("kernel_headers/"):
250 # skip 'kernel_headers'
251 continue
252 for name in files:
253 os.remove(os.path.join(root, name))
254 for name in dirs:
255 os.rmdir(os.path.join(root, name))
256
257def update_file( path, newdata ):
258 """update a file on disk, only if its content has changed"""
259 if os.path.exists( path ):
260 try:
261 f = open( path, "r" )
262 olddata = f.read()
263 f.close()
264 except:
265 D("update_file: cannot read existing file '%s'" % path)
266 return 0
267
268 if oldata == newdata:
269 D2("update_file: no change to file '%s'" % path )
270 return 0
271
272 update = 1
273 else:
274 try:
275 create_file_path(path)
276 except:
277 D("update_file: cannot create path to '%s'" % path)
278 return 0
279
280 f = open( path, "w" )
281 f.write( newdata )
282 f.close()
283
284 return 1
285
286
287class BatchFileUpdater:
288 """a class used to edit several files at once"""
289 def __init__(self):
290 self.old_files = set()
291 self.new_files = set()
292 self.new_data = {}
293
294 def readFile(self,path):
295 #path = os.path.realpath(path)
296 if os.path.exists(path):
297 self.old_files.add(path)
298
299 def readDir(self,path):
300 #path = os.path.realpath(path)
301 for root, dirs, files in os.walk(path):
302 for f in files:
303 dst = "%s/%s" % (root,f)
304 self.old_files.add(dst)
305
306 def editFile(self,dst,data):
307 """edit a destination file. if the file is not mapped from a source,
308 it will be added. return 0 if the file content wasn't changed,
309 1 if it was edited, or 2 if the file is new"""
310 #dst = os.path.realpath(dst)
311 result = 1
312 if os.path.exists(dst):
313 f = open(dst, "r")
314 olddata = f.read()
315 f.close()
316 if olddata == data:
317 self.old_files.remove(dst)
318 return 0
319 else:
320 result = 2
321
322 self.new_data[dst] = data
323 self.new_files.add(dst)
324 return result
325
326 def getChanges(self):
327 """determine changes, returns (adds, deletes, edits)"""
328 adds = set()
329 edits = set()
330 deletes = set()
331
332 for dst in self.new_files:
333 if not (dst in self.old_files):
334 adds.add(dst)
335 else:
336 edits.add(dst)
337
338 for dst in self.old_files:
339 if not dst in self.new_files:
340 deletes.add(dst)
341
342 return (adds, deletes, edits)
343
Elliott Hughesc95eb572013-01-29 18:15:55 -0800344 def _writeFile(self,dst):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800345 if not os.path.exists(os.path.dirname(dst)):
346 create_file_path(dst)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800347 f = open(dst, "w")
348 f.write(self.new_data[dst])
349 f.close()
350
351 def updateFiles(self):
352 adds, deletes, edits = self.getChanges()
353
354 for dst in sorted(adds):
355 self._writeFile(dst)
356
357 for dst in sorted(edits):
358 self._writeFile(dst)
359
360 for dst in sorted(deletes):
361 os.remove(dst)
362
David 'Digit' Turnerfc269312010-10-11 22:11:06 +0200363 def updateGitFiles(self):
364 adds, deletes, edits = self.getChanges()
365
366 if adds:
367 for dst in sorted(adds):
368 self._writeFile(dst)
369 commands.getoutput("git add " + " ".join(adds))
370
371 if deletes:
372 commands.getoutput("git rm " + " ".join(deletes))
373
374 if edits:
375 for dst in sorted(edits):
376 self._writeFile(dst)
377 commands.getoutput("git add " + " ".join(edits))