buildwin32.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import shutil
  2. import os
  3. import subprocess
  4. import sys
  5. from modulefinder import ModuleFinder
  6. import zipfile
  7. # Creates standalone Windows executable
  8. # First build by following instructions from installation.rst
  9. builddir = 'win32exe'
  10. pythonversion = str(sys.version_info[0]) + '.' + str(sys.version_info[1])
  11. if os.path.exists(builddir):
  12. shutil.rmtree(builddir)
  13. os.mkdir(builddir)
  14. os.mkdir(builddir + '/bin')
  15. library = zipfile.PyZipFile(os.path.join(builddir, 'library.zip'), mode='w')
  16. print('Compiling wrapper')
  17. gccpath = '' # check for compiler, path needed later
  18. for p in os.environ['PATH'].split(';'):
  19. if os.path.exists(os.path.join(p, 'g++.exe')):
  20. gccpath = p
  21. break
  22. if gccpath == '':
  23. print('g++ not found.')
  24. exit(1)
  25. source = open('wrapper.c', 'w')
  26. source.write(
  27. """
  28. #include <python""" + pythonversion + """m/python.h>
  29. #include <windows.h>
  30. #include <wchar.h>
  31. #include <string>
  32. #include "Shlwapi.h"
  33. int wmain(int argc , wchar_t *argv[] )
  34. {
  35. wchar_t path[MAX_PATH];
  36. GetModuleFileNameW(NULL, path, MAX_PATH);
  37. PathRemoveFileSpecW(path);
  38. std::wstring selfpath(path);
  39. std::wstring libpath = selfpath + L"/library.zip;" + selfpath + L"/bin";
  40. SetDllDirectoryW(path);
  41. Py_SetPath(libpath.c_str());
  42. Py_SetProgramName(argv[0]);
  43. Py_Initialize();
  44. PySys_SetArgv(argc, argv);
  45. PyImport_ImportModule("encodings.idna");
  46. PyRun_SimpleString("from runpy import run_module\\n"
  47. "run_module('borg')");
  48. Py_Finalize();
  49. return 0;
  50. }
  51. """)
  52. source.close()
  53. subprocess.check_call('g++ wrapper.c -lpython' + pythonversion + 'm -lshlwapi -municode -o ' + builddir + '/borg.exe')
  54. os.remove('wrapper.c')
  55. print('Searching modules')
  56. modulepath = os.path.abspath(os.path.join(gccpath, '../lib/python' + pythonversion + '/'))
  57. # Bundle all encodings - In theory user may use any encoding in command prompt
  58. for file in os.listdir(os.path.join(modulepath, 'encodings')):
  59. if os.path.isfile(os.path.join(modulepath, 'encodings', file)):
  60. library.write(os.path.join(modulepath, 'encodings', file), os.path.join('encodings', file))
  61. finder = ModuleFinder()
  62. finder.run_script('src/borg/__main__.py')
  63. # For some reason modulefinder does not find these, add them manually
  64. extramodules = [os.path.join(modulepath, 'site.py'), os.path.join(modulepath, 'encodings/idna.py'),
  65. os.path.join(modulepath, 'stringprep.py'), os.path.join(modulepath, 'ctypes/wintypes.py'),
  66. os.path.join(modulepath, 'lib-dynload/_sysconfigdata_m_win32_.py')]
  67. for module in extramodules:
  68. finder.run_script(module)
  69. print('Copying files')
  70. def finddlls(exe):
  71. re = []
  72. output = subprocess.check_output(['ntldd', '-R', exe])
  73. for line in output.decode('utf-8').split('\n'):
  74. if 'not found' in line:
  75. continue
  76. if 'windows' in line.lower():
  77. continue
  78. words = line.split()
  79. if len(words) < 3:
  80. if len(words) == 2:
  81. re.append(words[0])
  82. continue
  83. dll = words[2]
  84. re.append(dll)
  85. return re
  86. items = finder.modules.items()
  87. for name, mod in items:
  88. file = mod.__file__
  89. if file is None:
  90. continue
  91. lib = file.find('lib')
  92. if lib == -1:
  93. # Part of the borg package
  94. relpath = os.path.relpath(file)[4:]
  95. os.makedirs(os.path.join(builddir, 'bin', os.path.split(relpath)[0]), exist_ok=True)
  96. shutil.copyfile(file, os.path.join(builddir, 'bin', relpath))
  97. else:
  98. relativepath = file[file.find('lib')+len('lib/python' + pythonversion + '/'):]
  99. if 'encodings' in file:
  100. continue
  101. if relativepath not in library.namelist():
  102. if relativepath.startswith('site-packages'):
  103. relativepath = relativepath[len('site-packages/'):]
  104. library.write(file, relativepath)
  105. if file.endswith(('dll', 'DLL')):
  106. shutil.copyfile(file, os.path.join(builddir, 'bin', os.path.split(file)[1]))
  107. for dll in finddlls(file):
  108. if builddir not in dll:
  109. shutil.copyfile(dll, os.path.join(builddir, os.path.split(dll)[1]))
  110. for dll in finddlls(os.path.join(builddir, "borg.exe")):
  111. if builddir not in dll:
  112. shutil.copyfile(dll, os.path.join(builddir, os.path.split(dll)[1]))
  113. shutil.copyfile(os.path.join('src', 'borg', '__main__.py'), os.path.join(builddir, 'bin', 'borg', '__main__.py'))
  114. library.write(os.path.join(modulepath, 'lib-dynload/_sysconfigdata_m_win32_.py'), '_sysconfigdata_m_win32_.py')
  115. library.write(os.path.join(modulepath, 'ctypes/wintypes.py'), 'ctypes/wintypes.py')
  116. for extmodule in ['src/borg/chunker-cpython-' + str(sys.version_info[0]) + str(sys.version_info[1]) + 'm.dll',
  117. 'src/borg/compress-cpython-' + str(sys.version_info[0]) + str(sys.version_info[1]) + 'm.dll',
  118. 'src/borg/item-cpython-' + str(sys.version_info[0]) + str(sys.version_info[1]) + 'm.dll',
  119. 'src/borg/hashindex-cpython-' + str(sys.version_info[0]) + str(sys.version_info[1]) + 'm.dll']:
  120. for dll in finddlls(extmodule):
  121. if builddir not in dll:
  122. shutil.copyfile(dll, os.path.join(builddir, os.path.split(dll)[1]))