|
1 # |
|
2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # |
|
16 #! /usr/bin/python |
|
17 |
|
18 import sys |
|
19 import traceback |
|
20 import os |
|
21 import os.path |
|
22 import sys |
|
23 #import subprocess |
|
24 import shutil |
|
25 import re |
|
26 import types |
|
27 import zipfile |
|
28 |
|
29 from os.path import abspath |
|
30 |
|
31 class BuildError(Exception): |
|
32 pass |
|
33 |
|
34 def main(): |
|
35 tmpExtension = ".cpp" |
|
36 try: |
|
37 args = sys.argv[1:] |
|
38 |
|
39 # Get macro definitios |
|
40 macros = [a[2:] for a in args if a.startswith("-D")] |
|
41 args = [a for a in args if not a.startswith("-D")] |
|
42 |
|
43 opcode = args[0] |
|
44 fileName = args[1] |
|
45 |
|
46 exports = readExports(fileName, fileName+tmpExtension, macros) |
|
47 for src, dst in exports: |
|
48 isZipFile = False |
|
49 if src.startswith("___zip___"): |
|
50 isZipFile = True |
|
51 src = src[9:] |
|
52 |
|
53 if not os.path.exists(src): |
|
54 print "Exported file "+src+" doesn't exist" |
|
55 else: |
|
56 if opcode.lower() == "clean": |
|
57 if isZipFile: |
|
58 handleZipCommand(src, dst, doClean = True) |
|
59 else: |
|
60 delFile(dst) |
|
61 else: |
|
62 if isZipFile: |
|
63 handleZipCommand(src, dst, doClean = False) |
|
64 else: |
|
65 updateFile(dst, src) |
|
66 except: |
|
67 print "Error during export " |
|
68 traceback.print_exc() |
|
69 sys.exit(-1) |
|
70 |
|
71 delFile(fileName+tmpExtension, False) |
|
72 |
|
73 def readExports(fileName, cppedFile, macros): |
|
74 |
|
75 if not os.path.exists(fileName): |
|
76 print "No %s, nothing to export" % fileName |
|
77 return () |
|
78 |
|
79 # Preprocess bld.inf |
|
80 |
|
81 args = [ |
|
82 '-undef', |
|
83 '-nostdinc'] |
|
84 |
|
85 if os.name=="nt": |
|
86 args = [ |
|
87 '-undef', |
|
88 '-traditional', |
|
89 '-+', |
|
90 '-nostdinc'] |
|
91 |
|
92 cppFile( |
|
93 fileName, |
|
94 cppedFile, |
|
95 args, |
|
96 macros) |
|
97 |
|
98 lines = readFile(cppedFile) |
|
99 |
|
100 EXPORTS = {} |
|
101 |
|
102 bldInf = {} |
|
103 |
|
104 # Keeps track of which inf-file's contents inside the preprocessed |
|
105 # bld.inf-file are being processed |
|
106 infFile = None |
|
107 |
|
108 section = None |
|
109 |
|
110 # Read in bld.inf contents |
|
111 for line in [l.strip() for l in lines if len(l.strip()) > 0]: |
|
112 if line.startswith("#"): |
|
113 # Comment line indicates which inf-file any subsequent |
|
114 # lines were included from |
|
115 |
|
116 # Parse inf-file name |
|
117 infFile = re.search(r'^# \d+ "(.+)"', line).group(1) |
|
118 |
|
119 # Translate backslashes to slashes, remove dupes |
|
120 infFile = abspath(re.sub(r"\\+", "/", infFile)) |
|
121 elif line.lower().startswith("prj_"): |
|
122 section = line.lower()[4:] |
|
123 else: |
|
124 if not section: |
|
125 raise BuildError, \ |
|
126 "Data outside of data section in bld.inf: %s" % line |
|
127 |
|
128 # Collect lines by section (prj_exports etc), each entry is a |
|
129 # tuple of the actual data line and the name of the inf-file |
|
130 # where it originated from |
|
131 bldInf.setdefault( |
|
132 section, []).append((line.strip(), infFile)) |
|
133 |
|
134 # Collect exports to a separate list |
|
135 for export, infFile in bldInf.get("exports", []): |
|
136 isZipFile = False |
|
137 if export.strip().lower().startswith(":zip"): |
|
138 isZipFile = True |
|
139 export = export[5:] |
|
140 try: |
|
141 src, dst = export.split() |
|
142 except: |
|
143 raise BuildError, \ |
|
144 'Malformed line "%s" in %s' % (export, infFile) |
|
145 |
|
146 # Map source path through current inf file path |
|
147 if not src.startswith("\\") and not src.startswith("/"): |
|
148 src = os.path.join(os.path.split(infFile)[0], src) |
|
149 |
|
150 if dst.startswith("\\") or dst.startswith("/"): |
|
151 # Destination path is absolute, use it as is |
|
152 pass |
|
153 elif dst.startswith("|"): |
|
154 # Destination path is relative to the inf-file it was specified |
|
155 # in |
|
156 dst = os.path.join(os.path.dirname(infFile), dst[1:]) |
|
157 |
|
158 src, dst = map(abspath, [src, dst]) |
|
159 if isZipFile: |
|
160 src = "___zip___"+src |
|
161 EXPORTS[(src, dst)] = None |
|
162 |
|
163 return EXPORTS.keys() |
|
164 |
|
165 def handleZipCommand(zipfl, dirToUnzip, doClean = False): |
|
166 zfobj = zipfile.ZipFile(zipfl) |
|
167 if doClean: |
|
168 print("Deleting files extracted from %s:" % (zipfl)) |
|
169 else: |
|
170 print("Extracting files from %s:" % (zipfl)) |
|
171 for name in zfobj.namelist(): |
|
172 #Not handling cases where zip file contains empty dirs |
|
173 if not name.endswith('/'): |
|
174 print(" " + os.path.join(dirToUnzip,name)) |
|
175 destFile = os.path.join(dirToUnzip, name) |
|
176 if doClean: |
|
177 delFile(destFile, doPrint = False) |
|
178 else: |
|
179 makeDirs(destFile, isFile = True) |
|
180 if uptodate(destFile, zipfl): |
|
181 print(" Already extracted...") |
|
182 continue |
|
183 outfile = open(destFile, 'wb') |
|
184 outfile.write(zfobj.read(name)) |
|
185 outfile.close() |
|
186 |
|
187 |
|
188 |
|
189 def updateFile(dst, src): |
|
190 if not uptodate(dst, src): |
|
191 print("Copying %s to %s" % (src, dst)) |
|
192 shutil.copy(src, dst) |
|
193 |
|
194 def oldestTime(files): |
|
195 oldestTime = None |
|
196 oldestFile = None |
|
197 try: |
|
198 for f in files: |
|
199 fileTime = os.path.getmtime(f) |
|
200 if oldestTime == None or fileTime < oldestTime: |
|
201 oldestTime = fileTime |
|
202 oldestFile = f |
|
203 except: |
|
204 # One or more files don't exist |
|
205 return 0 |
|
206 |
|
207 #print "oldest file is %s with %d" % (oldestFile, oldestTime) |
|
208 return oldestTime |
|
209 |
|
210 def newestTime(files): |
|
211 newest = 0 |
|
212 newestFile = None |
|
213 |
|
214 for f in files: |
|
215 try: |
|
216 fileTime = os.path.getmtime(f) |
|
217 except: |
|
218 print("Expected to find file " + f) |
|
219 else: |
|
220 if fileTime > newest: |
|
221 newestFile = f |
|
222 newest = fileTime |
|
223 |
|
224 #print "newest file is %s with %d" % (newestFile, newest) |
|
225 return newest |
|
226 |
|
227 def uptodate(targets, sources): |
|
228 |
|
229 # Make non-list arguments lists |
|
230 if not isinstance(targets, list): |
|
231 targets = [targets] |
|
232 if not isinstance(sources, list): |
|
233 sources = [sources] |
|
234 |
|
235 if oldestTime(targets) < newestTime(sources): |
|
236 # One or more target files don't exist or are older than one or |
|
237 # more source files, rebuild is needed |
|
238 result = False |
|
239 else: |
|
240 # Rebuild is not needed |
|
241 result = True |
|
242 |
|
243 |
|
244 # Create missing directories |
|
245 if result == False: |
|
246 for f in targets: |
|
247 dirName = os.path.dirname(f) |
|
248 if not os.path.exists(dirName): |
|
249 os.makedirs(dirName) |
|
250 |
|
251 return result |
|
252 |
|
253 |
|
254 def cppFile( |
|
255 srcFile, |
|
256 dstFile, |
|
257 args, |
|
258 macros = []): |
|
259 |
|
260 """ Preprocesses a file with CPP using given arguments. |
|
261 |
|
262 By default dstFile is altered only if running CPP would change the file's |
|
263 contents. |
|
264 |
|
265 Returns true if dstFile was altered. |
|
266 """ |
|
267 |
|
268 # Target file doesn't exist or preprocessing was forced |
|
269 |
|
270 # Create any missing directories for destination file |
|
271 makeDirs(dstFile, isFile = True) |
|
272 |
|
273 for m in macros: |
|
274 if len(m) == 0: |
|
275 raise BuildError, "empty macro definition" |
|
276 if m == "NOKIA_SYMBIAN_BLD": |
|
277 args.extend([ |
|
278 "-D__GNUC__=4", |
|
279 "-I " + abspath("\\epoc32\\include\\"), |
|
280 "-imacros " + abspath("\\epoc32\\include\\platform_paths.hrh")]) |
|
281 |
|
282 cmd = ['cpp'] + \ |
|
283 args + \ |
|
284 ["-D" + m for m in macros] +\ |
|
285 [srcFile, \ |
|
286 dstFile] |
|
287 |
|
288 print " ".join(cmd) |
|
289 |
|
290 execute(cmd, False) |
|
291 return True |
|
292 |
|
293 |
|
294 def makeDirs(directory, isFile = False): |
|
295 """ Creates a directory if it doesn't exist. |
|
296 |
|
297 Doesn't do anything if the directory already exists. |
|
298 |
|
299 If isFile is True, the last element of the directory path is assumed to be |
|
300 a file and is discarded from the path to be created. """ |
|
301 |
|
302 if isFile: |
|
303 directory = os.path.dirname(directory) |
|
304 |
|
305 try: |
|
306 os.makedirs(directory) |
|
307 except: |
|
308 pass |
|
309 |
|
310 def readFile(f): |
|
311 """ Reads contents of a file to a list and returns it. """ |
|
312 |
|
313 f = open(f, 'r') |
|
314 contents = f.readlines() |
|
315 f.close() |
|
316 |
|
317 return contents |
|
318 |
|
319 def delFile(file, doPrint = True): |
|
320 """ Deletes a file. |
|
321 |
|
322 Fails if the file appears to be a directory. """ |
|
323 |
|
324 if os.path.exists(file): |
|
325 if os.path.isfile(file): |
|
326 if doPrint: |
|
327 print("Deleting file " + file) |
|
328 os.remove(file) |
|
329 else: |
|
330 print('Can not delete file "%s", is a directory' % (file)) |
|
331 |
|
332 def execute( |
|
333 cmd, |
|
334 printMsg = True, |
|
335 haltOnError = True, |
|
336 input = None, |
|
337 grabOutput = False, |
|
338 *args, |
|
339 **kwd): |
|
340 """ Executes external command, keeps track of time spent outside. |
|
341 |
|
342 Any arguments are passed directly to subprocess.call(). """ |
|
343 |
|
344 |
|
345 if printMsg: |
|
346 # Construct string to be displayed on screen |
|
347 msg = "" |
|
348 if kwd.has_key('cwd'): |
|
349 msg = "[" + kwd['cwd'] + "] " |
|
350 if type(cmd) == types.StringType: |
|
351 msg += cmd |
|
352 elif type(cmd) == types.ListType: |
|
353 msg += " ".join(cmd) |
|
354 else: |
|
355 raise Error, "Invalid type %s for cmd argument" % type(cmd) |
|
356 print msg |
|
357 |
|
358 kwd['args'] = cmd |
|
359 |
|
360 |
|
361 """ |
|
362 if input: |
|
363 # Input data for command was specified, pipe it |
|
364 kwd['stdin'] = subprocess.PIPE |
|
365 if grabOutput: |
|
366 # Command output should be grabbed |
|
367 kwd['stdout'] = subprocess.PIPE |
|
368 kwd['stderr'] = subprocess.PIPE |
|
369 |
|
370 process = subprocess.Popen(*args, **kwd) |
|
371 |
|
372 if input or grabOutput: |
|
373 stdout, stderr = process.communicate(input) |
|
374 else: |
|
375 process.wait() |
|
376 |
|
377 result = process.returncode |
|
378 """ |
|
379 cmd2 = " ".join(cmd) |
|
380 result = os.system(" ".join(cmd)) |
|
381 if not result == 0 and haltOnError == True: |
|
382 print 'Error running external command "' + \ |
|
383 (type(cmd) == types.StringType and cmd or ' '.join(cmd)) + \ |
|
384 '", return code ' + str(result) |
|
385 |
|
386 return result |
|
387 |
|
388 |
|
389 if __name__ == "__main__": |
|
390 main() |