xref: /qemu/scripts/nsis.py (revision 75ac231c)
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