Last active
August 29, 2015 14:01
-
-
Save mn4367/04c5590e47bbe2a6f204 to your computer and use it in GitHub Desktop.
OS X support for wkhtmltopdf build script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Copyright 2014 wkhtmltopdf authors | |
# | |
# This file is part of wkhtmltopdf. | |
# | |
# wkhtmltopdf is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU Lesser General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# wkhtmltopdf is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU Lesser General Public License | |
# along with wkhtmltopdf. If not, see <http:#www.gnu.org/licenses/>. | |
# --------------------------------------------------------------- CONFIGURATION | |
OPENSSL = { | |
'url' : 'http://www.openssl.org/source/openssl-1.0.1g.tar.gz', | |
'sha1' : 'b28b3bcb1dc3ee7b55024c9f795be60eb3183e3c', | |
'build' : { | |
'msvc*-win32*': { | |
'configure' : 'VC-WIN32 no-asm', | |
'debug' : 'debug-VC-WIN32 no-asm', | |
'build' : ['ms\\do_ms.bat', 'nmake /f ms\\nt.mak install'], | |
'libs' : ['ssleay32.lib', 'libeay32.lib'], | |
'os_libs' : '-lUser32 -lAdvapi32 -lGdi32 -lCrypt32' | |
}, | |
'msvc*-win64*': { | |
'configure' : 'VC-WIN64A', | |
'debug' : 'debug-VC-WIN64A', | |
'build' : ['ms\\do_win64a.bat', 'nmake /f ms\\nt.mak install'], | |
'libs' : ['ssleay32.lib', 'libeay32.lib'], | |
'os_libs' : '-lUser32 -lAdvapi32 -lGdi32 -lCrypt32' | |
}, | |
'mingw-w64-cross-win32': { | |
'configure' : '--cross-compile-prefix=i686-w64-mingw32- no-shared no-asm mingw64', | |
'build' : ['make', 'make install_sw'], | |
'libs' : ['libssl.a', 'libcrypto.a'], | |
'os_libs' : '-lws2_32 -lgdi32 -lcrypt32' | |
}, | |
'mingw-w64-cross-win64': { | |
'configure' : '--cross-compile-prefix=x86_64-w64-mingw32- no-shared no-asm mingw64', | |
'build' : ['make', 'make install_sw'], | |
'libs' : ['libssl.a', 'libcrypto.a'], | |
'os_libs' : '-lws2_32 -lgdi32 -lcrypt32' | |
} | |
} | |
} | |
QT_CONFIG = { | |
'common' : [ | |
'-opensource', | |
'-confirm-license', | |
'-fast', | |
'-release', | |
'-static', | |
'-graphicssystem raster', | |
'-webkit', | |
'-exceptions', # required by XmlPatterns | |
'-xmlpatterns', # required for TOC support | |
'-qt-zlib', # use bundled versions of libraries | |
'-qt-libpng', | |
'-qt-libjpeg', | |
'-no-libmng', | |
'-no-libtiff', | |
'-no-accessibility', | |
'-no-stl', | |
'-no-qt3support', | |
'-no-phonon', | |
'-no-phonon-backend', | |
'-no-opengl', | |
'-no-declarative', | |
'-no-scripttools', | |
'-no-sql-ibase', | |
'-no-sql-mysql', | |
'-no-sql-odbc', | |
'-no-sql-psql', | |
'-no-sql-sqlite', | |
'-no-sql-sqlite2', | |
'-no-mmx', | |
'-no-3dnow', | |
'-no-sse', | |
'-no-sse2', | |
'-no-multimedia', | |
'-nomake demos', | |
'-nomake docs', | |
'-nomake examples', | |
'-nomake tools', | |
'-nomake tests', | |
'-nomake translations' | |
], | |
'msvc': [ | |
'-mp', | |
'-no-script', | |
'-qt-style-windows', | |
'-qt-style-cleanlooks', | |
'-no-style-windowsxp', | |
'-no-style-windowsvista', | |
'-no-style-plastique', | |
'-no-style-motif', | |
'-no-style-cde', | |
'-openssl-linked' # static linkage for OpenSSL | |
], | |
'posix': [ | |
'-silent', # perform a silent build | |
'-script', # "make install" does not copy QtScript/qscriptengine.h | |
'-xrender', # xrender support is required | |
'-largefile', | |
'-rpath', | |
'-openssl', # load OpenSSL binaries at runtime | |
'-no-dbus', | |
'-no-nis', | |
'-no-cups', | |
'-no-iconv', | |
'-no-pch', | |
'-no-gtkstyle', | |
'-no-nas-sound', | |
'-no-sm', | |
'-no-xshape', | |
'-no-xinerama', | |
'-no-xcursor', | |
'-no-xfixes', | |
'-no-xrandr', | |
'-no-mitshm', | |
'-no-xinput', | |
'-no-xkb', | |
'-no-glib', | |
'-no-gstreamer', | |
'-D ENABLE_VIDEO=0', # required as otherwise gstreamer gets linked in | |
'-no-openvg', | |
'-no-xsync', | |
'-no-audio-backend', | |
'-no-sse3', | |
'-no-ssse3', | |
'-no-sse4.1', | |
'-no-sse4.2', | |
'-no-avx', | |
'-no-neon' | |
], | |
'mingw-w64-cross' : [ | |
'-silent', # perform a silent build | |
'-script', # "make install" does not copy QtScript/qscriptengine.h | |
'-openssl-linked', # static linkage for OpenSSL | |
'-no-reduce-exports', | |
'-no-rpath', | |
'-xplatform win32-g++-4.6' | |
], | |
'osx': [ | |
'-no-framework', | |
'-no-dwarf2', | |
'-xrender', # xrender support is required | |
'-openssl', # load OpenSSL binaries at runtime | |
'-largefile', | |
'-rpath' | |
] | |
} | |
BUILDERS = { | |
'msvc2008-win32': 'msvc', | |
'msvc2008-win64': 'msvc', | |
'msvc2010-win32': 'msvc', | |
'msvc2010-win64': 'msvc', | |
'msvc2012-win32': 'msvc', | |
'msvc2012-win64': 'msvc', | |
'msvc2013-win32': 'msvc', | |
'msvc2013-win64': 'msvc', | |
'msvc-winsdk71-win32': 'msvc_winsdk71', | |
'msvc-winsdk71-win64': 'msvc_winsdk71', | |
'setup-mingw-w64': 'setup_mingw64', | |
'setup-schroot-centos5': 'setup_schroot', | |
'setup-schroot-centos6': 'setup_schroot', | |
'setup-schroot-wheezy': 'setup_schroot', | |
'setup-schroot-trusty': 'setup_schroot', | |
'setup-schroot-precise': 'setup_schroot', | |
'update-all-schroots': 'update_schroot', | |
'centos5-i386': 'linux_schroot', | |
'centos5-amd64': 'linux_schroot', | |
'centos6-i386': 'linux_schroot', | |
'centos6-amd64': 'linux_schroot', | |
'wheezy-i386': 'linux_schroot', | |
'wheezy-amd64': 'linux_schroot', | |
'trusty-i386': 'linux_schroot', | |
'trusty-amd64': 'linux_schroot', | |
'precise-i386': 'linux_schroot', | |
'precise-amd64': 'linux_schroot', | |
'mingw-w64-cross-win32': 'mingw64_cross', | |
'mingw-w64-cross-win64': 'mingw64_cross', | |
'posix-local': 'posix_local', | |
'osx-cocoa-x86-64': 'osx', | |
'osx-carbon-i386': 'osx' | |
} | |
CHROOT_SETUP = { | |
'wheezy': [ | |
('debootstrap', 'wheezy', 'http://ftp.us.debian.org/debian/'), | |
('write_file', 'etc/apt/sources.list', """ | |
deb http://ftp.debian.org/debian/ wheezy main contrib non-free | |
deb http://ftp.debian.org/debian/ wheezy-updates main contrib non-free | |
deb http://security.debian.org/ wheezy/updates main contrib non-free | |
deb-src http://ftp.debian.org/debian/ wheezy main contrib non-free | |
deb-src http://ftp.debian.org/debian/ wheezy-updates main contrib non-free | |
deb-src http://security.debian.org/ wheezy/updates main contrib non-free"""), | |
('shell', 'apt-get update'), | |
('shell', 'apt-get dist-upgrade --assume-yes'), | |
('shell', 'apt-get install --assume-yes xz-utils'), | |
('shell', 'apt-get build-dep --assume-yes libqt4-core'), | |
('write_file', 'update.sh', 'apt-get update\napt-get dist-upgrade --assume-yes\n'), | |
('schroot_conf', 'Debian Wheezy') | |
], | |
'trusty': [ | |
('debootstrap', 'trusty', 'http://archive.ubuntu.com/ubuntu/'), | |
('write_file', 'etc/apt/sources.list', """ | |
deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse | |
deb http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse | |
deb http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse"""), | |
('shell', 'apt-get update'), | |
('shell', 'apt-get dist-upgrade --assume-yes'), | |
('shell', 'apt-get install --assume-yes xz-utils'), | |
('shell', 'apt-get build-dep --assume-yes libqt4-core'), | |
('write_file', 'update.sh', 'apt-get update\napt-get dist-upgrade --assume-yes\n'), | |
('schroot_conf', 'Ubuntu Trusty') | |
], | |
'precise': [ | |
('debootstrap', 'precise', 'http://archive.ubuntu.com/ubuntu/'), | |
('write_file', 'etc/apt/sources.list', """ | |
deb http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse | |
deb http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse | |
deb http://archive.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse | |
deb-src http://archive.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse"""), | |
('shell', 'apt-get update'), | |
('shell', 'apt-get dist-upgrade --assume-yes'), | |
('shell', 'apt-get install --assume-yes xz-utils'), | |
('shell', 'apt-get build-dep --assume-yes libqt4-core'), | |
('write_file', 'update.sh', 'apt-get update\napt-get dist-upgrade --assume-yes\n'), | |
('schroot_conf', 'Ubuntu Precise') | |
], | |
'centos5': [ | |
('rinse', 'centos-5'), | |
('shell', 'yum update -y'), | |
('append_file:amd64', 'etc/yum.conf', 'exclude = *.i?86\n'), | |
('shell', 'yum install -y gcc gcc-c++ make qt4-devel openssl-devel diffutils perl xz'), | |
('write_file', 'update.sh', 'yum update -y\n'), | |
('schroot_conf', 'CentOS 5') | |
], | |
'centos6': [ | |
('rinse', 'centos-6'), | |
('shell', 'yum update -y'), | |
('append_file:amd64', 'etc/yum.conf', 'exclude = *.i?86\n'), | |
('shell', 'yum install -y gcc gcc-c++ make qt4-devel openssl-devel diffutils perl tar xz'), | |
('write_file', 'update.sh', 'yum update -y\n'), | |
('schroot_conf', 'CentOS 6') | |
] | |
} | |
# --------------------------------------------------------------- HELPERS | |
import os, sys, platform, subprocess, shutil, re, fnmatch, multiprocessing, urllib, hashlib, tarfile | |
from os.path import exists | |
CPU_COUNT = max(2, multiprocessing.cpu_count()-1) # leave one CPU free | |
def rchop(s, e): | |
if s.endswith(e): | |
return s[:-len(e)] | |
return s | |
def error(msg): | |
print msg | |
sys.exit(1) | |
def shell(cmd): | |
ret = os.system(cmd) | |
if ret != 0: | |
error("command failed: exit code %d" % ret) | |
def get_output(*cmd): | |
try: | |
return subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() | |
except subprocess.CalledProcessError: | |
return None | |
def rmdir(path): | |
if exists(path): | |
shutil.rmtree(path) | |
def mkdir_p(path): | |
if not exists(path): | |
os.makedirs(path) | |
def get_version(basedir): | |
text = open(os.path.join(basedir, '..', 'VERSION'), 'r').read() | |
if '-' not in text: | |
return (text, text) | |
version = text[:text.index('-')] | |
os.chdir(os.path.join(basedir, '..')) | |
hash = get_output('git', 'rev-parse', '--short', 'HEAD') | |
if not hash: | |
return (text, version) | |
return ('%s-%s' % (version, hash), version) | |
def qt_config(key, *opts): | |
input, output = [], [] | |
input.extend(QT_CONFIG['common']) | |
input.extend(QT_CONFIG[key]) | |
input.extend(opts) | |
cfg = os.environ.get('WKHTMLTOX_QT_CONFIG') | |
if cfg: | |
input.extend(cfg.split()) | |
for arg in input: | |
if not arg.startswith('remove:-'): | |
output.append(arg) | |
elif arg[1+arg.index(':'):] in output: | |
output.remove(arg[1+arg.index(':'):]) | |
return ' '.join(output) | |
def download_file(url, dir, sha1): | |
name = url.split('/')[-1] | |
loc = os.path.join(dir, name) | |
if os.path.exists(loc): | |
hash = hashlib.sha1(open(loc, 'rb').read()).hexdigest() | |
if hash != sha1: | |
error('Checksum mismatch for %s' % name) | |
os.remove(loc) | |
return loc | |
def hook(cnt, bs, total): | |
pct = int(cnt*bs*100/total) | |
sys.stdout.write("\rDownloading: %s [%d%%]" % (name, pct)) | |
sys.stdout.flush() | |
urllib.urlretrieve(url, loc, reporthook=hook) | |
sys.stdout.write("\r") | |
sys.stdout.flush() | |
hash = hashlib.sha1(open(loc, 'rb').read()).hexdigest() | |
if hash != sha1: | |
error('Checksum mismatch for %s' % name) | |
os.remove(loc) | |
sys.stdout.write("\rDownloaded: %s [checksum OK]" % name) | |
return loc | |
def build_openssl(config, basedir): | |
cfg = None | |
for key in OPENSSL['build']: | |
if fnmatch.fnmatch(config, key): | |
cfg = key | |
if not cfg: | |
return | |
dstdir = os.path.join(basedir, config, 'openssl') | |
location = download_file(OPENSSL['url'], basedir, OPENSSL['sha1']) | |
relname = os.path.basename(location)[:os.path.basename(location).index('.tar')] | |
srcdir = os.path.join(basedir, relname) | |
def is_compiled(): | |
compiled = exists(os.path.join(dstdir, 'include', 'openssl', 'ssl.h')) | |
for lib in OPENSSL['build'][cfg]['libs']: | |
compiled = compiled and exists(os.path.join(dstdir, 'lib', lib)) | |
return compiled | |
if not is_compiled(): | |
rmdir(srcdir) | |
tarfile.open(location).extractall(basedir) | |
os.chdir(srcdir) | |
opts = OPENSSL['build'][cfg] | |
shell('perl Configure --openssldir=%s %s' % (dstdir, opts['configure'])) | |
for cmd in opts['build']: | |
shell(cmd) | |
if not is_compiled(): | |
error("Unable to compile OpenSSL for your system, aborting.") | |
return OPENSSL['build'][cfg]['os_libs'] | |
def check_running_on_debian(): | |
if not sys.platform.startswith('linux') or not exists('/etc/apt/sources.list'): | |
error('This can only be run on a Debian/Ubuntu distribution, aborting.') | |
if os.geteuid() != 0: | |
error('This script must be run as root.') | |
if platform.architecture()[0] == '64bit' and 'amd64' not in ARCH: | |
ARCH.insert(0, 'amd64') | |
PACKAGE_NAME = re.compile(r'ii\s+(.+?)\s+.*') | |
def install_packages(*names): | |
lines = get_output('dpkg-query', '--list', *names).split('\n') | |
avail = [PACKAGE_NAME.match(line).group(1) for line in lines if PACKAGE_NAME.match(line)] | |
if len(avail) != len(names): | |
shell('apt-get update') | |
shell('apt-get install --assume-yes %s' % (' '.join(names))) | |
# --------------------------------------------------------------- Linux chroot | |
ARCH = ['i386'] | |
def check_setup_schroot(config): | |
check_running_on_debian() | |
login = get_output('logname') or os.environ.get('SUDO_USER') | |
if not login: | |
error('Unable to determine the login for which schroot access is to be given.') | |
if login == 'root': | |
error('Please run via sudo to determine login for which schroot access is to be given.') | |
def build_setup_schroot(config, basedir): | |
install_packages('git', 'debootstrap', 'schroot', 'rinse') | |
login = get_output('logname') or os.environ.get('SUDO_USER') | |
chroot = config[1+config.rindex('-'):] | |
for arch in ARCH: | |
print '******************* %s-%s' % (chroot, arch) | |
root_dir = '/opt/wkhtmltopdf-build/%s-%s' % (chroot, arch) | |
rmdir(root_dir) | |
mkdir_p(root_dir) | |
for command in CHROOT_SETUP[chroot]: | |
# handle architecture-specific commands | |
name = command[0] | |
if ':' in name: | |
if name[1+name.rindex(':'):] != arch: | |
continue | |
else: | |
name = name[:name.rindex(':')] | |
# handle commands | |
if name == 'debootstrap': | |
shell('debootstrap --arch=%(arch)s --variant=buildd %(distro)s %(dir)s %(url)s' % { | |
'arch': arch, 'dir': root_dir, 'distro': command[1], 'url': command[2] }) | |
elif name == 'rinse': | |
cmd = (arch == 'i386' and 'linux32 rinse' or 'rinse') | |
shell('%s --arch %s --distribution %s --directory %s' % (cmd, arch, command[1], root_dir)) | |
elif name == 'shell': | |
cmd = (arch == 'i386' and 'linux32 chroot' or 'chroot') | |
shell('%s %s %s' % (cmd, root_dir, command[1])) | |
elif name == 'write_file': | |
open(os.path.join(root_dir, command[1]), 'w').write(command[2].strip()) | |
elif name == 'append_file': | |
open(os.path.join(root_dir, command[1]), 'a').write(command[2].strip()) | |
elif name == 'schroot_conf': | |
cfg = open('/etc/schroot/chroot.d/wkhtmltopdf-%s-%s' % (chroot, arch), 'w') | |
cfg.write('[wkhtmltopdf-%s-%s]\n' % (chroot, arch)) | |
cfg.write('type=directory\ndirectory=%s/\n' % root_dir) | |
cfg.write('description=%s %s for wkhtmltopdf\n' % (command[1], arch)) | |
cfg.write('users=%s\nroot-users=root\n' % login) | |
if arch == 'i386' and 'amd64' in ARCH: | |
cfg.write('personality=linux32\n') | |
cfg.close() | |
def check_update_schroot(config): | |
check_running_on_debian() | |
if not get_output('schroot', '--list'): | |
error('Unable to determine the list of available schroots.') | |
def build_update_schroot(config, basedir): | |
for name in get_output('schroot', '--list').split('\n'): | |
print '******************* %s' % name[name.index('wkhtmltopdf-'):] | |
shell('schroot -c %s -- /bin/bash /update.sh' % name[name.index('wkhtmltopdf-'):]) | |
def check_setup_mingw64(config): | |
check_running_on_debian() | |
def build_setup_mingw64(config, basedir): | |
install_packages('build-essential', 'mingw-w64', 'nsis') | |
# --------------------------------------------------------------- MSVC (2008-2013) | |
MSVC_LOCATION = { | |
'msvc2008': 'VS90COMNTOOLS', | |
'msvc2010': 'VS100COMNTOOLS', | |
'msvc2012': 'VS110COMNTOOLS', | |
'msvc2013': 'VS120COMNTOOLS' | |
} | |
def check_msvc(config): | |
version, arch = rchop(config, '-dbg').split('-') | |
env_var = MSVC_LOCATION[version] | |
if not env_var in os.environ: | |
error("%s does not seem to be installed." % version) | |
vcdir = os.path.join(os.environ[env_var], '..', '..', 'VC') | |
if not exists(os.path.join(vcdir, 'vcvarsall.bat')): | |
error("%s: unable to find vcvarsall.bat" % version) | |
if arch == 'win32' and not exists(os.path.join(vcdir, 'bin', 'cl.exe')): | |
error("%s: unable to find the x86 compiler" % version) | |
if arch == 'win64' and not exists(os.path.join(vcdir, 'bin', 'amd64', 'cl.exe')) \ | |
and not exists(os.path.join(vcdir, 'bin', 'x86_amd64', 'cl.exe')): | |
error("%s: unable to find the amd64 compiler" % version) | |
def build_msvc(config, basedir): | |
msvc, arch = rchop(config, '-dbg').split('-') | |
vcdir = os.path.join(os.environ[MSVC_LOCATION[msvc]], '..', '..', 'VC') | |
vcarg = 'x86' | |
if arch == 'win64': | |
if exists(os.path.join(vcdir, 'bin', 'amd64', 'cl.exe')): | |
vcarg = 'amd64' | |
else: | |
vcarg = 'x86_amd64' | |
python = sys.executable | |
process = subprocess.Popen('("%s" %s>nul)&&"%s" -c "import os; print repr(os.environ)"' % ( | |
os.path.join(vcdir, 'vcvarsall.bat'), vcarg, python), stdout=subprocess.PIPE, shell=True) | |
stdout, _ = process.communicate() | |
exitcode = process.wait() | |
if exitcode != 0: | |
error("%s: unable to initialize the environment" % msvc) | |
os.environ.update(eval(stdout.strip())) | |
build_msvc_common(config, basedir) | |
# --------------------------------------------------------------- MSVC via Windows SDK 7.1 | |
def check_msvc_winsdk71(config): | |
for pfile in ['ProgramFiles(x86)', 'ProgramFiles']: | |
if pfile in os.environ and exists(os.path.join(os.environ[pfile], 'Microsoft SDKs', 'Windows', 'v7.1', 'Bin', 'SetEnv.cmd')): | |
return | |
error("Unable to detect the location of Windows SDK 7.1") | |
def build_msvc_winsdk71(config, basedir): | |
arch = config[config.rindex('-'):] | |
setenv = None | |
for pfile in ['ProgramFiles(x86)', 'ProgramFiles']: | |
if not pfile in os.environ: | |
continue | |
setenv = os.path.join(os.environ[pfile], 'Microsoft SDKs', 'Windows', 'v7.1', 'Bin', 'SetEnv.cmd') | |
mode = debug and '/Debug' or '/Release' | |
if arch == 'win64': | |
args = '/2008 /x64 %s' % mode | |
else: | |
args = '/2008 /x86 %s' % mode | |
python = sys.executable | |
process = subprocess.Popen('("%s" %s>nul)&&"%s" -c "import os; print repr(os.environ)"' % ( | |
setenv, args, python), stdout=subprocess.PIPE, shell=True) | |
stdout, _ = process.communicate() | |
exitcode = process.wait() | |
if exitcode != 0: | |
error("unable to initialize the environment for Windows SDK 7.1") | |
os.environ.update(eval(stdout.strip())) | |
build_msvc_common(config, basedir) | |
def build_msvc_common(config, basedir): | |
version, simple_version = get_version(basedir) | |
ssl_libs = build_openssl(config, basedir) | |
ssldir = os.path.join(basedir, config, 'openssl') | |
qtdir = os.path.join(basedir, config, 'qt') | |
mkdir_p(qtdir) | |
configure_args = qt_config('msvc', | |
'-I %s\\include' % ssldir, | |
'-L %s\\lib' % ssldir, | |
'OPENSSL_LIBS="-L%s -lssleay32 -llibeay32 %s"' % (ssldir.replace('\\', '\\\\'), ssl_libs)) | |
os.chdir(qtdir) | |
if not exists('is_configured'): | |
shell('%s\\..\\qt\\configure.exe %s' % (basedir, configure_args)) | |
open('is_configured', 'w').write('') | |
shell('nmake') | |
appdir = os.path.join(basedir, config, 'app') | |
mkdir_p(appdir) | |
os.chdir(appdir) | |
rmdir('bin') | |
mkdir_p('bin') | |
os.environ['WKHTMLTOX_VERSION'] = version | |
shell('%s\\bin\\qmake %s\\..\\wkhtmltopdf.pro' % (qtdir, basedir)) | |
shell('nmake') | |
found = False | |
for pfile in ['ProgramFiles(x86)', 'ProgramFiles']: | |
if not pfile in os.environ or not exists(os.path.join(os.environ[pfile], 'NSIS', 'makensis.exe')): | |
continue | |
found = True | |
makensis = os.path.join(os.environ[pfile], 'NSIS', 'makensis.exe') | |
os.chdir(os.path.join(basedir, '..')) | |
shell('"%s" /DVERSION=%s /DSIMPLE_VERSION=%s /DTARGET=%s wkhtmltox.nsi' % \ | |
(makensis, version, simple_version, config)) | |
if not found: | |
print "\n\nCould not build installer as NSIS was not found." | |
# ------------------------------------------------ MinGW-W64 Cross Environment | |
MINGW_W64_PREFIX = { | |
'mingw-w64-cross-win32' : 'i686-w64-mingw32', | |
'mingw-w64-cross-win64' : 'x86_64-w64-mingw32', | |
} | |
def check_mingw64_cross(config): | |
shell('%s-gcc --version' % MINGW_W64_PREFIX[rchop(config, '-dbg')]) | |
def build_mingw64_cross(config, basedir): | |
version, simple_version = get_version(basedir) | |
ssl_libs = build_openssl(config, basedir) | |
ssldir = os.path.join(basedir, config, 'openssl') | |
build = os.path.join(basedir, config, 'qt_build') | |
qtdir = os.path.join(basedir, config, 'qt') | |
mkdir_p(build) | |
configure_args = qt_config('mingw-w64-cross', | |
'--prefix=%s' % qtdir, | |
'-I %s/include' % ssldir, | |
'-L %s/lib' % ssldir, | |
'-device-option CROSS_COMPILE=%s-' % MINGW_W64_PREFIX[rchop(config, '-dbg')]) | |
os.environ['OPENSSL_LIBS'] = '-lssl -lcrypto -L %s/lib %s' % (ssldir, ssl_libs) | |
os.chdir(build) | |
if not exists('is_configured'): | |
shell('%s/../qt/configure %s' % (basedir, configure_args)) | |
shell('touch is_configured') | |
shell('make -j%d' % CPU_COUNT) | |
shell('make install') | |
appdir = os.path.join(basedir, config, 'app') | |
mkdir_p(appdir) | |
os.chdir(appdir) | |
shell('rm -f bin/*') | |
# set up cross compiling prefix correctly (isn't set by make install) | |
os.environ['QTDIR'] = qtdir | |
os.environ['WKHTMLTOX_VERSION'] = version | |
shell('%s/bin/qmake -set CROSS_COMPILE %s-' % (qtdir, MINGW_W64_PREFIX[rchop(config, '-dbg')])) | |
shell('%s/bin/qmake -spec win32-g++-4.6 %s/../wkhtmltopdf.pro' % (qtdir, basedir)) | |
shell('make') | |
shutil.copy('bin/libwkhtmltox0.a', 'bin/wkhtmltox.lib') | |
os.chdir(os.path.join(basedir, '..')) | |
shell('makensis -DVERSION=%s -DSIMPLE_VERSION=%s -DTARGET=%s wkhtmltox.nsi' % \ | |
(version, simple_version, config)) | |
# -------------------------------------------------- Linux schroot environment | |
def check_linux_schroot(config): | |
shell('schroot -c wkhtmltopdf-%s -- gcc --version' % rchop(config, '-dbg')) | |
def build_linux_schroot(config, basedir): | |
version, simple_version = get_version(basedir) | |
dir = os.path.join(basedir, config) | |
script = os.path.join(dir, 'build.sh') | |
dist = os.path.join(dir, 'wkhtmltox-%s' % version) | |
mkdir_p(os.path.join(dir, 'qt_build')) | |
mkdir_p(os.path.join(dir, 'app')) | |
rmdir(dist) | |
mkdir_p(os.path.join(dist, 'bin')) | |
mkdir_p(os.path.join(dist, 'include', 'wkhtmltox')) | |
mkdir_p(os.path.join(dist, 'lib')) | |
configure_args = qt_config('posix', '--prefix=../qt') | |
lines = ['#!/bin/bash'] | |
lines.append('# start of autogenerated build script') | |
lines.append('cd qt_build') | |
if config == 'centos5-i386': | |
lines.append('export CFLAGS=-march=i486') | |
lines.append('export CXXFLAGS=-march=i486') | |
lines.append('if [ ! -f is_configured ]; then') | |
lines.append(' ../../../qt/configure %s || exit 1' % configure_args) | |
lines.append(' touch is_configured') | |
lines.append('fi') | |
lines.append('if ! make -j%d -q; then\n make -j%d || exit 1\nfi' % (CPU_COUNT, CPU_COUNT)) | |
lines.append('make install || exit 1') | |
lines.append('cd ../app') | |
lines.append('rm -f bin/*') | |
lines.append('export WKHTMLTOX_VERSION=%s' % version) | |
lines.append('../qt/bin/qmake ../../../wkhtmltopdf.pro') | |
lines.append('make -j%d || exit 1' % CPU_COUNT) | |
lines.append('strip bin/wkhtmltopdf bin/wkhtmltoimage') | |
lines.append('cp bin/wkhtmlto* ../wkhtmltox-%s/bin' % version) | |
lines.append('cp -P bin/libwkhtmltox*.so.* ../wkhtmltox-%s/lib' % version) | |
lines.append('cp ../../../include/wkhtmltox/*.h ../wkhtmltox-%s/include/wkhtmltox' % version) | |
lines.append('cp ../../../include/wkhtmltox/dll*.inc ../wkhtmltox-%s/include/wkhtmltox' % version) | |
lines.append('cd ..') | |
lines.append('tar -c -v -f ../wkhtmltox-%s_linux-%s.tar wkhtmltox-%s/' % (version, config, version)) | |
lines.append('xz --compress --force --verbose -9 ../wkhtmltox-%s_linux-%s.tar' % (version, config)) | |
lines.append('# end of build script') | |
open(script, 'w').write('\n'.join(lines)) | |
os.chdir(dir) | |
shell('chmod +x build.sh') | |
shell('schroot -c wkhtmltopdf-%s -- ./build.sh' % rchop(config, '-dbg')) | |
# -------------------------------------------------- POSIX local environment | |
def check_posix_local(config): | |
pass | |
def build_posix_local(config, basedir): | |
version, simple_version = get_version(basedir) | |
build = os.path.join(basedir, config, 'qt_build') | |
app = os.path.join(basedir, config, 'app') | |
qtdir = os.path.join(basedir, config, 'qt') | |
dist = os.path.join(basedir, config, 'wkhtmltox-%s' % version) | |
mkdir_p(build) | |
mkdir_p(app) | |
rmdir(dist) | |
mkdir_p(os.path.join(dist, 'bin')) | |
mkdir_p(os.path.join(dist, 'include', 'wkhtmltox')) | |
mkdir_p(os.path.join(dist, 'lib')) | |
os.chdir(build) | |
if not exists('is_configured'): | |
shell('../../../qt/configure %s' % qt_config('posix', '--prefix=../qt')) | |
shell('touch is_configured') | |
if subprocess.call(['make', '-j%d' % CPU_COUNT]): | |
shell('make -j%d' % CPU_COUNT) | |
shell('make install') | |
os.chdir(app) | |
shell('rm -f bin/*') | |
os.environ['WKHTMLTOX_VERSION'] = version | |
shell('../qt/bin/qmake ../../../wkhtmltopdf.pro') | |
shell('make -j%d' % CPU_COUNT) | |
shell('cp bin/wkhtmlto* ../wkhtmltox-%s/bin' % version) | |
shell('cp -P bin/libwkhtmltox*.so.* ../wkhtmltox-%s/lib' % version) | |
shell('cp ../../../include/wkhtmltox/*.h ../wkhtmltox-%s/include/wkhtmltox' % version) | |
shell('cp ../../../include/wkhtmltox/dll*.inc ../wkhtmltox-%s/include/wkhtmltox' % version) | |
os.chdir(basedir) | |
shell('tar -c -v -f ../wkhtmltox-%s_local-%s.tar wkhtmltox-%s/' % (version, platform.node(), version)) | |
shell('xz --compress --force --verbose -9 ../wkhtmltox-%s_local-%s.tar' % (version, platform.node())) | |
# --------------------------------------------------------------- OS X | |
OSX_CONFIG = { | |
'osx-10.6-carbon-i386': '-carbon -platform macx-g++42', # Assumes a regular Xcode 3.2.6 install | |
'osx-10.7-carbon-i386': '-carbon -reduce-exports -platform unsupported/macx-clang', | |
'osx-10.8-carbon-i386': '-carbon -reduce-exports -platform unsupported/macx-clang', | |
'osx-10.9-carbon-i386': '-carbon -reduce-exports -platform unsupported/macx-clang', | |
'osx-10.7-cocoa-x86-64': '-cocoa -platform unsupported/macx-clang', | |
'osx-10.8-cocoa-x86-64': '-cocoa -platform unsupported/macx-clang', | |
'osx-10.9-cocoa-x86-64': '-cocoa -platform unsupported/macx-clang-libc++' | |
} | |
def check_osx(config): | |
if not platform.system() == 'Darwin' or not platform.mac_ver()[0]: | |
error('This can only be run on a OS X system!') | |
osxver = platform.mac_ver()[0][:platform.mac_ver()[0].rindex('.')] | |
osxcfg = config.replace('osx-', 'osx-%s-' % osxver) | |
if not osxcfg in OSX_CONFIG: | |
error('This target is not supported: %s' % osxcfg) | |
def build_osx(config, basedir): | |
version, simple_version = get_version(basedir) | |
osxver = platform.mac_ver()[0][:platform.mac_ver()[0].rindex('.')] | |
osxcfg = config.replace('osx-', 'osx-%s-' % osxver) | |
qmake_flags = '' | |
# Locate 10.6 SDK for Carbon builds on 10.7 and newer | |
if 'carbon' in osxcfg and osxver != '10.6': | |
process = subprocess.Popen('xcodebuild -sdk macosx10.6 -version Path 2>/dev/null', stdout=subprocess.PIPE, shell=True) | |
osxsdk = process.communicate()[0].strip() | |
exitcode = process.wait() | |
if exitcode != 0: | |
error('Unable to find required OS X 10.6 SDK path for Carbon builds on >= OS X 10.7.\nSee INSTALL.md for OS X prerequisites.') | |
OSX_CONFIG[osxcfg] = OSX_CONFIG[osxcfg] + ' -sdk ' + osxsdk | |
# These QMAKE flags are needed to build wkhtmltopdf for Carbon | |
qmake_flags = str.format('"QMAKE_CFLAGS += {0}" "QMAKE_CXXFLAGS += {0}"', '-fvisibility=hidden -fvisibility-inlines-hidden') | |
# OS X 10.6 SDKs *normally* do not contain libstdc++.dylib by default in <osxsdk>/usr/lib. There is | |
# only libstdc++.6.dylib and libstdc++.6.0.9.dylib. Since the sysroot is changed with <osxsdk> the | |
# linker will therefore fail to link against libstdc++. The following trick solves the problem with | |
# symlinking /usr/lib/libstdc++.dylib in /usr/local/lib. This works because the linker also searches | |
# in <osxsdk>/usr/local/lib which itself is a symlink to the real /usr/local. /usr/local was chosen | |
# because directly symlinking in <osxsdk>/usr/lib often fails due to permissions. | |
if not os.path.isfile('%s/usr/local/lib/libstdc++.dylib' % osxsdk): | |
if not os.path.isfile('/usr/local/lib/libstdc++.dylib'): | |
print 'Carbon build: Symlinking /usr/lib/libstdc++.dylib in /usr/local/lib.\nYou have to use sudo if it fails...\n' | |
shell('ln -s /usr/lib/libstdc++.dylib /usr/local/lib/libstdc++.dylib') | |
build = os.path.join(basedir, osxcfg, 'qt_build') | |
app = os.path.join(basedir, osxcfg, 'app') | |
dist = os.path.join(basedir, osxcfg, 'wkhtmltox-%s' % version) | |
mkdir_p(build) | |
mkdir_p(app) | |
rmdir(dist) | |
mkdir_p(os.path.join(dist, 'bin')) | |
mkdir_p(os.path.join(dist, 'include', 'wkhtmltox')) | |
mkdir_p(os.path.join(dist, 'lib')) | |
os.chdir(build) | |
if not exists('is_configured'): | |
shell('../../../qt/configure %s' % qt_config('osx', '--prefix=../qt', OSX_CONFIG[osxcfg])) | |
shell('touch is_configured') | |
if subprocess.call(['make', '-j%d' % CPU_COUNT]): | |
shell('make -j%d' % CPU_COUNT) | |
shell('make install') | |
os.chdir(app) | |
shell('rm -f bin/*') | |
os.environ['WKHTMLTOX_VERSION'] = version | |
shell('../qt/bin/qmake %s ../../../wkhtmltopdf.pro' % qmake_flags) | |
shell('make -j%d' % CPU_COUNT) | |
shell('cp bin/wkhtmlto* ../wkhtmltox-%s/bin' % version) | |
shell('cp -P bin/libwkhtmltox*.dylib* ../wkhtmltox-%s/lib' % version) | |
shell('cp ../../../include/wkhtmltox/*.h ../wkhtmltox-%s/include/wkhtmltox' % version) | |
shell('cp ../../../include/wkhtmltox/dll*.inc ../wkhtmltox-%s/include/wkhtmltox' % version) | |
os.chdir(os.path.join(basedir, osxcfg)) | |
shell('tar -c -v -f ../wkhtmltox-%s_%s.tar wkhtmltox-%s/' % (version, osxcfg, version)) | |
shell('xz --compress --force --verbose -9 ../wkhtmltox-%s_%s.tar' % (version, osxcfg)) | |
# --------------------------------------------------------------- command line | |
def usage(exit_code=2): | |
print "Usage: scripts/build.py <target> [-clean] [-debug]\n\nThe supported targets are:\n", | |
opts = list(BUILDERS.keys()) | |
opts.sort() | |
for opt in opts: | |
print '* %s' % opt | |
sys.exit(exit_code) | |
def main(): | |
basedir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'static-build') | |
mkdir_p(basedir) | |
if len(sys.argv) == 1: | |
usage(0) | |
config = sys.argv[1] | |
if config not in BUILDERS: | |
usage() | |
for arg in sys.argv[2:]: | |
if not arg in ['-clean', '-debug']: | |
usage() | |
final_config = config | |
if '-debug' in sys.argv[2:]: | |
# use the debug OpenSSL configuration if possible | |
ssl = OPENSSL['build'] | |
for key in ssl: | |
if fnmatch.fnmatch(config, key) and 'debug' in ssl[key]: | |
ssl[key]['configure'] = ssl[key]['debug'] | |
# use a debug build of QT and WebKit | |
cfg = QT_CONFIG['common'] | |
cfg[cfg.index('-release')] = '-debug' | |
cfg[cfg.index('-webkit')] = '-webkit-debug' | |
final_config += '-dbg' | |
if '-clean' in sys.argv[2:]: | |
rmdir(os.path.join(basedir, config)) | |
globals()['check_%s' % BUILDERS[config]](final_config) | |
globals()['build_%s' % BUILDERS[config]](final_config, os.path.realpath(basedir)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment