More work on building calibre on windows

This commit is contained in:
Kovid Goyal 2019-06-19 12:15:30 +05:30
parent e804e48747
commit 947fb029d8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 54 additions and 47 deletions

View File

@ -31,8 +31,6 @@ dlls = [
'Sensors', 'Sensors',
'Sql', 'Sql',
'Svg', 'Svg',
'WebKit',
'WebKitWidgets',
'WebEngineCore', 'WebEngineCore',
'WebEngine', 'WebEngine',
'WebEngineWidgets', 'WebEngineWidgets',
@ -49,7 +47,7 @@ if islinux:
elif ismacos: elif ismacos:
dlls += ['MacExtras', 'DBus'] dlls += ['MacExtras', 'DBus']
elif iswindows: elif iswindows:
dlls += ['WinExtras', 'Angle'] dlls += ['WinExtras']
QT_DLLS = frozenset( QT_DLLS = frozenset(
'Qt5' + x for x in dlls 'Qt5' + x for x in dlls
@ -68,7 +66,7 @@ QT_PLUGINS = [
# 'audio', 'printsupport', 'bearer', 'position', # 'audio', 'printsupport', 'bearer', 'position',
] ]
if not ismacos: if not ismacos and not iswindows:
QT_PLUGINS.append('platforminputcontexts') QT_PLUGINS.append('platforminputcontexts')
if islinux: if islinux:
@ -88,8 +86,6 @@ PYQT_MODULES = (
'QtPrintSupport', 'QtPrintSupport',
'QtSensors', 'QtSensors',
'QtSvg', 'QtSvg',
'QtWebKit',
'QtWebKitWidgets',
'QtWidgets', 'QtWidgets',
'QtWebEngine', 'QtWebEngine',
'QtWebEngineCore', 'QtWebEngineCore',

View File

@ -1,4 +1,4 @@
# Requires installation of Visual Studio 2017 Community Edition, Git, Ruby, Python 3.7 and Perl # Requires installation of Visual Studio 2017 Community Edition, WiX Toolset, Git, Ruby, Python 3.7 and Perl
# git.exe must be in PATH. Must have ~100GB available disk space and 8GB RAM # git.exe must be in PATH. Must have ~100GB available disk space and 8GB RAM
# Install certifi in python 3 with: # Install certifi in python 3 with:
# py.exe -m pip install certifi # py.exe -m pip install certifi

View File

@ -8,6 +8,7 @@ import errno
import glob import glob
import os import os
import re import re
import runpy
import shutil import shutil
import stat import stat
import subprocess import subprocess
@ -15,12 +16,11 @@ import sys
import zipfile import zipfile
from bypy.constants import ( from bypy.constants import (
PREFIX, SRC as CALIBRE_DIR, SW, is64bit, build_dir, python_major_minor_version CL, LINK, PREFIX, RC, SRC as CALIBRE_DIR, SW, build_dir, is64bit,
python_major_minor_version
) )
from bypy.utils import py_compile, run, walk from bypy.utils import py_compile, run, walk
from .wix import create_installer
iv = globals()['init_env'] iv = globals()['init_env']
calibre_constants = iv['calibre_constants'] calibre_constants = iv['calibre_constants']
QT_PREFIX = os.path.join(PREFIX, 'qt') QT_PREFIX = os.path.join(PREFIX, 'qt')
@ -30,6 +30,9 @@ APPNAME, VERSION = calibre_constants['appname'], calibre_constants['version']
WINVER = VERSION + '.0' WINVER = VERSION + '.0'
machine = 'X64' if is64bit else 'X86' machine = 'X64' if is64bit else 'X86'
j, d, a, b = os.path.join, os.path.dirname, os.path.abspath, os.path.basename j, d, a, b = os.path.join, os.path.dirname, os.path.abspath, os.path.basename
create_installer = runpy.run_path(
j(d(a(__file__)), 'wix.py'), {'calibre_constants': calibre_constants}
)['create_installer']
DESCRIPTIONS = { DESCRIPTIONS = {
'calibre': 'The main calibre program', 'calibre': 'The main calibre program',
@ -51,7 +54,6 @@ DESCRIPTIONS = {
# https://msdn.microsoft.com/en-us/library/windows/desktop/dn481241(v=vs.85).aspx # https://msdn.microsoft.com/en-us/library/windows/desktop/dn481241(v=vs.85).aspx
SUPPORTED_OS = { SUPPORTED_OS = {
'vista': '{e2011457-1546-43c5-a5fe-008deee3d3f0}',
'w7': '{35138b9a-5d96-4fbd-8e2d-a2440225f93a}', 'w7': '{35138b9a-5d96-4fbd-8e2d-a2440225f93a}',
'w8': '{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}', 'w8': '{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}',
'w81': '{1f676c76-80e1-4239-95bb-83d0f6d0da78}', 'w81': '{1f676c76-80e1-4239-95bb-83d0f6d0da78}',
@ -70,7 +72,6 @@ EXE_MANIFEST = '''\
</trustInfo> </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>
<supportedOS Id="{vista}"/>
<supportedOS Id="{w7}"/> <supportedOS Id="{w7}"/>
<supportedOS Id="{w8}"/> <supportedOS Id="{w8}"/>
<supportedOS Id="{w81}"/> <supportedOS Id="{w81}"/>
@ -86,6 +87,10 @@ def printf(*args, **kw):
sys.stdout.flush() sys.stdout.flush()
def run_compiler(env, *cmd):
run(*cmd, cwd=env.obj_dir)
class Env(object): class Env(object):
def __init__(self, build_dir): def __init__(self, build_dir):
@ -160,6 +165,8 @@ def freeze(env, ext_dir):
printf('Adding Qt...') printf('Adding Qt...')
for x in QT_DLLS: for x in QT_DLLS:
copybin(os.path.join(QT_PREFIX, 'bin', x + '.dll')) copybin(os.path.join(QT_PREFIX, 'bin', x + '.dll'))
for x in 'libGLESv2 libEGL'.split():
copybin(os.path.join(QT_PREFIX, 'bin', x + '.dll'))
plugdir = j(QT_PREFIX, 'plugins') plugdir = j(QT_PREFIX, 'plugins')
tdir = j(env.app_base, 'qt_plugins') tdir = j(env.app_base, 'qt_plugins')
for d in QT_PLUGINS: for d in QT_PLUGINS:
@ -253,7 +260,7 @@ def extract_pyd_modules(env, site_packages_dir):
bpy = dest[:-1] bpy = dest[:-1]
if os.path.exists(bpy): if os.path.exists(bpy):
with open(bpy, 'rb') as f: with open(bpy, 'rb') as f:
raw = f.read().strip() raw = f.read().strip().decode('utf-8')
if (not raw.startswith('def __bootstrap__') or not raw.endswith('__bootstrap__()')): if (not raw.startswith('def __bootstrap__') or not raw.endswith('__bootstrap__()')):
raise ValueError('The file %r has non bootstrap code' % bpy) raise ValueError('The file %r has non bootstrap code' % bpy)
for ext in ('', 'c', 'o'): for ext in ('', 'c', 'o'):
@ -298,7 +305,7 @@ def embed_resources(env, module, desc=None, extra_data=None, product_description
icon_map = {'calibre': 'library', 'ebook-viewer': 'viewer', 'ebook-edit': 'ebook-edit', icon_map = {'calibre': 'library', 'ebook-viewer': 'viewer', 'ebook-edit': 'ebook-edit',
'lrfviewer': 'viewer', 'calibre-portable': 'library'} 'lrfviewer': 'viewer', 'calibre-portable': 'library'}
file_type = 'DLL' if module.endswith('.dll') else 'APP' file_type = 'DLL' if module.endswith('.dll') else 'APP'
template = open(env.rc_template, 'rb').read() template = open(env.rc_template, 'rb').read().decode('utf-8')
bname = b(module) bname = b(module)
internal_name = os.path.splitext(bname)[0] internal_name = os.path.splitext(bname)[0]
icon = icon_map.get(internal_name, 'command-prompt') icon = icon_map.get(internal_name, 'command-prompt')
@ -316,7 +323,7 @@ def embed_resources(env, module, desc=None, extra_data=None, product_description
if product_description is None: if product_description is None:
product_description = APPNAME + ' - E-book management' product_description = APPNAME + ' - E-book management'
rc = template.format( rc = template.format(
icon=icon, icon=icon.replace('\\', '/'),
file_type=e(file_type), file_type=e(file_type),
file_version=e(WINVER.replace('.', ',')), file_version=e(WINVER.replace('.', ',')),
file_version_str=e(WINVER), file_version_str=e(WINVER),
@ -334,10 +341,10 @@ def embed_resources(env, module, desc=None, extra_data=None, product_description
rc += '\nextra extra "%s"' % extra_data rc += '\nextra extra "%s"' % extra_data
tdir = env.obj_dir tdir = env.obj_dir
rcf = j(tdir, bname + '.rc') rcf = j(tdir, bname + '.rc')
with open(rcf, 'wb') as f: with open(rcf, 'w') as f:
f.write(rc) f.write(rc)
res = j(tdir, bname + '.res') res = j(tdir, bname + '.res')
run('rc', '/n', '/fo' + res, rcf) run(RC, '/n', '/fo' + res, rcf)
return res return res
@ -356,8 +363,8 @@ def build_portable_installer(env):
cflags.append(r'/I%s\include' % PREFIX) cflags.append(r'/I%s\include' % PREFIX)
cflags.append('/DUNCOMPRESSED_SIZE=%d' % usz) cflags.append('/DUNCOMPRESSED_SIZE=%d' % usz)
printf('Compiling', obj) printf('Compiling', obj)
cmd = ['cl.exe'] + cflags + ['/Fo' + obj, src] cmd = [CL] + cflags + ['/Fo' + obj, src]
run(*cmd) run_compiler(env, *cmd)
base = d(a(__file__)) base = d(a(__file__))
src = j(base, 'portable-installer.cpp') src = j(base, 'portable-installer.cpp')
@ -371,8 +378,8 @@ def build_portable_installer(env):
printf('Linking', exe) printf('Linking', exe)
manifest = exe + '.manifest' manifest = exe + '.manifest'
with open(manifest, 'wb') as f: with open(manifest, 'wb') as f:
f.write(EXE_MANIFEST) f.write(EXE_MANIFEST.encode('utf-8'))
cmd = ['link.exe'] + [ cmd = [LINK] + [
'/INCREMENTAL:NO', '/MACHINE:' + machine, '/INCREMENTAL:NO', '/MACHINE:' + machine,
'/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS', '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS',
'/LIBPATH:' + (PREFIX + r'\lib'), '/LIBPATH:' + (PREFIX + r'\lib'),
@ -397,12 +404,12 @@ def build_portable(env):
cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE /DUNICODE'.split() cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE /DUNICODE'.split()
printf('Compiling', obj) printf('Compiling', obj)
cmd = ['cl.exe'] + cflags + ['/Fo' + obj, '/Tc' + src] cmd = [CL] + cflags + ['/Fo' + obj, '/Tc' + src]
run(*cmd) run_compiler(env, *cmd)
exe = j(base, 'calibre-portable.exe') exe = j(base, 'calibre-portable.exe')
printf('Linking', exe) printf('Linking', exe)
cmd = ['link.exe'] + [ cmd = [LINK] + [
'/INCREMENTAL:NO', '/MACHINE:' + machine, '/INCREMENTAL:NO', '/MACHINE:' + machine,
'/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS', '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS',
'/RELEASE', '/RELEASE',
@ -482,16 +489,16 @@ def build_utils(env):
def build(src, name, subsys='CONSOLE', libs='setupapi.lib'.split()): def build(src, name, subsys='CONSOLE', libs='setupapi.lib'.split()):
printf('Building ' + name) printf('Building ' + name)
obj = j(env.obj_dir, (src) + '.obj') obj = j(env.obj_dir, os.path.basename(src) + '.obj')
cflags = '/c /EHsc /MD /W3 /Ox /nologo /D_UNICODE'.split() cflags = '/c /EHsc /MD /W3 /Ox /nologo /D_UNICODE'.split()
ftype = '/T' + ('c' if src.endswith('.c') else 'p') ftype = '/T' + ('c' if src.endswith('.c') else 'p')
cmd = ['cl.exe'] + cflags + ['/Fo' + obj, ftype + src] cmd = [CL] + cflags + ['/Fo' + obj, ftype + src]
run(*cmd) run_compiler(env, *cmd)
exe = j(env.dll_dir, name) exe = j(env.dll_dir, name)
mf = exe + '.manifest' mf = exe + '.manifest'
with open(mf, 'wb') as f: with open(mf, 'wb') as f:
f.write(EXE_MANIFEST) f.write(EXE_MANIFEST.encode('utf-8'))
cmd = ['link.exe'] + [ cmd = [LINK] + [
'/MACHINE:' + machine, '/MACHINE:' + machine,
'/SUBSYSTEM:' + subsys, '/RELEASE', '/MANIFEST:EMBED', '/MANIFESTINPUT:' + mf, '/SUBSYSTEM:' + subsys, '/RELEASE', '/MANIFEST:EMBED', '/MANIFESTINPUT:' + mf,
'/OUT:' + exe] + [embed_resources(env, exe), obj] + libs '/OUT:' + exe] + [embed_resources(env, exe), obj] + libs
@ -512,12 +519,12 @@ def build_launchers(env, debug=False):
cflags = '/c /EHsc /W3 /Ox /nologo /D_UNICODE'.split() cflags = '/c /EHsc /W3 /Ox /nologo /D_UNICODE'.split()
cflags += ['/DPYDLL="python%s.dll"' % env.py_ver.replace('.', ''), '/I%s/include' % env.python_base] cflags += ['/DPYDLL="python%s.dll"' % env.py_ver.replace('.', ''), '/I%s/include' % env.python_base]
for src, obj in zip(sources, objects): for src, obj in zip(sources, objects):
cmd = ['cl.exe'] + cflags + dflags + ['/MD', '/Fo' + obj, '/Tc' + src] cmd = [CL] + cflags + dflags + ['/MD', '/Fo' + obj, '/Tc' + src]
run(*cmd) run_compiler(env, *cmd)
dll = j(env.obj_dir, 'calibre-launcher.dll') dll = j(env.obj_dir, 'calibre-launcher.dll')
ver = '.'.join(VERSION.split('.')[:2]) ver = '.'.join(VERSION.split('.')[:2])
cmd = ['link.exe', '/DLL', '/VERSION:' + ver, '/LTCG', '/OUT:' + dll, cmd = [LINK, '/DLL', '/VERSION:' + ver, '/LTCG', '/OUT:' + dll,
'/nologo', '/MACHINE:' + machine] + dlflags + objects + \ '/nologo', '/MACHINE:' + machine] + dlflags + objects + \
[embed_resources(env, dll), [embed_resources(env, dll),
'/LIBPATH:%s/libs' % env.python_base, '/LIBPATH:%s/libs' % env.python_base,
@ -542,16 +549,16 @@ def build_launchers(env, debug=False):
'/DFUNCTION="%s"' % func] '/DFUNCTION="%s"' % func]
dest = j(env.obj_dir, bname + '.obj') dest = j(env.obj_dir, bname + '.obj')
printf('Compiling', bname) printf('Compiling', bname)
cmd = ['cl.exe'] + cflags + dflags + ['/Tc' + src, '/Fo' + dest] cmd = [CL] + cflags + dflags + ['/Tc' + src, '/Fo' + dest]
run(*cmd) run_compiler(env, *cmd)
exe = j(env.base, bname + '.exe') exe = j(env.base, bname + '.exe')
lib = dll.replace('.dll', '.lib') lib = dll.replace('.dll', '.lib')
u32 = ['user32.lib'] u32 = ['user32.lib']
printf('Linking', bname) printf('Linking', bname)
mf = dest + '.manifest' mf = dest + '.manifest'
with open(mf, 'wb') as f: with open(mf, 'wb') as f:
f.write(EXE_MANIFEST) f.write(EXE_MANIFEST.encode('utf-8'))
cmd = ['link.exe'] + [ cmd = [LINK] + [
'/MACHINE:' + machine, '/NODEFAULTLIB', '/ENTRY:start_here', '/MACHINE:' + machine, '/NODEFAULTLIB', '/ENTRY:start_here',
'/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:' + subsys, '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:' + subsys,
'/LIBPATH:%s/libs' % env.python_base, '/RELEASE', '/LIBPATH:%s/libs' % env.python_base, '/RELEASE',
@ -689,3 +696,7 @@ def main():
build_portable_installer(env) build_portable_installer(env)
if args.sign_installers: if args.sign_installers:
sign_installers(env) sign_installers(env)
if __name__ == '__main__':
main()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
@ -8,16 +8,16 @@ from itertools import count
import os import os
import shutil import shutil
from pkgs.constants import is64bit from bypy.constants import is64bit
from pkgs.utils import run from bypy.utils import run
from .. import calibre_constants
WIXP = r'C:\Program Files (x86)\WiX Toolset v3.10' WIXP = r'C:\Program Files (x86)\WiX Toolset v3.11'
if is64bit: if is64bit:
UPGRADE_CODE = '5DD881FF-756B-4097-9D82-8C0F11D521EA' UPGRADE_CODE = '5DD881FF-756B-4097-9D82-8C0F11D521EA'
else: else:
UPGRADE_CODE = 'BEB2A80D-E902-4DAD-ADF9-8BD2DA42CFE1' UPGRADE_CODE = 'BEB2A80D-E902-4DAD-ADF9-8BD2DA42CFE1'
MINVERHUMAN = 'Windows Vista SP2' MINVERHUMAN = 'Windows 7'
calibre_constants = globals()['calibre_constants']
CANDLE = WIXP + r'\bin\candle.exe' CANDLE = WIXP + r'\bin\candle.exe'
LIGHT = WIXP + r'\bin\light.exe' LIGHT = WIXP + r'\bin\light.exe'
@ -29,7 +29,7 @@ def create_installer(env):
shutil.rmtree(env.installer_dir) shutil.rmtree(env.installer_dir)
os.makedirs(env.installer_dir) os.makedirs(env.installer_dir)
template = open(j(d(__file__), 'wix-template.xml'), 'rb').read() template = open(j(d(__file__), 'wix-template.xml'), 'rb').read().decode('utf-8')
components, smap = get_components_from_files(env) components, smap = get_components_from_files(env)
wxs = template.format( wxs = template.format(
@ -40,7 +40,7 @@ def create_installer(env):
ProgramFilesFolder='ProgramFiles64Folder' if is64bit else 'ProgramFilesFolder', ProgramFilesFolder='ProgramFiles64Folder' if is64bit else 'ProgramFilesFolder',
x64=' 64bit' if is64bit else '', x64=' 64bit' if is64bit else '',
minverhuman=MINVERHUMAN, minverhuman=MINVERHUMAN,
minver='600', minver='601',
fix_wix='<Custom Action="OverwriteWixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />' if is64bit else '', fix_wix='<Custom Action="OverwriteWixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />' if is64bit else '',
compression='high', compression='high',
app_components=components, app_components=components,
@ -50,15 +50,15 @@ def create_installer(env):
editor_icon=j(env.src_root, 'icons', 'ebook-edit.ico'), editor_icon=j(env.src_root, 'icons', 'ebook-edit.ico'),
web_icon=j(env.src_root, 'icons', 'web.ico'), web_icon=j(env.src_root, 'icons', 'web.ico'),
) )
template = open(j(d(__file__), 'en-us.xml'), 'rb').read() template = open(j(d(__file__), 'en-us.xml'), 'rb').read().decode('utf-8')
enus = template.format(app=calibre_constants['appname']) enus = template.format(app=calibre_constants['appname'])
enusf = j(env.installer_dir, 'en-us.wxl') enusf = j(env.installer_dir, 'en-us.wxl')
wxsf = j(env.installer_dir, calibre_constants['appname'] + '.wxs') wxsf = j(env.installer_dir, calibre_constants['appname'] + '.wxs')
with open(wxsf, 'wb') as f: with open(wxsf, 'wb') as f:
f.write(wxs) f.write(wxs.encode('utf-8'))
with open(enusf, 'wb') as f: with open(enusf, 'wb') as f:
f.write(enus) f.write(enus.encode('utf-8'))
wixobj = j(env.installer_dir, calibre_constants['appname'] + '.wixobj') wixobj = j(env.installer_dir, calibre_constants['appname'] + '.wixobj')
arch = 'x64' if is64bit else 'x86' arch = 'x64' if is64bit else 'x86'
cmd = [CANDLE, '-nologo', '-arch', arch, '-ext', 'WiXUtilExtension', '-o', wixobj, wxsf] cmd = [CANDLE, '-nologo', '-arch', arch, '-ext', 'WiXUtilExtension', '-o', wixobj, wxsf]