diff --git a/scripts/dist.py b/scripts/dist.py index 8fe632255..8d5c0e495 100755 --- a/scripts/dist.py +++ b/scripts/dist.py @@ -4,18 +4,26 @@ Script for generating distributables based on platform skeletons. User supplies path for pyfa code base, root skeleton directory, and where the builds go. The builds are automatically named depending on the pyfa config -values of `version` and `tag`. If it's a Stable release, the naming convention is: +values of `version` and `tag`. If it's a Stable release, the naming +convention is: + pyfa-pyfaversion-expansion-expversion-platform -If it is not Stable (tag=git), we determine if the pyfa code base includes the -git repo to use as an ID. If not, uses randomly generated 6-character ID. The -unstable naming convention: + +If it is not Stable (tag=git), we determine if the pyfa code base includes +the git repo to use as an ID. If not, uses randomly generated 6-character ID. +The unstable naming convention: + pyfa-YYYMMDD-id-platform -dist.py can also build the Windows installer provided that it has a path to Inno -Setup (and, for generating on non-Windows platforms, that WINE is installed). -To build the EXE file, `win` must be included in the platforms to be built. +dist.py can also build the Windows installer provided that it has a path to +Inno Setup (and, for generating on non-Windows platforms, that WINE is +installed). To build the EXE file, `win` must be included in the platforms to +be built. """ +#@todo: ensure build directory can be written to +# todo: default build and dist directories + from optparse import OptionParser import os.path import shutil @@ -25,6 +33,7 @@ import datetime import random import string import zipfile +import errno from subprocess import call class FileStub(): @@ -34,6 +43,15 @@ class FileStub(): def flush(self, *args): pass +def copyanything(src, dst): + try: + shutil.copytree(src, dst, ignore=loginfo) + except: # python >2.5 + try: + shutil.copy(src, dst) + except: + raise + def id_generator(size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for x in range(size)) @@ -42,7 +60,7 @@ def zipdir(path, zip): for file in files: zip.write(os.path.join(root, file)) -skels = ['win', 'mac', 'src'] +skels = ['win', 'mac', 'src', 'win-wx3'] iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine if __name__ == "__main__": @@ -52,7 +70,6 @@ if __name__ == "__main__": parser.add_option("-b", "--base", dest="base", help="Location of cleaned read-only base directory") parser.add_option("-d", "--destination", dest="destination", help="Where to copy our distributable") parser.add_option("-p", "--platforms", dest="platforms", help="Comma-separated list of platforms to build", default="win,src,mac") - parser.add_option("-t", "--static", dest="static", help="Directory containing static files") parser.add_option("-q", "--quiet", dest="silent", action="store_true") parser.add_option("-w", "--winexe", dest="winexe", action="store_true", help="Build the Windows installer file (needs Inno Setup). Must include 'win' in platform options") parser.add_option("-z", "--zip", dest="zip", action="store_true", help="zip archive instead of tar") @@ -69,80 +86,109 @@ if __name__ == "__main__": options.platforms = options.platforms.split(",") - sys.path.append(options.base) - import config as pyfaconfig + #sys.path.append(options.base) + #import config as pyfaconfig for skel in skels: if skel not in options.platforms: continue print "\n======== %s ========"%skel - infoDict = {} + + info = {} + config = {} skeleton = os.path.expanduser(os.path.join(options.skeleton, skel)) - info = execfile(os.path.join(skeleton, "info.py"), infoDict) - dirName = infoDict["arcname"] + + execfile(os.path.join(options.base, "config.py"), config) + execfile(os.path.join(skeleton, "info.py"), info) + + destination = os.path.expanduser(options.destination) + if not os.path.isdir(destination) or not os.access(destination, os.W_OK | os.X_OK): + print "Destination directory does not exist or is not writable: {}".format(destination) + sys.exit() + + dirName = info["arcname"] + nowdt = datetime.datetime.now() now = "%04d%02d%02d" % (nowdt.year, nowdt.month, nowdt.day) + git = False - if pyfaconfig.tag.lower() == "git": + if config['tag'].lower() == "git": try: # if there is a git repo associated with base, use master commit - with open(os.path.join(options.base,".git","refs","heads","master"), 'r') as f: + with open(os.path.join(options.base, ".git", "refs", "heads", "master"), 'r') as f: id = f.readline()[0:6] git = True except: # else, use custom ID id = id_generator() - fileName = "pyfa-%s-%s-%s" % (now, id, infoDict["os"]) + fileName = "pyfa-{}-{}-{}".format(now, id, info["os"]) else: - fileName = "pyfa-%s-%s-%s-%s" % (pyfaconfig.version, pyfaconfig.expansionName.lower(), pyfaconfig.expansionVersion, infoDict["os"]) + fileName = "pyfa-{}-{}-{}-{}".format( + config['version'], + config['expansionName'].lower(), + config['expansionVersion'], + info["os"] + ) - archiveName = "%s.%s"%(fileName, "zip" if options.zip else "tar.bz2") + archiveName = "{}.{}".format(fileName, "zip" if options.zip else "tar.bz2") dst = os.path.join(os.getcwd(), dirName) # tmp directory where files are copied tmpFile = os.path.join(os.getcwd(), archiveName) - config = os.path.join(skeleton, "config.py") - destination = os.path.expanduser(options.destination) i = 0 - gitData = (".git", ".gitignore", ".gitmodules") + ignoreData = (".git", ".gitignore", ".gitmodules", "dist_assets", "saveddata", "build", "dist", "scripts", ".idea") def loginfo(path, names): + # Print out a "progress" and return directories / files to ignore global i i += 1 if i % 10 == 0: sys.stdout.write(".") sys.stdout.flush() - return gitData + return ignoreData try: print "Copying skeleton to ", dst i = 0 shutil.copytree(skeleton, dst, ignore=loginfo) print - base = os.path.join(dst, infoDict["base"]) - print "Copying base to ", base + if "win-wx3" not in skel: + # simply copying base into working build + base = os.path.join(dst, info["base"]) + print "Copying base to ", base - i = 0 - for stuff in os.listdir(os.path.expanduser(options.base)): - currSource = os.path.join(os.path.expanduser(options.base), stuff) - currDest = os.path.join(base, stuff) - if stuff in gitData: - continue - elif os.path.isdir(currSource): - shutil.copytree(currSource, currDest, ignore=loginfo) - else: - shutil.copy2(currSource, currDest) + i = 0 + for stuff in os.listdir(os.path.expanduser(options.base)): + currSource = os.path.join(os.path.expanduser(options.base), stuff) + currDest = os.path.join(base, stuff) + if stuff in ignoreData: + continue + elif os.path.isdir(currSource): + shutil.copytree(currSource, currDest, ignore=loginfo) + else: + shutil.copy2(currSource, currDest) - print + print + print "Copying done, making archive: ", tmpFile + else: + # this should work, but it's barely been tested + base = os.path.join(dst, info["base"]) + source = os.path.expanduser(options.base) + sys.path.append(source) + import setup - if os.path.exists(config): - print "Adding skeleton config file" - shutil.copy2(config, base) + libraryFile = os.path.join(base, "library.zip") + with zipfile.ZipFile(libraryFile, 'a') as library: + # change cwd to source so that the zip append will work correctly + oldcwd = os.getcwd() + os.chdir(source) + for dir in setup.packages: + zipdir(dir, library) + library.write('pyfa.py', 'pyfa__main__.py') + library.write('config.py') - if options.static is not None and os.path.exists(os.path.expanduser(options.static)): - print "Copying static data to ", os.path.join(base, "staticdata") - static = os.path.expanduser(options.static) - shutil.copytree(static, os.path.join(base, "staticdata"), ignore=loginfo) + for dir in setup.include_files: + copyanything(dir, os.path.join(base, dir)) - print "Copying done, making archive: ", tmpFile + os.chdir(oldcwd) if options.zip: archive = zipfile.ZipFile(tmpFile, 'w', compression=zipfile.ZIP_DEFLATED) @@ -150,7 +196,7 @@ if __name__ == "__main__": archive.close() else: archive = tarfile.open(tmpFile, "w:bz2") - archive.add(dst, arcname=infoDict["arcname"]) + archive.add(dst, arcname=info["arcname"]) archive.close() print "Moving archive to ", destination @@ -159,20 +205,20 @@ if __name__ == "__main__": if "win" in skel and options.winexe: print "Compiling EXE" - if pyfaconfig.tag.lower() == "git": + if config['tag'].lower() == "git": if git: # if git repo info available, use git commit expansion = "git-%s"%(id) else: # if there is no git repo, use timestamp expansion = now else: # if code is Stable, use expansion name - expansion = "%s %s"%(pyfaconfig.expansionName, pyfaconfig.expansionVersion), + expansion = "%s %s"%(config['expansionName'], config['expansionVersion']), calllist = ["wine"] if 'win' not in sys.platform else [] call(calllist + [ iscc, os.path.join(os.path.dirname(__file__), "pyfa-setup.iss"), - "/dMyAppVersion=%s"%(pyfaconfig.version), + "/dMyAppVersion=%s"%(config['version']), "/dMyAppExpansion=%s"%(expansion), "/dMyAppDir=%s"%dst, "/dMyOutputDir=%s"%destination,