__init__.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import logging
  2. import os
  3. import shutil
  4. from subprocess import check_output, call
  5. from urllib.request import urlretrieve
  6. from .copymodules import copy_modules
  7. pjoin = os.path.join
  8. logger = logging.getLogger(__name__)
  9. _PKGDIR = os.path.dirname(__file__)
  10. DEFAULT_PY_VERSION = '3.3.2'
  11. DEFAULT_BUILD_DIR = pjoin('build', 'nsis')
  12. DEFAULT_ICON = pjoin(_PKGDIR, 'glossyorb.ico')
  13. DEFAULT_INSTALLER_NAME = 'pynsis_installer.exe'
  14. if os.name == 'nt' and sys.maxsize == (2**63)-1:
  15. DEFAULT_BITNESS = 64
  16. else:
  17. DEFAULT_BITNESS = 32
  18. def fetch_python(version=DEFAULT_PY_VERSION, bitness=DEFAULT_BITNESS,
  19. destination=DEFAULT_BUILD_DIR):
  20. """Fetch the MSI for the specified version of Python.
  21. It will be placed in the destination directory, and validated using GPG
  22. if possible.
  23. """
  24. arch_tag = '.amd64' if (bitness==64) else ''
  25. url = 'http://python.org/ftp/python/{0}/python-{0}{1}.msi'.format(version, arch_tag)
  26. target = pjoin(destination, 'python-{0}{1}.msi'.format(version, arch_tag))
  27. if os.path.isfile(target):
  28. logger.info('Python MSI already in build directory.')
  29. return
  30. logger.info('Downloading Python MSI...')
  31. urlretrieve(url, target)
  32. urlretrieve(url+'.asc', target+'.asc')
  33. try:
  34. keys_file = os.path.join(_PKGDIR, 'python-pubkeys.txt')
  35. check_output(['gpg', '--import', keys_file])
  36. check_output(['gpg', '--verify', target+'.asc'])
  37. except FileNotFoundError:
  38. logger.warn("GPG not available - could not check signature of {0}".format(target))
  39. def write_nsis_file(nsi_file, definitions):
  40. with open(nsi_file, 'w') as f:
  41. for name, value in definitions.items():
  42. f.write('!define {} "{}"\n'.format(name, value))
  43. with open(pjoin(_PKGDIR, 'template.nsi')) as f2:
  44. f.write(f2.read())
  45. def run_nsis(nsi_file):
  46. call(['makensis', nsi_file])
  47. def all_steps(appname, version, script, packages=None, icon=DEFAULT_ICON,
  48. py_version=DEFAULT_PY_VERSION, py_bitness=DEFAULT_BITNESS,
  49. build_dir=DEFAULT_BUILD_DIR,
  50. installer_name=DEFAULT_INSTALLER_NAME):
  51. os.makedirs(build_dir, exist_ok=True)
  52. fetch_python(version=py_version, bitness=py_bitness, destination=build_dir)
  53. shutil.copy2(script, build_dir)
  54. shutil.copy2(icon, build_dir)
  55. # Packages
  56. build_pkg_dir = pjoin(build_dir, 'pkgs')
  57. if os.path.isdir(build_pkg_dir):
  58. shutil.rmtree(build_pkg_dir)
  59. if os.path.isdir('pynsis_pkgs'):
  60. shutil.copytree('pynsis_pkgs', build_pkg_dir)
  61. else:
  62. os.mkdir(build_pkg_dir)
  63. copy_modules(packages or [], build_pkg_dir)
  64. nsi_file = pjoin(build_dir, 'installer.nsi')
  65. definitions = {'PRODUCT_NAME': appname,
  66. 'PRODUCT_VERSION': version,
  67. 'PY_VERSION': py_version,
  68. 'SCRIPT': os.path.basename(script),
  69. 'PRODUCT_ICON': os.path.basename(icon),
  70. 'INSTALLER_NAME': installer_name,
  71. 'ARCH_TAG': '.amd64' if (py_bitness==64) else ''
  72. }
  73. write_nsis_file(nsi_file, definitions)
  74. run_nsis(nsi_file)
  75. def main(argv=None):
  76. logger.setLevel(logging.INFO)
  77. logger.addHandler(logging.StreamHandler())
  78. import argparse
  79. argp = argparse.ArgumentParser(prog='pynsis')
  80. argp.add_argument('config_file')
  81. options = argp.parse_args(argv)
  82. dirname, config_file = os.path.split(options.config_file)
  83. if dirname:
  84. os.chdir(dirname)
  85. import configparser
  86. cfg = configparser.ConfigParser()
  87. cfg.read(config_file)
  88. appcfg = cfg['Application']
  89. all_steps(
  90. appname = appcfg['name'],
  91. version = appcfg['version'],
  92. script = appcfg['script'],
  93. icon = appcfg.get('icon', DEFAULT_ICON),
  94. packages = cfg.get('Include', 'packages', fallback='').splitlines(),
  95. py_version = cfg.get('Python', 'version', fallback=DEFAULT_PY_VERSION),
  96. py_bitness = cfg.getint('Python', 'bitness', fallback=DEFAULT_BITNESS),
  97. build_dir = cfg.get('Build', 'directory', fallback=DEFAULT_BUILD_DIR),
  98. installer_name = cfg.get('Build', 'installer_name', fallback=DEFAULT_INSTALLER_NAME),
  99. )