blob: 15a45b95a23c4b4b38e03ed3d28b0fc5d6f95b7f [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
Ned Deily1931e642020-06-25 04:51:46 -04005NEW for 3.9.0 and backports:
6- 2.7 end-of-life issues:
7 - Python 3 installs now update the Current version link
8 in /Library/Frameworks/Python.framework/Versions
9- fully support running under Python 3 as well as 2.7
10- support building on newer macOS systems with SIP
11- fully support building on macOS 10.9+
12- support 10.6+ on best effort
13- support bypassing docs build by supplying a prebuilt
14 docs html tarball in the third-party source library,
15 in the format and filename conventional of those
16 downloadable from python.org:
17 python-3.x.y-docs-html.tar.bz2
18
Ned Deily8c9bb722018-01-30 07:42:14 -050019NEW for 3.7.0:
20- support Intel 64-bit-only () and 32-bit-only installer builds
Ned Deilyb9e7fe32018-03-29 08:47:27 -040021- build and use internal Tcl/Tk 8.6 for 10.6+ builds
Ned Deily8c9bb722018-01-30 07:42:14 -050022- deprecate use of explicit SDK (--sdk-path=) since all but the oldest
23 versions of Xcode support implicit setting of an SDK via environment
24 variables (SDKROOT and friends, see the xcrun man page for more info).
25 The SDK stuff was primarily needed for building universal installers
26 for 10.4; so as of 3.7.0, building installers for 10.4 is no longer
27 supported with build-installer.
28- use generic "gcc" as compiler (CC env var) rather than "gcc-4.2"
29
30TODO:
Ned Deily1931e642020-06-25 04:51:46 -040031- test building with SDKROOT and DEVELOPER_DIR xcrun env variables
Ned Deily4a96a372013-01-29 00:08:32 -080032
Thomas Wouters477c8d52006-05-27 19:21:47 +000033Usage: see USAGE variable in the script.
34"""
Ned Deily4a96a372013-01-29 00:08:32 -080035import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
36try:
37 import urllib2 as urllib_request
38except ImportError:
39 import urllib.request as urllib_request
40
41STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
42 | stat.S_IRGRP | stat.S_IXGRP
43 | stat.S_IROTH | stat.S_IXOTH )
44
45STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
46 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
47 | stat.S_IROTH | stat.S_IXOTH )
Thomas Wouters477c8d52006-05-27 19:21:47 +000048
Thomas Wouters89f507f2006-12-13 04:49:30 +000049INCLUDE_TIMESTAMP = 1
50VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000051
Ned Deily1931e642020-06-25 04:51:46 -040052RUNNING_ON_PYTHON2 = sys.version_info.major == 2
Thomas Wouters477c8d52006-05-27 19:21:47 +000053
Ned Deily1931e642020-06-25 04:51:46 -040054if RUNNING_ON_PYTHON2:
Thomas Wouters477c8d52006-05-27 19:21:47 +000055 from plistlib import writePlist
Ned Deily1931e642020-06-25 04:51:46 -040056else:
57 from plistlib import dump
58 def writePlist(path, plist):
59 with open(plist, 'wb') as fp:
60 dump(path, fp)
Thomas Wouters477c8d52006-05-27 19:21:47 +000061
62def shellQuote(value):
63 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000064 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000065 a shell command.
66 """
67 return "'%s'"%(value.replace("'", "'\"'\"'"))
68
69def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -080070 """
71 Return the unquoted value of a variable from a file..
72 QUOTED_VALUE='quotes' -> str('quotes')
73 UNQUOTED_VALUE=noquotes -> str('noquotes')
74 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000075 variable = variable + '='
76 for ln in open(fn, 'r'):
77 if ln.startswith(variable):
78 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080079 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080080 raise RuntimeError("Cannot find variable %s" % variable[:-1])
81
82_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000083
84def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080085 global _cache_getVersion
86 if _cache_getVersion is None:
87 _cache_getVersion = grepValue(
88 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
89 return _cache_getVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000090
Ned Deily4a96a372013-01-29 00:08:32 -080091def getVersionMajorMinor():
92 return tuple([int(n) for n in getVersion().split('.', 2)])
93
94_cache_getFullVersion = None
95
Thomas Wouters477c8d52006-05-27 19:21:47 +000096def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080097 global _cache_getFullVersion
98 if _cache_getFullVersion is not None:
99 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +0000100 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
101 for ln in open(fn):
102 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -0800103 _cache_getFullVersion = ln.split()[-1][1:-1]
104 return _cache_getFullVersion
105 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106
Ned Deily5d3febf2014-12-13 00:17:46 -0800107FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
108FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
Ned Deilydde4f632016-09-12 09:39:23 -0400109FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800110
Thomas Wouters89f507f2006-12-13 04:49:30 +0000111# The directory we'll use to create the build (will be erased and recreated)
112WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000113
Thomas Wouters89f507f2006-12-13 04:49:30 +0000114# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000115# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000116DEPSRC = os.path.join(WORKDIR, 'third-party')
117DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000118
Ronald Oussoren41761932020-11-08 10:05:27 +0100119universal_opts_map = { 'universal2': ('arm64', 'x86_64'),
120 '32-bit': ('i386', 'ppc',),
Ronald Oussoren1943f862009-03-30 19:39:14 +0000121 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000122 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500123 'intel-32': ('i386',),
124 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000125 '3-way': ('ppc', 'i386', 'x86_64'),
126 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
127default_target_map = {
Ronald Oussoren41761932020-11-08 10:05:27 +0100128 'universal2': '10.9',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000129 '64-bit': '10.5',
130 '3-way': '10.5',
131 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500132 'intel-32': '10.4',
133 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000134 'all': '10.5',
135}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000136
137UNIVERSALOPTS = tuple(universal_opts_map.keys())
138
139UNIVERSALARCHS = '32-bit'
140
141ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000142
Ezio Melotti42da6632011-03-15 05:18:48 +0200143# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000144SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000145 os.path.dirname(
146 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000147 os.path.abspath(__file__
148 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149
Ronald Oussoren1943f862009-03-30 19:39:14 +0000150# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500151DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000152
Ned Deily04cdfa12014-06-25 13:36:14 -0700153def getDeptargetTuple():
154 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
155
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100156def getBuildTuple():
157 return tuple([int(n) for n in platform.mac_ver()[0].split('.')[0:2]])
158
Ned Deily04cdfa12014-06-25 13:36:14 -0700159def getTargetCompilers():
160 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800161 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500162 '10.5': ('gcc', 'g++'),
163 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700164 }
Ned Deilyacd71632018-02-24 14:30:44 -0500165 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000166
Ned Deily04cdfa12014-06-25 13:36:14 -0700167CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000168
Ned Deily5d3febf2014-12-13 00:17:46 -0800169PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000170
Thomas Wouters89f507f2006-12-13 04:49:30 +0000171USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000172 Usage: build_python [options]
173
174 Options:
175 -? or -h: Show this message
176 -b DIR
177 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
178 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500179 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000180 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500181 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000182 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000183""")% globals()
184
Ned Deily4a96a372013-01-29 00:08:32 -0800185# Dict of object file names with shared library names to check after building.
186# This is to ensure that we ended up dynamically linking with the shared
187# library paths and versions we expected. For example:
188# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
189# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
190# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
191EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000192
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500193# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400194# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500195def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400196 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500197
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100198# Do we use 8.6.8 when building our own copy
199# of Tcl/Tk or a modern version.
200# We use the old version when buildin on
201# old versions of macOS due to build issues.
202def useOldTk():
203 return getBuildTuple() < (10, 15)
204
Ronald Oussoren41761932020-11-08 10:05:27 +0100205
206def tweak_tcl_build(basedir, archList):
207 with open("Makefile", "r") as fp:
208 contents = fp.readlines()
209
210 # For reasons I don't understand the tcl configure script
211 # decides that some stdlib symbols aren't present, before
212 # deciding that strtod is broken.
213 new_contents = []
214 for line in contents:
215 if line.startswith("COMPAT_OBJS"):
216 # note: the space before strtod.o is intentional,
217 # the detection of a broken strtod results in
218 # "fixstrod.o" on this line.
219 for nm in ("strstr.o", "strtoul.o", " strtod.o"):
220 line = line.replace(nm, "")
221 new_contents.append(line)
222
223 with open("Makefile", "w") as fp:
224 fp.writelines(new_contents)
225
Ned Deily5d3febf2014-12-13 00:17:46 -0800226# List of names of third party software built with this installer.
227# The names will be inserted into the rtf version of the License.
228THIRD_PARTY_LIBS = []
229
Thomas Wouters477c8d52006-05-27 19:21:47 +0000230# Instructions for building libraries that are necessary for building a
231# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000232# [The recipes are defined here for convenience but instantiated later after
233# command line options have been processed.]
234def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000235 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000236
Ned Deily04cdfa12014-06-25 13:36:14 -0700237 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800238
Ned Deilydde4f632016-09-12 09:39:23 -0400239 # Since Apple removed the header files for the deprecated system
240 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
241 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800242
Ned Deilydde4f632016-09-12 09:39:23 -0400243 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800244 dict(
Christian Heimesa54fc682021-03-30 02:00:34 +0200245 name="OpenSSL 1.1.1k",
246 url="https://www.openssl.org/source/openssl-1.1.1k.tar.gz",
247 checksum='c4e7d95f782b08116afa27b30393dd27',
Ned Deily5d3febf2014-12-13 00:17:46 -0800248 buildrecipe=build_universal_openssl,
249 configure=None,
250 install=None,
251 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400252 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800253
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500254 if internalTk():
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100255 if useOldTk():
256 tcl_tk_ver='8.6.8'
257 tcl_checksum='81656d3367af032e0ae6157eff134f89'
258
259 tk_checksum='5e0faecba458ee1386078fb228d008ba'
260 tk_patches = ['tk868_on_10_8_10_9.patch']
261
262 else:
Ned Deilya38e04b2021-01-04 04:43:11 -0500263 tcl_tk_ver='8.6.11'
264 tcl_checksum='8a4c004f48984a03a7747e9ba06e4da4'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100265
Ned Deilya38e04b2021-01-04 04:43:11 -0500266 tk_checksum='c7ee71a2d05bba78dfffd76528dc17c6'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100267 tk_patches = [ ]
268
269
Ned Deily5b3582c2013-10-25 00:41:46 -0700270 result.extend([
271 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100272 name="Tcl %s"%(tcl_tk_ver,),
273 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl%s-src.tar.gz"%(tcl_tk_ver,),
274 checksum=tcl_checksum,
Ned Deily5b3582c2013-10-25 00:41:46 -0700275 buildDir="unix",
276 configure_pre=[
277 '--enable-shared',
278 '--enable-threads',
279 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
280 ],
281 useLDFlags=False,
Ronald Oussoren41761932020-11-08 10:05:27 +0100282 buildrecipe=tweak_tcl_build,
Ned Deily5b3582c2013-10-25 00:41:46 -0700283 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
284 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500285 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700286 },
287 ),
288 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100289 name="Tk %s"%(tcl_tk_ver,),
290 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk%s-src.tar.gz"%(tcl_tk_ver,),
291 checksum=tk_checksum,
292 patches=tk_patches,
Ned Deily5b3582c2013-10-25 00:41:46 -0700293 buildDir="unix",
294 configure_pre=[
295 '--enable-aqua',
296 '--enable-shared',
297 '--enable-threads',
298 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
299 ],
300 useLDFlags=False,
301 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'%{
302 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500303 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
304 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700305 },
306 ),
307 ])
308
Ned Deilyed730102014-11-14 18:55:05 -0800309 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800310 result.extend([
311 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500312 name="XZ 5.2.3",
313 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
314 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800315 configure_pre=[
316 '--disable-dependency-tracking',
317 ]
318 ),
319 ])
320
321 result.extend([
322 dict(
323 name="NCurses 5.9",
324 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
325 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
326 configure_pre=[
327 "--enable-widec",
328 "--without-cxx",
329 "--without-cxx-binding",
330 "--without-ada",
331 "--without-curses-h",
332 "--enable-shared",
333 "--with-shared",
334 "--without-debug",
335 "--without-normal",
336 "--without-tests",
337 "--without-manpages",
338 "--datadir=/usr/share",
339 "--sysconfdir=/etc",
340 "--sharedstatedir=/usr/com",
341 "--with-terminfo-dirs=/usr/share/terminfo",
342 "--with-default-terminfo-dir=/usr/share/terminfo",
343 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
344 ],
345 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200346 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800347 "f54bf02a349f96a7c4f0d00922f3a0d4"),
348 ],
349 useLDFlags=False,
350 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
351 shellQuote(os.path.join(WORKDIR, 'libraries')),
352 shellQuote(os.path.join(WORKDIR, 'libraries')),
353 getVersion(),
354 ),
355 ),
356 dict(
Erlend Egeberg Aaslandce827812021-04-27 19:19:14 +0200357 name="SQLite 3.35.5",
358 url="https://sqlite.org/2021/sqlite-autoconf-3350500.tar.gz",
359 checksum='d1d1aba394c8e0443077dc9f1a681bb8',
Ned Deily4a96a372013-01-29 00:08:32 -0800360 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700361 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800362 '-DSQLITE_ENABLE_FTS4 '
363 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500364 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800365 '-DSQLITE_ENABLE_RTREE '
Erlend Egeberg Aasland555cbbe2021-05-03 02:37:35 +0200366 '-DSQLITE_OMIT_AUTOINIT '
Ned Deily4a96a372013-01-29 00:08:32 -0800367 '-DSQLITE_TCL=0 '
368 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
369 configure_pre=[
370 '--enable-threadsafe',
371 '--enable-shared=no',
372 '--enable-static=yes',
373 '--disable-readline',
374 '--disable-dependency-tracking',
375 ]
376 ),
377 ])
378
Ned Deily04cdfa12014-06-25 13:36:14 -0700379 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000380 result.extend([
381 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000382 name="Bzip2 1.0.6",
383 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
384 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000385 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500386 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800387 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000388 shellQuote(os.path.join(WORKDIR, 'libraries')),
389 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000390 ),
391 ),
392 dict(
393 name="ZLib 1.2.3",
394 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
395 checksum='debc62758716a169df9f62e6ab2bc634',
396 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500397 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800398 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000399 shellQuote(os.path.join(WORKDIR, 'libraries')),
400 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000401 ),
402 ),
403 dict(
404 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000405 name="GNU Readline 6.1.2",
406 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
407 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000408 patchlevel='0',
409 patches=[
410 # The readline maintainers don't do actual micro releases, but
411 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800412 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
413 'c642f2e84d820884b0bf9fd176bc6c3f'),
414 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
415 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000416 ]
417 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000418 ])
419
Ned Deily4f7ff782011-01-15 05:29:12 +0000420 if not PYTHON_3:
421 result.extend([
422 dict(
423 name="Sleepycat DB 4.7.25",
424 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
425 checksum='ec2b87e833779681a0c3a814aa71359e',
426 buildDir="build_unix",
427 configure="../dist/configure",
428 configure_pre=[
429 '--includedir=/usr/local/include/db4',
430 ]
431 ),
432 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000433
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000434 return result
435
Thomas Wouters477c8d52006-05-27 19:21:47 +0000436
Thomas Wouters477c8d52006-05-27 19:21:47 +0000437# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000438def pkg_recipes():
439 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
440 result = [
441 dict(
442 name="PythonFramework",
443 long_name="Python Framework",
444 source="/Library/Frameworks/Python.framework",
445 readme="""\
446 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500447 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000448 """,
449 postflight="scripts/postflight.framework",
450 selected='selected',
451 ),
452 dict(
453 name="PythonApplications",
454 long_name="GUI Applications",
455 source="/Applications/Python %(VER)s",
456 readme="""\
457 This package installs IDLE (an interactive Python IDE),
458 Python Launcher and Build Applet (create application bundles
459 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000460
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000461 It also installs a number of examples and demos.
462 """,
463 required=False,
464 selected='selected',
465 ),
466 dict(
467 name="PythonUnixTools",
468 long_name="UNIX command-line tools",
469 source="/usr/local/bin",
470 readme="""\
471 This package installs the unix tools in /usr/local/bin for
472 compatibility with older releases of Python. This package
473 is not necessary to use Python.
474 """,
475 required=False,
476 selected='selected',
477 ),
478 dict(
479 name="PythonDocumentation",
480 long_name="Python Documentation",
481 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
482 source="/pydocs",
483 readme="""\
484 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800485 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000486 """,
487 postflight="scripts/postflight.documentation",
488 required=False,
489 selected='selected',
490 ),
491 dict(
492 name="PythonProfileChanges",
493 long_name="Shell profile updater",
494 readme="""\
495 This packages updates your shell profile to make sure that
496 the Python tools are found by your shell in preference of
497 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000498
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000499 If you don't install this package you'll have to add
500 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
501 to your PATH by hand.
502 """,
503 postflight="scripts/postflight.patch-profile",
504 topdir="/Library/Frameworks/Python.framework",
505 source="/empty-dir",
506 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800507 selected='selected',
508 ),
509 dict(
510 name="PythonInstallPip",
511 long_name="Install or upgrade pip",
512 readme="""\
513 This package installs (or upgrades from an earlier version)
514 pip, a tool for installing and managing Python packages.
515 """,
516 postflight="scripts/postflight.ensurepip",
517 topdir="/Library/Frameworks/Python.framework",
518 source="/empty-dir",
519 required=False,
520 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000521 ),
522 ]
523
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000524 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000525
Thomas Wouters477c8d52006-05-27 19:21:47 +0000526def fatal(msg):
527 """
528 A fatal error, bail out.
529 """
530 sys.stderr.write('FATAL: ')
531 sys.stderr.write(msg)
532 sys.stderr.write('\n')
533 sys.exit(1)
534
535def fileContents(fn):
536 """
537 Return the contents of the named file
538 """
Ned Deily4a96a372013-01-29 00:08:32 -0800539 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000540
541def runCommand(commandline):
542 """
Ezio Melotti13925002011-03-16 11:05:33 +0200543 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000544 unless the command fails.
545 """
546 fd = os.popen(commandline, 'r')
547 data = fd.read()
548 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000549 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000550 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800551 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552
553 if VERBOSE:
554 sys.stdout.write(data); sys.stdout.flush()
555
556def captureCommand(commandline):
557 fd = os.popen(commandline, 'r')
558 data = fd.read()
559 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000560 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000561 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800562 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000563
564 return data
565
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000566def getTclTkVersion(configfile, versionline):
567 """
568 search Tcl or Tk configuration file for version line
569 """
570 try:
571 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300572 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000573 fatal("Framework configuration file not found: %s" % configfile)
574
575 for l in f:
576 if l.startswith(versionline):
577 f.close()
578 return l
579
580 fatal("Version variable %s not found in framework configuration file: %s"
581 % (versionline, configfile))
582
Thomas Wouters477c8d52006-05-27 19:21:47 +0000583def checkEnvironment():
584 """
585 Check that we're running on a supported system.
586 """
587
Ned Deily936533c2020-11-23 19:04:40 -0500588 if sys.version_info[0:2] < (2, 7):
589 fatal("This script must be run with Python 2.7 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000590
Thomas Wouters477c8d52006-05-27 19:21:47 +0000591 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500592 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000593
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000594 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500595 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000596
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000597 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500598 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000599 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500600 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
601 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
602 # enforce that the version of the user-installed framework also
603 # exists in the system-supplied Tcl/Tk frameworks. Time to support
604 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500605 if not internalTk():
606 frameworks = {}
607 for framework in ['Tcl', 'Tk']:
608 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
609 libfw = os.path.join('/', fwpth)
610 usrfw = os.path.join(os.getenv('HOME'), fwpth)
611 frameworks[framework] = os.readlink(libfw)
612 if not os.path.exists(libfw):
613 fatal("Please install a link to a current %s %s as %s so "
614 "the user can override the system framework."
615 % (framework, frameworks[framework], libfw))
616 if os.path.exists(usrfw):
617 fatal("Please rename %s to avoid possible dynamic load issues."
618 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000619
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500620 if frameworks['Tcl'] != frameworks['Tk']:
621 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000622
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500623 print(" -- Building with external Tcl/Tk %s frameworks"
624 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800625
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500626 # add files to check after build
627 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
628 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
629 % frameworks['Tcl'],
630 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
631 % frameworks['Tk'],
632 ]
633 else:
634 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500635 print("")
636
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000637 # Remove inherited environment variables which might influence build
638 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
639 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
640 for ev in list(os.environ):
641 for prefix in environ_var_prefixes:
642 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800643 print("INFO: deleting environment variable %s=%s" % (
644 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000645 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000646
Ned Deily4a96a372013-01-29 00:08:32 -0800647 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
648 if 'SDK_TOOLS_BIN' in os.environ:
649 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
650 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
651 # add its fixed location here if it exists
652 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
653 if os.path.isdir(OLD_DEVELOPER_TOOLS):
654 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
655 os.environ['PATH'] = base_path
656 print("Setting default PATH: %s"%(os.environ['PATH']))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000657
Thomas Wouters89f507f2006-12-13 04:49:30 +0000658def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000659 """
660 Parse arguments and update global settings.
661 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500662 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800663 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800664 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400665 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000666
667 if args is None:
668 args = sys.argv[1:]
669
670 try:
671 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000672 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
673 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800674 except getopt.GetoptError:
675 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676 sys.exit(1)
677
678 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800679 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000680 sys.exit(1)
681
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000682 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000683 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000684 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800685 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000686 sys.exit(0)
687
688 elif k in ('-d', '--build-dir'):
689 WORKDIR=v
690
691 elif k in ('--third-party',):
692 DEPSRC=v
693
694 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500695 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000696
697 elif k in ('--src-dir',):
698 SRCDIR=v
699
Ronald Oussoren1943f862009-03-30 19:39:14 +0000700 elif k in ('--dep-target', ):
701 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000702 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000703
704 elif k in ('--universal-archs', ):
705 if v in UNIVERSALOPTS:
706 UNIVERSALARCHS = v
707 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000708 if deptarget is None:
709 # Select alternate default deployment
710 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500711 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000712 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800713 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000714
Thomas Wouters477c8d52006-05-27 19:21:47 +0000715 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800716 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000717
718 SRCDIR=os.path.abspath(SRCDIR)
719 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000720 DEPSRC=os.path.abspath(DEPSRC)
721
Ned Deily04cdfa12014-06-25 13:36:14 -0700722 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000723
Ned Deily5d3febf2014-12-13 00:17:46 -0800724 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400725 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800726
727 print("-- Settings:")
728 print(" * Source directory: %s" % SRCDIR)
729 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800730 print(" * Third-party source: %s" % DEPSRC)
731 print(" * Deployment target: %s" % DEPTARGET)
732 print(" * Universal archs: %s" % str(ARCHLIST))
733 print(" * C compiler: %s" % CC)
734 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800735 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800736 print(" -- Building a Python %s framework at patch level %s"
737 % (getVersion(), getFullVersion()))
738 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000739
740def extractArchive(builddir, archiveName):
741 """
742 Extract a source archive into 'builddir'. Returns the path of the
743 extracted archive.
744
745 XXX: This function assumes that archives contain a toplevel directory
746 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700747 safe enough for almost anything we use. Unfortunately, it does not
748 work for current Tcl and Tk source releases where the basename of
749 the archive ends with "-src" but the uncompressed directory does not.
750 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000751 """
752 curdir = os.getcwd()
753 try:
754 os.chdir(builddir)
755 if archiveName.endswith('.tar.gz'):
756 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700757 if ((retval.startswith('tcl') or retval.startswith('tk'))
758 and retval.endswith('-src')):
759 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000760 if os.path.exists(retval):
761 shutil.rmtree(retval)
762 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
763
764 elif archiveName.endswith('.tar.bz2'):
765 retval = os.path.basename(archiveName[:-8])
766 if os.path.exists(retval):
767 shutil.rmtree(retval)
768 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
769
770 elif archiveName.endswith('.tar'):
771 retval = os.path.basename(archiveName[:-4])
772 if os.path.exists(retval):
773 shutil.rmtree(retval)
774 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
775
776 elif archiveName.endswith('.zip'):
777 retval = os.path.basename(archiveName[:-4])
778 if os.path.exists(retval):
779 shutil.rmtree(retval)
780 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
781
782 data = fp.read()
783 xit = fp.close()
784 if xit is not None:
785 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800786 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000787
788 return os.path.join(builddir, retval)
789
790 finally:
791 os.chdir(curdir)
792
Thomas Wouters477c8d52006-05-27 19:21:47 +0000793def downloadURL(url, fname):
794 """
795 Download the contents of the url into the file.
796 """
Ned Deily4a96a372013-01-29 00:08:32 -0800797 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000798 fpOut = open(fname, 'wb')
799 block = fpIn.read(10240)
800 try:
801 while block:
802 fpOut.write(block)
803 block = fpIn.read(10240)
804 fpIn.close()
805 fpOut.close()
806 except:
807 try:
808 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300809 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000810 pass
811
Ned Deily4a96a372013-01-29 00:08:32 -0800812def verifyThirdPartyFile(url, checksum, fname):
813 """
814 Download file from url to filename fname if it does not already exist.
815 Abort if file contents does not match supplied md5 checksum.
816 """
817 name = os.path.basename(fname)
818 if os.path.exists(fname):
819 print("Using local copy of %s"%(name,))
820 else:
821 print("Did not find local copy of %s"%(name,))
822 print("Downloading %s"%(name,))
823 downloadURL(url, fname)
824 print("Archive for %s stored as %s"%(name, fname))
825 if os.system(
826 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
827 % (shellQuote(fname), checksum) ):
828 fatal('MD5 checksum mismatch for file %s' % fname)
829
Ned Deily5d3febf2014-12-13 00:17:46 -0800830def build_universal_openssl(basedir, archList):
831 """
832 Special case build recipe for universal build of openssl.
833
834 The upstream OpenSSL build system does not directly support
835 OS X universal builds. We need to build each architecture
836 separately then lipo them together into fat libraries.
837 """
838
839 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
840 # If we are building on a 10.4.x or earlier system,
841 # unilaterally disable assembly code building to avoid the problem.
842 no_asm = int(platform.release().split(".")[0]) < 9
843
844 def build_openssl_arch(archbase, arch):
845 "Build one architecture of openssl"
846 arch_opts = {
847 "i386": ["darwin-i386-cc"],
848 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100849 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800850 "ppc": ["darwin-ppc-cc"],
851 "ppc64": ["darwin64-ppc-cc"],
852 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400853
854 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
855 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
856 # building on our 10.6 gcc-4.2 environment. There have been other
857 # reports of projects running into this when using older compilers.
858 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
859 # building for 10.6.
860 if getDeptargetTuple() == (10, 6):
861 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
862
Ned Deily5d3febf2014-12-13 00:17:46 -0800863 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800864 "no-idea",
865 "no-mdc2",
866 "no-rc5",
867 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800868 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800869 # "enable-unit-test",
870 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800871 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400872 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800873 ]
874 if no_asm:
875 configure_opts.append("no-asm")
876 runCommand(" ".join(["perl", "Configure"]
877 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500878 runCommand("make depend")
879 runCommand("make all")
880 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800881 # runCommand("make test")
882 return
883
884 srcdir = os.getcwd()
885 universalbase = os.path.join(srcdir, "..",
886 os.path.basename(srcdir) + "-universal")
887 os.mkdir(universalbase)
888 archbasefws = []
889 for arch in archList:
890 # fresh copy of the source tree
891 archsrc = os.path.join(universalbase, arch, "src")
892 shutil.copytree(srcdir, archsrc, symlinks=True)
893 # install base for this arch
894 archbase = os.path.join(universalbase, arch, "root")
895 os.mkdir(archbase)
896 # Python framework base within install_prefix:
897 # the build will install into this framework..
898 # This is to ensure that the resulting shared libs have
899 # the desired real install paths built into them.
900 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
901
902 # build one architecture
903 os.chdir(archsrc)
904 build_openssl_arch(archbase, arch)
905 os.chdir(srcdir)
906 archbasefws.append(archbasefw)
907
908 # copy arch-independent files from last build into the basedir framework
909 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
910 shutil.copytree(
911 os.path.join(archbasefw, "include", "openssl"),
912 os.path.join(basefw, "include", "openssl")
913 )
914
915 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
916 "SHLIB_VERSION_NUMBER")
917 # e.g. -> "1.0.0"
918 libcrypto = "libcrypto.dylib"
919 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
920 # e.g. -> "libcrypto.1.0.0.dylib"
921 libssl = "libssl.dylib"
922 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
923 # e.g. -> "libssl.1.0.0.dylib"
924
925 try:
926 os.mkdir(os.path.join(basefw, "lib"))
927 except OSError:
928 pass
929
930 # merge the individual arch-dependent shared libs into a fat shared lib
931 archbasefws.insert(0, basefw)
932 for (lib_unversioned, lib_versioned) in [
933 (libcrypto, libcrypto_versioned),
934 (libssl, libssl_versioned)
935 ]:
936 runCommand("lipo -create -output " +
937 " ".join(shellQuote(
938 os.path.join(fw, "lib", lib_versioned))
939 for fw in archbasefws))
940 # and create an unversioned symlink of it
941 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
942
943 # Create links in the temp include and lib dirs that will be injected
944 # into the Python build so that setup.py can find them while building
945 # and the versioned links so that the setup.py post-build import test
946 # does not fail.
947 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
948 for fn in [
949 ["include", "openssl"],
950 ["lib", libcrypto],
951 ["lib", libssl],
952 ["lib", libcrypto_versioned],
953 ["lib", libssl_versioned],
954 ]:
955 os.symlink(
956 os.path.join(relative_path, *fn),
957 os.path.join(basedir, "usr", "local", *fn)
958 )
959
960 return
961
Thomas Wouters477c8d52006-05-27 19:21:47 +0000962def buildRecipe(recipe, basedir, archList):
963 """
964 Build software using a recipe. This function does the
965 'configure;make;make install' dance for C software, with a possibility
966 to customize this process, basically a poor-mans DarwinPorts.
967 """
968 curdir = os.getcwd()
969
970 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800971 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000972 url = recipe['url']
973 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800974 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000975 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
976 shellQuote(basedir)))
977
978 archiveName = os.path.split(url)[-1]
979 sourceArchive = os.path.join(DEPSRC, archiveName)
980
981 if not os.path.exists(DEPSRC):
982 os.mkdir(DEPSRC)
983
Ned Deily4a96a372013-01-29 00:08:32 -0800984 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
985 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000986 buildDir=os.path.join(WORKDIR, '_bld')
987 if not os.path.exists(buildDir):
988 os.mkdir(buildDir)
989
990 workDir = extractArchive(buildDir, sourceArchive)
991 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000992
Ned Deily4a96a372013-01-29 00:08:32 -0800993 for patch in recipe.get('patches', ()):
994 if isinstance(patch, tuple):
995 url, checksum = patch
996 fn = os.path.join(DEPSRC, os.path.basename(url))
997 verifyThirdPartyFile(url, checksum, fn)
998 else:
999 # patch is a file in the source directory
1000 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001001 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1002 shellQuote(fn),))
1003
Ned Deily4a96a372013-01-29 00:08:32 -08001004 for patchscript in recipe.get('patchscripts', ()):
1005 if isinstance(patchscript, tuple):
1006 url, checksum = patchscript
1007 fn = os.path.join(DEPSRC, os.path.basename(url))
1008 verifyThirdPartyFile(url, checksum, fn)
1009 else:
1010 # patch is a file in the source directory
1011 fn = os.path.join(curdir, patchscript)
1012 if fn.endswith('.bz2'):
1013 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1014 fn = fn[:-4]
1015 runCommand('sh %s' % shellQuote(fn))
1016 os.unlink(fn)
1017
Ned Deily94764b22013-10-27 19:49:29 -07001018 if 'buildDir' in recipe:
1019 os.chdir(recipe['buildDir'])
1020
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001021 if configure is not None:
1022 configure_args = [
1023 "--prefix=/usr/local",
1024 "--enable-static",
1025 "--disable-shared",
1026 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1027 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001028
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001029 if 'configure_pre' in recipe:
1030 args = list(recipe['configure_pre'])
1031 if '--disable-static' in args:
1032 configure_args.remove('--enable-static')
1033 if '--enable-shared' in args:
1034 configure_args.remove('--disable-shared')
1035 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001036
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001037 if recipe.get('useLDFlags', 1):
1038 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001039 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001040 "-I%s/usr/local/include"%(
1041 recipe.get('extra_cflags', ''),
1042 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001043 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001044 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001045 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001046 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001047 shellQuote(basedir)[1:-1],
1048 ' -arch '.join(archList)),
1049 ])
1050 else:
1051 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001052 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001053 "-I%s/usr/local/include"%(
1054 recipe.get('extra_cflags', ''),
1055 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001056 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001057 shellQuote(basedir)[1:-1],),
1058 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001059
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001060 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001061 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001062
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001063 configure_args.insert(0, configure)
1064 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001065
Ned Deily4a96a372013-01-29 00:08:32 -08001066 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001067 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001068
Ned Deily5d3febf2014-12-13 00:17:46 -08001069 if buildrecipe is not None:
1070 # call special-case build recipe, e.g. for openssl
1071 buildrecipe(basedir, archList)
1072
1073 if install is not None:
1074 print("Running install for %s"%(name,))
1075 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001076
Ned Deily4a96a372013-01-29 00:08:32 -08001077 print("Done %s"%(name,))
1078 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001079
1080 os.chdir(curdir)
1081
1082def buildLibraries():
1083 """
1084 Build our dependencies into $WORKDIR/libraries/usr/local
1085 """
Ned Deily4a96a372013-01-29 00:08:32 -08001086 print("")
1087 print("Building required libraries")
1088 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089 universal = os.path.join(WORKDIR, 'libraries')
1090 os.mkdir(universal)
1091 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1092 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1093
Ronald Oussoren1943f862009-03-30 19:39:14 +00001094 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001095 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001096
1097
1098
1099def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001100 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001101 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001102 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001103 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001104 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001105 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001106 curDir = os.getcwd()
1107 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001108 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001109
1110 # Search third-party source directory for a pre-built version of the docs.
1111 # Use the naming convention of the docs.python.org html downloads:
1112 # python-3.9.0b1-docs-html.tar.bz2
1113 doctarfiles = [ f for f in os.listdir(DEPSRC)
1114 if f.startswith('python-'+getFullVersion())
1115 if f.endswith('-docs-html.tar.bz2') ]
1116 if doctarfiles:
1117 doctarfile = doctarfiles[0]
1118 if not os.path.exists('build'):
1119 os.mkdir('build')
1120 # if build directory existed, it was emptied by make clean, above
1121 os.chdir('build')
1122 # Extract the first archive found for this version into build
1123 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1124 # see if tar extracted a directory ending in -docs-html
1125 archivefiles = [ f for f in os.listdir('.')
1126 if f.endswith('-docs-html')
1127 if os.path.isdir(f) ]
1128 if archivefiles:
1129 archivefile = archivefiles[0]
1130 # make it our 'Docs/build/html' directory
1131 print(' -- using pre-built python documentation from %s'%archivefile)
1132 os.rename(archivefile, 'html')
1133 os.chdir(buildDir)
1134
1135 htmlDir = os.path.join('build', 'html')
1136 if not os.path.exists(htmlDir):
1137 # Create virtual environment for docs builds with blurb and sphinx
1138 runCommand('make venv')
Ned Deily63fc55b2020-06-08 03:52:43 -04001139 runCommand('make html PYTHON=venv/bin/python')
1140 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001141 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001142
1143
1144def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001145 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001146
1147 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1148 rootDir = os.path.join(WORKDIR, '_root')
1149
1150 if os.path.exists(buildDir):
1151 shutil.rmtree(buildDir)
1152 if os.path.exists(rootDir):
1153 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001154 os.makedirs(buildDir)
1155 os.makedirs(rootDir)
1156 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001157 curdir = os.getcwd()
1158 os.chdir(buildDir)
1159
Thomas Wouters477c8d52006-05-27 19:21:47 +00001160 # Extract the version from the configure file, needed to calculate
1161 # several paths.
1162 version = getVersion()
1163
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001164 # Since the extra libs are not in their installed framework location
1165 # during the build, augment the library path so that the interpreter
1166 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001167
Ned Deily4a96a372013-01-29 00:08:32 -08001168 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001169 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001170 "--with-universal-archs=%s "
1171 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001172 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001173 "%s "
1174 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001175 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001176 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001177 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001178 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001179 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001180 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001181 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001182 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1183 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001184 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1185 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1186 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1187 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001188 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001189 shellQuote(WORKDIR)[1:-1]))
1190
Ned Deily1931e642020-06-25 04:51:46 -04001191 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1192 # environment variables are no longer automatically inherited
1193 # by child processes from their parents. We used to just set
1194 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1195 # in build-installer.py's process environment and it was
1196 # passed through the make utility into the environment of
1197 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1198 # the existing RUNSHARED configuration value when we call
1199 # make for extension module builds.
1200
1201 runshared_for_make = "".join([
1202 " RUNSHARED=",
1203 "'",
1204 grepValue("Makefile", "RUNSHARED"),
1205 ' DYLD_LIBRARY_PATH=',
1206 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1207 "'" ])
1208
Ned Deilyb364d9f2017-07-24 04:58:43 -04001209 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1210 # and, if defined, append its value to the make command. This allows
1211 # us to pass in version control tags, like GITTAG, to a build from a
1212 # tarball rather than from a vcs checkout, thus eliminating the need
1213 # to have a working copy of the vcs program on the build machine.
1214 #
1215 # A typical use might be:
1216 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1217 # GITVERSION='echo 123456789a' \
1218 # GITTAG='echo v3.6.0' \
1219 # GITBRANCH='echo 3.6'"
1220
1221 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1222 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001223 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001224 else:
Ned Deily1931e642020-06-25 04:51:46 -04001225 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001226 print("Running " + make_cmd)
1227 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001228
Ned Deily1931e642020-06-25 04:51:46 -04001229 make_cmd = "make install DESTDIR=%s %s"%(
1230 shellQuote(rootDir),
1231 runshared_for_make)
1232 print("Running " + make_cmd)
1233 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001234
Ned Deily1931e642020-06-25 04:51:46 -04001235 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1236 shellQuote(rootDir),
1237 runshared_for_make)
1238 print("Running " + make_cmd)
1239 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001240
Ned Deily4a96a372013-01-29 00:08:32 -08001241 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001242 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001243 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001244 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001245 'Python.framework', 'Versions', getVersion(), 'lib')
1246 fw_lib_dir = os.path.join(
1247 WORKDIR, '_root', 'Library', 'Frameworks',
1248 'Python.framework', 'Versions', getVersion(), 'lib')
1249 if internalTk():
1250 # move Tcl and Tk pkgconfig files
1251 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1252 shellQuote(build_lib_dir),
1253 shellQuote(fw_lib_dir) ))
1254 runCommand("rm -r %s/pkgconfig"%(
1255 shellQuote(build_lib_dir), ))
1256 runCommand("mv %s/* %s"%(
1257 shellQuote(build_lib_dir),
1258 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001259
Ned Deilydde4f632016-09-12 09:39:23 -04001260 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1261 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1262 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1263 # create directory for OpenSSL certificates
1264 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1265 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001266
Ned Deily4a96a372013-01-29 00:08:32 -08001267 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001268 gid = grp.getgrnam('admin').gr_gid
1269
Ned Deily4a96a372013-01-29 00:08:32 -08001270 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001271 for dirpath, dirnames, filenames in os.walk(frmDir):
1272 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001273 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001274 os.chown(os.path.join(dirpath, dn), -1, gid)
1275
Thomas Wouters477c8d52006-05-27 19:21:47 +00001276 for fn in filenames:
1277 if os.path.islink(fn):
1278 continue
1279
1280 # "chmod g+w $fn"
1281 p = os.path.join(dirpath, fn)
1282 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001283 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1284 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001285
Ned Deily4a96a372013-01-29 00:08:32 -08001286 if fn in EXPECTED_SHARED_LIBS:
1287 # check to see that this file was linked with the
1288 # expected library path and version
1289 data = captureCommand("otool -L %s" % shellQuote(p))
1290 for sl in EXPECTED_SHARED_LIBS[fn]:
1291 if ("\t%s " % sl) not in data:
1292 print("Expected shared lib %s was not linked with %s"
1293 % (sl, p))
1294 shared_lib_error = True
1295
1296 if shared_lib_error:
1297 fatal("Unexpected shared library errors.")
1298
Ned Deilye59e4c52011-01-29 18:56:28 +00001299 if PYTHON_3:
1300 LDVERSION=None
1301 VERSION=None
1302 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001303
Ned Deilye59e4c52011-01-29 18:56:28 +00001304 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001305 for ln in fp:
1306 if ln.startswith('VERSION='):
1307 VERSION=ln.split()[1]
1308 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001309 ABIFLAGS=ln.split()
1310 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001311 if ln.startswith('LDVERSION='):
1312 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001313 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001314
Ned Deilye59e4c52011-01-29 18:56:28 +00001315 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1316 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1317 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001318 if getVersionMajorMinor() >= (3, 6):
1319 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001320 else:
1321 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001322
Thomas Wouters477c8d52006-05-27 19:21:47 +00001323 # We added some directories to the search path during the configure
1324 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001325 # the end-users system. Also remove the directories from _sysconfigdata.py
1326 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001327
Ned Deilya4f6b002013-10-25 00:47:38 -07001328 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1329 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1330
Ned Deilya4f6b002013-10-25 00:47:38 -07001331 # fix Makefile
1332 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1333 fp = open(path, 'r')
1334 data = fp.read()
1335 fp.close()
1336
1337 for p in (include_path, lib_path):
1338 data = data.replace(" " + p, '')
1339 data = data.replace(p + " ", '')
1340
1341 fp = open(path, 'w')
1342 fp.write(data)
1343 fp.close()
1344
Ned Deily652bad42016-08-15 14:37:14 -04001345 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001346 #
1347 # TODO: make this more robust! test_sysconfig_module of
1348 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1349 # the output from get_config_var in both sysconfig and
1350 # distutils.sysconfig is exactly the same for both CFLAGS and
1351 # LDFLAGS. The fixing up is now complicated by the pretty
1352 # printing in _sysconfigdata.py. Also, we are using the
1353 # pprint from the Python running the installer build which
1354 # may not cosmetically format the same as the pprint in the Python
1355 # being built (and which is used to originally generate
1356 # _sysconfigdata.py).
1357
1358 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001359 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001360 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001361 path = os.path.join(path_to_lib,
1362 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001363 else:
1364 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1365 fp = open(path, 'r')
1366 data = fp.read()
1367 fp.close()
1368 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001369 if RUNNING_ON_PYTHON2:
1370 exec(data)
1371 else:
1372 g_dict = {}
1373 l_dict = {}
1374 exec(data, g_dict, l_dict)
1375 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001376 vars = {}
1377 for k, v in build_time_vars.items():
1378 if type(v) == type(''):
1379 for p in (include_path, lib_path):
1380 v = v.replace(' ' + p, '')
1381 v = v.replace(p + ' ', '')
1382 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001383
Ned Deily652bad42016-08-15 14:37:14 -04001384 fp = open(path, 'w')
1385 # duplicated from sysconfig._generate_posix_vars()
1386 fp.write('# system configuration generated and used by'
1387 ' the sysconfig module\n')
1388 fp.write('build_time_vars = ')
1389 pprint.pprint(vars, stream=fp)
1390 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001391
1392 # Add symlinks in /usr/local/bin, using relative links
1393 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1394 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1395 'Python.framework', 'Versions', version, 'bin')
1396 if os.path.exists(usr_local_bin):
1397 shutil.rmtree(usr_local_bin)
1398 os.makedirs(usr_local_bin)
1399 for fn in os.listdir(
1400 os.path.join(frmDir, 'Versions', version, 'bin')):
1401 os.symlink(os.path.join(to_framework, fn),
1402 os.path.join(usr_local_bin, fn))
1403
1404 os.chdir(curdir)
1405
Thomas Wouters477c8d52006-05-27 19:21:47 +00001406def patchFile(inPath, outPath):
1407 data = fileContents(inPath)
1408 data = data.replace('$FULL_VERSION', getFullVersion())
1409 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001410 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001411 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001412 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001413 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001414
1415 # This one is not handy as a template variable
1416 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001417 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001418 fp.write(data)
1419 fp.close()
1420
1421def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001422 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001423 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001424 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001425 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001426 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001427 fp.write(data)
1428 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001429 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001430
1431
1432
1433def packageFromRecipe(targetDir, recipe):
1434 curdir = os.getcwd()
1435 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001436 # The major version (such as 2.5) is included in the package name
1437 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001438 # common.
1439 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001440 srcdir = recipe.get('source')
1441 pkgroot = recipe.get('topdir', srcdir)
1442 postflight = recipe.get('postflight')
1443 readme = textwrap.dedent(recipe['readme'])
1444 isRequired = recipe.get('required', True)
1445
Ned Deily4a96a372013-01-29 00:08:32 -08001446 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001447
1448 # Substitute some variables
1449 textvars = dict(
1450 VER=getVersion(),
1451 FULLVER=getFullVersion(),
1452 )
1453 readme = readme % textvars
1454
1455 if pkgroot is not None:
1456 pkgroot = pkgroot % textvars
1457 else:
1458 pkgroot = '/'
1459
1460 if srcdir is not None:
1461 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1462 srcdir = srcdir % textvars
1463
1464 if postflight is not None:
1465 postflight = os.path.abspath(postflight)
1466
1467 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1468 os.makedirs(packageContents)
1469
1470 if srcdir is not None:
1471 os.chdir(srcdir)
1472 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1473 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1474 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1475
1476 fn = os.path.join(packageContents, 'PkgInfo')
1477 fp = open(fn, 'w')
1478 fp.write('pmkrpkg1')
1479 fp.close()
1480
1481 rsrcDir = os.path.join(packageContents, "Resources")
1482 os.mkdir(rsrcDir)
1483 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1484 fp.write(readme)
1485 fp.close()
1486
1487 if postflight is not None:
1488 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1489
1490 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001491 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001492 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001493 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1494 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1495 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001496 CFBundleShortVersionString=vers,
1497 IFMajorVersion=major,
1498 IFMinorVersion=minor,
1499 IFPkgFormatVersion=0.10000000149011612,
1500 IFPkgFlagAllowBackRev=False,
1501 IFPkgFlagAuthorizationAction="RootAuthorization",
1502 IFPkgFlagDefaultLocation=pkgroot,
1503 IFPkgFlagFollowLinks=True,
1504 IFPkgFlagInstallFat=True,
1505 IFPkgFlagIsRequired=isRequired,
1506 IFPkgFlagOverwritePermissions=False,
1507 IFPkgFlagRelocatable=False,
1508 IFPkgFlagRestartAction="NoRestart",
1509 IFPkgFlagRootVolumeOnly=True,
1510 IFPkgFlagUpdateInstalledLangauges=False,
1511 )
1512 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1513
Ned Deily1931e642020-06-25 04:51:46 -04001514 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001515 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001516 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001517 IFPkgDescriptionVersion=vers,
1518 )
1519 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1520
1521 finally:
1522 os.chdir(curdir)
1523
1524
1525def makeMpkgPlist(path):
1526
1527 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001528 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001529
Ned Deily1931e642020-06-25 04:51:46 -04001530 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001531 CFBundleGetInfoString="Python %s"%(vers,),
1532 CFBundleIdentifier='org.python.Python',
1533 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001534 CFBundleShortVersionString=vers,
1535 IFMajorVersion=major,
1536 IFMinorVersion=minor,
1537 IFPkgFlagComponentDirectory="Contents/Packages",
1538 IFPkgFlagPackageList=[
1539 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001540 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001541 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001542 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001543 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001544 ],
1545 IFPkgFormatVersion=0.10000000149011612,
1546 IFPkgFlagBackgroundScaling="proportional",
1547 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001548 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001549 )
1550
1551 writePlist(pl, path)
1552
1553
1554def buildInstaller():
1555
1556 # Zap all compiled files
1557 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1558 for fn in filenames:
1559 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1560 os.unlink(os.path.join(dirpath, fn))
1561
1562 outdir = os.path.join(WORKDIR, 'installer')
1563 if os.path.exists(outdir):
1564 shutil.rmtree(outdir)
1565 os.mkdir(outdir)
1566
Ronald Oussoren1943f862009-03-30 19:39:14 +00001567 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001568 pkgcontents = os.path.join(pkgroot, 'Packages')
1569 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001570 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001571 packageFromRecipe(pkgcontents, recipe)
1572
1573 rsrcDir = os.path.join(pkgroot, 'Resources')
1574
1575 fn = os.path.join(pkgroot, 'PkgInfo')
1576 fp = open(fn, 'w')
1577 fp.write('pmkrpkg1')
1578 fp.close()
1579
1580 os.mkdir(rsrcDir)
1581
1582 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001583 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001584 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001585 IFPkgDescriptionVersion=getVersion(),
1586 )
1587
1588 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1589 for fn in os.listdir('resources'):
1590 if fn == '.svn': continue
1591 if fn.endswith('.jpg'):
1592 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1593 else:
1594 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1595
Thomas Wouters477c8d52006-05-27 19:21:47 +00001596
1597def installSize(clear=False, _saved=[]):
1598 if clear:
1599 del _saved[:]
1600 if not _saved:
1601 data = captureCommand("du -ks %s"%(
1602 shellQuote(os.path.join(WORKDIR, '_root'))))
1603 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1604 return _saved[0]
1605
1606
1607def buildDMG():
1608 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001609 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001610 """
1611 outdir = os.path.join(WORKDIR, 'diskimage')
1612 if os.path.exists(outdir):
1613 shutil.rmtree(outdir)
1614
Erlend Egeberg Aaslandc94ee132021-01-04 05:48:19 +01001615 # We used to use the deployment target as the last characters of the
Ned Deily936533c2020-11-23 19:04:40 -05001616 # installer file name. With the introduction of weaklinked installer
1617 # variants, we may have two variants with the same file name, i.e.
1618 # both ending in '10.9'. To avoid this, we now use the major/minor
Ned Deily8a374632021-04-27 13:23:39 -04001619 # version numbers of the macOS version we are building on.
1620 # Also, as of macOS 11, operating system version numbering has
1621 # changed from three components to two, i.e.
1622 # 10.14.1, 10.14.2, ...
1623 # 10.15.1, 10.15.2, ...
1624 # 11.1, 11.2, ...
1625 # 12.1, 12.2, ...
1626 # (A further twist is that, when running on macOS 11, binaries built
1627 # on older systems may be shown an operating system version of 10.16
1628 # instead of 11. We should not run into that situation here.)
1629 # Also we should use "macos" instead of "macosx" going forward.
1630 #
1631 # To maintain compability for legacy variants, the file name for
1632 # builds on macOS 10.15 and earlier remains:
1633 # python-3.x.y-macosx10.z.{dmg->pkg}
1634 # e.g. python-3.9.4-macosx10.9.{dmg->pkg}
1635 # and for builds on macOS 11+:
1636 # python-3.x.y-macosz.{dmg->pkg}
1637 # e.g. python-3.9.4-macos11.{dmg->pkg}
1638
1639 build_tuple = getBuildTuple()
1640 if build_tuple[0] < 11:
1641 os_name = 'macosx'
1642 build_system_version = '%s.%s' % build_tuple
1643 else:
1644 os_name = 'macos'
1645 build_system_version = str(build_tuple[0])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001646 imagepath = os.path.join(outdir,
Ned Deily8a374632021-04-27 13:23:39 -04001647 'python-%s-%s%s'%(getFullVersion(),os_name,build_system_version))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001648 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001649 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001650 imagepath = imagepath + '.dmg'
1651
1652 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001653
1654 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1655 # when hdiutil create fails with "Resource busy". For now, just retry
1656 # the create a few times and hope that it eventually works.
1657
Ronald Oussoren1943f862009-03-30 19:39:14 +00001658 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001659 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001660 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001661 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001662 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001663 for i in range(5):
1664 fd = os.popen(cmd, 'r')
1665 data = fd.read()
1666 xit = fd.close()
1667 if not xit:
1668 break
1669 sys.stdout.write(data)
1670 print(" -- retrying hdiutil create")
1671 time.sleep(5)
1672 else:
cclaussd3371692019-06-03 05:19:44 +02001673 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001674
1675 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1676 os.mkdir(os.path.join(WORKDIR, "mnt"))
1677 runCommand("hdiutil attach %s -mountroot %s"%(
1678 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1679
1680 # Custom icon for the DMG, shown when the DMG is mounted.
1681 shutil.copy("../Icons/Disk Image.icns",
1682 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001683 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001684 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1685
1686 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1687
1688 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1689 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1690 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1691 setIcon(imagepath, "../Icons/Disk Image.icns")
1692
1693 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001694
1695 return imagepath
1696
1697
1698def setIcon(filePath, icnsPath):
1699 """
1700 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001701 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001702
Ronald Oussoren70050672010-04-30 15:00:26 +00001703 dirPath = os.path.normpath(os.path.dirname(__file__))
1704 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001705 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1706 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1707 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001708 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1709 if not os.path.exists(appPath):
1710 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001711 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1712 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001713
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001714 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1715 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001716
1717def main():
1718 # First parse options and check if we can perform our work
1719 parseOptions()
1720 checkEnvironment()
1721
Ronald Oussoren1943f862009-03-30 19:39:14 +00001722 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001723 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001724 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001725
1726 if os.path.exists(WORKDIR):
1727 shutil.rmtree(WORKDIR)
1728 os.mkdir(WORKDIR)
1729
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001730 os.environ['LC_ALL'] = 'C'
1731
Thomas Wouters477c8d52006-05-27 19:21:47 +00001732 # Then build third-party libraries such as sleepycat DB4.
1733 buildLibraries()
1734
1735 # Now build python itself
1736 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001737
1738 # And then build the documentation
1739 # Remove the Deployment Target from the shell
1740 # environment, it's no longer needed and
1741 # an unexpected build target can cause problems
1742 # when Sphinx and its dependencies need to
1743 # be (re-)installed.
1744 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001745 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001746
1747
1748 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001749 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001750 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001751 fn = os.path.join(folder, "License.rtf")
1752 patchFile("resources/License.rtf", fn)
1753 fn = os.path.join(folder, "ReadMe.rtf")
1754 patchFile("resources/ReadMe.rtf", fn)
1755 fn = os.path.join(folder, "Update Shell Profile.command")
1756 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001757 fn = os.path.join(folder, "Install Certificates.command")
1758 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001759 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001760 setIcon(folder, "../Icons/Python Folder.icns")
1761
1762 # Create the installer
1763 buildInstaller()
1764
1765 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001766 patchFile('resources/ReadMe.rtf',
1767 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001768
1769 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001770 patchFile('resources/License.rtf',
1771 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001772
1773 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001774 fp.write("# BUILD INFO\n")
1775 fp.write("# Date: %s\n" % time.ctime())
1776 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001777 fp.close()
1778
Thomas Wouters477c8d52006-05-27 19:21:47 +00001779 # And copy it to a DMG
1780 buildDMG()
1781
Thomas Wouters477c8d52006-05-27 19:21:47 +00001782if __name__ == "__main__":
1783 main()