mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor the build system to allow cross compiling windows native code extensions on linux
This commit is contained in:
parent
d6a0f4bb9d
commit
d13404d9ea
311
setup/build.py
311
setup/build.py
@ -4,15 +4,35 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import textwrap, os, shlex, subprocess, glob, shutil, sys, json, errno, sysconfig
|
import errno
|
||||||
from collections import namedtuple
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
import textwrap
|
||||||
|
from functools import partial
|
||||||
|
from typing import NamedTuple, List
|
||||||
|
|
||||||
|
from setup import SRC, Command, isbsd, isfreebsd, ishaiku, islinux, ismacos, iswindows
|
||||||
|
|
||||||
from setup import Command, islinux, isbsd, isfreebsd, ismacos, ishaiku, SRC, iswindows
|
|
||||||
isunix = islinux or ismacos or isbsd or ishaiku
|
isunix = islinux or ismacos or isbsd or ishaiku
|
||||||
|
|
||||||
py_lib = os.path.join(sys.prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2])
|
py_lib = os.path.join(sys.prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2])
|
||||||
CompileCommand = namedtuple('CompileCommand', 'cmd src dest')
|
|
||||||
LinkCommand = namedtuple('LinkCommand', 'cmd objects dest')
|
class CompileCommand(NamedTuple):
|
||||||
|
cmd: List[str]
|
||||||
|
src: str
|
||||||
|
dest: str
|
||||||
|
|
||||||
|
|
||||||
|
class LinkCommand(NamedTuple):
|
||||||
|
cmd: List[str]
|
||||||
|
objects: List[str]
|
||||||
|
dest: str
|
||||||
|
|
||||||
|
|
||||||
def walk(path='.'):
|
def walk(path='.'):
|
||||||
@ -75,6 +95,7 @@ class Extension:
|
|||||||
flag = '/O%d' if iswindows else '-O%d'
|
flag = '/O%d' if iswindows else '-O%d'
|
||||||
of = flag % of
|
of = flag % of
|
||||||
self.cflags.insert(0, of)
|
self.cflags.insert(0, of)
|
||||||
|
self.only_build_for = kwargs.get('only', '')
|
||||||
|
|
||||||
|
|
||||||
def lazy_load(name):
|
def lazy_load(name):
|
||||||
@ -87,19 +108,27 @@ def lazy_load(name):
|
|||||||
raise ImportError('The setup.build_environment module has no symbol named: %s' % name)
|
raise ImportError('The setup.build_environment module has no symbol named: %s' % name)
|
||||||
|
|
||||||
|
|
||||||
def expand_file_list(items, is_paths=True):
|
def expand_file_list(items, is_paths=True, cross_compile_for='native'):
|
||||||
if not items:
|
if not items:
|
||||||
return []
|
return []
|
||||||
ans = []
|
ans = []
|
||||||
for item in items:
|
for item in items:
|
||||||
if item.startswith('!'):
|
if item.startswith('!'):
|
||||||
|
if cross_compile_for == 'native' or not item.endswith('_dirs'):
|
||||||
item = lazy_load(item)
|
item = lazy_load(item)
|
||||||
if hasattr(item, 'rjust'):
|
if hasattr(item, 'rjust'):
|
||||||
item = [item]
|
item = [item]
|
||||||
ans.extend(expand_file_list(item, is_paths=is_paths))
|
items = expand_file_list(item, is_paths=is_paths, cross_compile_for=cross_compile_for)
|
||||||
|
else:
|
||||||
|
pkg, category = item[1:].split('_')[:2]
|
||||||
|
if category == 'inc':
|
||||||
|
category = 'include'
|
||||||
|
items = [f'bypy/b/windows/64/{pkg}/{category}']
|
||||||
|
items = expand_file_list(item, is_paths=is_paths, cross_compile_for=cross_compile_for)
|
||||||
|
ans.extend(items)
|
||||||
else:
|
else:
|
||||||
if '*' in item:
|
if '*' in item:
|
||||||
ans.extend(expand_file_list(sorted(glob.glob(os.path.join(SRC, item))), is_paths=is_paths))
|
ans.extend(expand_file_list(sorted(glob.glob(os.path.join(SRC, item))), is_paths=is_paths, cross_compile_for=cross_compile_for))
|
||||||
else:
|
else:
|
||||||
item = [item]
|
item = [item]
|
||||||
if is_paths:
|
if is_paths:
|
||||||
@ -108,35 +137,40 @@ def expand_file_list(items, is_paths=True):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def is_ext_allowed(ext):
|
def is_ext_allowed(cross_compile_for: str, ext: Extension) -> bool:
|
||||||
only = ext.get('only', '')
|
only = ext.only_build_for
|
||||||
if only:
|
if only:
|
||||||
|
if islinux and only == cross_compile_for:
|
||||||
|
return True
|
||||||
only = set(only.split())
|
only = set(only.split())
|
||||||
q = set(filter(lambda x: globals()["is" + x], ["bsd", "freebsd", "haiku", "linux", "macos", "windows"]))
|
q = set(filter(lambda x: globals()["is" + x], ["bsd", "freebsd", "haiku", "linux", "macos", "windows"]))
|
||||||
return len(q.intersection(only)) > 0
|
return len(q.intersection(only)) > 0
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def parse_extension(ext):
|
def parse_extension(ext, compiling_for='native'):
|
||||||
ext = ext.copy()
|
ext = ext.copy()
|
||||||
ext.pop('only', None)
|
only = ext.pop('only', None)
|
||||||
kw = {}
|
kw = {}
|
||||||
name = ext.pop('name')
|
name = ext.pop('name')
|
||||||
|
get_key = 'linux_'
|
||||||
|
if iswindows:
|
||||||
|
get_key = 'windows_'
|
||||||
|
elif ismacos:
|
||||||
|
get_key = 'macos_'
|
||||||
|
elif isbsd:
|
||||||
|
get_key = 'bsd_'
|
||||||
|
elif isfreebsd:
|
||||||
|
get_key = 'freebsd_'
|
||||||
|
elif ishaiku:
|
||||||
|
get_key = 'haiku_'
|
||||||
|
if compiling_for == 'windows':
|
||||||
|
get_key = 'windows_'
|
||||||
|
|
||||||
|
|
||||||
def get(k, default=''):
|
def get(k, default=''):
|
||||||
ans = ext.pop(k, default)
|
ans = ext.pop(k, default)
|
||||||
if iswindows:
|
ans = ext.pop(get_key + k, ans)
|
||||||
ans = ext.pop('windows_' + k, ans)
|
|
||||||
elif ismacos:
|
|
||||||
ans = ext.pop('macos_' + k, ans)
|
|
||||||
elif isbsd:
|
|
||||||
ans = ext.pop('bsd_' + k, ans)
|
|
||||||
elif isfreebsd:
|
|
||||||
ans = ext.pop('freebsd_' + k, ans)
|
|
||||||
elif ishaiku:
|
|
||||||
ans = ext.pop('haiku_' + k, ans)
|
|
||||||
else:
|
|
||||||
ans = ext.pop('linux_' + k, ans)
|
|
||||||
return ans
|
return ans
|
||||||
for k in 'libraries qt_private ldflags cflags error'.split():
|
for k in 'libraries qt_private ldflags cflags error'.split():
|
||||||
kw[k] = expand_file_list(get(k).split(), is_paths=False)
|
kw[k] = expand_file_list(get(k).split(), is_paths=False)
|
||||||
@ -145,13 +179,14 @@ def parse_extension(ext):
|
|||||||
if 'cflags' not in kw:
|
if 'cflags' not in kw:
|
||||||
kw['cflags'] = []
|
kw['cflags'] = []
|
||||||
cflags = kw['cflags']
|
cflags = kw['cflags']
|
||||||
prefix = '/D' if iswindows else '-D'
|
prefix = '/D' if get_key == 'windows_' else '-D'
|
||||||
cflags.extend(prefix + x for x in defines.split())
|
cflags.extend(prefix + x for x in defines.split())
|
||||||
for k in 'inc_dirs lib_dirs sources headers sip_files'.split():
|
for k in 'inc_dirs lib_dirs sources headers sip_files'.split():
|
||||||
v = get(k)
|
v = get(k)
|
||||||
if v:
|
if v:
|
||||||
kw[k] = expand_file_list(v.split())
|
kw[k] = expand_file_list(v.split())
|
||||||
kw.update(ext)
|
kw.update(ext)
|
||||||
|
kw['only'] = only
|
||||||
return Extension(name, **kw)
|
return Extension(name, **kw)
|
||||||
|
|
||||||
|
|
||||||
@ -192,9 +227,48 @@ def basic_windows_flags(debug=False):
|
|||||||
return cflags, ldflags
|
return cflags, ldflags
|
||||||
|
|
||||||
|
|
||||||
def init_env(debug=False, sanitize=False):
|
class Environment(NamedTuple):
|
||||||
from setup.build_environment import win_ld, win_inc, win_lib, NMAKE, win_cc
|
cc: str
|
||||||
|
cxx: str
|
||||||
|
linker: str
|
||||||
|
cflags: List[str]
|
||||||
|
ldflags: List[str]
|
||||||
|
make: str
|
||||||
|
internal_inc_prefix: str
|
||||||
|
external_inc_prefix: str
|
||||||
|
libdir_prefix: str
|
||||||
|
lib_prefix: str
|
||||||
|
lib_suffix: str
|
||||||
|
obj_suffix: str
|
||||||
|
cc_input_c_flag: str
|
||||||
|
cc_input_cpp_flag: str
|
||||||
|
cc_output_flag: str
|
||||||
|
platform_name: str
|
||||||
|
dest_ext: str
|
||||||
|
|
||||||
|
def inc_dirs_to_cflags(self, dirs) -> List[str]:
|
||||||
|
return [self.external_inc_prefix+x for x in dirs]
|
||||||
|
|
||||||
|
def lib_dirs_to_ldflags(self, dirs) -> List[str]:
|
||||||
|
return [self.libdir_prefix+x for x in dirs if x]
|
||||||
|
|
||||||
|
def libraries_to_ldflags(self, dirs):
|
||||||
|
return [self.lib_prefix+x+self.lib_suffix for x in dirs]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def init_env(debug=False, sanitize=False, compiling_for='native'):
|
||||||
|
from setup.build_environment import NMAKE, win_cc, win_inc, win_ld, win_lib
|
||||||
linker = None
|
linker = None
|
||||||
|
internal_inc_prefix = external_inc_prefix = '-I'
|
||||||
|
libdir_prefix = '-L'
|
||||||
|
lib_prefix = '-l'
|
||||||
|
lib_suffix = ''
|
||||||
|
obj_suffix = '.o'
|
||||||
|
cc_input_c_flag = cc_input_cpp_flag = '-c'
|
||||||
|
cc_output_flag = '-o'
|
||||||
|
platform_name = 'linux'
|
||||||
|
dest_ext = '.so'
|
||||||
if isunix:
|
if isunix:
|
||||||
cc = os.environ.get('CC', 'gcc')
|
cc = os.environ.get('CC', 'gcc')
|
||||||
cxx = os.environ.get('CXX', 'g++')
|
cxx = os.environ.get('CXX', 'g++')
|
||||||
@ -236,6 +310,7 @@ def init_env(debug=False, sanitize=False):
|
|||||||
ldflags += (sysconfig.get_config_var('LINKFORSHARED') or '').split()
|
ldflags += (sysconfig.get_config_var('LINKFORSHARED') or '').split()
|
||||||
|
|
||||||
if ismacos:
|
if ismacos:
|
||||||
|
platform_name = 'macos'
|
||||||
if is_macos_universal_build:
|
if is_macos_universal_build:
|
||||||
cflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
|
cflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
|
||||||
ldflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
|
ldflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
|
||||||
@ -244,19 +319,50 @@ def init_env(debug=False, sanitize=False):
|
|||||||
cflags.extend(['-fno-common', '-dynamic'])
|
cflags.extend(['-fno-common', '-dynamic'])
|
||||||
cflags.extend('-I' + x for x in get_python_include_paths())
|
cflags.extend('-I' + x for x in get_python_include_paths())
|
||||||
|
|
||||||
if iswindows:
|
if iswindows or compiling_for == 'windows':
|
||||||
|
platform_name = 'windows'
|
||||||
cc = cxx = win_cc
|
cc = cxx = win_cc
|
||||||
|
linker = win_ld
|
||||||
cflags, ldflags = basic_windows_flags(debug)
|
cflags, ldflags = basic_windows_flags(debug)
|
||||||
|
if compiling_for == 'windows':
|
||||||
|
cc = 'clang-cl'
|
||||||
|
linker = 'lld-link'
|
||||||
|
splat = '.build-cache/xwin/splat'
|
||||||
|
for I in 'sdk/include/um sdk/include/cppwinrt sdk/include/shared sdk/include/ucrt crt/include':
|
||||||
|
cflags.append('/external:I')
|
||||||
|
cflags.append(f'{splat}/{I}')
|
||||||
|
for L in './sdk/lib/um/x86_64 crt/lib/x86_64':
|
||||||
|
ldflags.append(f'/libpath:{splat}/{L}')
|
||||||
|
else:
|
||||||
for p in win_inc:
|
for p in win_inc:
|
||||||
cflags.append('-I'+p)
|
cflags.append('-I'+p)
|
||||||
for p in win_lib:
|
for p in win_lib:
|
||||||
if p:
|
if p:
|
||||||
ldflags.append('/LIBPATH:'+p)
|
ldflags.append('/LIBPATH:'+p)
|
||||||
|
internal_inc_prefix = external_inc_prefix = '/I'
|
||||||
|
libdir_prefix = '/libpath:'
|
||||||
|
lib_prefix = ''
|
||||||
|
lib_suffix = '.lib'
|
||||||
|
cc_input_c_flag = '/Tc'
|
||||||
|
cc_input_cpp_flag = '/Tp'
|
||||||
|
cc_output_flag = '/Fo'
|
||||||
|
obj_suffix = '.obj'
|
||||||
|
dest_ext = '.pyd'
|
||||||
|
if compiling_for == 'windows':
|
||||||
|
external_inc_prefix = '/external:I'
|
||||||
|
dest_ext = '.cross-windows-x64' + dest_ext
|
||||||
|
obj_suffix = '.cross-windows-x64' + obj_suffix
|
||||||
|
cflags.append('/external:I')
|
||||||
|
cflags.append('bypy/b/windows/64/pkg/python/private/python/include')
|
||||||
|
ldflags.append('/libpath:' + 'bypy/b/windows/64/pkg/python/private/python/libs')
|
||||||
|
else:
|
||||||
cflags.extend('-I' + x for x in get_python_include_paths())
|
cflags.extend('-I' + x for x in get_python_include_paths())
|
||||||
ldflags.append('/LIBPATH:'+os.path.join(sysconfig.get_config_var('prefix'), 'libs'))
|
ldflags.append('/LIBPATH:'+os.path.join(sysconfig.get_config_var('prefix'), 'libs'))
|
||||||
linker = win_ld
|
return Environment(
|
||||||
return namedtuple('Environment', 'cc cxx cflags ldflags linker make')(
|
platform_name=platform_name, dest_ext=dest_ext,
|
||||||
cc=cc, cxx=cxx, cflags=cflags, ldflags=ldflags, linker=linker, make=NMAKE if iswindows else 'make')
|
cc=cc, cxx=cxx, cflags=cflags, ldflags=ldflags, linker=linker, make=NMAKE if iswindows else 'make', lib_prefix=lib_prefix,
|
||||||
|
obj_suffix=obj_suffix, cc_input_c_flag=cc_input_c_flag, cc_input_cpp_flag=cc_input_cpp_flag, cc_output_flag=cc_output_flag,
|
||||||
|
internal_inc_prefix=internal_inc_prefix, external_inc_prefix=external_inc_prefix, libdir_prefix=libdir_prefix, lib_suffix=lib_suffix)
|
||||||
|
|
||||||
|
|
||||||
class Build(Command):
|
class Build(Command):
|
||||||
@ -285,7 +391,7 @@ class Build(Command):
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
def add_options(self, parser):
|
def add_options(self, parser):
|
||||||
choices = [e['name'] for e in read_extensions() if is_ext_allowed(e)]+['all', 'headless']
|
choices = [e['name'] for e in read_extensions()]+['all', 'headless']
|
||||||
parser.add_option('-1', '--only', choices=choices, default='all',
|
parser.add_option('-1', '--only', choices=choices, default='all',
|
||||||
help=('Build only the named extension. Available: '+ ', '.join(choices)+'. Default:%default'))
|
help=('Build only the named extension. Available: '+ ', '.join(choices)+'. Default:%default'))
|
||||||
parser.add_option('--no-compile', default=False, action='store_true',
|
parser.add_option('--no-compile', default=False, action='store_true',
|
||||||
@ -298,6 +404,9 @@ class Build(Command):
|
|||||||
help='Build in debug mode')
|
help='Build in debug mode')
|
||||||
parser.add_option('--sanitize', default=False, action='store_true',
|
parser.add_option('--sanitize', default=False, action='store_true',
|
||||||
help='Build with sanitization support. Run with LD_PRELOAD=$(gcc -print-file-name=libasan.so)')
|
help='Build with sanitization support. Run with LD_PRELOAD=$(gcc -print-file-name=libasan.so)')
|
||||||
|
parser.add_option('--cross-compile-extensions', choices='windows disabled'.split(), default='disabled',
|
||||||
|
help=('Cross compile extensions for other platforms. Useful for development.'
|
||||||
|
' Currently supports of windows extensions on Linux. Remember to run ./setup.py xwin first to install the Windows SDK locally. '))
|
||||||
|
|
||||||
def dump_db(self, name, db):
|
def dump_db(self, name, db):
|
||||||
os.makedirs('build', exist_ok=True)
|
os.makedirs('build', exist_ok=True)
|
||||||
@ -309,12 +418,18 @@ class Build(Command):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def run(self, opts):
|
def run(self, opts):
|
||||||
from setup.parallel_build import parallel_build, create_job
|
from setup.parallel_build import create_job, parallel_build
|
||||||
if opts.no_compile:
|
if opts.no_compile:
|
||||||
self.info('--no-compile specified, skipping compilation')
|
self.info('--no-compile specified, skipping compilation')
|
||||||
return
|
return
|
||||||
|
self.compiling_for = 'native'
|
||||||
|
if islinux and opts.cross_compile_extensions == 'windows':
|
||||||
|
self.compiling_for = 'windows'
|
||||||
|
if not os.path.exists('.build-cache/xwin/splat'):
|
||||||
|
subprocess.check_call([sys.executable, 'setup.py', 'xwin'])
|
||||||
self.env = init_env(debug=opts.debug)
|
self.env = init_env(debug=opts.debug)
|
||||||
all_extensions = map(parse_extension, filter(is_ext_allowed, read_extensions()))
|
self.windows_cross_env = init_env(debug=opts.debug, compiling_for='windows')
|
||||||
|
all_extensions = tuple(map(partial(parse_extension, compiling_for=self.compiling_for), read_extensions()))
|
||||||
self.build_dir = os.path.abspath(opts.build_dir or self.DEFAULT_BUILDDIR)
|
self.build_dir = os.path.abspath(opts.build_dir or self.DEFAULT_BUILDDIR)
|
||||||
self.output_dir = os.path.abspath(opts.output_dir or self.DEFAULT_OUTPUTDIR)
|
self.output_dir = os.path.abspath(opts.output_dir or self.DEFAULT_OUTPUTDIR)
|
||||||
self.obj_dir = os.path.join(self.build_dir, 'objects')
|
self.obj_dir = os.path.join(self.build_dir, 'objects')
|
||||||
@ -324,23 +439,26 @@ class Build(Command):
|
|||||||
for ext in all_extensions:
|
for ext in all_extensions:
|
||||||
if opts.only != 'all' and opts.only != ext.name:
|
if opts.only != 'all' and opts.only != ext.name:
|
||||||
continue
|
continue
|
||||||
|
if not is_ext_allowed(self.compiling_for, ext):
|
||||||
|
continue
|
||||||
if ext.error:
|
if ext.error:
|
||||||
if ext.optional:
|
if ext.optional:
|
||||||
self.warn(ext.error)
|
self.warn(ext.error)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise Exception(ext.error)
|
raise Exception(ext.error)
|
||||||
dest = self.dest(ext)
|
(pyqt_extensions if ext.sip_files else extensions).append(ext)
|
||||||
os.makedirs(self.d(dest), exist_ok=True)
|
|
||||||
(pyqt_extensions if ext.sip_files else extensions).append((ext, dest))
|
|
||||||
|
|
||||||
jobs = []
|
jobs = []
|
||||||
objects_map = {}
|
objects_map = {}
|
||||||
self.info(f'Building {len(extensions)+len(pyqt_extensions)} extensions')
|
self.info(f'Building {len(extensions)+len(pyqt_extensions)} extensions')
|
||||||
ccdb = []
|
ccdb = []
|
||||||
for (ext, dest) in extensions:
|
for ext in all_extensions:
|
||||||
cmds, objects = self.get_compile_commands(ext, dest, ccdb)
|
if ext in pyqt_extensions:
|
||||||
|
continue
|
||||||
|
cmds, objects = self.get_compile_commands(ext, ccdb)
|
||||||
objects_map[id(ext)] = objects
|
objects_map[id(ext)] = objects
|
||||||
|
if ext in extensions:
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
jobs.append(create_job(cmd.cmd))
|
jobs.append(create_job(cmd.cmd))
|
||||||
self.dump_db('compile', ccdb)
|
self.dump_db('compile', ccdb)
|
||||||
@ -349,10 +467,12 @@ class Build(Command):
|
|||||||
if not parallel_build(jobs, self.info):
|
if not parallel_build(jobs, self.info):
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
jobs, link_commands, lddb = [], [], []
|
jobs, link_commands, lddb = [], [], []
|
||||||
for (ext, dest) in extensions:
|
for ext in all_extensions:
|
||||||
|
if ext in pyqt_extensions:
|
||||||
|
continue
|
||||||
objects = objects_map[id(ext)]
|
objects = objects_map[id(ext)]
|
||||||
cmd = self.get_link_command(ext, dest, objects, lddb)
|
cmd = self.get_link_command(ext, objects, lddb)
|
||||||
if cmd is not None:
|
if ext in extensions and cmd is not None:
|
||||||
link_commands.append(cmd)
|
link_commands.append(cmd)
|
||||||
jobs.append(create_job(cmd.cmd))
|
jobs.append(create_job(cmd.cmd))
|
||||||
self.dump_db('link', lddb)
|
self.dump_db('link', lddb)
|
||||||
@ -365,7 +485,7 @@ class Build(Command):
|
|||||||
|
|
||||||
jobs = []
|
jobs = []
|
||||||
sbf_map = {}
|
sbf_map = {}
|
||||||
for (ext, dest) in pyqt_extensions:
|
for ext in pyqt_extensions:
|
||||||
cmd, sbf, cwd = self.get_sip_commands(ext)
|
cmd, sbf, cwd = self.get_sip_commands(ext)
|
||||||
sbf_map[id(ext)] = sbf
|
sbf_map[id(ext)] = sbf
|
||||||
if cmd is not None:
|
if cmd is not None:
|
||||||
@ -374,58 +494,67 @@ class Build(Command):
|
|||||||
self.info(f'SIPing {len(jobs)} files...')
|
self.info(f'SIPing {len(jobs)} files...')
|
||||||
if not parallel_build(jobs, self.info):
|
if not parallel_build(jobs, self.info):
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
for (ext, dest) in pyqt_extensions:
|
for ext in pyqt_extensions:
|
||||||
sbf = sbf_map[id(ext)]
|
sbf = sbf_map[id(ext)]
|
||||||
if not os.path.exists(sbf):
|
if not os.path.exists(sbf):
|
||||||
self.build_pyqt_extension(ext, dest, sbf)
|
self.build_pyqt_extension(ext, sbf)
|
||||||
|
|
||||||
if opts.only in {'all', 'headless'}:
|
if opts.only in {'all', 'headless'}:
|
||||||
self.build_headless()
|
self.build_headless()
|
||||||
|
|
||||||
def dest(self, ext):
|
def dest(self, ext, env):
|
||||||
ex = '.pyd' if iswindows else '.so'
|
return os.path.join(self.output_dir, getattr(ext, 'name', ext))+env.dest_ext
|
||||||
return os.path.join(self.output_dir, getattr(ext, 'name', ext))+ex
|
|
||||||
|
|
||||||
def inc_dirs_to_cflags(self, dirs):
|
def env_for_compilation_db(self, ext):
|
||||||
return ['-I'+x for x in dirs]
|
if is_ext_allowed('native', ext):
|
||||||
|
return self.env
|
||||||
|
if ext.only_build_for == 'windows':
|
||||||
|
return self.windows_cross_env
|
||||||
|
|
||||||
def lib_dirs_to_ldflags(self, dirs):
|
def get_compile_commands(self, ext, db):
|
||||||
pref = '/LIBPATH:' if iswindows else '-L'
|
obj_dir = self.j(self.obj_dir, ext.name)
|
||||||
return [pref+x for x in dirs if x]
|
|
||||||
|
|
||||||
def libraries_to_ldflags(self, dirs):
|
def get(src: str, env: Environment) -> CompileCommand:
|
||||||
pref = '' if iswindows else '-l'
|
compiler = env.cxx if ext.needs_cxx else env.cc
|
||||||
suff = '.lib' if iswindows else ''
|
obj = self.j(obj_dir, os.path.splitext(self.b(src))[0]+env.obj_suffix)
|
||||||
return [pref+x+suff for x in dirs]
|
inf = env.cc_input_cpp_flag if src.endswith('.cpp') or src.endswith('.cxx') else env.cc_input_c_flag
|
||||||
|
sinc = [inf, src]
|
||||||
|
if env.cc_output_flag.startswith('/'):
|
||||||
|
oinc = [env.cc_output_flag + obj]
|
||||||
|
sinc = [inf + src]
|
||||||
|
else:
|
||||||
|
oinc = [env.cc_output_flag, obj]
|
||||||
|
einc = env.inc_dirs_to_cflags(ext.inc_dirs)
|
||||||
|
cmd = [compiler] + env.cflags + ext.cflags + einc + sinc + oinc
|
||||||
|
return CompileCommand(cmd, src, obj)
|
||||||
|
|
||||||
def get_compile_commands(self, ext, dest, db):
|
|
||||||
compiler = self.env.cxx if ext.needs_cxx else self.env.cc
|
|
||||||
objects = []
|
objects = []
|
||||||
ans = []
|
ans = []
|
||||||
obj_dir = self.j(self.obj_dir, ext.name)
|
|
||||||
einc = self.inc_dirs_to_cflags(ext.inc_dirs)
|
|
||||||
os.makedirs(obj_dir, exist_ok=True)
|
os.makedirs(obj_dir, exist_ok=True)
|
||||||
|
|
||||||
for src in ext.sources:
|
for src in ext.sources:
|
||||||
obj = self.j(obj_dir, os.path.splitext(self.b(src))[0]+'.o')
|
cc = get(src, self.windows_cross_env if self.compiling_for == 'windows' else self.env)
|
||||||
objects.append(obj)
|
objects.append(cc.dest)
|
||||||
inf = '/Tp' if src.endswith('.cpp') or src.endswith('.cxx') else '/Tc'
|
if self.newer(cc.dest, [src]+ext.headers):
|
||||||
sinc = [inf+src] if iswindows else ['-c', src]
|
ans.append(cc)
|
||||||
oinc = ['/Fo'+obj] if iswindows else ['-o', obj]
|
env = self.env_for_compilation_db(ext)
|
||||||
cmd = [compiler] + self.env.cflags + ext.cflags + einc + sinc + oinc
|
if env is None:
|
||||||
db.append({'arguments': cmd, 'directory': os.getcwd(), 'file': os.path.relpath(src, os.getcwd()), 'output': os.path.relpath(obj, os.getcwd())})
|
continue
|
||||||
if self.newer(obj, [src]+ext.headers):
|
db.append({
|
||||||
ans.append(CompileCommand(cmd, src, obj))
|
'arguments': get(src, env).cmd, 'directory': os.getcwd(), 'file': os.path.relpath(src, os.getcwd()),
|
||||||
|
'output': os.path.relpath(cc.dest, os.getcwd())})
|
||||||
return ans, objects
|
return ans, objects
|
||||||
|
|
||||||
def get_link_command(self, ext, dest, objects, lddb):
|
def get_link_command(self, ext, objects, lddb):
|
||||||
compiler = self.env.cxx if ext.needs_cxx else self.env.cc
|
|
||||||
linker = self.env.linker if iswindows else compiler
|
def get(env: Environment) -> LinkCommand:
|
||||||
dest = self.dest(ext)
|
dest = self.dest(ext, env)
|
||||||
elib = self.lib_dirs_to_ldflags(ext.lib_dirs)
|
compiler = env.cxx if ext.needs_cxx else env.cc
|
||||||
xlib = self.libraries_to_ldflags(ext.libraries)
|
linker = self.env.linker or compiler
|
||||||
cmd = [linker]
|
cmd = [linker]
|
||||||
if iswindows:
|
elib = env.lib_dirs_to_ldflags(ext.lib_dirs)
|
||||||
|
xlib = env.libraries_to_ldflags(ext.libraries)
|
||||||
|
if iswindows or env is self.windows_cross_env:
|
||||||
pre_ld_flags = []
|
pre_ld_flags = []
|
||||||
if ext.uses_icu:
|
if ext.uses_icu:
|
||||||
# windows has its own ICU libs that dont work
|
# windows has its own ICU libs that dont work
|
||||||
@ -434,11 +563,18 @@ class Build(Command):
|
|||||||
['/EXPORT:' + init_symbol_name(ext.name)] + objects + ext.extra_objs + ['/OUT:'+dest]
|
['/EXPORT:' + init_symbol_name(ext.name)] + objects + ext.extra_objs + ['/OUT:'+dest]
|
||||||
else:
|
else:
|
||||||
cmd += objects + ext.extra_objs + ['-o', dest] + self.env.ldflags + ext.ldflags + elib + xlib
|
cmd += objects + ext.extra_objs + ['-o', dest] + self.env.ldflags + ext.ldflags + elib + xlib
|
||||||
lddb.append({'arguments': cmd, 'directory': os.getcwd(), 'output': os.path.relpath(dest, os.getcwd())})
|
|
||||||
|
|
||||||
if self.newer(dest, objects+ext.extra_objs):
|
|
||||||
return LinkCommand(cmd, objects, dest)
|
return LinkCommand(cmd, objects, dest)
|
||||||
|
|
||||||
|
env = self.env_for_compilation_db(ext)
|
||||||
|
if env is not None:
|
||||||
|
ld = get(env)
|
||||||
|
lddb.append({'arguments': ld.cmd, 'directory': os.getcwd(), 'output': os.path.relpath(self.dest(ext, env), os.getcwd())})
|
||||||
|
|
||||||
|
env = self.windows_cross_env if self.compiling_for == 'windows' else self.env
|
||||||
|
lc = get(env)
|
||||||
|
if self.newer(lc.dest, objects+ext.extra_objs):
|
||||||
|
return lc
|
||||||
|
|
||||||
def post_link_cleanup(self, link_command):
|
def post_link_cleanup(self, link_command):
|
||||||
if iswindows:
|
if iswindows:
|
||||||
dest = link_command.dest
|
dest = link_command.dest
|
||||||
@ -478,7 +614,7 @@ class Build(Command):
|
|||||||
'calibre/headless/headless_integration.cpp',
|
'calibre/headless/headless_integration.cpp',
|
||||||
])
|
])
|
||||||
others = a(['calibre/headless/headless.json'])
|
others = a(['calibre/headless/headless.json'])
|
||||||
target = self.dest('headless')
|
target = self.dest('headless', self.env)
|
||||||
if not ismacos:
|
if not ismacos:
|
||||||
target = target.replace('headless', 'libheadless')
|
target = target.replace('headless', 'libheadless')
|
||||||
if not self.newer(target, headers + sources + others):
|
if not self.newer(target, headers + sources + others):
|
||||||
@ -557,7 +693,7 @@ sip-file = "{os.path.basename(sipf)}"
|
|||||||
]
|
]
|
||||||
return cmd, sbf, cwd
|
return cmd, sbf, cwd
|
||||||
|
|
||||||
def build_pyqt_extension(self, ext, dest, sbf):
|
def build_pyqt_extension(self, ext, sbf):
|
||||||
self.info(f'\n####### Building {ext.name} extension', '#'*7)
|
self.info(f'\n####### Building {ext.name} extension', '#'*7)
|
||||||
src_dir = os.path.dirname(sbf)
|
src_dir = os.path.dirname(sbf)
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
@ -570,7 +706,7 @@ sip-file = "{os.path.basename(sipf)}"
|
|||||||
raise SystemExit(f'No built PyQt extension file in {os.path.join(os.getcwd(), ext.name)}')
|
raise SystemExit(f'No built PyQt extension file in {os.path.join(os.getcwd(), ext.name)}')
|
||||||
if len(m) != 1:
|
if len(m) != 1:
|
||||||
raise SystemExit(f'Found extra PyQt extension files: {m}')
|
raise SystemExit(f'Found extra PyQt extension files: {m}')
|
||||||
shutil.copy2(m[0], dest)
|
shutil.copy2(m[0], self.dest(ext, self.env))
|
||||||
with open(sbf, 'w') as f:
|
with open(sbf, 'w') as f:
|
||||||
f.write('done')
|
f.write('done')
|
||||||
finally:
|
finally:
|
||||||
@ -578,10 +714,13 @@ sip-file = "{os.path.basename(sipf)}"
|
|||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.output_dir = self.DEFAULT_OUTPUTDIR
|
self.output_dir = self.DEFAULT_OUTPUTDIR
|
||||||
extensions = map(parse_extension, filter(is_ext_allowed, read_extensions()))
|
extensions = map(parse_extension, read_extensions())
|
||||||
|
env = init_env()
|
||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
dest = self.dest(ext)
|
dest = self.dest(ext, env)
|
||||||
for x in (dest, dest+'.manifest'):
|
b, d = os.path.basename(dest), os.path.dirname(dest)
|
||||||
|
b = b.split('.')[0] + '.*'
|
||||||
|
for x in glob.glob(os.path.join(d, b)):
|
||||||
if os.path.exists(x):
|
if os.path.exists(x):
|
||||||
os.remove(x)
|
os.remove(x)
|
||||||
build_dir = self.DEFAULT_BUILDDIR
|
build_dir = self.DEFAULT_BUILDDIR
|
||||||
|
@ -5,10 +5,13 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, subprocess, re, shutil
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
from setup import ismacos, iswindows, islinux, ishaiku
|
from setup import isfreebsd, ishaiku, islinux, ismacos, iswindows
|
||||||
|
|
||||||
NMAKE = RC = msvc = MT = win_inc = win_lib = win_cc = win_ld = None
|
NMAKE = RC = msvc = MT = win_inc = win_lib = win_cc = win_ld = None
|
||||||
|
|
||||||
@ -115,23 +118,35 @@ def readvar(name):
|
|||||||
|
|
||||||
qt = {x:readvar(y) for x, y in {'libs':'QT_INSTALL_LIBS', 'plugins':'QT_INSTALL_PLUGINS'}.items()}
|
qt = {x:readvar(y) for x, y in {'libs':'QT_INSTALL_LIBS', 'plugins':'QT_INSTALL_PLUGINS'}.items()}
|
||||||
qmakespec = readvar('QMAKE_SPEC') if iswindows else None
|
qmakespec = readvar('QMAKE_SPEC') if iswindows else None
|
||||||
ft_lib_dirs = []
|
freetype_lib_dirs = []
|
||||||
ft_libs = []
|
freetype_libs = []
|
||||||
ft_inc_dirs = []
|
freetype_inc_dirs = []
|
||||||
|
|
||||||
podofo_inc = '/usr/include/podofo'
|
podofo_inc = '/usr/include/podofo'
|
||||||
podofo_lib = '/usr/lib'
|
podofo_lib = '/usr/lib'
|
||||||
|
|
||||||
|
usb_library = 'usb' if isfreebsd else 'usb-1.0'
|
||||||
|
|
||||||
chmlib_inc_dirs = chmlib_lib_dirs = []
|
chmlib_inc_dirs = chmlib_lib_dirs = []
|
||||||
|
|
||||||
sqlite_inc_dirs = []
|
sqlite_inc_dirs = []
|
||||||
|
|
||||||
icu_inc_dirs = []
|
icu_inc_dirs = []
|
||||||
icu_lib_dirs = []
|
icu_lib_dirs = []
|
||||||
|
|
||||||
zlib_inc_dirs = []
|
zlib_inc_dirs = []
|
||||||
zlib_lib_dirs = []
|
zlib_lib_dirs = []
|
||||||
|
|
||||||
hunspell_inc_dirs = []
|
hunspell_inc_dirs = []
|
||||||
hunspell_lib_dirs = []
|
hunspell_lib_dirs = []
|
||||||
|
|
||||||
hyphen_inc_dirs = []
|
hyphen_inc_dirs = []
|
||||||
hyphen_lib_dirs = []
|
hyphen_lib_dirs = []
|
||||||
|
|
||||||
uchardet_inc_dirs, uchardet_lib_dirs, uchardet_libs = [], [], ['uchardet']
|
uchardet_inc_dirs, uchardet_lib_dirs, uchardet_libs = [], [], ['uchardet']
|
||||||
|
|
||||||
openssl_inc_dirs, openssl_lib_dirs = [], []
|
openssl_inc_dirs, openssl_lib_dirs = [], []
|
||||||
|
|
||||||
ICU = sw = ''
|
ICU = sw = ''
|
||||||
|
|
||||||
if iswindows:
|
if iswindows:
|
||||||
@ -149,9 +164,9 @@ if iswindows:
|
|||||||
sqlite_inc_dirs = [sw_inc_dir]
|
sqlite_inc_dirs = [sw_inc_dir]
|
||||||
chmlib_inc_dirs = [sw_inc_dir]
|
chmlib_inc_dirs = [sw_inc_dir]
|
||||||
chmlib_lib_dirs = [sw_lib_dir]
|
chmlib_lib_dirs = [sw_lib_dir]
|
||||||
ft_lib_dirs = [sw_lib_dir]
|
freetype_lib_dirs = [sw_lib_dir]
|
||||||
ft_libs = ['freetype']
|
freetype_libs = ['freetype']
|
||||||
ft_inc_dirs = [os.path.join(sw_inc_dir, 'freetype2'), sw_inc_dir]
|
freetype_inc_dirs = [os.path.join(sw_inc_dir, 'freetype2'), sw_inc_dir]
|
||||||
hunspell_inc_dirs = [os.path.join(sw_inc_dir, 'hunspell')]
|
hunspell_inc_dirs = [os.path.join(sw_inc_dir, 'hunspell')]
|
||||||
hunspell_lib_dirs = [sw_lib_dir]
|
hunspell_lib_dirs = [sw_lib_dir]
|
||||||
zlib_inc_dirs = [sw_inc_dir]
|
zlib_inc_dirs = [sw_inc_dir]
|
||||||
@ -166,8 +181,8 @@ elif ismacos:
|
|||||||
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
|
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
|
||||||
hunspell_inc_dirs = [os.path.join(sw_inc_dir, 'hunspell')]
|
hunspell_inc_dirs = [os.path.join(sw_inc_dir, 'hunspell')]
|
||||||
podofo_lib = sw_lib_dir
|
podofo_lib = sw_lib_dir
|
||||||
ft_libs = ['freetype']
|
freetype_libs = ['freetype']
|
||||||
ft_inc_dirs = [sw + '/include/freetype2']
|
freetype_inc_dirs = [sw + '/include/freetype2']
|
||||||
uchardet_inc_dirs = [sw + '/include/uchardet']
|
uchardet_inc_dirs = [sw + '/include/uchardet']
|
||||||
SSL = os.environ.get('OPENSSL_DIR', os.path.join(sw, 'private', 'ssl'))
|
SSL = os.environ.get('OPENSSL_DIR', os.path.join(sw, 'private', 'ssl'))
|
||||||
openssl_inc_dirs = [os.path.join(SSL, 'include')]
|
openssl_inc_dirs = [os.path.join(SSL, 'include')]
|
||||||
@ -175,10 +190,10 @@ elif ismacos:
|
|||||||
if os.path.exists(os.path.join(sw_bin_dir, 'cmake')):
|
if os.path.exists(os.path.join(sw_bin_dir, 'cmake')):
|
||||||
CMAKE = os.path.join(sw_bin_dir, 'cmake')
|
CMAKE = os.path.join(sw_bin_dir, 'cmake')
|
||||||
else:
|
else:
|
||||||
ft_inc_dirs = pkgconfig_include_dirs('freetype2', 'FT_INC_DIR',
|
freetype_inc_dirs = pkgconfig_include_dirs('freetype2', 'FT_INC_DIR',
|
||||||
'/usr/include/freetype2')
|
'/usr/include/freetype2')
|
||||||
ft_lib_dirs = pkgconfig_lib_dirs('freetype2', 'FT_LIB_DIR', '/usr/lib')
|
freetype_lib_dirs = pkgconfig_lib_dirs('freetype2', 'FT_LIB_DIR', '/usr/lib')
|
||||||
ft_libs = pkgconfig_libs('freetype2', '', '')
|
freetype_libs = pkgconfig_libs('freetype2', '', '')
|
||||||
hunspell_inc_dirs = pkgconfig_include_dirs('hunspell', 'HUNSPELL_INC_DIR', '/usr/include/hunspell')
|
hunspell_inc_dirs = pkgconfig_include_dirs('hunspell', 'HUNSPELL_INC_DIR', '/usr/include/hunspell')
|
||||||
hunspell_lib_dirs = pkgconfig_lib_dirs('hunspell', 'HUNSPELL_LIB_DIR', '/usr/lib')
|
hunspell_lib_dirs = pkgconfig_lib_dirs('hunspell', 'HUNSPELL_LIB_DIR', '/usr/lib')
|
||||||
sw = os.environ.get('SW', os.path.expanduser('~/sw'))
|
sw = os.environ.get('SW', os.path.expanduser('~/sw'))
|
||||||
@ -198,4 +213,5 @@ podofo_error = None if os.path.exists(os.path.join(podofo_inc, 'podofo.h')) else
|
|||||||
('PoDoFo not found on your system. Various PDF related',
|
('PoDoFo not found on your system. Various PDF related',
|
||||||
' functionality will not work. Use the PODOFO_INC_DIR and',
|
' functionality will not work. Use the PODOFO_INC_DIR and',
|
||||||
' PODOFO_LIB_DIR environment variables.')
|
' PODOFO_LIB_DIR environment variables.')
|
||||||
podofo_inc = [podofo_inc, os.path.dirname(podofo_inc)]
|
podofo_inc_dirs = [podofo_inc, os.path.dirname(podofo_inc)]
|
||||||
|
podofo_lib_dirs = [podofo_lib]
|
||||||
|
@ -97,9 +97,9 @@
|
|||||||
{
|
{
|
||||||
"name": "freetype",
|
"name": "freetype",
|
||||||
"sources": "calibre/utils/fonts/freetype.cpp",
|
"sources": "calibre/utils/fonts/freetype.cpp",
|
||||||
"libraries": "!ft_libs",
|
"libraries": "!freetype_libs",
|
||||||
"inc_dirs": "!ft_inc_dirs",
|
"inc_dirs": "!freetype_inc_dirs",
|
||||||
"lib_dirs": "!ft_lib_dirs"
|
"lib_dirs": "!freetype_lib_dirs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "msdes",
|
"name": "msdes",
|
||||||
@ -121,8 +121,8 @@
|
|||||||
"sources": "calibre/utils/podofo/utils.cpp calibre/utils/podofo/output.cpp calibre/utils/podofo/doc.cpp calibre/utils/podofo/outline.cpp calibre/utils/podofo/fonts.cpp calibre/utils/podofo/impose.cpp calibre/utils/podofo/images.cpp calibre/utils/podofo/outlines.cpp calibre/utils/podofo/podofo.cpp",
|
"sources": "calibre/utils/podofo/utils.cpp calibre/utils/podofo/output.cpp calibre/utils/podofo/doc.cpp calibre/utils/podofo/outline.cpp calibre/utils/podofo/fonts.cpp calibre/utils/podofo/impose.cpp calibre/utils/podofo/images.cpp calibre/utils/podofo/outlines.cpp calibre/utils/podofo/podofo.cpp",
|
||||||
"headers": "calibre/utils/podofo/global.h",
|
"headers": "calibre/utils/podofo/global.h",
|
||||||
"libraries": "podofo",
|
"libraries": "podofo",
|
||||||
"lib_dirs": "!podofo_lib",
|
"lib_dirs": "!podofo_lib_dirs",
|
||||||
"inc_dirs": "!podofo_inc",
|
"inc_dirs": "!podofo_inc_dirs",
|
||||||
"error": "!podofo_error",
|
"error": "!podofo_error",
|
||||||
"needs_c++": "11"
|
"needs_c++": "11"
|
||||||
},
|
},
|
||||||
@ -190,7 +190,7 @@
|
|||||||
"headers": "calibre/utils/cpp_binding.h calibre/utils/windows/common.h",
|
"headers": "calibre/utils/cpp_binding.h calibre/utils/windows/common.h",
|
||||||
"sources": "calibre/utils/windows/winspeech.cpp",
|
"sources": "calibre/utils/windows/winspeech.cpp",
|
||||||
"libraries": "WindowsApp",
|
"libraries": "WindowsApp",
|
||||||
"needs_c++": "17",
|
"needs_c++": "20",
|
||||||
"cflags": "/X /Zc:__cplusplus /bigobj /await /permissive- /WX /Zc:twoPhase-"
|
"cflags": "/X /Zc:__cplusplus /bigobj /await /permissive- /WX /Zc:twoPhase-"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -222,15 +222,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "libusb",
|
"name": "libusb",
|
||||||
"only": "macos linux haiku",
|
"only": "macos linux haiku freebsd",
|
||||||
"sources": "calibre/devices/libusb/libusb.c",
|
"sources": "calibre/devices/libusb/libusb.c",
|
||||||
"libraries": "usb-1.0"
|
"libraries": "!usb_library"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "libusb",
|
|
||||||
"only": "freebsd",
|
|
||||||
"sources": "calibre/devices/libusb/libusb.c",
|
|
||||||
"libraries": "usb"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "libmtp",
|
"name": "libmtp",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user