blob: 7d3bed2f64578740163d8933dc54cb6924f357b8 [file] [log] [blame]
Ned Deily4a96a372013-01-29 00:08:32 -08001#!/usr/bin/env python
Thomas Wouters477c8d52006-05-27 19:21:47 +00002"""
Ned Deily8c9bb722018-01-30 07:42:14 -05003This script is used to build "official" universal installers on macOS.
4
5NEW for 3.7.0:
6- support Intel 64-bit-only () and 32-bit-only installer builds
Ned Deily7a6f59e2018-03-29 07:41:11 -04007- build and use internal Tcl/Tk 8.6 for 10.6+ builds
Ned Deily8c9bb722018-01-30 07:42:14 -05008- deprecate use of explicit SDK (--sdk-path=) since all but the oldest
9 versions of Xcode support implicit setting of an SDK via environment
10 variables (SDKROOT and friends, see the xcrun man page for more info).
11 The SDK stuff was primarily needed for building universal installers
12 for 10.4; so as of 3.7.0, building installers for 10.4 is no longer
13 supported with build-installer.
14- use generic "gcc" as compiler (CC env var) rather than "gcc-4.2"
15
16TODO:
17- support SDKROOT and DEVELOPER_DIR xcrun env variables
18- test with 10.5 and 10.4 and determine support status
Thomas Wouters477c8d52006-05-27 19:21:47 +000019
Ned Deilye59e4c52011-01-29 18:56:28 +000020Please ensure that this script keeps working with Python 2.5, to avoid
Ned Deily8c9bb722018-01-30 07:42:14 -050021bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Doc builds
22use current versions of Sphinx and require a reasonably current python3.
23Sphinx and dependencies are installed into a venv using the python3's pip
24so will fetch them from PyPI if necessary. Since python3 is now used for
25Sphinx, build-installer.py should also be converted to use python3!
Thomas Wouters477c8d52006-05-27 19:21:47 +000026
Ned Deily7a6f59e2018-03-29 07:41:11 -040027For 3.7.0, when building for a 10.6 or higher deployment target,
28build-installer builds and links with its own copy of Tcl/Tk 8.6.
29Otherwise, it requires an installed third-party version of
Ned Deily8c9bb722018-01-30 07:42:14 -050030Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets), Tcl/TK 8.5
31(for 10.6 or later), or Tcl/TK 8.6 (for 10.9 or later)
32installed in /Library/Frameworks. When installed,
Ned Deily4a96a372013-01-29 00:08:32 -080033the Python built by this script will attempt to dynamically link first to
34Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
35back to the ones in /System/Library/Framework. For the build, we recommend
Ned Deily8c9bb722018-01-30 07:42:14 -050036installing the most recent ActiveTcl 8.6. 8.5, or 8.4 version, depending
37on the deployment target. The actual version linked to depends on the
38path of /Library/Frameworks/{Tcl,Tk}.framework/Versions/Current.
Ned Deily4a96a372013-01-29 00:08:32 -080039
Thomas Wouters477c8d52006-05-27 19:21:47 +000040Usage: see USAGE variable in the script.
41"""
Ned Deily4a96a372013-01-29 00:08:32 -080042import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
43try:
44 import urllib2 as urllib_request
45except ImportError:
46 import urllib.request as urllib_request
47
48STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
49 | stat.S_IRGRP | stat.S_IXGRP
50 | stat.S_IROTH | stat.S_IXOTH )
51
52STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
53 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
54 | stat.S_IROTH | stat.S_IXOTH )
Thomas Wouters477c8d52006-05-27 19:21:47 +000055
Thomas Wouters89f507f2006-12-13 04:49:30 +000056INCLUDE_TIMESTAMP = 1
57VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000058
59from plistlib import Plist
60
Thomas Wouters477c8d52006-05-27 19:21:47 +000061try:
62 from plistlib import writePlist
63except ImportError:
64 # We're run using python2.3
65 def writePlist(plist, path):
66 plist.write(path)
67
68def shellQuote(value):
69 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000070 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000071 a shell command.
72 """
73 return "'%s'"%(value.replace("'", "'\"'\"'"))
74
75def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -080076 """
77 Return the unquoted value of a variable from a file..
78 QUOTED_VALUE='quotes' -> str('quotes')
79 UNQUOTED_VALUE=noquotes -> str('noquotes')
80 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000081 variable = variable + '='
82 for ln in open(fn, 'r'):
83 if ln.startswith(variable):
84 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080085 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080086 raise RuntimeError("Cannot find variable %s" % variable[:-1])
87
88_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000089
90def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080091 global _cache_getVersion
92 if _cache_getVersion is None:
93 _cache_getVersion = grepValue(
94 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
95 return _cache_getVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000096
Ned Deily4a96a372013-01-29 00:08:32 -080097def getVersionMajorMinor():
98 return tuple([int(n) for n in getVersion().split('.', 2)])
99
100_cache_getFullVersion = None
101
Thomas Wouters477c8d52006-05-27 19:21:47 +0000102def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -0800103 global _cache_getFullVersion
104 if _cache_getFullVersion is not None:
105 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
107 for ln in open(fn):
108 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -0800109 _cache_getFullVersion = ln.split()[-1][1:-1]
110 return _cache_getFullVersion
111 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000112
Ned Deily5d3febf2014-12-13 00:17:46 -0800113FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
114FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
Ned Deilydde4f632016-09-12 09:39:23 -0400115FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800116
Thomas Wouters89f507f2006-12-13 04:49:30 +0000117# The directory we'll use to create the build (will be erased and recreated)
118WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000119
Thomas Wouters89f507f2006-12-13 04:49:30 +0000120# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000121# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000122DEPSRC = os.path.join(WORKDIR, 'third-party')
123DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000124
Ronald Oussoren1943f862009-03-30 19:39:14 +0000125universal_opts_map = { '32-bit': ('i386', 'ppc',),
126 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000127 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500128 'intel-32': ('i386',),
129 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000130 '3-way': ('ppc', 'i386', 'x86_64'),
131 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
132default_target_map = {
133 '64-bit': '10.5',
134 '3-way': '10.5',
135 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500136 'intel-32': '10.4',
137 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000138 'all': '10.5',
139}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000140
141UNIVERSALOPTS = tuple(universal_opts_map.keys())
142
143UNIVERSALARCHS = '32-bit'
144
145ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000146
Ezio Melotti42da6632011-03-15 05:18:48 +0200147# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000148SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149 os.path.dirname(
150 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000151 os.path.abspath(__file__
152 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000153
Ronald Oussoren1943f862009-03-30 19:39:14 +0000154# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500155DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000156
Ned Deily04cdfa12014-06-25 13:36:14 -0700157def getDeptargetTuple():
158 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
159
160def getTargetCompilers():
161 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800162 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500163 '10.5': ('gcc', 'g++'),
164 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700165 }
Miss Islington (bot)d24c5a02018-02-24 11:59:25 -0800166 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000167
Ned Deily04cdfa12014-06-25 13:36:14 -0700168CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000169
Ned Deily5d3febf2014-12-13 00:17:46 -0800170PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000171
Thomas Wouters89f507f2006-12-13 04:49:30 +0000172USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000173 Usage: build_python [options]
174
175 Options:
176 -? or -h: Show this message
177 -b DIR
178 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
179 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500180 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000181 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500182 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000183 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000184""")% globals()
185
Ned Deily4a96a372013-01-29 00:08:32 -0800186# Dict of object file names with shared library names to check after building.
187# This is to ensure that we ended up dynamically linking with the shared
188# library paths and versions we expected. For example:
189# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
190# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
191# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
192EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000193
Ned Deily7fdefac2018-01-30 17:29:53 -0500194# Are we building and linking with our own copy of Tcl/TK?
Ned Deily7a6f59e2018-03-29 07:41:11 -0400195# For now, do so if deployment target is 10.6+.
Ned Deily7fdefac2018-01-30 17:29:53 -0500196def internalTk():
Ned Deily7a6f59e2018-03-29 07:41:11 -0400197 return getDeptargetTuple() >= (10, 6)
Ned Deily7fdefac2018-01-30 17:29:53 -0500198
Ned Deily5d3febf2014-12-13 00:17:46 -0800199# List of names of third party software built with this installer.
200# The names will be inserted into the rtf version of the License.
201THIRD_PARTY_LIBS = []
202
Thomas Wouters477c8d52006-05-27 19:21:47 +0000203# Instructions for building libraries that are necessary for building a
204# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000205# [The recipes are defined here for convenience but instantiated later after
206# command line options have been processed.]
207def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000208 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000209
Ned Deily04cdfa12014-06-25 13:36:14 -0700210 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800211
Ned Deilydde4f632016-09-12 09:39:23 -0400212 # Since Apple removed the header files for the deprecated system
213 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
214 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800215
Ned Deilydde4f632016-09-12 09:39:23 -0400216 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800217 dict(
Miss Islington (bot)3235fac2018-09-11 09:07:11 -0700218 name="OpenSSL 1.1.0i",
219 url="https://www.openssl.org/source/openssl-1.1.0i.tar.gz",
220 checksum='9495126aafd2659d357ea66a969c3fe1',
Ned Deily5d3febf2014-12-13 00:17:46 -0800221 buildrecipe=build_universal_openssl,
222 configure=None,
223 install=None,
224 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400225 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800226
Ned Deily7fdefac2018-01-30 17:29:53 -0500227 if internalTk():
Ned Deily5b3582c2013-10-25 00:41:46 -0700228 result.extend([
229 dict(
Ned Deilya7edca72018-02-27 17:36:12 -0500230 name="Tcl 8.6.8",
231 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
232 checksum='81656d3367af032e0ae6157eff134f89',
Ned Deily5b3582c2013-10-25 00:41:46 -0700233 buildDir="unix",
234 configure_pre=[
235 '--enable-shared',
236 '--enable-threads',
237 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
238 ],
239 useLDFlags=False,
240 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
241 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily7fdefac2018-01-30 17:29:53 -0500242 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700243 },
244 ),
Ned Deily5b3582c2013-10-25 00:41:46 -0700245 ])
246
Ned Deilyd9cfe5e2018-09-25 23:38:11 -0400247 # temporary workaround in 3.7.1 for addressing bpo-34370:
248 # use development snapshot of Tk 8.6 branch (post 8.6.8) to pick up
249 # potential fixes for various scrolling problems seen with 8.6.8.
250 # However, the snapshot fails to build on 10.6. For the moment,
251 # continue to build the 3.7.x 10.6 variant with the standard
252 # 8.6.6 branch.
253 if getDeptargetTuple() < (10, 9):
254 result.extend([
255 dict(
256 name="Tk 8.6.8",
257 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
258 checksum='5e0faecba458ee1386078fb228d008ba',
259 patches=[
260 "tk868_on_10_8_10_9.patch",
261 ],
262 buildDir="unix",
263 configure_pre=[
264 '--enable-aqua',
265 '--enable-shared',
266 '--enable-threads',
267 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
268 ],
269 useLDFlags=False,
270 install='make TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
271 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
272 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
273 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
274 },
275 ),
276 ])
277 else:
278 result.extend([
279 dict(
280 name="Tk 8.6.8+",
281 url="http://core.tcl.tk/tk/tarball/16fdad9d/tk-16fdad9d.tar.gz",
282 checksum='b8e0df69021924e8392f03d506252bdb',
283 patches=[
284 "tk868_on_10_8_10_9.patch",
285 ],
286 buildDir="unix",
287 configure_pre=[
288 '--enable-aqua',
289 '--enable-shared',
290 '--enable-threads',
291 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
292 ],
293 useLDFlags=False,
294 install='make TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
295 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
296 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
297 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
298 },
299 ),
300 ])
301
Ned Deilyed730102014-11-14 18:55:05 -0800302 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800303 result.extend([
304 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500305 name="XZ 5.2.3",
306 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
307 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800308 configure_pre=[
309 '--disable-dependency-tracking',
310 ]
311 ),
312 ])
313
314 result.extend([
315 dict(
316 name="NCurses 5.9",
317 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
318 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
319 configure_pre=[
320 "--enable-widec",
321 "--without-cxx",
322 "--without-cxx-binding",
323 "--without-ada",
324 "--without-curses-h",
325 "--enable-shared",
326 "--with-shared",
327 "--without-debug",
328 "--without-normal",
329 "--without-tests",
330 "--without-manpages",
331 "--datadir=/usr/share",
332 "--sysconfdir=/etc",
333 "--sharedstatedir=/usr/com",
334 "--with-terminfo-dirs=/usr/share/terminfo",
335 "--with-default-terminfo-dir=/usr/share/terminfo",
336 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
337 ],
338 patchscripts=[
339 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
340 "f54bf02a349f96a7c4f0d00922f3a0d4"),
341 ],
342 useLDFlags=False,
343 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
344 shellQuote(os.path.join(WORKDIR, 'libraries')),
345 shellQuote(os.path.join(WORKDIR, 'libraries')),
346 getVersion(),
347 ),
348 ),
349 dict(
Ned Deily8c9bb722018-01-30 07:42:14 -0500350 name="SQLite 3.22.0",
351 url="https://www.sqlite.org/2018/sqlite-autoconf-3220000.tar.gz",
352 checksum='96b5648d542e8afa6ab7ffb8db8ddc3d',
Ned Deily4a96a372013-01-29 00:08:32 -0800353 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700354 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800355 '-DSQLITE_ENABLE_FTS4 '
356 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500357 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800358 '-DSQLITE_ENABLE_RTREE '
359 '-DSQLITE_TCL=0 '
360 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
361 configure_pre=[
362 '--enable-threadsafe',
363 '--enable-shared=no',
364 '--enable-static=yes',
365 '--disable-readline',
366 '--disable-dependency-tracking',
367 ]
368 ),
369 ])
370
Ned Deily04cdfa12014-06-25 13:36:14 -0700371 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000372 result.extend([
373 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000374 name="Bzip2 1.0.6",
375 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
376 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000377 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500378 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800379 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000380 shellQuote(os.path.join(WORKDIR, 'libraries')),
381 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000382 ),
383 ),
384 dict(
385 name="ZLib 1.2.3",
386 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
387 checksum='debc62758716a169df9f62e6ab2bc634',
388 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500389 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800390 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000391 shellQuote(os.path.join(WORKDIR, 'libraries')),
392 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000393 ),
394 ),
395 dict(
396 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000397 name="GNU Readline 6.1.2",
398 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
399 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000400 patchlevel='0',
401 patches=[
402 # The readline maintainers don't do actual micro releases, but
403 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800404 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
405 'c642f2e84d820884b0bf9fd176bc6c3f'),
406 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
407 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000408 ]
409 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000410 ])
411
Ned Deily4f7ff782011-01-15 05:29:12 +0000412 if not PYTHON_3:
413 result.extend([
414 dict(
415 name="Sleepycat DB 4.7.25",
416 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
417 checksum='ec2b87e833779681a0c3a814aa71359e',
418 buildDir="build_unix",
419 configure="../dist/configure",
420 configure_pre=[
421 '--includedir=/usr/local/include/db4',
422 ]
423 ),
424 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000425
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000426 return result
427
Thomas Wouters477c8d52006-05-27 19:21:47 +0000428
Thomas Wouters477c8d52006-05-27 19:21:47 +0000429# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000430def pkg_recipes():
431 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
432 result = [
433 dict(
434 name="PythonFramework",
435 long_name="Python Framework",
436 source="/Library/Frameworks/Python.framework",
437 readme="""\
438 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500439 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000440 """,
441 postflight="scripts/postflight.framework",
442 selected='selected',
443 ),
444 dict(
445 name="PythonApplications",
446 long_name="GUI Applications",
447 source="/Applications/Python %(VER)s",
448 readme="""\
449 This package installs IDLE (an interactive Python IDE),
450 Python Launcher and Build Applet (create application bundles
451 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000452
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000453 It also installs a number of examples and demos.
454 """,
455 required=False,
456 selected='selected',
457 ),
458 dict(
459 name="PythonUnixTools",
460 long_name="UNIX command-line tools",
461 source="/usr/local/bin",
462 readme="""\
463 This package installs the unix tools in /usr/local/bin for
464 compatibility with older releases of Python. This package
465 is not necessary to use Python.
466 """,
467 required=False,
468 selected='selected',
469 ),
470 dict(
471 name="PythonDocumentation",
472 long_name="Python Documentation",
473 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
474 source="/pydocs",
475 readme="""\
476 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800477 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000478 """,
479 postflight="scripts/postflight.documentation",
480 required=False,
481 selected='selected',
482 ),
483 dict(
484 name="PythonProfileChanges",
485 long_name="Shell profile updater",
486 readme="""\
487 This packages updates your shell profile to make sure that
488 the Python tools are found by your shell in preference of
489 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000490
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000491 If you don't install this package you'll have to add
492 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
493 to your PATH by hand.
494 """,
495 postflight="scripts/postflight.patch-profile",
496 topdir="/Library/Frameworks/Python.framework",
497 source="/empty-dir",
498 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800499 selected='selected',
500 ),
501 dict(
502 name="PythonInstallPip",
503 long_name="Install or upgrade pip",
504 readme="""\
505 This package installs (or upgrades from an earlier version)
506 pip, a tool for installing and managing Python packages.
507 """,
508 postflight="scripts/postflight.ensurepip",
509 topdir="/Library/Frameworks/Python.framework",
510 source="/empty-dir",
511 required=False,
512 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000513 ),
514 ]
515
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000516 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000517
Thomas Wouters477c8d52006-05-27 19:21:47 +0000518def fatal(msg):
519 """
520 A fatal error, bail out.
521 """
522 sys.stderr.write('FATAL: ')
523 sys.stderr.write(msg)
524 sys.stderr.write('\n')
525 sys.exit(1)
526
527def fileContents(fn):
528 """
529 Return the contents of the named file
530 """
Ned Deily4a96a372013-01-29 00:08:32 -0800531 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000532
533def runCommand(commandline):
534 """
Ezio Melotti13925002011-03-16 11:05:33 +0200535 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000536 unless the command fails.
537 """
538 fd = os.popen(commandline, 'r')
539 data = fd.read()
540 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000541 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000542 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800543 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000544
545 if VERBOSE:
546 sys.stdout.write(data); sys.stdout.flush()
547
548def captureCommand(commandline):
549 fd = os.popen(commandline, 'r')
550 data = fd.read()
551 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000552 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000553 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800554 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000555
556 return data
557
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000558def getTclTkVersion(configfile, versionline):
559 """
560 search Tcl or Tk configuration file for version line
561 """
562 try:
563 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300564 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000565 fatal("Framework configuration file not found: %s" % configfile)
566
567 for l in f:
568 if l.startswith(versionline):
569 f.close()
570 return l
571
572 fatal("Version variable %s not found in framework configuration file: %s"
573 % (versionline, configfile))
574
Thomas Wouters477c8d52006-05-27 19:21:47 +0000575def checkEnvironment():
576 """
577 Check that we're running on a supported system.
578 """
579
Ned Deily8c9bb722018-01-30 07:42:14 -0500580 if sys.version_info[0:2] < (2, 5):
581 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000582
Thomas Wouters477c8d52006-05-27 19:21:47 +0000583 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500584 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000585
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000586 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500587 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000588
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000589 # Because we only support dynamic load of only one major/minor version of
Ned Deily7fdefac2018-01-30 17:29:53 -0500590 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000591 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500592 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
593 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
594 # enforce that the version of the user-installed framework also
595 # exists in the system-supplied Tcl/Tk frameworks. Time to support
596 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily7fdefac2018-01-30 17:29:53 -0500597 if not internalTk():
598 frameworks = {}
599 for framework in ['Tcl', 'Tk']:
600 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
601 libfw = os.path.join('/', fwpth)
602 usrfw = os.path.join(os.getenv('HOME'), fwpth)
603 frameworks[framework] = os.readlink(libfw)
604 if not os.path.exists(libfw):
605 fatal("Please install a link to a current %s %s as %s so "
606 "the user can override the system framework."
607 % (framework, frameworks[framework], libfw))
608 if os.path.exists(usrfw):
609 fatal("Please rename %s to avoid possible dynamic load issues."
610 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000611
Ned Deily7fdefac2018-01-30 17:29:53 -0500612 if frameworks['Tcl'] != frameworks['Tk']:
613 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000614
Ned Deily7fdefac2018-01-30 17:29:53 -0500615 print(" -- Building with external Tcl/Tk %s frameworks"
616 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800617
Ned Deily7fdefac2018-01-30 17:29:53 -0500618 # add files to check after build
619 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
620 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
621 % frameworks['Tcl'],
622 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
623 % frameworks['Tk'],
624 ]
625 else:
626 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500627 print("")
628
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000629 # Remove inherited environment variables which might influence build
630 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
631 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
632 for ev in list(os.environ):
633 for prefix in environ_var_prefixes:
634 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800635 print("INFO: deleting environment variable %s=%s" % (
636 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000637 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000638
Ned Deily4a96a372013-01-29 00:08:32 -0800639 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
640 if 'SDK_TOOLS_BIN' in os.environ:
641 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
642 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
643 # add its fixed location here if it exists
644 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
645 if os.path.isdir(OLD_DEVELOPER_TOOLS):
646 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
647 os.environ['PATH'] = base_path
648 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400649 # Ensure we have access to sphinx-build.
650 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700651 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000652
Thomas Wouters89f507f2006-12-13 04:49:30 +0000653def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000654 """
655 Parse arguments and update global settings.
656 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500657 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800658 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800659 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400660 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000661
662 if args is None:
663 args = sys.argv[1:]
664
665 try:
666 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000667 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
668 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800669 except getopt.GetoptError:
670 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000671 sys.exit(1)
672
673 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800674 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000675 sys.exit(1)
676
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000677 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000679 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800680 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000681 sys.exit(0)
682
683 elif k in ('-d', '--build-dir'):
684 WORKDIR=v
685
686 elif k in ('--third-party',):
687 DEPSRC=v
688
689 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500690 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000691
692 elif k in ('--src-dir',):
693 SRCDIR=v
694
Ronald Oussoren1943f862009-03-30 19:39:14 +0000695 elif k in ('--dep-target', ):
696 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000697 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000698
699 elif k in ('--universal-archs', ):
700 if v in UNIVERSALOPTS:
701 UNIVERSALARCHS = v
702 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000703 if deptarget is None:
704 # Select alternate default deployment
705 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500706 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000707 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800708 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000709
Thomas Wouters477c8d52006-05-27 19:21:47 +0000710 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800711 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000712
713 SRCDIR=os.path.abspath(SRCDIR)
714 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000715 DEPSRC=os.path.abspath(DEPSRC)
716
Ned Deily04cdfa12014-06-25 13:36:14 -0700717 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000718
Ned Deily5d3febf2014-12-13 00:17:46 -0800719 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400720 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800721
722 print("-- Settings:")
723 print(" * Source directory: %s" % SRCDIR)
724 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800725 print(" * Third-party source: %s" % DEPSRC)
726 print(" * Deployment target: %s" % DEPTARGET)
727 print(" * Universal archs: %s" % str(ARCHLIST))
728 print(" * C compiler: %s" % CC)
729 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800730 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800731 print(" -- Building a Python %s framework at patch level %s"
732 % (getVersion(), getFullVersion()))
733 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000734
735def extractArchive(builddir, archiveName):
736 """
737 Extract a source archive into 'builddir'. Returns the path of the
738 extracted archive.
739
740 XXX: This function assumes that archives contain a toplevel directory
741 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700742 safe enough for almost anything we use. Unfortunately, it does not
743 work for current Tcl and Tk source releases where the basename of
744 the archive ends with "-src" but the uncompressed directory does not.
745 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000746 """
747 curdir = os.getcwd()
748 try:
749 os.chdir(builddir)
750 if archiveName.endswith('.tar.gz'):
751 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700752 if ((retval.startswith('tcl') or retval.startswith('tk'))
753 and retval.endswith('-src')):
754 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000755 if os.path.exists(retval):
756 shutil.rmtree(retval)
757 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
758
759 elif archiveName.endswith('.tar.bz2'):
760 retval = os.path.basename(archiveName[:-8])
761 if os.path.exists(retval):
762 shutil.rmtree(retval)
763 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
764
765 elif archiveName.endswith('.tar'):
766 retval = os.path.basename(archiveName[:-4])
767 if os.path.exists(retval):
768 shutil.rmtree(retval)
769 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
770
771 elif archiveName.endswith('.zip'):
772 retval = os.path.basename(archiveName[:-4])
773 if os.path.exists(retval):
774 shutil.rmtree(retval)
775 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
776
777 data = fp.read()
778 xit = fp.close()
779 if xit is not None:
780 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800781 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000782
783 return os.path.join(builddir, retval)
784
785 finally:
786 os.chdir(curdir)
787
Thomas Wouters477c8d52006-05-27 19:21:47 +0000788def downloadURL(url, fname):
789 """
790 Download the contents of the url into the file.
791 """
Ned Deily4a96a372013-01-29 00:08:32 -0800792 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000793 fpOut = open(fname, 'wb')
794 block = fpIn.read(10240)
795 try:
796 while block:
797 fpOut.write(block)
798 block = fpIn.read(10240)
799 fpIn.close()
800 fpOut.close()
801 except:
802 try:
803 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300804 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000805 pass
806
Ned Deily4a96a372013-01-29 00:08:32 -0800807def verifyThirdPartyFile(url, checksum, fname):
808 """
809 Download file from url to filename fname if it does not already exist.
810 Abort if file contents does not match supplied md5 checksum.
811 """
812 name = os.path.basename(fname)
813 if os.path.exists(fname):
814 print("Using local copy of %s"%(name,))
815 else:
816 print("Did not find local copy of %s"%(name,))
817 print("Downloading %s"%(name,))
818 downloadURL(url, fname)
819 print("Archive for %s stored as %s"%(name, fname))
820 if os.system(
821 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
822 % (shellQuote(fname), checksum) ):
823 fatal('MD5 checksum mismatch for file %s' % fname)
824
Ned Deily5d3febf2014-12-13 00:17:46 -0800825def build_universal_openssl(basedir, archList):
826 """
827 Special case build recipe for universal build of openssl.
828
829 The upstream OpenSSL build system does not directly support
830 OS X universal builds. We need to build each architecture
831 separately then lipo them together into fat libraries.
832 """
833
834 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
835 # If we are building on a 10.4.x or earlier system,
836 # unilaterally disable assembly code building to avoid the problem.
837 no_asm = int(platform.release().split(".")[0]) < 9
838
839 def build_openssl_arch(archbase, arch):
840 "Build one architecture of openssl"
841 arch_opts = {
842 "i386": ["darwin-i386-cc"],
843 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
844 "ppc": ["darwin-ppc-cc"],
845 "ppc64": ["darwin64-ppc-cc"],
846 }
847 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800848 "no-idea",
849 "no-mdc2",
850 "no-rc5",
851 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800852 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800853 # "enable-unit-test",
854 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800855 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400856 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800857 ]
858 if no_asm:
859 configure_opts.append("no-asm")
860 runCommand(" ".join(["perl", "Configure"]
861 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500862 runCommand("make depend")
863 runCommand("make all")
864 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800865 # runCommand("make test")
866 return
867
868 srcdir = os.getcwd()
869 universalbase = os.path.join(srcdir, "..",
870 os.path.basename(srcdir) + "-universal")
871 os.mkdir(universalbase)
872 archbasefws = []
873 for arch in archList:
874 # fresh copy of the source tree
875 archsrc = os.path.join(universalbase, arch, "src")
876 shutil.copytree(srcdir, archsrc, symlinks=True)
877 # install base for this arch
878 archbase = os.path.join(universalbase, arch, "root")
879 os.mkdir(archbase)
880 # Python framework base within install_prefix:
881 # the build will install into this framework..
882 # This is to ensure that the resulting shared libs have
883 # the desired real install paths built into them.
884 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
885
886 # build one architecture
887 os.chdir(archsrc)
888 build_openssl_arch(archbase, arch)
889 os.chdir(srcdir)
890 archbasefws.append(archbasefw)
891
892 # copy arch-independent files from last build into the basedir framework
893 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
894 shutil.copytree(
895 os.path.join(archbasefw, "include", "openssl"),
896 os.path.join(basefw, "include", "openssl")
897 )
898
899 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
900 "SHLIB_VERSION_NUMBER")
901 # e.g. -> "1.0.0"
902 libcrypto = "libcrypto.dylib"
903 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
904 # e.g. -> "libcrypto.1.0.0.dylib"
905 libssl = "libssl.dylib"
906 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
907 # e.g. -> "libssl.1.0.0.dylib"
908
909 try:
910 os.mkdir(os.path.join(basefw, "lib"))
911 except OSError:
912 pass
913
914 # merge the individual arch-dependent shared libs into a fat shared lib
915 archbasefws.insert(0, basefw)
916 for (lib_unversioned, lib_versioned) in [
917 (libcrypto, libcrypto_versioned),
918 (libssl, libssl_versioned)
919 ]:
920 runCommand("lipo -create -output " +
921 " ".join(shellQuote(
922 os.path.join(fw, "lib", lib_versioned))
923 for fw in archbasefws))
924 # and create an unversioned symlink of it
925 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
926
927 # Create links in the temp include and lib dirs that will be injected
928 # into the Python build so that setup.py can find them while building
929 # and the versioned links so that the setup.py post-build import test
930 # does not fail.
931 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
932 for fn in [
933 ["include", "openssl"],
934 ["lib", libcrypto],
935 ["lib", libssl],
936 ["lib", libcrypto_versioned],
937 ["lib", libssl_versioned],
938 ]:
939 os.symlink(
940 os.path.join(relative_path, *fn),
941 os.path.join(basedir, "usr", "local", *fn)
942 )
943
944 return
945
Thomas Wouters477c8d52006-05-27 19:21:47 +0000946def buildRecipe(recipe, basedir, archList):
947 """
948 Build software using a recipe. This function does the
949 'configure;make;make install' dance for C software, with a possibility
950 to customize this process, basically a poor-mans DarwinPorts.
951 """
952 curdir = os.getcwd()
953
954 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800955 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000956 url = recipe['url']
957 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800958 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000959 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
960 shellQuote(basedir)))
961
962 archiveName = os.path.split(url)[-1]
963 sourceArchive = os.path.join(DEPSRC, archiveName)
964
965 if not os.path.exists(DEPSRC):
966 os.mkdir(DEPSRC)
967
Ned Deily4a96a372013-01-29 00:08:32 -0800968 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
969 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000970 buildDir=os.path.join(WORKDIR, '_bld')
971 if not os.path.exists(buildDir):
972 os.mkdir(buildDir)
973
974 workDir = extractArchive(buildDir, sourceArchive)
975 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000976
Ned Deily4a96a372013-01-29 00:08:32 -0800977 for patch in recipe.get('patches', ()):
978 if isinstance(patch, tuple):
979 url, checksum = patch
980 fn = os.path.join(DEPSRC, os.path.basename(url))
981 verifyThirdPartyFile(url, checksum, fn)
982 else:
983 # patch is a file in the source directory
984 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000985 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
986 shellQuote(fn),))
987
Ned Deily4a96a372013-01-29 00:08:32 -0800988 for patchscript in recipe.get('patchscripts', ()):
989 if isinstance(patchscript, tuple):
990 url, checksum = patchscript
991 fn = os.path.join(DEPSRC, os.path.basename(url))
992 verifyThirdPartyFile(url, checksum, fn)
993 else:
994 # patch is a file in the source directory
995 fn = os.path.join(curdir, patchscript)
996 if fn.endswith('.bz2'):
997 runCommand('bunzip2 -fk %s' % shellQuote(fn))
998 fn = fn[:-4]
999 runCommand('sh %s' % shellQuote(fn))
1000 os.unlink(fn)
1001
Ned Deily94764b22013-10-27 19:49:29 -07001002 if 'buildDir' in recipe:
1003 os.chdir(recipe['buildDir'])
1004
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001005 if configure is not None:
1006 configure_args = [
1007 "--prefix=/usr/local",
1008 "--enable-static",
1009 "--disable-shared",
1010 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1011 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001012
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001013 if 'configure_pre' in recipe:
1014 args = list(recipe['configure_pre'])
1015 if '--disable-static' in args:
1016 configure_args.remove('--enable-static')
1017 if '--enable-shared' in args:
1018 configure_args.remove('--disable-shared')
1019 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001020
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001021 if recipe.get('useLDFlags', 1):
1022 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001023 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001024 "-I%s/usr/local/include"%(
1025 recipe.get('extra_cflags', ''),
1026 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001027 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001028 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001029 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001030 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001031 shellQuote(basedir)[1:-1],
1032 ' -arch '.join(archList)),
1033 ])
1034 else:
1035 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001036 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001037 "-I%s/usr/local/include"%(
1038 recipe.get('extra_cflags', ''),
1039 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001040 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001041 shellQuote(basedir)[1:-1],),
1042 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001043
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001044 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001045 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001046
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001047 configure_args.insert(0, configure)
1048 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001049
Ned Deily4a96a372013-01-29 00:08:32 -08001050 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001051 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001052
Ned Deily5d3febf2014-12-13 00:17:46 -08001053 if buildrecipe is not None:
1054 # call special-case build recipe, e.g. for openssl
1055 buildrecipe(basedir, archList)
1056
1057 if install is not None:
1058 print("Running install for %s"%(name,))
1059 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001060
Ned Deily4a96a372013-01-29 00:08:32 -08001061 print("Done %s"%(name,))
1062 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001063
1064 os.chdir(curdir)
1065
1066def buildLibraries():
1067 """
1068 Build our dependencies into $WORKDIR/libraries/usr/local
1069 """
Ned Deily4a96a372013-01-29 00:08:32 -08001070 print("")
1071 print("Building required libraries")
1072 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001073 universal = os.path.join(WORKDIR, 'libraries')
1074 os.mkdir(universal)
1075 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1076 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1077
Ronald Oussoren1943f862009-03-30 19:39:14 +00001078 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001079 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001080
1081
1082
1083def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001084 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001085 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001086 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001087 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001088 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001090 curDir = os.getcwd()
1091 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001092 runCommand('make clean')
Ned Deily4c7532e2017-07-23 16:39:54 -04001093 # Create virtual environment for docs builds with blurb and sphinx
1094 runCommand('make venv')
1095 runCommand('make html PYTHON=venv/bin/python')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001096 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001097 if not os.path.exists(docdir):
1098 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001099 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001100
1101
1102def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001103 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001104
1105 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1106 rootDir = os.path.join(WORKDIR, '_root')
1107
1108 if os.path.exists(buildDir):
1109 shutil.rmtree(buildDir)
1110 if os.path.exists(rootDir):
1111 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001112 os.makedirs(buildDir)
1113 os.makedirs(rootDir)
1114 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001115 curdir = os.getcwd()
1116 os.chdir(buildDir)
1117
Thomas Wouters477c8d52006-05-27 19:21:47 +00001118 # Extract the version from the configure file, needed to calculate
1119 # several paths.
1120 version = getVersion()
1121
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001122 # Since the extra libs are not in their installed framework location
1123 # during the build, augment the library path so that the interpreter
1124 # will find them during its extension import sanity checks.
1125 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1126 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001127 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001128 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001129 "--with-universal-archs=%s "
1130 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001131 "%s "
Ned Deily7fdefac2018-01-30 17:29:53 -05001132 "%s "
1133 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001134 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001135 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001136 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001137 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001138 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001139 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily7fdefac2018-01-30 17:29:53 -05001140 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1141 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1142 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1143 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001144 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145 shellQuote(WORKDIR)[1:-1]))
1146
Ned Deilyb364d9f2017-07-24 04:58:43 -04001147 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1148 # and, if defined, append its value to the make command. This allows
1149 # us to pass in version control tags, like GITTAG, to a build from a
1150 # tarball rather than from a vcs checkout, thus eliminating the need
1151 # to have a working copy of the vcs program on the build machine.
1152 #
1153 # A typical use might be:
1154 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1155 # GITVERSION='echo 123456789a' \
1156 # GITTAG='echo v3.6.0' \
1157 # GITBRANCH='echo 3.6'"
1158
1159 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1160 if make_extras:
1161 make_cmd = "make " + make_extras
1162 else:
1163 make_cmd = "make"
1164 print("Running " + make_cmd)
1165 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001166
Ned Deily4a96a372013-01-29 00:08:32 -08001167 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001168 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001169 shellQuote(rootDir)))
1170
Ned Deily4a96a372013-01-29 00:08:32 -08001171 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001172 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1173 shellQuote(rootDir)))
1174
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001175 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001176 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001177 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily7fdefac2018-01-30 17:29:53 -05001178 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001179 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily7fdefac2018-01-30 17:29:53 -05001180 'Python.framework', 'Versions', getVersion(), 'lib')
1181 fw_lib_dir = os.path.join(
1182 WORKDIR, '_root', 'Library', 'Frameworks',
1183 'Python.framework', 'Versions', getVersion(), 'lib')
1184 if internalTk():
1185 # move Tcl and Tk pkgconfig files
1186 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1187 shellQuote(build_lib_dir),
1188 shellQuote(fw_lib_dir) ))
1189 runCommand("rm -r %s/pkgconfig"%(
1190 shellQuote(build_lib_dir), ))
1191 runCommand("mv %s/* %s"%(
1192 shellQuote(build_lib_dir),
1193 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001194
Ned Deilydde4f632016-09-12 09:39:23 -04001195 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1196 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1197 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1198 # create directory for OpenSSL certificates
1199 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1200 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001201
Ned Deily4a96a372013-01-29 00:08:32 -08001202 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001203 gid = grp.getgrnam('admin').gr_gid
1204
Ned Deily4a96a372013-01-29 00:08:32 -08001205 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001206 for dirpath, dirnames, filenames in os.walk(frmDir):
1207 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001208 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001209 os.chown(os.path.join(dirpath, dn), -1, gid)
1210
Thomas Wouters477c8d52006-05-27 19:21:47 +00001211 for fn in filenames:
1212 if os.path.islink(fn):
1213 continue
1214
1215 # "chmod g+w $fn"
1216 p = os.path.join(dirpath, fn)
1217 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001218 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1219 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001220
Ned Deily4a96a372013-01-29 00:08:32 -08001221 if fn in EXPECTED_SHARED_LIBS:
1222 # check to see that this file was linked with the
1223 # expected library path and version
1224 data = captureCommand("otool -L %s" % shellQuote(p))
1225 for sl in EXPECTED_SHARED_LIBS[fn]:
1226 if ("\t%s " % sl) not in data:
1227 print("Expected shared lib %s was not linked with %s"
1228 % (sl, p))
1229 shared_lib_error = True
1230
1231 if shared_lib_error:
1232 fatal("Unexpected shared library errors.")
1233
Ned Deilye59e4c52011-01-29 18:56:28 +00001234 if PYTHON_3:
1235 LDVERSION=None
1236 VERSION=None
1237 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001238
Ned Deilye59e4c52011-01-29 18:56:28 +00001239 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001240 for ln in fp:
1241 if ln.startswith('VERSION='):
1242 VERSION=ln.split()[1]
1243 if ln.startswith('ABIFLAGS='):
1244 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001245 if ln.startswith('LDVERSION='):
1246 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001247 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001248
Ned Deilye59e4c52011-01-29 18:56:28 +00001249 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1250 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1251 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001252 if getVersionMajorMinor() >= (3, 6):
1253 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001254 else:
1255 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001256
Thomas Wouters477c8d52006-05-27 19:21:47 +00001257 # We added some directories to the search path during the configure
1258 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001259 # the end-users system. Also remove the directories from _sysconfigdata.py
1260 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001261
Ned Deilya4f6b002013-10-25 00:47:38 -07001262 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1263 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1264
Ned Deilya4f6b002013-10-25 00:47:38 -07001265 # fix Makefile
1266 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1267 fp = open(path, 'r')
1268 data = fp.read()
1269 fp.close()
1270
1271 for p in (include_path, lib_path):
1272 data = data.replace(" " + p, '')
1273 data = data.replace(p + " ", '')
1274
1275 fp = open(path, 'w')
1276 fp.write(data)
1277 fp.close()
1278
Ned Deily652bad42016-08-15 14:37:14 -04001279 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001280 #
1281 # TODO: make this more robust! test_sysconfig_module of
1282 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1283 # the output from get_config_var in both sysconfig and
1284 # distutils.sysconfig is exactly the same for both CFLAGS and
1285 # LDFLAGS. The fixing up is now complicated by the pretty
1286 # printing in _sysconfigdata.py. Also, we are using the
1287 # pprint from the Python running the installer build which
1288 # may not cosmetically format the same as the pprint in the Python
1289 # being built (and which is used to originally generate
1290 # _sysconfigdata.py).
1291
1292 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001293 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001294 # XXX this is extra-fragile
1295 path = os.path.join(path_to_lib, '_sysconfigdata_m_darwin_darwin.py')
Ned Deily652bad42016-08-15 14:37:14 -04001296 else:
1297 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1298 fp = open(path, 'r')
1299 data = fp.read()
1300 fp.close()
1301 # create build_time_vars dict
1302 exec(data)
1303 vars = {}
1304 for k, v in build_time_vars.items():
1305 if type(v) == type(''):
1306 for p in (include_path, lib_path):
1307 v = v.replace(' ' + p, '')
1308 v = v.replace(p + ' ', '')
1309 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001310
Ned Deily652bad42016-08-15 14:37:14 -04001311 fp = open(path, 'w')
1312 # duplicated from sysconfig._generate_posix_vars()
1313 fp.write('# system configuration generated and used by'
1314 ' the sysconfig module\n')
1315 fp.write('build_time_vars = ')
1316 pprint.pprint(vars, stream=fp)
1317 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001318
1319 # Add symlinks in /usr/local/bin, using relative links
1320 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1321 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1322 'Python.framework', 'Versions', version, 'bin')
1323 if os.path.exists(usr_local_bin):
1324 shutil.rmtree(usr_local_bin)
1325 os.makedirs(usr_local_bin)
1326 for fn in os.listdir(
1327 os.path.join(frmDir, 'Versions', version, 'bin')):
1328 os.symlink(os.path.join(to_framework, fn),
1329 os.path.join(usr_local_bin, fn))
1330
1331 os.chdir(curdir)
1332
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001333 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001334 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001335 # with an already installed version of python 2
1336 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1337 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001338
1339def patchFile(inPath, outPath):
1340 data = fileContents(inPath)
1341 data = data.replace('$FULL_VERSION', getFullVersion())
1342 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001343 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001344 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001345 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001346 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001347
1348 # This one is not handy as a template variable
1349 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001350 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001351 fp.write(data)
1352 fp.close()
1353
1354def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001355 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001356 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001357 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001358 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001359 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001360 fp.write(data)
1361 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001362 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001363
1364
1365
1366def packageFromRecipe(targetDir, recipe):
1367 curdir = os.getcwd()
1368 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001369 # The major version (such as 2.5) is included in the package name
1370 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001371 # common.
1372 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001373 srcdir = recipe.get('source')
1374 pkgroot = recipe.get('topdir', srcdir)
1375 postflight = recipe.get('postflight')
1376 readme = textwrap.dedent(recipe['readme'])
1377 isRequired = recipe.get('required', True)
1378
Ned Deily4a96a372013-01-29 00:08:32 -08001379 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001380
1381 # Substitute some variables
1382 textvars = dict(
1383 VER=getVersion(),
1384 FULLVER=getFullVersion(),
1385 )
1386 readme = readme % textvars
1387
1388 if pkgroot is not None:
1389 pkgroot = pkgroot % textvars
1390 else:
1391 pkgroot = '/'
1392
1393 if srcdir is not None:
1394 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1395 srcdir = srcdir % textvars
1396
1397 if postflight is not None:
1398 postflight = os.path.abspath(postflight)
1399
1400 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1401 os.makedirs(packageContents)
1402
1403 if srcdir is not None:
1404 os.chdir(srcdir)
1405 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1406 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1407 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1408
1409 fn = os.path.join(packageContents, 'PkgInfo')
1410 fp = open(fn, 'w')
1411 fp.write('pmkrpkg1')
1412 fp.close()
1413
1414 rsrcDir = os.path.join(packageContents, "Resources")
1415 os.mkdir(rsrcDir)
1416 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1417 fp.write(readme)
1418 fp.close()
1419
1420 if postflight is not None:
1421 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1422
1423 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001424 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001425 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001426 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1427 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1428 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001429 CFBundleShortVersionString=vers,
1430 IFMajorVersion=major,
1431 IFMinorVersion=minor,
1432 IFPkgFormatVersion=0.10000000149011612,
1433 IFPkgFlagAllowBackRev=False,
1434 IFPkgFlagAuthorizationAction="RootAuthorization",
1435 IFPkgFlagDefaultLocation=pkgroot,
1436 IFPkgFlagFollowLinks=True,
1437 IFPkgFlagInstallFat=True,
1438 IFPkgFlagIsRequired=isRequired,
1439 IFPkgFlagOverwritePermissions=False,
1440 IFPkgFlagRelocatable=False,
1441 IFPkgFlagRestartAction="NoRestart",
1442 IFPkgFlagRootVolumeOnly=True,
1443 IFPkgFlagUpdateInstalledLangauges=False,
1444 )
1445 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1446
1447 pl = Plist(
1448 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001449 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001450 IFPkgDescriptionVersion=vers,
1451 )
1452 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1453
1454 finally:
1455 os.chdir(curdir)
1456
1457
1458def makeMpkgPlist(path):
1459
1460 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001461 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001462
1463 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001464 CFBundleGetInfoString="Python %s"%(vers,),
1465 CFBundleIdentifier='org.python.Python',
1466 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001467 CFBundleShortVersionString=vers,
1468 IFMajorVersion=major,
1469 IFMinorVersion=minor,
1470 IFPkgFlagComponentDirectory="Contents/Packages",
1471 IFPkgFlagPackageList=[
1472 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001473 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001474 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001475 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001476 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001477 ],
1478 IFPkgFormatVersion=0.10000000149011612,
1479 IFPkgFlagBackgroundScaling="proportional",
1480 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001481 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001482 )
1483
1484 writePlist(pl, path)
1485
1486
1487def buildInstaller():
1488
1489 # Zap all compiled files
1490 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1491 for fn in filenames:
1492 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1493 os.unlink(os.path.join(dirpath, fn))
1494
1495 outdir = os.path.join(WORKDIR, 'installer')
1496 if os.path.exists(outdir):
1497 shutil.rmtree(outdir)
1498 os.mkdir(outdir)
1499
Ronald Oussoren1943f862009-03-30 19:39:14 +00001500 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001501 pkgcontents = os.path.join(pkgroot, 'Packages')
1502 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001503 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001504 packageFromRecipe(pkgcontents, recipe)
1505
1506 rsrcDir = os.path.join(pkgroot, 'Resources')
1507
1508 fn = os.path.join(pkgroot, 'PkgInfo')
1509 fp = open(fn, 'w')
1510 fp.write('pmkrpkg1')
1511 fp.close()
1512
1513 os.mkdir(rsrcDir)
1514
1515 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1516 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001517 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001518 IFPkgDescriptionVersion=getVersion(),
1519 )
1520
1521 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1522 for fn in os.listdir('resources'):
1523 if fn == '.svn': continue
1524 if fn.endswith('.jpg'):
1525 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1526 else:
1527 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1528
Thomas Wouters477c8d52006-05-27 19:21:47 +00001529
1530def installSize(clear=False, _saved=[]):
1531 if clear:
1532 del _saved[:]
1533 if not _saved:
1534 data = captureCommand("du -ks %s"%(
1535 shellQuote(os.path.join(WORKDIR, '_root'))))
1536 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1537 return _saved[0]
1538
1539
1540def buildDMG():
1541 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001542 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001543 """
1544 outdir = os.path.join(WORKDIR, 'diskimage')
1545 if os.path.exists(outdir):
1546 shutil.rmtree(outdir)
1547
1548 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001549 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001550 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001551 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001552 imagepath = imagepath + '.dmg'
1553
1554 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001555 volname='Python %s'%(getFullVersion())
1556 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1557 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001558 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001559 shellQuote(imagepath + ".tmp.dmg" )))
1560
Miss Islington (bot)50903bf2018-05-01 22:45:59 -07001561 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1562 # when hdiutil fails with "Resource busy"
1563
1564 time.sleep(10)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001565
1566 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1567 os.mkdir(os.path.join(WORKDIR, "mnt"))
1568 runCommand("hdiutil attach %s -mountroot %s"%(
1569 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1570
1571 # Custom icon for the DMG, shown when the DMG is mounted.
1572 shutil.copy("../Icons/Disk Image.icns",
1573 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001574 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001575 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1576
1577 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1578
1579 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1580 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1581 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1582 setIcon(imagepath, "../Icons/Disk Image.icns")
1583
1584 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001585
1586 return imagepath
1587
1588
1589def setIcon(filePath, icnsPath):
1590 """
1591 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001592 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001593
Ronald Oussoren70050672010-04-30 15:00:26 +00001594 dirPath = os.path.normpath(os.path.dirname(__file__))
1595 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001596 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1597 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1598 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001599 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1600 if not os.path.exists(appPath):
1601 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001602 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1603 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001604
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001605 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1606 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001607
1608def main():
1609 # First parse options and check if we can perform our work
1610 parseOptions()
1611 checkEnvironment()
1612
Ronald Oussoren1943f862009-03-30 19:39:14 +00001613 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001614 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001615 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001616
1617 if os.path.exists(WORKDIR):
1618 shutil.rmtree(WORKDIR)
1619 os.mkdir(WORKDIR)
1620
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001621 os.environ['LC_ALL'] = 'C'
1622
Thomas Wouters477c8d52006-05-27 19:21:47 +00001623 # Then build third-party libraries such as sleepycat DB4.
1624 buildLibraries()
1625
1626 # Now build python itself
1627 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001628
1629 # And then build the documentation
1630 # Remove the Deployment Target from the shell
1631 # environment, it's no longer needed and
1632 # an unexpected build target can cause problems
1633 # when Sphinx and its dependencies need to
1634 # be (re-)installed.
1635 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001636 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001637
1638
1639 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001640 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001641 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001642 fn = os.path.join(folder, "License.rtf")
1643 patchFile("resources/License.rtf", fn)
1644 fn = os.path.join(folder, "ReadMe.rtf")
1645 patchFile("resources/ReadMe.rtf", fn)
1646 fn = os.path.join(folder, "Update Shell Profile.command")
1647 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001648 fn = os.path.join(folder, "Install Certificates.command")
1649 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001650 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001651 setIcon(folder, "../Icons/Python Folder.icns")
1652
1653 # Create the installer
1654 buildInstaller()
1655
1656 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001657 patchFile('resources/ReadMe.rtf',
1658 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001659
1660 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001661 patchFile('resources/License.rtf',
1662 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001663
1664 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001665 fp.write("# BUILD INFO\n")
1666 fp.write("# Date: %s\n" % time.ctime())
1667 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001668 fp.close()
1669
Thomas Wouters477c8d52006-05-27 19:21:47 +00001670 # And copy it to a DMG
1671 buildDMG()
1672
Thomas Wouters477c8d52006-05-27 19:21:47 +00001673if __name__ == "__main__":
1674 main()