Support cross compiling for Windows target.

Restructure deploy.py by moving the platform decisions out of the calling
scripts. This is necessary when cross compiling since this is only decided in
deploy.py. Add support for passing a cross compiler prefix on the command line
and always build targeting Windows if set.

Correct some whitespace errors and long lines while at it.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29531 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dominik Riebeling 2011-03-06 00:04:26 +00:00
parent 71e4b03ed6
commit 43a40caa37
3 changed files with 119 additions and 97 deletions

View file

@ -17,7 +17,6 @@
#
import deploy
import sys
deploy.program = "RockboxUtility"
deploy.project = "rbutil/rbutilqt/rbutilqt.pro"
@ -48,22 +47,29 @@ deploy.bundlecopy = {
"icons/rbutilqt.icns" : "Contents/Resources/",
"Info.plist" : "Contents/"
}
# Windows nees some special treatment. Differentiate between program name
# and executable filename.
if sys.platform == "win32":
deploy.progexe = "Release/" + deploy.program + ".exe"
deploy.make = "mingw32-make"
elif sys.platform == "darwin":
deploy.progexe = deploy.program + ".app"
# OS X 10.6 defaults to gcc 4.2. Building universal binaries that are
# compatible with 10.4 requires using gcc-4.0.
if not "QMAKESPEC" in deploy.environment:
deploy.environment["QMAKESPEC"] = "macx-g++40"
else:
deploy.progexe = deploy.program
deploy.progexe = {
"win32" : "release/RockboxUtility.exe",
"darwin" : "RockboxUtility.app",
"linux" : "RockboxUtility"
}
# OS X 10.6 defaults to gcc 4.2. Building universal binaries that are
# compatible with 10.4 requires using gcc-4.0.
deploy.qmakespec = {
"win32" : "",
"darwin" : "macx-g++40",
"linux" : ""
}
deploy.make = {
"win32" : "mingw32-make",
"darwin" : "make",
"linux" : "make"
}
# all files of the program. Will get put into an archive after building
# (zip on w32, tar.bz2 on Linux). Does not apply on Mac which uses dmg.
deploy.programfiles = [ deploy.progexe ]
# progexe will get added automatically.
deploy.programfiles = [ ]
deploy.nsisscript = ""
deploy.deploy()

View file

@ -17,7 +17,6 @@
#
import deploy
import sys
deploy.program = "rbthemeeditor"
deploy.project = "utils/themeeditor/themeeditor.pro"
@ -31,22 +30,28 @@ deploy.bundlecopy = {
"resources/windowicon.icns" : "Contents/Resources/",
"Info.plist" : "Contents/"
}
# Windows nees some special treatment. Differentiate between program name
# and executable filename.
if sys.platform == "win32":
deploy.progexe = "Release/" + deploy.program + ".exe"
deploy.make = "mingw32-make"
elif sys.platform == "darwin":
deploy.progexe = deploy.program + ".app"
# OS X 10.6 defaults to gcc 4.2. Building universal binaries that are
# compatible with 10.4 requires using gcc-4.0.
if not "QMAKESPEC" in deploy.environment:
deploy.environment["QMAKESPEC"] = "macx-g++40"
else:
deploy.progexe = deploy.program
deploy.progexe = {
"win32" : "release/rbthemeeditor.exe",
"darwin" : "rbthemeeditor.app",
"linux" : "rbthemeeditor"
}
# OS X 10.6 defaults to gcc 4.2. Building universal binaries that are
# compatible with 10.4 requires using gcc-4.0.
deploy.qmakespec = {
"win32" : "",
"darwin" : "macx-g++40",
"linux" : ""
}
deploy.make = {
"win32" : "mingw32-make",
"darwin" : "make",
"linux" : "make"
}
# all files of the program. Will get put into an archive after building
# (zip on w32, tar.bz2 on Linux). Does not apply on Mac which uses dmg.
deploy.programfiles = [ deploy.progexe ]
# progexe will get added automatically.
deploy.programfiles = [ ]
deploy.nsisscript = "utils/themeeditor/themeeditor.nsi"
deploy.deploy()

View file

@ -45,6 +45,7 @@ import getopt
import time
import hashlib
import tempfile
import string
# modules that are not part of python itself.
try:
@ -68,27 +69,6 @@ except ImportError:
print "Warning: multiprocessing module not found. Assuming 1 core."
# == Global stuff ==
# Windows nees some special treatment. Differentiate between program name
# and executable filename.
program = ""
project = ""
environment = os.environ
progexe = ""
make = "make"
programfiles = []
nsisscript = ""
svnserver = ""
# Paths and files to retrieve from svn when creating a tarball.
# This is a mixed list, holding both paths and filenames.
svnpaths = [ ]
# set this to true to run upx on the resulting binary, false to skip this step.
# only used on w32.
useupx = False
# OS X: files to copy into the bundle. Workaround for out-of-tree builds.
bundlecopy = { }
# DLL files to ignore when searching for required DLL files.
systemdlls = ['advapi32.dll',
'comdlg32.dll',
@ -121,11 +101,14 @@ def usage(myself):
print " -n, --makensis=<file> path to makensis for building Windows setup program."
if sys.platform != "darwin":
print " -d, --dynamic link dynamically instead of static"
if sys.platform != "win32":
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."
def getsources(svnsrv, filelist, dest):
'''Get the files listed in filelist from svnsrv and put it at dest.'''
client = pysvn.Client()
@ -171,10 +154,10 @@ def findversion(versionfile):
return m.group(1)
def findqt():
def findqt(cross=""):
'''Search for Qt4 installation. Return path to qmake.'''
print "Searching for Qt"
bins = ["qmake", "qmake-qt4"]
bins = [cross + "qmake", cross + "qmake-qt4"]
for binary in bins:
try:
q = which.which(binary)
@ -212,14 +195,19 @@ def checkqt(qmakebin):
return result
def qmake(qmake="qmake", projfile=project, wd=".", static=True):
def qmake(qmake, projfile, platform=sys.platform, wd=".", static=True, cross=""):
print "Running qmake in %s..." % wd
command = [qmake, "-config", "release", "-config", "noccache"]
if static == True:
command.append("-config")
command.append("static")
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"])
command.append(projfile)
output = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=wd, env=environment)
output = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=wd)
output.communicate()
if not output.returncode == 0:
print "qmake returned an error!"
@ -227,10 +215,11 @@ def qmake(qmake="qmake", projfile=project, wd=".", static=True):
return 0
def build(wd="."):
def build(wd=".", platform=sys.platform, cross=""):
# make
print "Building ..."
command = [make]
# use the current platforms make here, cross compiling uses the native make.
command = [make[sys.platform]]
if cpus > 1:
command.append("-j")
command.append(str(cpus))
@ -246,10 +235,11 @@ def build(wd="."):
print "Build failed!"
return -1
break
if sys.platform != "darwin":
if platform != "darwin":
# strip. OS X handles this via macdeployqt.
print "Stripping binary."
output = subprocess.Popen(["strip", progexe], stdout=subprocess.PIPE, cwd=wd)
output = subprocess.Popen([cross + "strip", progexe[platform]], \
stdout=subprocess.PIPE, cwd=wd)
output.communicate()
if not output.returncode == 0:
print "Stripping failed!"
@ -257,10 +247,11 @@ def build(wd="."):
return 0
def upxfile(wd="."):
def upxfile(wd=".", platform=sys.platform):
# run upx on binary
print "UPX'ing binary ..."
output = subprocess.Popen(["upx", progexe], stdout=subprocess.PIPE, cwd=wd)
output = subprocess.Popen(["upx", progexe[platform]], \
stdout=subprocess.PIPE, cwd=wd)
output.communicate()
if not output.returncode == 0:
print "UPX'ing failed!"
@ -279,11 +270,14 @@ def runnsis(versionstring, nsis, script, srcfolder):
# FIXME: instead of copying binaries around copy the NSI file and inject
# the correct paths.
b = srcfolder + "/" + os.path.dirname(script) + "/" + os.path.dirname(progexe)
# Only win32 supported as target platform so hard coded.
b = srcfolder + "/" + os.path.dirname(script) + "/" \
+ os.path.dirname(progexe["win32"])
if not os.path.exists(b):
os.mkdir(b)
shutil.copy(srcfolder + "/" + progexe, b)
output = subprocess.Popen([nsis, srcfolder + "/" + script], stdout=subprocess.PIPE)
shutil.copy(srcfolder + "/" + progexe["win32"], b)
output = subprocess.Popen([nsis, srcfolder + "/" + script], \
stdout=subprocess.PIPE)
output.communicate()
if not output.returncode == 0:
print "NSIS failed!"
@ -297,7 +291,8 @@ def runnsis(versionstring, nsis, script, srcfolder):
if nsissetup == "":
print "Could not retrieve output file name!"
return -1
shutil.copy(srcfolder + "/" + os.path.dirname(script) + "/" + nsissetup, setupfile)
shutil.copy(srcfolder + "/" + os.path.dirname(script) + "/" + nsissetup, \
setupfile)
return 0
@ -309,20 +304,24 @@ def nsisfileinject(nsis, outscript, filelist):
output = open(outscript, "w")
for line in open(nsis, "r"):
output.write(line)
# inject files after the progexe binary. Match the basename only to avoid path mismatches.
if re.match(r'^\s*File\s*.*' + os.path.basename(progexe), line, re.IGNORECASE):
# inject files after the progexe binary.
# Match the basename only to avoid path mismatches.
if re.match(r'^\s*File\s*.*' + os.path.basename(progexe["win32"]), \
line, re.IGNORECASE):
for f in filelist:
injection = " File /oname=$INSTDIR\\" + os.path.basename(f) + " " + os.path.normcase(f) + "\n"
injection = " File /oname=$INSTDIR\\" + os.path.basename(f) \
+ " " + os.path.normcase(f) + "\n"
output.write(injection)
output.write(" ; end of injected files\n")
output.close()
def finddlls(program, extrapaths = []):
def finddlls(program, extrapaths=[], cross=""):
'''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.
output = subprocess.Popen(["objdump", "-x", program], stdout=subprocess.PIPE)
output = subprocess.Popen([cross + "objdump", "-x", program], \
stdout=subprocess.PIPE)
cmdout = output.communicate()
# create list of used DLLs. Store as lower case as W32 is case-insensitive.
@ -342,20 +341,20 @@ def finddlls(program, extrapaths = []):
for path in extrapaths:
if os.path.exists(path + "/" + file):
dllpath = re.sub(r"\\", r"/", path + "/" + file)
print file + ": found at " + dllpath
print file + ": found at " + dllpath
dllpaths.append(dllpath)
break
if dllpath == "":
try:
dllpath = re.sub(r"\\", r"/", which.which(file))
print file + ": found at " + dllpath
dllpaths.append(dllpath)
print file + ": found at " + dllpath
dllpaths.append(dllpath)
except:
print file + ": NOT FOUND."
return dllpaths
def zipball(versionstring, buildfolder):
def zipball(versionstring, buildfolder, platform=sys.platform):
'''package created binary'''
print "Creating binary zipball."
archivebase = program + "-" + versionstring
@ -364,6 +363,7 @@ def zipball(versionstring, buildfolder):
# create output folder
os.mkdir(outfolder)
# move program files to output folder
programfiles.append(progexe[platform])
for f in programfiles:
if re.match(r'^(/|[a-zA-Z]:)', f) != None:
shutil.copy(f, outfolder)
@ -373,12 +373,12 @@ def zipball(versionstring, buildfolder):
zf = zipfile.ZipFile(archivename, mode='w', compression=zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(outfolder):
for name in files:
physname = os.path.join(root, name)
filename = re.sub("^" + buildfolder, "", physname)
physname = os.path.normpath(os.path.join(root, name))
filename = string.replace(physname, os.path.normpath(buildfolder), "")
zf.write(physname, filename)
for name in dirs:
physname = os.path.join(root, name)
filename = re.sub("^" + buildfolder, "", physname)
physname = os.path.normpath(os.path.join(root, name))
filename = string.replace(physname, os.path.normpath(buildfolder), "")
zf.write(physname, filename)
zf.close()
# remove output folder
@ -406,10 +406,10 @@ def tarball(versionstring, buildfolder):
return archivename
def macdeploy(versionstring, buildfolder):
def macdeploy(versionstring, buildfolder, platform=sys.platform):
'''package created binary to dmg'''
dmgfile = program + "-" + versionstring + ".dmg"
appbundle = buildfolder + "/" + progexe
appbundle = buildfolder + "/" + progexe[platform]
# workaround to Qt issues when building out-of-tree. Copy files into bundle.
sourcebase = buildfolder + re.sub('[^/]+.pro$', '', project) + "/"
@ -418,7 +418,8 @@ def macdeploy(versionstring, buildfolder):
shutil.copy(sourcebase + src, appbundle + "/" + bundlecopy[src])
# end of Qt workaround
output = subprocess.Popen(["macdeployqt", progexe, "-dmg"], stdout=subprocess.PIPE, cwd=buildfolder)
output = subprocess.Popen(["macdeployqt", progexe[platform], "-dmg"], \
stdout=subprocess.PIPE, cwd=buildfolder)
output.communicate()
if not output.returncode == 0:
print "macdeployqt failed!"
@ -427,6 +428,7 @@ def macdeploy(versionstring, buildfolder):
shutil.copy(buildfolder + "/" + program + ".dmg", dmgfile)
return dmgfile
def filehashes(filename):
'''Calculate md5 and sha1 hashes for a given file.'''
if not os.path.exists(filename):
@ -468,8 +470,9 @@ def deploy():
startup = time.time()
try:
opts, args = getopt.getopt(sys.argv[1:], "q:p:t:a:n:sbdkh",
["qmake=", "project=", "tag=", "add=", "makensis=", "source-only", "binary-only", "dynamic", "keep-temp", "help"])
opts, args = getopt.getopt(sys.argv[1:], "q:p:t:a:n:sbdkx:h",
["qmake=", "project=", "tag=", "add=", "makensis=", "source-only",
"binary-only", "dynamic", "keep-temp", "cross=", "help"])
except getopt.GetoptError, err:
print str(err)
usage(sys.argv[0])
@ -484,6 +487,8 @@ def deploy():
source = True
keeptemp = False
makensis = ""
cross = ""
platform = sys.platform
if sys.platform != "darwin":
static = True
else:
@ -509,6 +514,9 @@ def deploy():
static = False
if o in ("-k", "--keep-temp"):
keeptemp = True
if o in ("-x", "--cross") and sys.platform != "win32":
cross = a
platform = "win32"
if o in ("-h", "--help"):
usage(sys.argv[0])
sys.exit(0)
@ -517,9 +525,10 @@ def deploy():
print "Building build neither source nor binary means nothing to do. Exiting."
sys.exit(1)
print "Building " + progexe[platform] + " for " + platform
# search for qmake
if qt == "":
qm = findqt()
qm = findqt(cross)
else:
qm = checkqt(qt)
if qm == "":
@ -581,28 +590,30 @@ def deploy():
print len(header) * "="
# build it.
if not qmake(qm, proj, sourcefolder, static) == 0:
if not qmake(qm, proj, platform, sourcefolder, static, cross) == 0:
tempclean(workfolder, cleanup and not keeptemp)
sys.exit(1)
if not build(sourcefolder) == 0:
if not build(sourcefolder, platform, cross) == 0:
tempclean(workfolder, cleanup and not keeptemp)
sys.exit(1)
buildtime = time.time() - buildstart
if sys.platform == "win32":
if platform == "win32":
if useupx == True:
if not upxfile(sourcefolder) == 0:
if not upxfile(sourcefolder, platform) == 0:
tempclean(workfolder, cleanup and not keeptemp)
sys.exit(1)
dllfiles = finddlls(sourcefolder + "/" + progexe, [os.path.dirname(qm)])
dllfiles = finddlls(sourcefolder + "/" + progexe[platform], \
[os.path.dirname(qm)], cross)
if dllfiles.count > 0:
programfiles.extend(dllfiles)
archive = zipball(ver, sourcefolder)
archive = zipball(ver, sourcefolder, platform)
# only when running native right now.
if nsisscript != "" and makensis != "":
nsisfileinject(sourcefolder + "/" + nsisscript, sourcefolder + "/" + nsisscript + ".tmp", dllfiles)
nsisfileinject(sourcefolder + "/" + nsisscript, sourcefolder \
+ "/" + nsisscript + ".tmp", dllfiles)
runnsis(ver, makensis, nsisscript + ".tmp", sourcefolder)
elif sys.platform == "darwin":
archive = macdeploy(ver, sourcefolder)
elif platform == "darwin":
archive = macdeploy(ver, sourcefolder, platform)
else:
if os.uname()[4].endswith("64"):
ver += "-64bit"
@ -627,5 +638,5 @@ def deploy():
if __name__ == "__main__":
deploy()
print "You cannot run this module directly!"
print "Set required environment and call deploy()."