diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2010-08-30 17:51:53 +0000 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2010-08-30 17:51:53 +0000 |
commit | 4756a77b47cc4a0c86315d8949468b347268c8eb (patch) | |
tree | 0d88c08afd0e5a1ad710d7922f41817cd09bd190 | |
parent | 811877e5b3ae95b70e285b786bb7cc9d73d333e0 (diff) | |
download | rockbox-4756a77b47cc4a0c86315d8949468b347268c8eb.tar.gz rockbox-4756a77b47cc4a0c86315d8949468b347268c8eb.zip |
Support resolving of DLLs when running on Windows.
Resolve the DLLs required by the built executable and try to add the required
DLL files that are not recognized as system libraries to the resulting zip /
NSIS installer. This means that it's now possible to easily build both Theme
Editor and Rockbox Utility as dynamically linked binary without the risk of
missing required DLLs in the package. The major advantage of this is that
it's not necessary anymore to have a statically built Qt installation for
building releases. The drawback is that the created binaries will rely on
additional DLL files, so it's no longer a single-run binary.
Binary release of Rockbox Utility should still be statically build.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27945 a1c6a512-1295-4272-9138-f99709370657
-rwxr-xr-x | utils/common/deploy.py | 110 |
1 files changed, 97 insertions, 13 deletions
diff --git a/utils/common/deploy.py b/utils/common/deploy.py index 662a104e34..9912bfc2ca 100755 --- a/utils/common/deploy.py +++ b/utils/common/deploy.py | |||
@@ -82,6 +82,25 @@ useupx = False | |||
82 | # OS X: files to copy into the bundle. Workaround for out-of-tree builds. | 82 | # OS X: files to copy into the bundle. Workaround for out-of-tree builds. |
83 | bundlecopy = { } | 83 | bundlecopy = { } |
84 | 84 | ||
85 | # DLL files to ignore when searching for required DLL files. | ||
86 | systemdlls = ['advapi32.dll', | ||
87 | 'comdlg32.dll', | ||
88 | 'gdi32.dll', | ||
89 | 'imm32.dll', | ||
90 | 'kernel32.dll', | ||
91 | 'msvcrt.dll', | ||
92 | 'msvcrt.dll', | ||
93 | 'netapi32.dll', | ||
94 | 'ole32.dll', | ||
95 | 'oleaut32.dll', | ||
96 | 'setupapi.dll', | ||
97 | 'shell32.dll', | ||
98 | 'user32.dll', | ||
99 | 'winmm.dll', | ||
100 | 'winspool.drv', | ||
101 | 'ws2_32.dll'] | ||
102 | |||
103 | |||
85 | # == Functions == | 104 | # == Functions == |
86 | def usage(myself): | 105 | def usage(myself): |
87 | print "Usage: %s [options]" % myself | 106 | print "Usage: %s [options]" % myself |
@@ -238,19 +257,22 @@ def upxfile(wd="."): | |||
238 | return 0 | 257 | return 0 |
239 | 258 | ||
240 | 259 | ||
241 | def runnsis(versionstring, nsis, srcfolder): | 260 | def runnsis(versionstring, nsis, script, srcfolder): |
242 | # run script through nsis to create installer. | 261 | # run script through nsis to create installer. |
243 | print "Running NSIS ..." | 262 | print "Running NSIS ..." |
263 | |||
244 | # Assume the generated installer gets placed in the same folder the nsi | 264 | # Assume the generated installer gets placed in the same folder the nsi |
245 | # script lives in. This seems to be a valid assumption unless the nsi | 265 | # script lives in. This seems to be a valid assumption unless the nsi |
246 | # script specifies a path. NSIS expects files relative to source folder so | 266 | # script specifies a path. NSIS expects files relative to source folder so |
247 | # copy the relevant binaries. | 267 | # copy progexe. Additional files are injected into the nsis script. |
248 | for f in programfiles: | 268 | |
249 | b = srcfolder + "/" + os.path.dirname(nsisscript) + "/" + os.path.dirname(f) | 269 | # FIXME: instead of copying binaries around copy the NSI file and inject |
250 | if not os.path.exists(b): | 270 | # the correct paths. |
251 | os.mkdir(b) | 271 | b = srcfolder + "/" + os.path.dirname(script) + "/" + os.path.dirname(progexe) |
252 | shutil.copy(srcfolder + "/" + f, b) | 272 | if not os.path.exists(b): |
253 | output = subprocess.Popen([nsis, srcfolder + "/" + nsisscript], stdout=subprocess.PIPE) | 273 | os.mkdir(b) |
274 | shutil.copy(srcfolder + "/" + progexe, b) | ||
275 | output = subprocess.Popen([nsis, srcfolder + "/" + script], stdout=subprocess.PIPE) | ||
254 | output.communicate() | 276 | output.communicate() |
255 | if not output.returncode == 0: | 277 | if not output.returncode == 0: |
256 | print "NSIS failed!" | 278 | print "NSIS failed!" |
@@ -258,16 +280,70 @@ def runnsis(versionstring, nsis, srcfolder): | |||
258 | setupfile = program + "-" + versionstring + "-setup.exe" | 280 | setupfile = program + "-" + versionstring + "-setup.exe" |
259 | # find output filename in nsis script file | 281 | # find output filename in nsis script file |
260 | nsissetup = "" | 282 | nsissetup = "" |
261 | for line in open(srcfolder + "/" + nsisscript): | 283 | for line in open(srcfolder + "/" + script): |
262 | if re.match(r'^[^;]*OutFile\s+', line) != None: | 284 | if re.match(r'^[^;]*OutFile\s+', line) != None: |
263 | nsissetup = re.sub(r'^[^;]*OutFile\s+"(.+)"', r'\1', line).rstrip() | 285 | nsissetup = re.sub(r'^[^;]*OutFile\s+"(.+)"', r'\1', line).rstrip() |
264 | if nsissetup == "": | 286 | if nsissetup == "": |
265 | print "Could not retrieve output file name!" | 287 | print "Could not retrieve output file name!" |
266 | return -1 | 288 | return -1 |
267 | shutil.copy(srcfolder + "/" + os.path.dirname(nsisscript) + "/" + nsissetup, setupfile) | 289 | shutil.copy(srcfolder + "/" + os.path.dirname(script) + "/" + nsissetup, setupfile) |
268 | return 0 | 290 | return 0 |
269 | 291 | ||
270 | 292 | ||
293 | def nsisfileinject(nsis, outscript, filelist): | ||
294 | '''Inject files in filelist into NSIS script file after the File line | ||
295 | containing the main binary. This assumes that the main binary is present | ||
296 | in the NSIS script and that all additiona files (dlls etc) to get placed | ||
297 | into $INSTDIR.''' | ||
298 | output = open(outscript, "w") | ||
299 | for line in open(nsis, "r"): | ||
300 | output.write(line) | ||
301 | # inject files after the progexe binary. Match the basename only to avoid path mismatches. | ||
302 | if re.match(r'^\s*File\s*.*' + os.path.basename(progexe), line, re.IGNORECASE): | ||
303 | for f in filelist: | ||
304 | injection = " File /oname=$INSTDIR\\" + os.path.basename(f) + " " + os.path.normcase(f) + "\n" | ||
305 | output.write(injection) | ||
306 | output.write(" ; end of injected files\n") | ||
307 | output.close() | ||
308 | |||
309 | |||
310 | def finddlls(program, extrapaths = []): | ||
311 | '''Check program for required DLLs. Find all required DLLs except ignored | ||
312 | ones and return a list of DLL filenames (including path).''' | ||
313 | # ask objdump about dependencies. | ||
314 | output = subprocess.Popen(["objdump", "-x", program], stdout=subprocess.PIPE) | ||
315 | cmdout = output.communicate() | ||
316 | |||
317 | # create list of used DLLs. Store as lower case as W32 is case-insensitive. | ||
318 | dlls = [] | ||
319 | for line in cmdout[0].split('\n'): | ||
320 | if re.match(r'\s*DLL Name', line) != None: | ||
321 | dll = re.sub(r'^\s*DLL Name:\s+([a-zA-Z_\-0-9\.]+).*$', r'\1', line) | ||
322 | dlls.append(dll.lower()) | ||
323 | |||
324 | # find DLLs in extrapaths and PATH environment variable. | ||
325 | dllpaths = [] | ||
326 | for file in dlls: | ||
327 | if file in systemdlls: | ||
328 | print file + ": System DLL" | ||
329 | continue | ||
330 | dllpath = "" | ||
331 | for path in extrapaths: | ||
332 | if os.path.exists(path + "/" + file): | ||
333 | dllpath = re.sub(r"\\", r"/", path + "/" + file) | ||
334 | print file + ": found at " + dllpath | ||
335 | dllpaths.append(dllpath) | ||
336 | break | ||
337 | if dllpath == "": | ||
338 | try: | ||
339 | dllpath = re.sub(r"\\", r"/", which.which(file)) | ||
340 | print file + ": found at " + dllpath | ||
341 | dllpaths.append(dllpath) | ||
342 | except: | ||
343 | print file + ": NOT FOUND." | ||
344 | return dllpaths | ||
345 | |||
346 | |||
271 | def zipball(versionstring, buildfolder): | 347 | def zipball(versionstring, buildfolder): |
272 | '''package created binary''' | 348 | '''package created binary''' |
273 | print "Creating binary zipball." | 349 | print "Creating binary zipball." |
@@ -278,7 +354,10 @@ def zipball(versionstring, buildfolder): | |||
278 | os.mkdir(outfolder) | 354 | os.mkdir(outfolder) |
279 | # move program files to output folder | 355 | # move program files to output folder |
280 | for f in programfiles: | 356 | for f in programfiles: |
281 | shutil.copy(buildfolder + "/" + f, outfolder) | 357 | if re.match(r'^(/|[a-zA-Z]:)', f) != None: |
358 | shutil.copy(f, outfolder) | ||
359 | else: | ||
360 | shutil.copy(buildfolder + "/" + f, outfolder) | ||
282 | # create zipball from output folder | 361 | # create zipball from output folder |
283 | zf = zipfile.ZipFile(archivename, mode='w', compression=zipfile.ZIP_DEFLATED) | 362 | zf = zipfile.ZipFile(archivename, mode='w', compression=zipfile.ZIP_DEFLATED) |
284 | for root, dirs, files in os.walk(outfolder): | 363 | for root, dirs, files in os.walk(outfolder): |
@@ -503,15 +582,20 @@ def deploy(): | |||
503 | if not upxfile(sourcefolder) == 0: | 582 | if not upxfile(sourcefolder) == 0: |
504 | tempclean(workfolder, cleanup and not keeptemp) | 583 | tempclean(workfolder, cleanup and not keeptemp) |
505 | sys.exit(1) | 584 | sys.exit(1) |
585 | dllfiles = finddlls(sourcefolder + "/" + progexe, [os.path.dirname(qm)]) | ||
586 | if dllfiles.count > 0: | ||
587 | programfiles.extend(dllfiles) | ||
506 | archive = zipball(ver, sourcefolder) | 588 | archive = zipball(ver, sourcefolder) |
589 | # only when running native right now. | ||
590 | if nsisscript != "" and makensis != "": | ||
591 | nsisfileinject(sourcefolder + "/" + nsisscript, sourcefolder + "/" + nsisscript + ".tmp", dllfiles) | ||
592 | runnsis(ver, makensis, nsisscript + ".tmp", sourcefolder) | ||
507 | elif sys.platform == "darwin": | 593 | elif sys.platform == "darwin": |
508 | archive = macdeploy(ver, sourcefolder) | 594 | archive = macdeploy(ver, sourcefolder) |
509 | else: | 595 | else: |
510 | if os.uname()[4].endswith("64"): | 596 | if os.uname()[4].endswith("64"): |
511 | ver += "-64bit" | 597 | ver += "-64bit" |
512 | archive = tarball(ver, sourcefolder) | 598 | archive = tarball(ver, sourcefolder) |
513 | if nsisscript != "" and makensis != "": | ||
514 | runnsis(ver, makensis, sourcefolder) | ||
515 | 599 | ||
516 | # remove temporary files | 600 | # remove temporary files |
517 | tempclean(workfolder, cleanup) | 601 | tempclean(workfolder, cleanup) |