Add credits for Marek
[trojita.git] / packaging / mingw-bundledlls
blob8d6005aa58ffe63bd03307bb791e1184704dc088
1 #!/usr/bin/python3
3 # The MIT License (MIT)
5 # Copyright (c) 2015 Martin Preisler
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in all
15 # copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 # SOFTWARE.
26 import subprocess
27 import os.path
28 import argparse
29 import shutil
31 # The mingw path matches where Fedora 21 installs mingw32
32 default_path_prefixes = [
33     "", "/usr/bin", "/usr/i686-w64-mingw32/sys-root/mingw/bin", "/mingw64/bin",
36 # This blacklist may need extending
37 blacklist = [
38     "advapi32.dll", "kernel32.dll", "msvcrt.dll", "ole32.dll", "user32.dll",
39     "ws2_32.dll", "comdlg32.dll", "gdi32.dll", "imm32.dll", "oleaut32.dll",
40     "shell32.dll", "winmm.dll", "winspool.drv", "wldap32.dll",
41     "ntdll.dll", "d3d9.dll", "mpr.dll", "crypt32.dll", "dnsapi.dll",
42     "shlwapi.dll", "version.dll", "iphlpapi.dll",
46 def find_full_path(filename, path_prefixes):
47     path = None
49     for path_prefix in path_prefixes:
50         path_candidate = os.path.join(path_prefix, filename)
52         if os.path.exists(path_candidate):
53             path = path_candidate
54             break
56     if path is None:
57         raise RuntimeError(
58             "Can't find " + filename + ". If it is an inbuilt Windows DLL, "
59             "please add it to the blacklist variable in the script and send "
60             "a pull request!"
61         )
63     return path
66 def gather_deps(path, path_prefixes, seen):
67     ret = [path]
68     output = subprocess.check_output(["objdump", "-p", path]).decode('utf-8').split("\n")
69     for line in output:
70         if not line.startswith("\tDLL Name: "):
71             continue
73         dep = line.split("DLL Name: ")[1].strip()
74         ldep = dep.lower()
76         if ldep in blacklist:
77             continue
79         if ldep in seen:
80             continue
82         dep_path = find_full_path(dep, path_prefixes)
83         seen.extend([ldep])
84         subdeps = gather_deps(dep_path, path_prefixes, seen)
85         ret.extend(subdeps)
87     return ret
90 def main():
91     parser = argparse.ArgumentParser()
92     parser.add_argument(
93         "exe_file",
94         nargs='+',
95         help="EXE or DLL file that you need to bundle dependencies for"
96     )
97     parser.add_argument(
98         "--copy",
99         action="store_true",
100         help="In addition to printing out the dependencies, also copy them next to the exe_file"
101     )
102     parser.add_argument(
103         "--upx",
104         action="store_true",
105         help="Only valid if --copy is provided. Run UPX on all the DLLs and EXE."
106     )
107     args = parser.parse_args()
109     if args.upx and not args.copy:
110         raise RuntimeError("Can't run UPX if --copy hasn't been provided.")
112     seen = []
113     all_deps = []
114     for exe in args.exe_file:
115         all_deps.extend(set(gather_deps(exe, default_path_prefixes, seen)))
116     for exe in args.exe_file:
117         all_deps.remove(exe)
119     print("\n".join(all_deps))
121     if args.copy:
122         print("Copying enabled, will now copy all dependencies next to the exe_file.\n")
124         parent_dir = os.path.dirname(os.path.abspath(args.exe_file))
126         for dep in all_deps:
127             target = os.path.join(parent_dir, os.path.basename(dep))
128             print("Copying '%s' to '%s'" % (dep, target))
129             shutil.copy(dep, parent_dir)
131             if args.upx:
132                 subprocess.call(["upx", target])
135 if __name__ == "__main__":
136     main()