Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'windows-installer/construct.py')
-rw-r--r--windows-installer/construct.py192
1 files changed, 192 insertions, 0 deletions
diff --git a/windows-installer/construct.py b/windows-installer/construct.py
new file mode 100644
index 000000000..f0724f5f4
--- /dev/null
+++ b/windows-installer/construct.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+import contextlib
+import ctypes
+import os
+import shutil
+import struct
+import subprocess
+import sys
+import tempfile
+import time
+
+PYTHON_VERSION = (3, 7, 4)
+PYTHON_BITNESS = 32
+PYWIN32_VERSION = 227 # do not forget to edit pywin32 dependency accordingly in setup.py
+NSIS_VERSION = '3.04'
+
+
+def main():
+ build_path, repo_path, venv_path, venv_python = _prepare_environment()
+
+ _copy_assets(build_path, repo_path)
+
+ installer_cfg_path = _generate_pynsist_config(repo_path, build_path)
+
+ _prepare_build_tools(venv_path, venv_python, repo_path)
+ _compile_wheels(repo_path, build_path, venv_python)
+ _build_installer(installer_cfg_path, venv_path)
+
+ print('Done')
+
+
+def _build_installer(installer_cfg_path, venv_path):
+ print('Build the installer')
+ subprocess.check_call([os.path.join(venv_path, 'Scripts', 'pynsist.exe'), installer_cfg_path])
+
+
+def _compile_wheels(repo_path, build_path, venv_python):
+ print('Compile wheels')
+
+ wheels_path = os.path.join(build_path, 'wheels')
+ os.makedirs(wheels_path)
+
+ certbot_packages = ['acme', 'certbot']
+ # Uncomment following line to include all DNS plugins in the installer
+ # certbot_packages.extend([name for name in os.listdir(repo_path) if name.startswith('certbot-dns-')])
+ wheels_project = [os.path.join(repo_path, package) for package in certbot_packages]
+
+ with _prepare_constraints(repo_path) as constraints_file_path:
+ command = [venv_python, '-m', 'pip', 'wheel', '-w', wheels_path, '--constraint', constraints_file_path]
+ command.extend(wheels_project)
+ subprocess.check_call(command)
+
+
+def _prepare_build_tools(venv_path, venv_python, repo_path):
+ print('Prepare build tools')
+ subprocess.check_call([sys.executable, '-m', 'venv', venv_path])
+ subprocess.check_call([venv_python, os.path.join(repo_path, 'letsencrypt-auto-source', 'pieces', 'pipstrap.py')])
+ subprocess.check_call([venv_python, os.path.join(repo_path, 'tools', 'pip_install.py'), 'pynsist'])
+ subprocess.check_call(['choco', 'upgrade', '--allow-downgrade', '-y', 'nsis', '--version', NSIS_VERSION])
+
+
+@contextlib.contextmanager
+def _prepare_constraints(repo_path):
+ requirements = os.path.join(repo_path, 'letsencrypt-auto-source', 'pieces', 'dependency-requirements.txt')
+ constraints = subprocess.check_output(
+ [sys.executable, os.path.join(repo_path, 'tools', 'strip_hashes.py'), requirements],
+ universal_newlines=True)
+ workdir = tempfile.mkdtemp()
+ try:
+ constraints_file_path = os.path.join(workdir, 'constraints.txt')
+ with open(constraints_file_path, 'a') as file_h:
+ file_h.write(constraints)
+ file_h.write('pywin32=={0}'.format(PYWIN32_VERSION))
+ yield constraints_file_path
+ finally:
+ shutil.rmtree(workdir)
+
+
+def _copy_assets(build_path, repo_path):
+ print('Copy assets')
+ if os.path.exists(build_path):
+ os.rename(build_path, '{0}.{1}.bak'.format(build_path, int(time.time())))
+ os.makedirs(build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'certbot.ico'), build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'run.bat'), build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'template.nsi'), build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-up.ps1'), build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-down.ps1'), build_path)
+
+
+def _generate_pynsist_config(repo_path, build_path):
+ print('Generate pynsist configuration')
+
+ pywin32_paths_file = os.path.join(build_path, 'pywin32_paths.py')
+
+ # Pywin32 uses non-standard folders to hold its packages. We need to instruct pynsist bootstrap
+ # explicitly to add them into sys.path. This is done with a custom "pywin32_paths.py" that is
+ # referred in the pynsist configuration as an "extra_preamble".
+ # Reference example: https://github.com/takluyver/pynsist/tree/master/examples/pywebview
+ with open(pywin32_paths_file, 'w') as file_h:
+ file_h.write('''\
+pkgdir = os.path.join(os.path.dirname(installdir), 'pkgs')
+
+sys.path.extend([
+ os.path.join(pkgdir, 'win32'),
+ os.path.join(pkgdir, 'win32', 'lib'),
+])
+
+# Preload pywintypes and pythoncom
+pwt = os.path.join(pkgdir, 'pywin32_system32', 'pywintypes{0}{1}.dll')
+pcom = os.path.join(pkgdir, 'pywin32_system32', 'pythoncom{0}{1}.dll')
+import warnings
+with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ import imp
+imp.load_dynamic('pywintypes', pwt)
+imp.load_dynamic('pythoncom', pcom)
+'''.format(PYTHON_VERSION[0], PYTHON_VERSION[1]))
+
+ installer_cfg_path = os.path.join(build_path, 'installer.cfg')
+
+ certbot_pkg_path = os.path.join(repo_path, 'certbot')
+ certbot_version = subprocess.check_output([sys.executable, '-c', 'import certbot; print(certbot.__version__)'],
+ universal_newlines=True, cwd=certbot_pkg_path).strip()
+
+ with open(installer_cfg_path, 'w') as file_h:
+ file_h.write('''\
+[Application]
+name=Certbot
+version={certbot_version}
+icon=certbot.ico
+publisher=Electronic Frontier Foundation
+target=$INSTDIR\\run.bat
+
+[Build]
+directory=nsis
+nsi_template=template.nsi
+installer_name=certbot-beta-installer-{installer_suffix}.exe
+
+[Python]
+version={python_version}
+bitness={python_bitness}
+
+[Include]
+local_wheels=wheels\\*.whl
+files=run.bat
+ renew-up.ps1
+ renew-down.ps1
+
+[Command certbot]
+entry_point=certbot.main:main
+extra_preamble=pywin32_paths.py
+'''.format(certbot_version=certbot_version,
+ installer_suffix='win_amd64' if PYTHON_BITNESS == 64 else 'win32',
+ python_bitness=PYTHON_BITNESS,
+ python_version='.'.join([str(item) for item in PYTHON_VERSION])))
+
+ return installer_cfg_path
+
+
+def _prepare_environment():
+ print('Prepare environment')
+ try:
+ subprocess.check_output(['choco', '--version'])
+ except subprocess.CalledProcessError:
+ raise RuntimeError('Error: Chocolatey (https://chocolatey.org/) needs '
+ 'to be installed to run this script.')
+ script_path = os.path.realpath(__file__)
+ repo_path = os.path.dirname(os.path.dirname(script_path))
+ build_path = os.path.join(repo_path, 'windows-installer', 'build')
+ venv_path = os.path.join(build_path, 'venv-config')
+ venv_python = os.path.join(venv_path, 'Scripts', 'python.exe')
+
+ return build_path, repo_path, venv_path, venv_python
+
+
+if __name__ == '__main__':
+ if not os.name == 'nt':
+ raise RuntimeError('This script must be run under Windows.')
+
+ if ctypes.windll.shell32.IsUserAnAdmin() == 0:
+ # Administrator privileges are required to properly install NSIS through Chocolatey
+ raise RuntimeError('This script must be run with administrator privileges.')
+
+ if sys.version_info[:2] != PYTHON_VERSION[:2]:
+ raise RuntimeError('This script must be run with Python {0}'
+ .format('.'.join([str(item) for item in PYTHON_VERSION[0:2]])))
+
+ if struct.calcsize('P') * 8 != PYTHON_BITNESS:
+ raise RuntimeError('This script must be run with a {0} bit version of Python.'
+ .format(PYTHON_BITNESS))
+ main()