2009-06-18 22:17:17 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# __________ __ ___.
|
|
|
|
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
# \/ \/ \/ \/ \/
|
|
|
|
# $Id$
|
|
|
|
#
|
|
|
|
# Copyright (c) 2009 Dominik Riebeling
|
|
|
|
#
|
|
|
|
# All files in this archive are subject to the GNU General Public License.
|
|
|
|
# See the file COPYING in the source tree root for full license agreement.
|
|
|
|
#
|
|
|
|
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
# KIND, either express or implied.
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Automate building releases for deployment.
|
2009-10-04 16:45:59 +00:00
|
|
|
# Run from any folder to build
|
|
|
|
# - trunk
|
|
|
|
# - any tag (using the -t option)
|
|
|
|
# - any local folder (using the -p option)
|
|
|
|
# Will build a binary archive (tar.bz2 / zip) and source archive.
|
|
|
|
# The source archive won't be built for local builds. Trunk and
|
|
|
|
# tag builds will retrieve the sources directly from svn and build
|
|
|
|
# below the systems temporary folder.
|
|
|
|
#
|
2009-06-18 22:17:17 +00:00
|
|
|
# If the required Qt installation isn't in PATH use --qmake option.
|
|
|
|
# Tested on Linux and MinGW / W32
|
|
|
|
#
|
|
|
|
# requires upx.exe in PATH on Windows.
|
|
|
|
#
|
|
|
|
|
|
|
|
import re
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import tarfile
|
|
|
|
import zipfile
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import getopt
|
2009-09-20 14:59:29 +00:00
|
|
|
import time
|
2009-10-04 16:45:59 +00:00
|
|
|
import hashlib
|
|
|
|
import tempfile
|
2015-06-07 20:19:54 +00:00
|
|
|
from datetime import datetime
|
2020-06-13 18:55:40 +00:00
|
|
|
import multiprocessing
|
2019-11-17 10:55:46 +00:00
|
|
|
import gitscraper
|
2009-06-18 22:17:17 +00:00
|
|
|
|
2020-06-13 18:55:40 +00:00
|
|
|
CPUS = multiprocessing.cpu_count()
|
|
|
|
print("Info: %s cores found." % CPUS)
|
2009-10-30 21:40:07 +00:00
|
|
|
|
2009-06-18 22:17:17 +00:00
|
|
|
# == Global stuff ==
|
2010-08-30 17:51:53 +00:00
|
|
|
# DLL files to ignore when searching for required DLL files.
|
2019-11-17 10:55:46 +00:00
|
|
|
SYSTEMDLLS = [
|
2019-11-17 10:25:37 +00:00
|
|
|
'advapi32.dll',
|
2010-08-30 17:51:53 +00:00
|
|
|
'comdlg32.dll',
|
2019-11-17 10:25:37 +00:00
|
|
|
'crypt32.dll',
|
|
|
|
'd3d9.dll',
|
|
|
|
'dwmapi.dll',
|
|
|
|
'dxva2.dll',
|
|
|
|
'evr.dll',
|
2010-08-30 17:51:53 +00:00
|
|
|
'gdi32.dll',
|
|
|
|
'imm32.dll',
|
2019-11-17 10:25:37 +00:00
|
|
|
'imm32.dll',
|
|
|
|
'iphlpapi.dll',
|
2010-08-30 17:51:53 +00:00
|
|
|
'kernel32.dll',
|
2019-11-17 10:25:37 +00:00
|
|
|
'mf.dll',
|
|
|
|
'mfplat.dll',
|
2010-08-30 17:51:53 +00:00
|
|
|
'msvcrt.dll',
|
|
|
|
'msvcrt.dll',
|
|
|
|
'netapi32.dll',
|
|
|
|
'ole32.dll',
|
|
|
|
'oleaut32.dll',
|
|
|
|
'setupapi.dll',
|
|
|
|
'shell32.dll',
|
|
|
|
'user32.dll',
|
2019-11-17 10:25:37 +00:00
|
|
|
'userenv.dll',
|
|
|
|
'uxtheme.dll',
|
|
|
|
'version.dll',
|
2010-08-30 17:51:53 +00:00
|
|
|
'winmm.dll',
|
|
|
|
'winspool.drv',
|
2019-11-17 10:25:37 +00:00
|
|
|
'ws2_32.dll',
|
|
|
|
'wtsapi32.dll'
|
|
|
|
]
|
2010-08-30 17:51:53 +00:00
|
|
|
|
2012-02-05 21:00:57 +00:00
|
|
|
gitrepo = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
|
2010-08-30 17:51:53 +00:00
|
|
|
|
2012-02-26 16:09:08 +00:00
|
|
|
|
2009-06-18 22:17:17 +00:00
|
|
|
# == Functions ==
|
|
|
|
def usage(myself):
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Usage: %s [options]" % myself)
|
|
|
|
print(" -q, --qmake=<qmake> path to qmake")
|
|
|
|
print(" -p, --project=<pro> path to .pro file for building with local tree")
|
|
|
|
print(" -t, --tag=<tag> use specified tag from svn")
|
|
|
|
print(" -a, --add=<file> add file to build folder before building")
|
|
|
|
print(" -s, --source-only only create source archive")
|
|
|
|
print(" -b, --binary-only only create binary archive")
|
2010-08-08 20:56:58 +00:00
|
|
|
if nsisscript != "":
|
2019-11-17 10:55:46 +00:00
|
|
|
print(" -n, --makensis=<file> path to makensis for building Windows setup program.")
|
2009-11-29 21:09:21 +00:00
|
|
|
if sys.platform != "darwin":
|
2019-11-17 10:55:46 +00:00
|
|
|
print(" -d, --dynamic link dynamically instead of static")
|
2011-03-06 00:04:26 +00:00
|
|
|
if sys.platform != "win32":
|
2019-11-17 10:55:46 +00:00
|
|
|
print(" -x, --cross= prefix to cross compile for win32")
|
|
|
|
print(" -k, --keep-temp keep temporary folder on build failure")
|
|
|
|
print(" -h, --help this help")
|
|
|
|
print(" If neither a project file nor tag is specified trunk will get downloaded")
|
|
|
|
print(" from svn.")
|
2009-10-04 16:45:59 +00:00
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
|
2011-08-14 18:57:49 +00:00
|
|
|
def which(executable):
|
|
|
|
path = os.environ.get("PATH", "").split(os.pathsep)
|
|
|
|
for p in path:
|
|
|
|
fullpath = p + "/" + executable
|
|
|
|
if os.path.exists(fullpath):
|
|
|
|
return fullpath
|
2019-11-17 10:55:46 +00:00
|
|
|
print("which: could not find " + executable)
|
2011-08-14 18:57:49 +00:00
|
|
|
return ""
|
|
|
|
|
|
|
|
|
2012-02-05 21:00:57 +00:00
|
|
|
def getsources(treehash, filelist, dest):
|
2009-10-04 16:45:59 +00:00
|
|
|
'''Get the files listed in filelist from svnsrv and put it at dest.'''
|
2012-02-05 21:00:57 +00:00
|
|
|
gitscraper.scrape_files(gitrepo, treehash, filelist, dest)
|
2009-10-04 16:45:59 +00:00
|
|
|
return 0
|
|
|
|
|
|
|
|
|
2011-05-08 19:34:03 +00:00
|
|
|
def getfolderrev(svnsrv):
|
|
|
|
'''Get the most recent revision for svnsrv'''
|
2009-10-04 16:45:59 +00:00
|
|
|
client = pysvn.Client()
|
|
|
|
entries = client.info2(svnsrv, recurse=False)
|
|
|
|
return entries[0][1].rev.number
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def findversion(versionfile):
|
|
|
|
'''figure most recent program version from version.h,
|
|
|
|
returns version string.'''
|
|
|
|
h = open(versionfile, "r")
|
|
|
|
c = h.read()
|
|
|
|
h.close()
|
2015-05-30 15:59:50 +00:00
|
|
|
version = dict()
|
|
|
|
for v in ['MAJOR', 'MINOR', 'MICRO']:
|
|
|
|
r = re.compile("#define +VERSION_" + v + " +([0-9a-z]+)")
|
|
|
|
m = re.search(r, c)
|
|
|
|
version[v] = m.group(1)
|
|
|
|
return "%s.%s.%s" % (version['MAJOR'], version['MINOR'], version['MICRO'])
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def findqt(cross=""):
|
2009-06-18 22:17:17 +00:00
|
|
|
'''Search for Qt4 installation. Return path to qmake.'''
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Searching for Qt")
|
2011-03-06 00:04:26 +00:00
|
|
|
bins = [cross + "qmake", cross + "qmake-qt4"]
|
2009-06-18 22:17:17 +00:00
|
|
|
for binary in bins:
|
2009-10-03 18:16:41 +00:00
|
|
|
try:
|
2011-08-14 18:57:49 +00:00
|
|
|
q = which(binary)
|
2009-10-03 18:16:41 +00:00
|
|
|
if len(q) > 0:
|
|
|
|
result = checkqt(q)
|
|
|
|
if not result == "":
|
|
|
|
return result
|
|
|
|
except:
|
2019-11-17 12:40:42 +00:00
|
|
|
print(sys.exc_info()[1])
|
2009-10-03 18:16:41 +00:00
|
|
|
|
|
|
|
return ""
|
2009-10-03 17:19:07 +00:00
|
|
|
|
2009-06-18 22:17:17 +00:00
|
|
|
|
2009-10-03 17:19:07 +00:00
|
|
|
def checkqt(qmakebin):
|
|
|
|
'''Check if given path to qmake exists and is a suitable version.'''
|
|
|
|
result = ""
|
|
|
|
# check if binary exists
|
|
|
|
if not os.path.exists(qmakebin):
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Specified qmake path does not exist!")
|
2009-10-03 17:19:07 +00:00
|
|
|
return result
|
|
|
|
# check version
|
2019-11-17 10:55:46 +00:00
|
|
|
output = subprocess.Popen(
|
|
|
|
[qmakebin, "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
2009-10-03 17:19:07 +00:00
|
|
|
cmdout = output.communicate()
|
|
|
|
# don't check the qmake return code here, Qt3 doesn't return 0 on -version.
|
|
|
|
for ou in cmdout:
|
2019-11-17 12:40:42 +00:00
|
|
|
r = re.compile(b'Qt[^0-9]+([0-9\.]+[a-z]*)')
|
2009-10-03 17:19:07 +00:00
|
|
|
m = re.search(r, ou)
|
2019-11-17 12:40:42 +00:00
|
|
|
if m is not None:
|
|
|
|
print("Qt found: %s" % m.group(1).decode())
|
|
|
|
s = re.compile(b'[45]\..*')
|
2009-10-03 17:19:07 +00:00
|
|
|
n = re.search(s, m.group(1))
|
2019-11-17 10:55:46 +00:00
|
|
|
if n is not None:
|
2009-10-03 17:19:07 +00:00
|
|
|
result = qmakebin
|
2009-06-18 22:17:17 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def qmake(qmake, projfile, platform=sys.platform, wd=".", static=True, cross=""):
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Running qmake in %s..." % wd)
|
2009-10-05 18:35:28 +00:00
|
|
|
command = [qmake, "-config", "release", "-config", "noccache"]
|
|
|
|
if static == True:
|
2011-03-06 00:04:26 +00:00
|
|
|
command.extend(["-config", "-static"])
|
|
|
|
# special spec required?
|
|
|
|
if len(qmakespec[platform]) > 0:
|
|
|
|
command.extend(["-spec", qmakespec[platform]])
|
|
|
|
# cross compiling prefix set?
|
|
|
|
if len(cross) > 0:
|
|
|
|
command.extend(["-config", "cross"])
|
2009-10-05 18:35:28 +00:00
|
|
|
command.append(projfile)
|
2011-03-06 00:04:26 +00:00
|
|
|
output = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=wd)
|
2009-10-03 17:19:07 +00:00
|
|
|
output.communicate()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("qmake returned an error!")
|
2009-10-03 17:19:07 +00:00
|
|
|
return -1
|
|
|
|
return 0
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def build(wd=".", platform=sys.platform, cross=""):
|
2009-06-18 22:17:17 +00:00
|
|
|
# make
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Building ...")
|
2011-03-06 00:04:26 +00:00
|
|
|
# use the current platforms make here, cross compiling uses the native make.
|
|
|
|
command = [make[sys.platform]]
|
2020-06-13 18:55:40 +00:00
|
|
|
if CPUS > 1:
|
2010-12-19 20:10:22 +00:00
|
|
|
command.append("-j")
|
2020-06-13 18:55:40 +00:00
|
|
|
command.append(str(CPUS))
|
2010-12-19 20:10:22 +00:00
|
|
|
output = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=wd)
|
2009-10-22 22:02:56 +00:00
|
|
|
while True:
|
|
|
|
c = output.stdout.readline()
|
|
|
|
sys.stdout.write(".")
|
|
|
|
sys.stdout.flush()
|
|
|
|
if not output.poll() == None:
|
|
|
|
sys.stdout.write("\n")
|
|
|
|
sys.stdout.flush()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Build failed!")
|
2009-10-22 22:02:56 +00:00
|
|
|
return -1
|
|
|
|
break
|
2011-03-06 00:04:26 +00:00
|
|
|
if platform != "darwin":
|
2009-11-29 21:09:21 +00:00
|
|
|
# strip. OS X handles this via macdeployqt.
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Stripping binary.")
|
2011-03-06 00:04:26 +00:00
|
|
|
output = subprocess.Popen([cross + "strip", progexe[platform]], \
|
|
|
|
stdout=subprocess.PIPE, cwd=wd)
|
2009-11-29 21:09:21 +00:00
|
|
|
output.communicate()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Stripping failed!")
|
2009-11-29 21:09:21 +00:00
|
|
|
return -1
|
2009-10-03 17:19:07 +00:00
|
|
|
return 0
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def upxfile(wd=".", platform=sys.platform):
|
2009-06-18 22:17:17 +00:00
|
|
|
# run upx on binary
|
2019-11-17 10:55:46 +00:00
|
|
|
print("UPX'ing binary ...")
|
2011-03-06 00:04:26 +00:00
|
|
|
output = subprocess.Popen(["upx", progexe[platform]], \
|
|
|
|
stdout=subprocess.PIPE, cwd=wd)
|
2009-10-03 17:19:07 +00:00
|
|
|
output.communicate()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("UPX'ing failed!")
|
2009-10-03 17:19:07 +00:00
|
|
|
return -1
|
|
|
|
return 0
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2010-08-30 17:51:53 +00:00
|
|
|
def runnsis(versionstring, nsis, script, srcfolder):
|
2010-08-08 20:56:58 +00:00
|
|
|
# run script through nsis to create installer.
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Running NSIS ...")
|
2010-08-30 17:51:53 +00:00
|
|
|
|
2010-08-08 20:56:58 +00:00
|
|
|
# Assume the generated installer gets placed in the same folder the nsi
|
|
|
|
# script lives in. This seems to be a valid assumption unless the nsi
|
|
|
|
# script specifies a path. NSIS expects files relative to source folder so
|
2010-08-30 17:51:53 +00:00
|
|
|
# copy progexe. Additional files are injected into the nsis script.
|
|
|
|
|
|
|
|
# FIXME: instead of copying binaries around copy the NSI file and inject
|
|
|
|
# the correct paths.
|
2011-03-06 00:04:26 +00:00
|
|
|
# Only win32 supported as target platform so hard coded.
|
|
|
|
b = srcfolder + "/" + os.path.dirname(script) + "/" \
|
|
|
|
+ os.path.dirname(progexe["win32"])
|
2010-08-30 17:51:53 +00:00
|
|
|
if not os.path.exists(b):
|
|
|
|
os.mkdir(b)
|
2011-03-06 00:04:26 +00:00
|
|
|
shutil.copy(srcfolder + "/" + progexe["win32"], b)
|
|
|
|
output = subprocess.Popen([nsis, srcfolder + "/" + script], \
|
|
|
|
stdout=subprocess.PIPE)
|
2010-08-08 20:56:58 +00:00
|
|
|
output.communicate()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("NSIS failed!")
|
2010-08-08 20:56:58 +00:00
|
|
|
return -1
|
|
|
|
setupfile = program + "-" + versionstring + "-setup.exe"
|
2010-08-09 17:44:03 +00:00
|
|
|
# find output filename in nsis script file
|
|
|
|
nsissetup = ""
|
2010-08-30 17:51:53 +00:00
|
|
|
for line in open(srcfolder + "/" + script):
|
2010-08-09 17:44:03 +00:00
|
|
|
if re.match(r'^[^;]*OutFile\s+', line) != None:
|
|
|
|
nsissetup = re.sub(r'^[^;]*OutFile\s+"(.+)"', r'\1', line).rstrip()
|
|
|
|
if nsissetup == "":
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Could not retrieve output file name!")
|
2010-08-09 17:44:03 +00:00
|
|
|
return -1
|
2011-03-06 00:04:26 +00:00
|
|
|
shutil.copy(srcfolder + "/" + os.path.dirname(script) + "/" + nsissetup, \
|
|
|
|
setupfile)
|
2010-08-08 20:56:58 +00:00
|
|
|
return 0
|
|
|
|
|
|
|
|
|
2010-08-30 17:51:53 +00:00
|
|
|
def nsisfileinject(nsis, outscript, filelist):
|
|
|
|
'''Inject files in filelist into NSIS script file after the File line
|
|
|
|
containing the main binary. This assumes that the main binary is present
|
|
|
|
in the NSIS script and that all additiona files (dlls etc) to get placed
|
|
|
|
into $INSTDIR.'''
|
|
|
|
output = open(outscript, "w")
|
|
|
|
for line in open(nsis, "r"):
|
|
|
|
output.write(line)
|
2011-03-06 00:04:26 +00:00
|
|
|
# inject files after the progexe binary.
|
|
|
|
# Match the basename only to avoid path mismatches.
|
2019-11-17 12:40:42 +00:00
|
|
|
if re.match(r'^\s*File\s*.*' + os.path.basename(progexe["win32"]),
|
2011-03-06 00:04:26 +00:00
|
|
|
line, re.IGNORECASE):
|
2010-08-30 17:51:53 +00:00
|
|
|
for f in filelist:
|
2011-03-06 00:04:26 +00:00
|
|
|
injection = " File /oname=$INSTDIR\\" + os.path.basename(f) \
|
|
|
|
+ " " + os.path.normcase(f) + "\n"
|
2010-08-30 17:51:53 +00:00
|
|
|
output.write(injection)
|
|
|
|
output.write(" ; end of injected files\n")
|
|
|
|
output.close()
|
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def finddlls(program, extrapaths=[], cross=""):
|
2010-08-30 17:51:53 +00:00
|
|
|
'''Check program for required DLLs. Find all required DLLs except ignored
|
|
|
|
ones and return a list of DLL filenames (including path).'''
|
|
|
|
# ask objdump about dependencies.
|
2011-03-06 00:04:26 +00:00
|
|
|
output = subprocess.Popen([cross + "objdump", "-x", program], \
|
|
|
|
stdout=subprocess.PIPE)
|
2010-08-30 17:51:53 +00:00
|
|
|
cmdout = output.communicate()
|
|
|
|
|
|
|
|
# create list of used DLLs. Store as lower case as W32 is case-insensitive.
|
|
|
|
dlls = []
|
2019-11-17 12:40:42 +00:00
|
|
|
for line in cmdout[0].decode().split('\n'):
|
2010-08-30 17:51:53 +00:00
|
|
|
if re.match(r'\s*DLL Name', line) != None:
|
2010-09-23 16:47:23 +00:00
|
|
|
dll = re.sub(r'^\s*DLL Name:\s+([a-zA-Z_\-0-9\.\+]+).*$', r'\1', line)
|
2010-08-30 17:51:53 +00:00
|
|
|
dlls.append(dll.lower())
|
|
|
|
|
|
|
|
# find DLLs in extrapaths and PATH environment variable.
|
|
|
|
dllpaths = []
|
|
|
|
for file in dlls:
|
2019-11-17 10:55:46 +00:00
|
|
|
if file in SYSTEMDLLS:
|
|
|
|
print("System DLL: " + file)
|
2010-08-30 17:51:53 +00:00
|
|
|
continue
|
|
|
|
dllpath = ""
|
|
|
|
for path in extrapaths:
|
|
|
|
if os.path.exists(path + "/" + file):
|
|
|
|
dllpath = re.sub(r"\\", r"/", path + "/" + file)
|
2019-11-17 10:55:46 +00:00
|
|
|
print(file + ": found at " + dllpath)
|
2010-08-30 17:51:53 +00:00
|
|
|
dllpaths.append(dllpath)
|
|
|
|
break
|
|
|
|
if dllpath == "":
|
|
|
|
try:
|
2011-08-14 18:57:49 +00:00
|
|
|
dllpath = re.sub(r"\\", r"/", which(file))
|
2019-11-17 10:55:46 +00:00
|
|
|
print(file + ": found at " + dllpath)
|
2011-03-06 00:04:26 +00:00
|
|
|
dllpaths.append(dllpath)
|
2010-08-30 17:51:53 +00:00
|
|
|
except:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("MISSING DLL: " + file)
|
2010-08-30 17:51:53 +00:00
|
|
|
return dllpaths
|
|
|
|
|
|
|
|
|
2011-03-06 17:37:27 +00:00
|
|
|
def zipball(programfiles, versionstring, buildfolder, platform=sys.platform):
|
2009-06-18 22:17:17 +00:00
|
|
|
'''package created binary'''
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Creating binary zipball.")
|
2009-10-04 16:45:59 +00:00
|
|
|
archivebase = program + "-" + versionstring
|
|
|
|
outfolder = buildfolder + "/" + archivebase
|
|
|
|
archivename = archivebase + ".zip"
|
2009-06-18 22:17:17 +00:00
|
|
|
# create output folder
|
|
|
|
os.mkdir(outfolder)
|
|
|
|
# move program files to output folder
|
|
|
|
for f in programfiles:
|
2010-08-30 17:51:53 +00:00
|
|
|
if re.match(r'^(/|[a-zA-Z]:)', f) != None:
|
|
|
|
shutil.copy(f, outfolder)
|
2019-11-17 10:25:37 +00:00
|
|
|
else:
|
2010-08-30 17:51:53 +00:00
|
|
|
shutil.copy(buildfolder + "/" + f, outfolder)
|
2009-06-18 22:17:17 +00:00
|
|
|
# create zipball from output folder
|
|
|
|
zf = zipfile.ZipFile(archivename, mode='w', compression=zipfile.ZIP_DEFLATED)
|
|
|
|
for root, dirs, files in os.walk(outfolder):
|
|
|
|
for name in files:
|
2011-03-06 00:04:26 +00:00
|
|
|
physname = os.path.normpath(os.path.join(root, name))
|
2015-05-30 16:21:32 +00:00
|
|
|
filename = os.path.relpath(physname, buildfolder)
|
2009-10-04 16:45:59 +00:00
|
|
|
zf.write(physname, filename)
|
2009-06-18 22:17:17 +00:00
|
|
|
zf.close()
|
|
|
|
# remove output folder
|
2009-10-04 16:45:59 +00:00
|
|
|
shutil.rmtree(outfolder)
|
2009-10-03 17:19:07 +00:00
|
|
|
return archivename
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2011-03-06 17:37:27 +00:00
|
|
|
def tarball(programfiles, versionstring, buildfolder):
|
2009-06-18 22:17:17 +00:00
|
|
|
'''package created binary'''
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Creating binary tarball.")
|
2009-10-04 16:45:59 +00:00
|
|
|
archivebase = program + "-" + versionstring
|
|
|
|
outfolder = buildfolder + "/" + archivebase
|
|
|
|
archivename = archivebase + ".tar.bz2"
|
2009-06-18 22:17:17 +00:00
|
|
|
# create output folder
|
|
|
|
os.mkdir(outfolder)
|
|
|
|
# move program files to output folder
|
|
|
|
for f in programfiles:
|
2009-10-04 16:45:59 +00:00
|
|
|
shutil.copy(buildfolder + "/" + f, outfolder)
|
2009-06-18 22:17:17 +00:00
|
|
|
# create tarball from output folder
|
|
|
|
tf = tarfile.open(archivename, mode='w:bz2')
|
2009-10-04 16:45:59 +00:00
|
|
|
tf.add(outfolder, archivebase)
|
2009-06-18 22:17:17 +00:00
|
|
|
tf.close()
|
|
|
|
# remove output folder
|
2009-10-04 16:45:59 +00:00
|
|
|
shutil.rmtree(outfolder)
|
2009-10-03 17:19:07 +00:00
|
|
|
return archivename
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
def macdeploy(versionstring, buildfolder, platform=sys.platform):
|
2009-11-29 21:09:21 +00:00
|
|
|
'''package created binary to dmg'''
|
|
|
|
dmgfile = program + "-" + versionstring + ".dmg"
|
2011-03-06 00:04:26 +00:00
|
|
|
appbundle = buildfolder + "/" + progexe[platform]
|
2009-11-29 21:09:21 +00:00
|
|
|
|
2010-07-10 21:22:22 +00:00
|
|
|
# workaround to Qt issues when building out-of-tree. Copy files into bundle.
|
2010-07-28 21:05:16 +00:00
|
|
|
sourcebase = buildfolder + re.sub('[^/]+.pro$', '', project) + "/"
|
2019-11-17 10:55:46 +00:00
|
|
|
print(sourcebase)
|
2010-07-10 21:22:22 +00:00
|
|
|
for src in bundlecopy:
|
2010-07-28 21:05:16 +00:00
|
|
|
shutil.copy(sourcebase + src, appbundle + "/" + bundlecopy[src])
|
2009-11-29 21:09:21 +00:00
|
|
|
# end of Qt workaround
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
output = subprocess.Popen(["macdeployqt", progexe[platform], "-dmg"], \
|
|
|
|
stdout=subprocess.PIPE, cwd=buildfolder)
|
2009-11-29 21:09:21 +00:00
|
|
|
output.communicate()
|
|
|
|
if not output.returncode == 0:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("macdeployqt failed!")
|
2009-11-29 21:09:21 +00:00
|
|
|
return -1
|
|
|
|
# copy dmg to output folder
|
|
|
|
shutil.copy(buildfolder + "/" + program + ".dmg", dmgfile)
|
|
|
|
return dmgfile
|
|
|
|
|
2011-03-06 00:04:26 +00:00
|
|
|
|
2009-10-04 16:45:59 +00:00
|
|
|
def filehashes(filename):
|
|
|
|
'''Calculate md5 and sha1 hashes for a given file.'''
|
|
|
|
if not os.path.exists(filename):
|
|
|
|
return ["", ""]
|
|
|
|
m = hashlib.md5()
|
|
|
|
s = hashlib.sha1()
|
|
|
|
f = open(filename, 'rb')
|
|
|
|
while True:
|
|
|
|
d = f.read(65536)
|
2019-11-17 12:40:42 +00:00
|
|
|
if d == b"":
|
2009-10-04 16:45:59 +00:00
|
|
|
break
|
|
|
|
m.update(d)
|
|
|
|
s.update(d)
|
|
|
|
return [m.hexdigest(), s.hexdigest()]
|
|
|
|
|
|
|
|
|
|
|
|
def filestats(filename):
|
|
|
|
if not os.path.exists(filename):
|
|
|
|
return
|
|
|
|
st = os.stat(filename)
|
2019-11-17 12:40:42 +00:00
|
|
|
print("%s\n%s" % (filename, "-" * len(filename)))
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Size: %i bytes" % st.st_size)
|
2009-10-04 16:45:59 +00:00
|
|
|
h = filehashes(filename)
|
2019-11-17 10:55:46 +00:00
|
|
|
print("md5sum: %s" % h[0])
|
|
|
|
print("sha1sum: %s" % h[1])
|
2019-11-17 12:40:42 +00:00
|
|
|
print("%s\n" % ("-" * len(filename)))
|
2009-10-04 16:45:59 +00:00
|
|
|
|
|
|
|
|
2010-03-31 20:52:07 +00:00
|
|
|
def tempclean(workfolder, nopro):
|
|
|
|
if nopro == True:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Cleaning up working folder %s" % workfolder)
|
2010-03-31 20:52:07 +00:00
|
|
|
shutil.rmtree(workfolder)
|
|
|
|
else:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Project file specified or cleanup disabled!")
|
|
|
|
print("Temporary files kept at %s" % workfolder)
|
2010-03-31 20:52:07 +00:00
|
|
|
|
|
|
|
|
2010-07-28 18:37:12 +00:00
|
|
|
def deploy():
|
2009-09-20 14:59:29 +00:00
|
|
|
startup = time.time()
|
2010-07-28 18:37:12 +00:00
|
|
|
|
2009-06-18 22:17:17 +00:00
|
|
|
try:
|
2019-11-17 10:55:46 +00:00
|
|
|
opts, args = getopt.getopt(
|
|
|
|
sys.argv[1:], "q:p:t:a:n:sbdkx:i:h",
|
2011-03-06 00:04:26 +00:00
|
|
|
["qmake=", "project=", "tag=", "add=", "makensis=", "source-only",
|
2011-05-05 17:19:00 +00:00
|
|
|
"binary-only", "dynamic", "keep-temp", "cross=", "buildid=", "help"])
|
2019-11-17 10:55:46 +00:00
|
|
|
except getopt.GetoptError as err:
|
|
|
|
print(str(err))
|
2009-06-18 22:17:17 +00:00
|
|
|
usage(sys.argv[0])
|
|
|
|
sys.exit(1)
|
|
|
|
qt = ""
|
2009-10-04 16:45:59 +00:00
|
|
|
proj = ""
|
|
|
|
svnbase = svnserver + "trunk/"
|
|
|
|
tag = ""
|
2009-10-04 21:17:59 +00:00
|
|
|
addfiles = []
|
2009-10-04 16:45:59 +00:00
|
|
|
cleanup = True
|
2009-10-05 18:35:28 +00:00
|
|
|
binary = True
|
|
|
|
source = True
|
2010-03-31 20:52:07 +00:00
|
|
|
keeptemp = False
|
2010-08-08 20:56:58 +00:00
|
|
|
makensis = ""
|
2011-03-06 00:04:26 +00:00
|
|
|
cross = ""
|
2011-05-05 17:19:00 +00:00
|
|
|
buildid = None
|
2011-03-06 00:04:26 +00:00
|
|
|
platform = sys.platform
|
2012-02-05 21:00:57 +00:00
|
|
|
treehash = gitscraper.get_refs(gitrepo)['refs/remotes/origin/HEAD']
|
2009-11-29 21:09:21 +00:00
|
|
|
if sys.platform != "darwin":
|
|
|
|
static = True
|
|
|
|
else:
|
|
|
|
static = False
|
2009-06-18 22:17:17 +00:00
|
|
|
for o, a in opts:
|
|
|
|
if o in ("-q", "--qmake"):
|
|
|
|
qt = a
|
2009-10-03 20:42:02 +00:00
|
|
|
if o in ("-p", "--project"):
|
|
|
|
proj = a
|
2009-10-04 16:45:59 +00:00
|
|
|
cleanup = False
|
2009-10-04 21:17:59 +00:00
|
|
|
if o in ("-a", "--add"):
|
|
|
|
addfiles.append(a)
|
2010-08-08 20:56:58 +00:00
|
|
|
if o in ("-n", "--makensis"):
|
|
|
|
makensis = a
|
2009-10-05 18:35:28 +00:00
|
|
|
if o in ("-s", "--source-only"):
|
|
|
|
binary = False
|
|
|
|
if o in ("-b", "--binary-only"):
|
|
|
|
source = False
|
2009-11-29 21:09:21 +00:00
|
|
|
if o in ("-d", "--dynamic") and sys.platform != "darwin":
|
2009-10-05 18:35:28 +00:00
|
|
|
static = False
|
2010-03-31 20:52:07 +00:00
|
|
|
if o in ("-k", "--keep-temp"):
|
|
|
|
keeptemp = True
|
2012-02-05 21:00:57 +00:00
|
|
|
if o in ("-t", "--tree"):
|
|
|
|
treehash = a
|
2011-03-06 00:04:26 +00:00
|
|
|
if o in ("-x", "--cross") and sys.platform != "win32":
|
|
|
|
cross = a
|
|
|
|
platform = "win32"
|
2011-05-05 17:19:00 +00:00
|
|
|
if o in ("-i", "--buildid"):
|
|
|
|
buildid = a
|
2009-06-18 22:17:17 +00:00
|
|
|
if o in ("-h", "--help"):
|
|
|
|
usage(sys.argv[0])
|
|
|
|
sys.exit(0)
|
|
|
|
|
2009-10-05 18:35:28 +00:00
|
|
|
if source == False and binary == False:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Building build neither source nor binary means nothing to do. Exiting.")
|
2009-10-05 18:35:28 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Building " + progexe[platform] + " for " + platform)
|
2009-10-04 16:45:59 +00:00
|
|
|
# search for qmake
|
2009-06-18 22:17:17 +00:00
|
|
|
if qt == "":
|
2011-03-06 00:04:26 +00:00
|
|
|
qm = findqt(cross)
|
2009-10-03 17:19:07 +00:00
|
|
|
else:
|
|
|
|
qm = checkqt(qt)
|
|
|
|
if qm == "":
|
2019-11-17 10:55:46 +00:00
|
|
|
print("ERROR: No suitable Qt installation found.")
|
2009-10-03 17:19:07 +00:00
|
|
|
sys.exit(1)
|
2009-10-04 16:45:59 +00:00
|
|
|
|
|
|
|
# create working folder. Use current directory if -p option used.
|
|
|
|
if proj == "":
|
|
|
|
w = tempfile.mkdtemp()
|
|
|
|
# make sure the path doesn't contain backslashes to prevent issues
|
|
|
|
# later when running on windows.
|
|
|
|
workfolder = re.sub(r'\\', '/', w)
|
2012-02-05 21:00:57 +00:00
|
|
|
revision = gitscraper.describe_treehash(gitrepo, treehash)
|
|
|
|
# try to find a version number from describe output.
|
|
|
|
# WARNING: this is broken and just a temporary workaround!
|
2019-11-17 12:40:42 +00:00
|
|
|
v = re.findall(b'([\d\.a-f]+)', revision)
|
2012-02-05 21:00:57 +00:00
|
|
|
if v:
|
2019-11-17 12:40:42 +00:00
|
|
|
if v[-1].decode().find('.') >= 0:
|
|
|
|
revision = "v" + v[-1].decode()
|
2012-02-05 21:00:57 +00:00
|
|
|
else:
|
2019-11-17 12:40:42 +00:00
|
|
|
revision = v[-1].decode()
|
2011-05-05 17:19:00 +00:00
|
|
|
if buildid == None:
|
|
|
|
versionextra = ""
|
|
|
|
else:
|
|
|
|
versionextra = "-" + buildid
|
2012-02-05 21:00:57 +00:00
|
|
|
sourcefolder = workfolder + "/" + program + "-" + str(revision) + versionextra + "/"
|
|
|
|
archivename = program + "-" + str(revision) + versionextra + "-src.tar.bz2"
|
|
|
|
ver = str(revision)
|
2009-10-04 16:45:59 +00:00
|
|
|
os.mkdir(sourcefolder)
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Version: %s" % revision)
|
2009-10-04 16:45:59 +00:00
|
|
|
else:
|
|
|
|
workfolder = "."
|
|
|
|
sourcefolder = "."
|
|
|
|
archivename = ""
|
|
|
|
# check if project file explicitly given. If yes, don't get sources from svn
|
|
|
|
if proj == "":
|
|
|
|
proj = sourcefolder + project
|
|
|
|
# get sources and pack source tarball
|
2019-11-17 10:55:46 +00:00
|
|
|
if getsources(treehash, svnpaths, sourcefolder) != 0:
|
2010-03-31 20:52:07 +00:00
|
|
|
tempclean(workfolder, cleanup and not keeptemp)
|
2009-10-04 16:45:59 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2011-05-05 17:19:00 +00:00
|
|
|
# replace version strings.
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Updating version information in sources")
|
2011-05-05 17:19:00 +00:00
|
|
|
for f in regreplace:
|
|
|
|
infile = open(sourcefolder + "/" + f, "r")
|
|
|
|
incontents = infile.readlines()
|
|
|
|
infile.close()
|
|
|
|
|
|
|
|
outfile = open(sourcefolder + "/" + f, "w")
|
|
|
|
for line in incontents:
|
|
|
|
newline = line
|
|
|
|
for r in regreplace[f]:
|
2011-04-06 20:17:29 +00:00
|
|
|
# replacements made on the replacement string:
|
|
|
|
# %REVISION% is replaced with the revision number
|
2011-05-08 19:34:03 +00:00
|
|
|
replacement = re.sub("%REVISION%", str(revision), r[1])
|
2012-03-06 21:06:40 +00:00
|
|
|
newline = re.sub(r[0], replacement, newline)
|
|
|
|
# %BUILD% is replaced with buildid as passed on the command line
|
2011-05-05 17:19:00 +00:00
|
|
|
if buildid != None:
|
2012-03-06 21:06:40 +00:00
|
|
|
replacement = re.sub("%BUILDID%", "-" + str(buildid), replacement)
|
|
|
|
else:
|
|
|
|
replacement = re.sub("%BUILDID%", "", replacement)
|
|
|
|
newline = re.sub(r[0], replacement, newline)
|
2011-05-05 17:19:00 +00:00
|
|
|
outfile.write(newline)
|
|
|
|
outfile.close()
|
2011-04-06 20:17:29 +00:00
|
|
|
|
2009-10-05 18:35:28 +00:00
|
|
|
if source == True:
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Creating source tarball %s\n" % archivename)
|
2009-10-05 18:35:28 +00:00
|
|
|
tf = tarfile.open(archivename, mode='w:bz2')
|
|
|
|
tf.add(sourcefolder, os.path.basename(re.subn('/$', '', sourcefolder)[0]))
|
|
|
|
tf.close()
|
|
|
|
if binary == False:
|
|
|
|
shutil.rmtree(workfolder)
|
|
|
|
sys.exit(0)
|
2009-10-04 16:45:59 +00:00
|
|
|
else:
|
|
|
|
# figure version from sources. Need to take path to project file into account.
|
|
|
|
versionfile = re.subn('[\w\.]+$', "version.h", proj)[0]
|
2015-06-07 20:19:54 +00:00
|
|
|
ver = findversion(versionfile) + "-dev" + datetime.now().strftime('%Y%m%d%H%M%S')
|
2011-05-05 17:19:00 +00:00
|
|
|
# append buildid if any.
|
|
|
|
if buildid != None:
|
|
|
|
ver += "-" + buildid
|
2009-10-04 16:45:59 +00:00
|
|
|
|
2009-10-03 20:42:02 +00:00
|
|
|
# check project file
|
|
|
|
if not os.path.exists(proj):
|
2019-11-17 10:55:46 +00:00
|
|
|
print("ERROR: path to project file wrong.")
|
2009-10-03 20:42:02 +00:00
|
|
|
sys.exit(1)
|
2009-06-18 22:17:17 +00:00
|
|
|
|
2009-10-04 21:17:59 +00:00
|
|
|
# copy specified (--add) files to working folder
|
|
|
|
for f in addfiles:
|
|
|
|
shutil.copy(f, sourcefolder)
|
2009-10-04 16:45:59 +00:00
|
|
|
buildstart = time.time()
|
2009-06-18 22:17:17 +00:00
|
|
|
header = "Building %s %s" % (program, ver)
|
2019-11-17 10:55:46 +00:00
|
|
|
print(header)
|
|
|
|
print(len(header) * "=")
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
# build it.
|
2011-03-06 00:04:26 +00:00
|
|
|
if not qmake(qm, proj, platform, sourcefolder, static, cross) == 0:
|
2010-03-31 20:52:07 +00:00
|
|
|
tempclean(workfolder, cleanup and not keeptemp)
|
2009-10-04 16:45:59 +00:00
|
|
|
sys.exit(1)
|
2011-03-06 00:04:26 +00:00
|
|
|
if not build(sourcefolder, platform, cross) == 0:
|
2010-03-31 20:52:07 +00:00
|
|
|
tempclean(workfolder, cleanup and not keeptemp)
|
2009-10-03 17:19:07 +00:00
|
|
|
sys.exit(1)
|
2010-08-08 20:56:58 +00:00
|
|
|
buildtime = time.time() - buildstart
|
2011-03-06 17:37:27 +00:00
|
|
|
progfiles = programfiles
|
|
|
|
progfiles.append(progexe[platform])
|
2011-03-06 00:04:26 +00:00
|
|
|
if platform == "win32":
|
2010-06-11 20:02:59 +00:00
|
|
|
if useupx == True:
|
2011-03-06 00:04:26 +00:00
|
|
|
if not upxfile(sourcefolder, platform) == 0:
|
2010-06-11 20:02:59 +00:00
|
|
|
tempclean(workfolder, cleanup and not keeptemp)
|
|
|
|
sys.exit(1)
|
2011-03-06 00:04:26 +00:00
|
|
|
dllfiles = finddlls(sourcefolder + "/" + progexe[platform], \
|
|
|
|
[os.path.dirname(qm)], cross)
|
2012-02-26 16:09:08 +00:00
|
|
|
if len(dllfiles) > 0:
|
2011-03-06 17:37:27 +00:00
|
|
|
progfiles.extend(dllfiles)
|
|
|
|
archive = zipball(progfiles, ver, sourcefolder, platform)
|
2010-08-30 17:51:53 +00:00
|
|
|
# only when running native right now.
|
|
|
|
if nsisscript != "" and makensis != "":
|
2011-03-06 00:04:26 +00:00
|
|
|
nsisfileinject(sourcefolder + "/" + nsisscript, sourcefolder \
|
|
|
|
+ "/" + nsisscript + ".tmp", dllfiles)
|
2010-08-30 17:51:53 +00:00
|
|
|
runnsis(ver, makensis, nsisscript + ".tmp", sourcefolder)
|
2011-03-06 00:04:26 +00:00
|
|
|
elif platform == "darwin":
|
|
|
|
archive = macdeploy(ver, sourcefolder, platform)
|
2009-06-18 22:17:17 +00:00
|
|
|
else:
|
2019-11-17 12:40:42 +00:00
|
|
|
if platform in ['linux', 'linux2']:
|
2012-02-26 16:09:08 +00:00
|
|
|
for p in progfiles:
|
|
|
|
prog = sourcefolder + "/" + p
|
2019-11-17 10:55:46 +00:00
|
|
|
output = subprocess.Popen(
|
|
|
|
["file", prog], stdout=subprocess.PIPE)
|
2012-02-26 16:09:08 +00:00
|
|
|
res = output.communicate()
|
|
|
|
if re.findall("ELF 64-bit", res[0]):
|
|
|
|
ver += "-64bit"
|
|
|
|
break
|
|
|
|
|
2011-03-06 17:37:27 +00:00
|
|
|
archive = tarball(progfiles, ver, sourcefolder)
|
2009-10-04 16:45:59 +00:00
|
|
|
|
|
|
|
# remove temporary files
|
2010-03-31 20:52:07 +00:00
|
|
|
tempclean(workfolder, cleanup)
|
2009-10-04 16:45:59 +00:00
|
|
|
|
|
|
|
# display summary
|
|
|
|
headline = "Build Summary for %s" % program
|
2019-11-17 12:40:42 +00:00
|
|
|
print("\n%s\n%s" % (headline, "=" * len(headline)))
|
|
|
|
if archivename != "":
|
2009-10-04 16:45:59 +00:00
|
|
|
filestats(archivename)
|
|
|
|
filestats(archive)
|
2009-09-20 14:59:29 +00:00
|
|
|
duration = time.time() - startup
|
|
|
|
durmins = (int)(duration / 60)
|
2009-10-03 17:19:07 +00:00
|
|
|
dursecs = (int)(duration % 60)
|
2010-08-08 20:56:58 +00:00
|
|
|
buildmins = (int)(buildtime / 60)
|
|
|
|
buildsecs = (int)(buildtime % 60)
|
2019-11-17 10:55:46 +00:00
|
|
|
print("Overall time %smin %ssec, building took %smin %ssec." % \
|
|
|
|
(durmins, dursecs, buildmins, buildsecs))
|
2009-06-18 22:17:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2019-11-17 10:55:46 +00:00
|
|
|
print("You cannot run this module directly!")
|
|
|
|
print("Set required environment and call deploy().")
|