mingw-bundledlls 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #!/usr/bin/env python3
  2. # The MIT License (MIT)
  3. #
  4. # Copyright (c) 2015 Martin Preisler
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining a copy
  7. # of this software and associated documentation files (the "Software"), to deal
  8. # in the Software without restriction, including without limitation the rights
  9. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. # copies of the Software, and to permit persons to whom the Software is
  11. # furnished to do so, subject to the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be included in all
  14. # copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. # SOFTWARE.
  23. import subprocess
  24. import os.path
  25. import argparse
  26. import shutil
  27. # The mingw path matches where Fedora 21 installs mingw32; this is the default
  28. # fallback if no other search path is specified in $MINGW_BUNDLEDLLS_SEARCH_PATH
  29. DEFAULT_PATH_PREFIXES = [
  30. "", "/usr/bin", "/usr/i686-w64-mingw32/sys-root/mingw/bin", "/mingw64/bin",
  31. "/usr/i686-w64-mingw32/sys-root/mingw/lib",
  32. "C:\\msys64\\mingw64\\bin",
  33. "/opt/mxe/usr/i686-w64-mingw32.shared/lib",
  34. "/opt/mxe/usr/i686-w64-mingw32.shared/bin",
  35. "/home/alexander/bfgminer/libbase58/.libs",
  36. "/home/alexander/bfgminer/libblkmaker/.libs"
  37. ]
  38. env_path_prefixes = os.environ.get('MINGW_BUNDLEDLLS_SEARCH_PATH', None)
  39. if env_path_prefixes is not None:
  40. path_prefixes = [path for path in env_path_prefixes.split(os.pathsep) if path]
  41. else:
  42. path_prefixes = DEFAULT_PATH_PREFIXES
  43. # This blacklist may need extending
  44. blacklist = [
  45. "advapi32.dll", "kernel32.dll", "msvcrt.dll", "ole32.dll", "user32.dll",
  46. "ws2_32.dll", "comdlg32.dll", "gdi32.dll", "imm32.dll", "oleaut32.dll",
  47. "shell32.dll", "winmm.dll", "winspool.drv", "wldap32.dll",
  48. "ntdll.dll", "d3d9.dll", "mpr.dll", "crypt32.dll", "dnsapi.dll",
  49. "shlwapi.dll", "version.dll", "iphlpapi.dll", "msimg32.dll", "setupapi.dll",
  50. "opengl32.dll", "dwmapi.dll", "uxtheme.dll", "secur32.dll", "gdiplus.dll",
  51. "usp10.dll", "comctl32.dll", "wsock32.dll", "netapi32.dll", "userenv.dll",
  52. "avicap32.dll", "avrt.dll", "psapi.dll", "mswsock.dll", "glu32.dll",
  53. "bcrypt.dll", "rpcrt4.dll"
  54. ]
  55. def find_full_path(filename, path_prefixes):
  56. for path_prefix in path_prefixes:
  57. path = os.path.join(path_prefix, filename)
  58. path_low = os.path.join(path_prefix, filename.lower())
  59. if os.path.exists(path):
  60. return path
  61. if os.path.exists(path_low):
  62. return path_low
  63. else:
  64. raise RuntimeError(
  65. "Can't find " + filename + ". If it is an inbuilt Windows DLL, "
  66. "please add it to the blacklist variable in the script and send "
  67. "a pull request!"
  68. )
  69. def gather_deps(path, path_prefixes, seen):
  70. ret = [path]
  71. output = subprocess.check_output(["objdump", "-p", path]).decode(
  72. "utf-8", "replace").split("\n")
  73. for line in output:
  74. if not line.startswith("\tDLL Name: "):
  75. continue
  76. dep = line.split("DLL Name: ")[1].strip()
  77. ldep = dep.lower()
  78. if ldep in blacklist:
  79. continue
  80. if ldep in seen:
  81. continue
  82. dep_path = find_full_path(dep, path_prefixes)
  83. seen.add(ldep)
  84. subdeps = gather_deps(dep_path, path_prefixes, seen)
  85. ret.extend(subdeps)
  86. return ret
  87. def main():
  88. parser = argparse.ArgumentParser()
  89. parser.add_argument(
  90. "exe_file",
  91. help="EXE or DLL file that you need to bundle dependencies for"
  92. )
  93. parser.add_argument(
  94. "--copy",
  95. action="store_true",
  96. help="In addition to printing out the dependencies, also copy them next to the exe_file"
  97. )
  98. parser.add_argument(
  99. "--upx",
  100. action="store_true",
  101. help="Only valid if --copy is provided. Run UPX on all the DLLs and EXE."
  102. )
  103. args = parser.parse_args()
  104. if args.upx and not args.copy:
  105. raise RuntimeError("Can't run UPX if --copy hasn't been provided.")
  106. all_deps = set(gather_deps(args.exe_file, path_prefixes, set()))
  107. all_deps.remove(args.exe_file)
  108. print("\n".join(all_deps))
  109. if args.copy:
  110. print("Copying enabled, will now copy all dependencies next to the exe_file.\n")
  111. parent_dir = os.path.dirname(os.path.abspath(args.exe_file))
  112. for dep in all_deps:
  113. target = os.path.join(parent_dir, os.path.basename(dep))
  114. try:
  115. print("Copying '%s' to '%s'" % (dep, target))
  116. shutil.copy(dep, parent_dir)
  117. except shutil.SameFileError:
  118. print("Dependency '%s' was already in target directory, "
  119. "skipping..." % (dep))
  120. if args.upx:
  121. subprocess.call(["upx", target])
  122. if __name__ == "__main__":
  123. main()