From c1dd30004ee08e3fc98b83a4678abb6122f47224 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 30 Nov 2012 18:19:12 +0530 Subject: [PATCH] 64 bit windows msi builds, though it's still picking up the 32 bit paths for some reason. Sigh. --- setup/__init__.py | 2 + setup/commands.py | 5 +- setup/installer/windows/__init__.py | 34 +++++++++---- setup/installer/windows/freeze.py | 61 ++++++++++++++---------- setup/installer/windows/notes.rst | 6 +-- setup/installer/windows/wix-template.xml | 30 ++++++------ setup/installer/windows/wix.py | 42 ++++++++++------ 7 files changed, 110 insertions(+), 70 deletions(-) diff --git a/setup/__init__.py b/setup/__init__.py index 4a8d9870be..49b84dd074 100644 --- a/setup/__init__.py +++ b/setup/__init__.py @@ -215,6 +215,8 @@ class Command(object): sys.stdout.flush() def installer_name(ext, is64bit=False): + if is64bit and ext == 'msi': + return 'dist/%s-64bit-%s.msi'%(__appname__, __version__) if ext in ('exe', 'msi'): return 'dist/%s-%s.%s'%(__appname__, __version__, ext) if ext == 'dmg': diff --git a/setup/commands.py b/setup/commands.py index 4afb19b8a6..410b73d026 100644 --- a/setup/commands.py +++ b/setup/commands.py @@ -20,7 +20,7 @@ __all__ = [ 'upload_user_manual', 'upload_demo', 'reupload', 'linux32', 'linux64', 'linux', 'linux_freeze', 'osx32_freeze', 'osx', 'rsync', 'push', - 'win32_freeze', 'win32', 'win', + 'win32_freeze', 'win32', 'win64', 'win', 'stage1', 'stage2', 'stage3', 'stage4', 'stage5', 'publish' ] @@ -91,9 +91,10 @@ osx = OSX() from setup.installer.osx.app.main import OSX32_Freeze osx32_freeze = OSX32_Freeze() -from setup.installer.windows import Win, Win32 +from setup.installer.windows import Win, Win32, Win64 win = Win() win32 = Win32() +win64 = Win64() from setup.installer.windows.freeze import Win32Freeze win32_freeze = Win32Freeze() diff --git a/setup/installer/windows/__init__.py b/setup/installer/windows/__init__.py index 61149a9f26..f3c1c2d0cd 100644 --- a/setup/installer/windows/__init__.py +++ b/setup/installer/windows/__init__.py @@ -20,20 +20,21 @@ class Win(Command): def run(self, opts): pass - -class Win32(VMInstaller): - - description = 'Build 32bit windows binary installer' - - INSTALLER_EXT = 'exe' - VM_NAME = 'xp_build' - VM = '/vmware/bin/%s'%VM_NAME - VM_CHECK = 'calibre_windows_xp_home' +class WinBase(VMInstaller): FREEZE_COMMAND = 'win32_freeze' FREEZE_TEMPLATE = 'python -OO setup.py {freeze_command} --no-ice' INSTALLER_EXT = 'msi' SHUTDOWN_CMD = ['shutdown.exe', '-s', '-f', '-t', '0'] + +class Win32(WinBase): + + description = 'Build 32bit windows binary installer' + + VM_NAME = 'xp_build' + VM = '/vmware/bin/%s'%VM_NAME + VM_CHECK = 'calibre_windows_xp_home' + def sign_msi(self): print ('Signing installers ...') subprocess.check_call(['ssh', self.VM_NAME, '~/sign.sh'], shell=False) @@ -57,4 +58,19 @@ class Win32(VMInstaller): self.warn('Failed to get portable installer') raise SystemExit(1) +class Win64(WinBase): + + description = 'Build 64bit windows binary installer' + + VM_NAME = 'win64' + VM = '/vmware/bin/%s'%VM_NAME + VM_CHECK = 'win64' + IS_64_BIT = True + BUILD_PREFIX = WinBase.BUILD_PREFIX + [ + 'if [ -f "$HOME/.bash_profile" ] ; then', + ' source "$HOME/.bash_profile"', + 'fi', + ] + + diff --git a/setup/installer/windows/freeze.py b/setup/installer/windows/freeze.py index 89ed778fd4..a19b05f326 100644 --- a/setup/installer/windows/freeze.py +++ b/setup/installer/windows/freeze.py @@ -25,6 +25,7 @@ LZMA = r'Q:\easylzma\build\easylzma-0.0.8' VERSION = re.sub('[a-z]\d+', '', __version__) WINVER = VERSION+'.0' +machine = 'X64' if is64bit else 'X86' DESCRIPTIONS = { 'calibre' : 'The main calibre program', @@ -132,6 +133,22 @@ class Win32Freeze(Command, WixMixIn): # used instead shutil.copy2(f, tgt) + def fix_pyd_bootstraps_in(self, crypto_dir): + for dirpath, dirnames, filenames in os.walk(crypto_dir): + for f in filenames: + name, ext = os.path.splitext(f) + if ext == '.pyd': + with open(self.j(dirpath, name+'.py')) as f: + raw = f.read().strip() + if (not raw.startswith('def __bootstrap__') or not + raw.endswith('__bootstrap__()')): + raise Exception('The file %r has non' + ' bootstrap code'%self.j(dirpath, f)) + for ext in ('.py', '.pyc', '.pyo'): + x = self.j(dirpath, name+ext) + if os.path.exists(x): + os.remove(x) + def freeze(self): shutil.copy2(self.j(self.src_root, 'LICENSE'), self.base) @@ -184,23 +201,11 @@ class Win32Freeze(Command, WixMixIn): shutil.copytree(self.j(comext, 'shell'), self.j(sp_dir, 'win32com', 'shell')) shutil.rmtree(comext) - # Fix PyCrypto, removing the bootstrap .py modules that load the .pyd - # modules, since they do not work when in a zip file - for crypto_dir in glob.glob(self.j(sp_dir, 'pycrypto-*', 'Crypto')): - for dirpath, dirnames, filenames in os.walk(crypto_dir): - for f in filenames: - name, ext = os.path.splitext(f) - if ext == '.pyd': - with open(self.j(dirpath, name+'.py')) as f: - raw = f.read().strip() - if (not raw.startswith('def __bootstrap__') or not - raw.endswith('__bootstrap__()')): - raise Exception('The PyCrypto file %r has non' - ' bootstrap code'%self.j(dirpath, f)) - for ext in ('.py', '.pyc', '.pyo'): - x = self.j(dirpath, name+ext) - if os.path.exists(x): - os.remove(x) + # Fix PyCrypto and Pillow, removing the bootstrap .py modules that load + # the .pyd modules, since they do not work when in a zip file + for crypto_dir in glob.glob(self.j(sp_dir, 'pycrypto-*', 'Crypto') + ) + glob.glob(self.j(sp_dir, 'Pillow-*')): + self.fix_pyd_bootstraps_in(crypto_dir) for pat in (r'PyQt4\uic\port_v3', ): x = glob.glob(self.j(self.lib_dir, 'site-packages', pat))[0] @@ -401,7 +406,7 @@ class Win32Freeze(Command, WixMixIn): exe = self.j('dist', 'calibre-portable-installer-%s.exe'%VERSION) if self.newer(exe, [obj, xobj]): self.info('Linking', exe) - cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86', + cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:'+machine, '/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:WINDOWS', '/LIBPATH:'+(LZMA+r'\lib\Release'), '/RELEASE', '/MANIFEST', '/MANIFESTUAC:level="asInvoker" uiAccess="false"', @@ -458,7 +463,7 @@ class Win32Freeze(Command, WixMixIn): exe = self.j(base, 'calibre-portable.exe') if self.newer(exe, [obj]): self.info('Linking', exe) - cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86', + cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:'+machine, '/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:WINDOWS', '/RELEASE', '/ENTRY:wWinMainCRTStartup', @@ -517,7 +522,7 @@ class Win32Freeze(Command, WixMixIn): ver = '.'.join(__version__.split('.')[:2]) if self.newer(dll, objects): cmd = [msvc.linker, '/DLL', '/INCREMENTAL:NO', '/VERSION:'+ver, - '/OUT:'+dll, '/nologo', '/MACHINE:X86'] + objects + \ + '/OUT:'+dll, '/nologo', '/MACHINE:'+machine] + objects + \ [self.embed_resources(dll), '/LIBPATH:C:/Python%s/libs'%self.py_ver, 'python%s.lib'%self.py_ver, @@ -547,7 +552,7 @@ class Win32Freeze(Command, WixMixIn): lib = dll.replace('.dll', '.lib') if self.newer(exe, [dest, lib, self.rc_template, __file__]): self.info('Linking', bname) - cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86', + cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:'+machine, '/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:'+subsys, '/LIBPATH:C:/Python%s/libs'%self.py_ver, '/RELEASE', '/OUT:'+exe, self.embed_resources(exe), @@ -581,7 +586,8 @@ class Win32Freeze(Command, WixMixIn): sp = self.j(self.lib_dir, 'site-packages') # Special handling for PIL and pywin32 handled = set(['PIL.pth', 'pywin32.pth', 'PIL', 'win32']) - self.add_to_zipfile(zf, 'PIL', sp) + if not is64bit: + self.add_to_zipfile(zf, 'PIL', sp) base = self.j(sp, 'win32', 'lib') for x in os.listdir(base): if os.path.splitext(x)[1] not in ('.exe',): @@ -593,16 +599,17 @@ class Win32Freeze(Command, WixMixIn): self.add_to_zipfile(zf, x, base) handled.add('easy-install.pth') + # We dont want the site.py from site-packages + handled.add('site.pyo') + for d in self.get_pth_dirs(self.j(sp, 'easy-install.pth')): handled.add(self.b(d)) for x in os.listdir(d): - if x == 'EGG-INFO': + if x in {'EGG-INFO', 'site.py', 'site.pyc', 'site.pyo'}: continue self.add_to_zipfile(zf, x, d) # The rest of site-packages - # We dont want the site.py from site-packages - handled.add('site.pyo') for x in os.listdir(sp): if x in handled or x.endswith('.egg-info'): continue @@ -622,8 +629,10 @@ class Win32Freeze(Command, WixMixIn): line = line.strip() if not line or line.startswith('#') or line.startswith('import'): continue - candidate = self.j(base, line) + candidate = os.path.abspath(self.j(base, line)) if os.path.exists(candidate): + if not os.path.isdir(candidate): + raise ValueError('%s is not a directory'%candidate) yield candidate def add_to_zipfile(self, zf, name, base, exclude=frozenset()): diff --git a/setup/installer/windows/notes.rst b/setup/installer/windows/notes.rst index b78f4faacc..9110da8f4b 100644 --- a/setup/installer/windows/notes.rst +++ b/setup/installer/windows/notes.rst @@ -109,10 +109,8 @@ of mimetypes from the windows registry Python packages ------------------ -Install setuptools from http://pypi.python.org/pypi/setuptools If there are no -windows binaries already compiled for the version of python you are using then -download the source and run the following command in the folder where the -source has been unpacked:: +Install setuptools from http://pypi.python.org/pypi/setuptools. Use the source +tarball. Edit setup.py and set zip_safe=False. Then run:: python setup.py install diff --git a/setup/installer/windows/wix-template.xml b/setup/installer/windows/wix-template.xml index 46b5a07ead..f4596ca12f 100644 --- a/setup/installer/windows/wix-template.xml +++ b/setup/installer/windows/wix-template.xml @@ -2,13 +2,13 @@ - + SummaryCodepage='1252' />