diff options
Diffstat (limited to 'Mac/BuildScript/build-installer.py')
| -rwxr-xr-x | Mac/BuildScript/build-installer.py | 391 |
1 files changed, 275 insertions, 116 deletions
diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index fd5d5c3..8728289 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -8,7 +8,9 @@ OS X 10.5 and the 10.5 SDK. Please ensure that this script keeps working with Python 2.5, to avoid bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Sphinx, which is used to build the documentation, currently requires at least -Python 2.4. +Python 2.4. However, as of Python 3.4.1, Doc builds require an external +sphinx-build and the current versions of Sphinx now require at least +Python 2.6. In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script requires an installed version of hg and a third-party version of @@ -21,8 +23,8 @@ installing the most recent ActiveTcl 8.4 or 8.5 version. 32-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5 and the installation of additional components, such as a newer Python -(2.5 is needed for Python parser updates), hg, and svn (for the documentation -build). +(2.5 is needed for Python parser updates), hg, and for the documentation +build either svn (pre-3.4.1) or sphinx-build (3.4.1 and later). Usage: see USAGE variable in the script. """ @@ -60,11 +62,16 @@ def shellQuote(value): return "'%s'"%(value.replace("'", "'\"'\"'")) def grepValue(fn, variable): + """ + Return the unquoted value of a variable from a file.. + QUOTED_VALUE='quotes' -> str('quotes') + UNQUOTED_VALUE=noquotes -> str('noquotes') + """ variable = variable + '=' for ln in open(fn, 'r'): if ln.startswith(variable): value = ln[len(variable):].strip() - return value[1:-1] + return value.strip("\"'") raise RuntimeError("Cannot find variable %s" % variable[:-1]) _cache_getVersion = None @@ -76,9 +83,6 @@ def getVersion(): os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION') return _cache_getVersion -def getVersionTuple(): - return tuple([int(n) for n in getVersion().split('.')]) - def getVersionMajorMinor(): return tuple([int(n) for n in getVersion().split('.', 2)]) @@ -95,6 +99,9 @@ def getFullVersion(): return _cache_getFullVersion raise RuntimeError("Cannot find full version??") +FW_PREFIX = ["Library", "Frameworks", "Python.framework"] +FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions + # The directory we'll use to create the build (will be erased and recreated) WORKDIR = "/tmp/_py" @@ -148,19 +155,21 @@ SRCDIR = os.path.dirname( # $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level DEPTARGET = '10.3' -target_cc_map = { +def getDeptargetTuple(): + return tuple([int(n) for n in DEPTARGET.split('.')[0:2]]) + +def getTargetCompilers(): + target_cc_map = { '10.3': ('gcc-4.0', 'g++-4.0'), '10.4': ('gcc-4.0', 'g++-4.0'), '10.5': ('gcc-4.2', 'g++-4.2'), '10.6': ('gcc-4.2', 'g++-4.2'), - '10.7': ('clang', 'clang++'), - '10.8': ('clang', 'clang++'), - '10.9': ('clang', 'clang++'), -} + } + return target_cc_map.get(DEPTARGET, ('clang', 'clang++') ) -CC, CXX = target_cc_map[DEPTARGET] +CC, CXX = getTargetCompilers() -PYTHON_3 = getVersionTuple() >= (3, 0) +PYTHON_3 = getVersionMajorMinor() >= (3, 0) USAGE = textwrap.dedent("""\ Usage: build_python [options] @@ -184,6 +193,10 @@ USAGE = textwrap.dedent("""\ # '/Library/Frameworks/Tk.framework/Versions/8.5/Tk'] EXPECTED_SHARED_LIBS = {} +# List of names of third party software built with this installer. +# The names will be inserted into the rtf version of the License. +THIRD_PARTY_LIBS = [] + # Instructions for building libraries that are necessary for building a # batteries included python. # [The recipes are defined here for convenience but instantiated later after @@ -191,9 +204,53 @@ EXPECTED_SHARED_LIBS = {} def library_recipes(): result = [] - LT_10_5 = bool(DEPTARGET < '10.5') + LT_10_5 = bool(getDeptargetTuple() < (10, 5)) + + if getDeptargetTuple() < (10, 6): + # The OpenSSL libs shipped with OS X 10.5 and earlier are + # hopelessly out-of-date and do not include Apple's tie-in to + # the root certificates in the user and system keychains via TEA + # that was introduced in OS X 10.6. Note that this applies to + # programs built and linked with a 10.5 SDK even when run on + # newer versions of OS X. + # + # Dealing with CAs is messy. For now, just supply a + # local libssl and libcrypto for the older installer variants + # (e.g. the python.org 10.5+ 32-bit-only installer) that use the + # same default ssl certfile location as the system libs do: + # /System/Library/OpenSSL/cert.pem + # Then at least TLS connections can be negotiated with sites that + # use sha-256 certs like python.org, assuming the proper CA certs + # have been supplied. The default CA cert management issues for + # 10.5 and earlier builds are the same as before, other than it is + # now more obvious with cert checking enabled by default in the + # standard library. + # + # For builds with 10.6+ SDKs, continue to use the deprecated but + # less out-of-date Apple 0.9.8 libs for now. While they are less + # secure than using an up-to-date 1.0.1 version, doing so + # avoids the big problems of forcing users to have to manage + # default CAs themselves, thanks to the Apple libs using private TEA + # APIs for cert validation from keychains if validation using the + # standard OpenSSL locations (/System/Library/OpenSSL, normally empty) + # fails. + + result.extend([ + dict( + name="OpenSSL 1.0.2a", + url="https://www.openssl.org/source/openssl-1.0.2a.tar.gz", + checksum='a06c547dac9044161a477211049f60ef', + patches=[ + "openssl_sdk_makedepend.patch", + ], + buildrecipe=build_universal_openssl, + configure=None, + install=None, + ), + ]) - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): +# Disable for now + if False: # if getDeptargetTuple() > (10, 5): result.extend([ dict( name="Tcl 8.5.15", @@ -234,12 +291,12 @@ def library_recipes(): ), ]) - if getVersionTuple() >= (3, 3): + if PYTHON_3: result.extend([ dict( - name="XZ 5.0.3", - url="http://tukaani.org/xz/xz-5.0.3.tar.gz", - checksum='fefe52f9ecd521de2a8ce38c21a27574', + name="XZ 5.0.5", + url="http://tukaani.org/xz/xz-5.0.5.tar.gz", + checksum='19d924e066b6fff0bc9d1981b4e53196', configure_pre=[ '--disable-dependency-tracking', ] @@ -282,9 +339,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.7.13", - url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz", - checksum='c97df403e8a3d5b67bb408fcd6aabd8e', + name="SQLite 3.8.3.1", + url="http://www.sqlite.org/2014/sqlite-autoconf-3080301.tar.gz", + checksum='509ff98d8dc9729b618b7e96612079c6', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS4 ' '-DSQLITE_ENABLE_FTS3_PARENTHESIS ' @@ -301,7 +358,7 @@ def library_recipes(): ), ]) - if DEPTARGET < '10.5': + if getDeptargetTuple() < (10, 5): result.extend([ dict( name="Bzip2 1.0.6", @@ -432,11 +489,24 @@ def pkg_recipes(): topdir="/Library/Frameworks/Python.framework", source="/empty-dir", required=False, - selected=unselected_for_python3, + selected='selected', + ), + dict( + name="PythonInstallPip", + long_name="Install or upgrade pip", + readme="""\ + This package installs (or upgrades from an earlier version) + pip, a tool for installing and managing Python packages. + """, + postflight="scripts/postflight.ensurepip", + topdir="/Library/Frameworks/Python.framework", + source="/empty-dir", + required=False, + selected='selected', ), ] - if DEPTARGET < '10.4' and not PYTHON_3: + if getDeptargetTuple() < (10, 4) and not PYTHON_3: result.append( dict( name="PythonSystemFixes", @@ -453,6 +523,7 @@ def pkg_recipes(): selected=unselected_for_python3, ) ) + return result def fatal(msg): @@ -567,20 +638,6 @@ def checkEnvironment(): % frameworks['Tk'], ] - # For 10.6+ builds, we build two versions of _tkinter: - # - the traditional version (renamed to _tkinter_library.so) linked - # with /Library/Frameworks/{Tcl,Tk}.framework - # - the default version linked with our builtin copies of Tcl and Tk - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - EXPECTED_SHARED_LIBS['_tkinter_library.so'] = \ - EXPECTED_SHARED_LIBS['_tkinter.so'] - EXPECTED_SHARED_LIBS['_tkinter.so'] = [ - "/Library/Frameworks/Python.framework/Versions/%s/lib/libtcl%s.dylib" - % (getVersion(), frameworks['Tcl']), - "/Library/Frameworks/Python.framework/Versions/%s/lib/libtk%s.dylib" - % (getVersion(), frameworks['Tk']), - ] - # Remove inherited environment variables which might influence build environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_', 'LD_', 'LIBRARY_', 'PATH', 'PYTHON'] @@ -601,7 +658,10 @@ def checkEnvironment(): base_path = base_path + ':' + OLD_DEVELOPER_TOOLS os.environ['PATH'] = base_path print("Setting default PATH: %s"%(os.environ['PATH'])) - + # Ensure ws have access to hg and to sphinx-build. + # You may have to create links in /usr/bin for them. + runCommand('hg --version') + runCommand('sphinx-build --version') def parseOptions(args=None): """ @@ -609,6 +669,7 @@ def parseOptions(args=None): """ global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX + global FW_VERSION_PREFIX if args is None: args = sys.argv[1:] @@ -666,21 +727,23 @@ def parseOptions(args=None): SDKPATH=os.path.abspath(SDKPATH) DEPSRC=os.path.abspath(DEPSRC) - CC, CXX=target_cc_map[DEPTARGET] - - print("Settings:") - print(" * Source directory:", SRCDIR) - print(" * Build directory: ", WORKDIR) - print(" * SDK location: ", SDKPATH) - print(" * Third-party source:", DEPSRC) - print(" * Deployment target:", DEPTARGET) - print(" * Universal architectures:", ARCHLIST) - print(" * C compiler:", CC) - print(" * C++ compiler:", CXX) - print("") - + CC, CXX = getTargetCompilers() + FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()] + print("-- Settings:") + print(" * Source directory: %s" % SRCDIR) + print(" * Build directory: %s" % WORKDIR) + print(" * SDK location: %s" % SDKPATH) + print(" * Third-party source: %s" % DEPSRC) + print(" * Deployment target: %s" % DEPTARGET) + print(" * Universal archs: %s" % str(ARCHLIST)) + print(" * C compiler: %s" % CC) + print(" * C++ compiler: %s" % CXX) + print("") + print(" -- Building a Python %s framework at patch level %s" + % (getVersion(), getFullVersion())) + print("") def extractArchive(builddir, archiveName): """ @@ -772,6 +835,132 @@ def verifyThirdPartyFile(url, checksum, fname): % (shellQuote(fname), checksum) ): fatal('MD5 checksum mismatch for file %s' % fname) +def build_universal_openssl(basedir, archList): + """ + Special case build recipe for universal build of openssl. + + The upstream OpenSSL build system does not directly support + OS X universal builds. We need to build each architecture + separately then lipo them together into fat libraries. + """ + + # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4). + # If we are building on a 10.4.x or earlier system, + # unilaterally disable assembly code building to avoid the problem. + no_asm = int(platform.release().split(".")[0]) < 9 + + def build_openssl_arch(archbase, arch): + "Build one architecture of openssl" + arch_opts = { + "i386": ["darwin-i386-cc"], + "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"], + "ppc": ["darwin-ppc-cc"], + "ppc64": ["darwin64-ppc-cc"], + } + configure_opts = [ + "no-krb5", + "no-idea", + "no-mdc2", + "no-rc5", + "no-zlib", + "enable-tlsext", + "no-ssl2", + "no-ssl3", + "no-ssl3-method", + # "enable-unit-test", + "shared", + "--install_prefix=%s"%shellQuote(archbase), + "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX), + "--openssldir=/System/Library/OpenSSL", + ] + if no_asm: + configure_opts.append("no-asm") + runCommand(" ".join(["perl", "Configure"] + + arch_opts[arch] + configure_opts)) + runCommand("make depend OSX_SDK=%s" % SDKPATH) + runCommand("make all OSX_SDK=%s" % SDKPATH) + runCommand("make install_sw OSX_SDK=%s" % SDKPATH) + # runCommand("make test") + return + + srcdir = os.getcwd() + universalbase = os.path.join(srcdir, "..", + os.path.basename(srcdir) + "-universal") + os.mkdir(universalbase) + archbasefws = [] + for arch in archList: + # fresh copy of the source tree + archsrc = os.path.join(universalbase, arch, "src") + shutil.copytree(srcdir, archsrc, symlinks=True) + # install base for this arch + archbase = os.path.join(universalbase, arch, "root") + os.mkdir(archbase) + # Python framework base within install_prefix: + # the build will install into this framework.. + # This is to ensure that the resulting shared libs have + # the desired real install paths built into them. + archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX) + + # build one architecture + os.chdir(archsrc) + build_openssl_arch(archbase, arch) + os.chdir(srcdir) + archbasefws.append(archbasefw) + + # copy arch-independent files from last build into the basedir framework + basefw = os.path.join(basedir, *FW_VERSION_PREFIX) + shutil.copytree( + os.path.join(archbasefw, "include", "openssl"), + os.path.join(basefw, "include", "openssl") + ) + + shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"), + "SHLIB_VERSION_NUMBER") + # e.g. -> "1.0.0" + libcrypto = "libcrypto.dylib" + libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".") + # e.g. -> "libcrypto.1.0.0.dylib" + libssl = "libssl.dylib" + libssl_versioned = libssl.replace(".", "."+shlib_version_number+".") + # e.g. -> "libssl.1.0.0.dylib" + + try: + os.mkdir(os.path.join(basefw, "lib")) + except OSError: + pass + + # merge the individual arch-dependent shared libs into a fat shared lib + archbasefws.insert(0, basefw) + for (lib_unversioned, lib_versioned) in [ + (libcrypto, libcrypto_versioned), + (libssl, libssl_versioned) + ]: + runCommand("lipo -create -output " + + " ".join(shellQuote( + os.path.join(fw, "lib", lib_versioned)) + for fw in archbasefws)) + # and create an unversioned symlink of it + os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned)) + + # Create links in the temp include and lib dirs that will be injected + # into the Python build so that setup.py can find them while building + # and the versioned links so that the setup.py post-build import test + # does not fail. + relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX) + for fn in [ + ["include", "openssl"], + ["lib", libcrypto], + ["lib", libssl], + ["lib", libcrypto_versioned], + ["lib", libssl_versioned], + ]: + os.symlink( + os.path.join(relative_path, *fn), + os.path.join(basedir, "usr", "local", *fn) + ) + + return + def buildRecipe(recipe, basedir, archList): """ Build software using a recipe. This function does the @@ -781,8 +970,10 @@ def buildRecipe(recipe, basedir, archList): curdir = os.getcwd() name = recipe['name'] + THIRD_PARTY_LIBS.append(name) url = recipe['url'] configure = recipe.get('configure', './configure') + buildrecipe = recipe.get('buildrecipe', None) install = recipe.get('install', 'make && make install DESTDIR=%s'%( shellQuote(basedir))) @@ -854,7 +1045,7 @@ def buildRecipe(recipe, basedir, archList): ' -arch '.join(archList), shellQuote(SDKPATH)[1:-1], shellQuote(basedir)[1:-1],), - "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%( + "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%( DEPTARGET, shellQuote(SDKPATH)[1:-1], shellQuote(basedir)[1:-1], @@ -880,8 +1071,13 @@ def buildRecipe(recipe, basedir, archList): print("Running configure for %s"%(name,)) runCommand(' '.join(configure_args) + ' 2>&1') - print("Running install for %s"%(name,)) - runCommand('{ ' + install + ' ;} 2>&1') + if buildrecipe is not None: + # call special-case build recipe, e.g. for openssl + buildrecipe(basedir, archList) + + if install is not None: + print("Running install for %s"%(name,)) + runCommand('{ ' + install + ' ;} 2>&1') print("Done %s"%(name,)) print("") @@ -914,8 +1110,10 @@ def buildPythonDocs(): docdir = os.path.join(rootDir, 'pydocs') curDir = os.getcwd() os.chdir(buildDir) - runCommand('make update') - runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable)) + # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9 + runCommand('make clean') + # Assume sphinx-build is on our PATH, checked in checkEnvironment + runCommand('make html') os.chdir(curDir) if not os.path.exists(docdir): os.mkdir(docdir) @@ -955,34 +1153,22 @@ def buildPython(): runCommand("%s -C --enable-framework --enable-universalsdk=%s " "--with-universal-archs=%s " "%s " + "%s " "LDFLAGS='-g -L%s/libraries/usr/local/lib' " "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%( shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH), UNIVERSALARCHS, (' ', '--with-computed-gotos ')[PYTHON_3], + (' ', '--without-ensurepip ')[PYTHON_3], shellQuote(WORKDIR)[1:-1], shellQuote(WORKDIR)[1:-1])) + print("Running make touch") + runCommand("make touch") + print("Running make") runCommand("make") - # For deployment targets of 10.6 and higher, we build our own version - # of Tcl and Cocoa Aqua Tk libs because the Apple-supplied Tk 8.5 is - # out-of-date and has critical bugs. Save the _tkinter.so that was - # linked with /Library/Frameworks/{Tck,Tk}.framework and build - # another _tkinter.so linked with our builtin Tcl and Tk libs. - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - runCommand("find build -name '_tkinter.so' " - " -execdir mv '{}' _tkinter_library.so \;") - print("Running make to build builtin _tkinter") - runCommand("make TCLTK_INCLUDES='-I%s/libraries/usr/local/include' " - "TCLTK_LIBS='-L%s/libraries/usr/local/lib -ltcl8.5 -ltk8.5'"%( - shellQuote(WORKDIR)[1:-1], - shellQuote(WORKDIR)[1:-1])) - # make a copy which will be moved to lib-tkinter later - runCommand("find build -name '_tkinter.so' " - " -execdir cp -p '{}' _tkinter_builtin.so \;") - print("Running make install") runCommand("make install DESTDIR=%s"%( shellQuote(rootDir))) @@ -1007,27 +1193,11 @@ def buildPython(): 'Python.framework', 'Versions', version, 'lib', 'python%s'%(version,)) - # If we made multiple versions of _tkinter, move them to - # their own directories under python lib. This allows - # users to select which to import by manipulating sys.path - # directly or with PYTHONPATH. - - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - TKINTERS = ['builtin', 'library'] - tkinter_moves = [('_tkinter_' + tkn + '.so', - os.path.join(path_to_lib, 'lib-tkinter', tkn)) - for tkn in TKINTERS] - # Create the destination directories under lib-tkinter. - # The permissions and uid/gid will be fixed up next. - for tkm in tkinter_moves: - os.makedirs(tkm[1]) - print("Fix file modes") frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework') gid = grp.getgrnam('admin').gr_gid shared_lib_error = False - moves_list = [] for dirpath, dirnames, filenames in os.walk(frmDir): for dn in dirnames: os.chmod(os.path.join(dirpath, dn), STAT_0o775) @@ -1053,25 +1223,9 @@ def buildPython(): % (sl, p)) shared_lib_error = True - # If this is a _tkinter variant, move it to its own directory - # now that we have fixed its permissions and checked that it - # was linked properly. The directory was created earlier. - # The files are moved after the entire tree has been walked - # since the shared library checking depends on the files - # having unique names. - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - for tkm in tkinter_moves: - if fn == tkm[0]: - moves_list.append( - (p, os.path.join(tkm[1], '_tkinter.so'))) - if shared_lib_error: fatal("Unexpected shared library errors.") - # Now do the moves. - for ml in moves_list: - shutil.move(ml[0], ml[1]) - if PYTHON_3: LDVERSION=None VERSION=None @@ -1179,6 +1333,7 @@ def patchFile(inPath, outPath): data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later'))) data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS])) data = data.replace('$INSTALL_SIZE', installSize()) + data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS)) # This one is not handy as a template variable data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework') @@ -1187,7 +1342,9 @@ def patchFile(inPath, outPath): fp.close() def patchScript(inPath, outPath): + major, minor = getVersionMajorMinor() data = fileContents(inPath) + data = data.replace('@PYMAJOR@', str(major)) data = data.replace('@PYVER@', getVersion()) fp = open(outPath, 'w') fp.write(data) @@ -1359,8 +1516,6 @@ def buildInstaller(): else: patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn)) - shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt')) - def installSize(clear=False, _saved=[]): if clear: @@ -1468,12 +1623,14 @@ def main(): # Prepare the applications folder - fn = os.path.join(WORKDIR, "_root", "Applications", - "Python %s"%(getVersion(),), "Update Shell Profile.command") - patchScript("scripts/postflight.patch-profile", fn) - folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%( getVersion(),)) + fn = os.path.join(folder, "License.rtf") + patchFile("resources/License.rtf", fn) + fn = os.path.join(folder, "ReadMe.rtf") + patchFile("resources/ReadMe.rtf", fn) + fn = os.path.join(folder, "Update Shell Profile.command") + patchScript("scripts/postflight.patch-profile", fn) os.chmod(folder, STAT_0o755) setIcon(folder, "../Icons/Python Folder.icns") @@ -1481,10 +1638,12 @@ def main(): buildInstaller() # And copy the readme into the directory containing the installer - patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt')) + patchFile('resources/ReadMe.rtf', + os.path.join(WORKDIR, 'installer', 'ReadMe.rtf')) # Ditto for the license file. - shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt')) + patchFile('resources/License.rtf', + os.path.join(WORKDIR, 'installer', 'License.rtf')) fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w') fp.write("# BUILD INFO\n") |
