1#!/usr/bin/env python3 2# 3# Copyright (C) 2020 Red Hat, Inc. 4# 5# SPDX-License-Identifier: GPL-2.0-or-later 6 7import argparse 8import glob 9import os 10import shutil 11import subprocess 12import tempfile 13 14 15def signcode(path): 16 cmd = os.environ.get("SIGNCODE") 17 if not cmd: 18 return 19 subprocess.run([cmd, path]) 20 21def find_deps(exe_or_dll, search_path, analyzed_deps): 22 deps = [exe_or_dll] 23 output = subprocess.check_output(["objdump", "-p", exe_or_dll], text=True) 24 output = output.split("\n") 25 for line in output: 26 if not line.startswith("\tDLL Name: "): 27 continue 28 29 dep = line.split("DLL Name: ")[1].strip() 30 if dep in analyzed_deps: 31 continue 32 33 dll = os.path.join(search_path, dep) 34 if not os.path.exists(dll): 35 # assume it's a Windows provided dll, skip it 36 continue 37 38 analyzed_deps.add(dep) 39 # locate the dll dependencies recursively 40 rdeps = find_deps(dll, search_path, analyzed_deps) 41 deps.extend(rdeps) 42 43 return deps 44 45def main(): 46 parser = argparse.ArgumentParser(description="QEMU NSIS build helper.") 47 parser.add_argument("outfile") 48 parser.add_argument("prefix") 49 parser.add_argument("srcdir") 50 parser.add_argument("dlldir") 51 parser.add_argument("cpu") 52 parser.add_argument("nsisargs", nargs="*") 53 args = parser.parse_args() 54 55 # canonicalize the Windows native prefix path 56 prefix = os.path.splitdrive(args.prefix)[1] 57 destdir = tempfile.mkdtemp() 58 try: 59 subprocess.run(["make", "install", "DESTDIR=" + destdir]) 60 with open( 61 os.path.join(destdir + prefix, "system-emulations.nsh"), "w" 62 ) as nsh, open( 63 os.path.join(destdir + prefix, "system-mui-text.nsh"), "w" 64 ) as muinsh: 65 for exe in sorted(glob.glob( 66 os.path.join(destdir + prefix, "qemu-system-*.exe") 67 )): 68 exe = os.path.basename(exe) 69 arch = exe[12:-4] 70 nsh.write( 71 """ 72 Section "{0}" Section_{0} 73 SetOutPath "$INSTDIR" 74 File "${{BINDIR}}\\{1}" 75 SectionEnd 76 """.format( 77 arch, exe 78 ) 79 ) 80 if arch.endswith('w'): 81 desc = arch[:-1] + " emulation (GUI)." 82 else: 83 desc = arch + " emulation." 84 85 muinsh.write( 86 """ 87 !insertmacro MUI_DESCRIPTION_TEXT ${{Section_{0}}} "{1}" 88 """.format(arch, desc)) 89 90 search_path = args.dlldir 91 print("Searching '%s' for the dependent dlls ..." % search_path) 92 dlldir = os.path.join(destdir + prefix, "dll") 93 os.mkdir(dlldir) 94 95 for exe in glob.glob(os.path.join(destdir + prefix, "*.exe")): 96 signcode(exe) 97 98 # find all dll dependencies 99 deps = set(find_deps(exe, search_path, set())) 100 deps.remove(exe) 101 102 # copy all dlls to the DLLDIR 103 for dep in deps: 104 dllfile = os.path.join(dlldir, os.path.basename(dep)) 105 if (os.path.exists(dllfile)): 106 continue 107 print("Copying '%s' to '%s'" % (dep, dllfile)) 108 shutil.copy(dep, dllfile) 109 110 makensis = [ 111 "makensis", 112 "-V2", 113 "-NOCD", 114 "-DSRCDIR=" + args.srcdir, 115 "-DBINDIR=" + destdir + prefix, 116 ] 117 if args.cpu == "x86_64": 118 makensis += ["-DW64"] 119 makensis += ["-DDLLDIR=" + dlldir] 120 121 makensis += ["-DOUTFILE=" + args.outfile] + args.nsisargs 122 subprocess.run(makensis) 123 signcode(args.outfile) 124 finally: 125 shutil.rmtree(destdir) 126 127 128if __name__ == "__main__": 129 main() 130