blob: 864661ec9e1b6c6a6f4a400dc17dc908f7cc6458 [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(
Ned Deily02424942021-03-01 02:39:08 -0500245 name="OpenSSL 1.1.1j",
246 url="https://www.openssl.org/source/openssl-1.1.1j.tar.gz",
247 checksum='cccaa064ed860a2b4d1303811bf5c682',
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 Aaslandc94ee132021-01-04 05:48:19 +0100357 name="SQLite 3.34.0",
358 url="https://sqlite.org/2020/sqlite-autoconf-3340000.tar.gz",
359 checksum='7f33c9db7b713957fcb9271fe9049fef',
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 '
366 '-DSQLITE_TCL=0 '
367 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
368 configure_pre=[
369 '--enable-threadsafe',
370 '--enable-shared=no',
371 '--enable-static=yes',
372 '--disable-readline',
373 '--disable-dependency-tracking',
374 ]
375 ),
376 ])
377
Ned Deily04cdfa12014-06-25 13:36:14 -0700378 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000379 result.extend([
380 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000381 name="Bzip2 1.0.6",
382 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
383 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000384 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500385 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800386 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000387 shellQuote(os.path.join(WORKDIR, 'libraries')),
388 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000389 ),
390 ),
391 dict(
392 name="ZLib 1.2.3",
393 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
394 checksum='debc62758716a169df9f62e6ab2bc634',
395 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500396 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800397 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000398 shellQuote(os.path.join(WORKDIR, 'libraries')),
399 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000400 ),
401 ),
402 dict(
403 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000404 name="GNU Readline 6.1.2",
405 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
406 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000407 patchlevel='0',
408 patches=[
409 # The readline maintainers don't do actual micro releases, but
410 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800411 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
412 'c642f2e84d820884b0bf9fd176bc6c3f'),
413 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
414 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000415 ]
416 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000417 ])
418
Ned Deily4f7ff782011-01-15 05:29:12 +0000419 if not PYTHON_3:
420 result.extend([
421 dict(
422 name="Sleepycat DB 4.7.25",
423 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
424 checksum='ec2b87e833779681a0c3a814aa71359e',
425 buildDir="build_unix",
426 configure="../dist/configure",
427 configure_pre=[
428 '--includedir=/usr/local/include/db4',
429 ]
430 ),
431 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000432
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000433 return result
434
Thomas Wouters477c8d52006-05-27 19:21:47 +0000435
Thomas Wouters477c8d52006-05-27 19:21:47 +0000436# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000437def pkg_recipes():
438 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
439 result = [
440 dict(
441 name="PythonFramework",
442 long_name="Python Framework",
443 source="/Library/Frameworks/Python.framework",
444 readme="""\
445 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500446 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000447 """,
448 postflight="scripts/postflight.framework",
449 selected='selected',
450 ),
451 dict(
452 name="PythonApplications",
453 long_name="GUI Applications",
454 source="/Applications/Python %(VER)s",
455 readme="""\
456 This package installs IDLE (an interactive Python IDE),
457 Python Launcher and Build Applet (create application bundles
458 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000459
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000460 It also installs a number of examples and demos.
461 """,
462 required=False,
463 selected='selected',
464 ),
465 dict(
466 name="PythonUnixTools",
467 long_name="UNIX command-line tools",
468 source="/usr/local/bin",
469 readme="""\
470 This package installs the unix tools in /usr/local/bin for
471 compatibility with older releases of Python. This package
472 is not necessary to use Python.
473 """,
474 required=False,
475 selected='selected',
476 ),
477 dict(
478 name="PythonDocumentation",
479 long_name="Python Documentation",
480 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
481 source="/pydocs",
482 readme="""\
483 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800484 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000485 """,
486 postflight="scripts/postflight.documentation",
487 required=False,
488 selected='selected',
489 ),
490 dict(
491 name="PythonProfileChanges",
492 long_name="Shell profile updater",
493 readme="""\
494 This packages updates your shell profile to make sure that
495 the Python tools are found by your shell in preference of
496 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000497
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000498 If you don't install this package you'll have to add
499 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
500 to your PATH by hand.
501 """,
502 postflight="scripts/postflight.patch-profile",
503 topdir="/Library/Frameworks/Python.framework",
504 source="/empty-dir",
505 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800506 selected='selected',
507 ),
508 dict(
509 name="PythonInstallPip",
510 long_name="Install or upgrade pip",
511 readme="""\
512 This package installs (or upgrades from an earlier version)
513 pip, a tool for installing and managing Python packages.
514 """,
515 postflight="scripts/postflight.ensurepip",
516 topdir="/Library/Frameworks/Python.framework",
517 source="/empty-dir",
518 required=False,
519 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000520 ),
521 ]
522
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000523 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000524
Thomas Wouters477c8d52006-05-27 19:21:47 +0000525def fatal(msg):
526 """
527 A fatal error, bail out.
528 """
529 sys.stderr.write('FATAL: ')
530 sys.stderr.write(msg)
531 sys.stderr.write('\n')
532 sys.exit(1)
533
534def fileContents(fn):
535 """
536 Return the contents of the named file
537 """
Ned Deily4a96a372013-01-29 00:08:32 -0800538 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000539
540def runCommand(commandline):
541 """
Ezio Melotti13925002011-03-16 11:05:33 +0200542 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543 unless the command fails.
544 """
545 fd = os.popen(commandline, 'r')
546 data = fd.read()
547 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000548 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800550 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000551
552 if VERBOSE:
553 sys.stdout.write(data); sys.stdout.flush()
554
555def captureCommand(commandline):
556 fd = os.popen(commandline, 'r')
557 data = fd.read()
558 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000559 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000560 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800561 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000562
563 return data
564
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000565def getTclTkVersion(configfile, versionline):
566 """
567 search Tcl or Tk configuration file for version line
568 """
569 try:
570 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300571 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000572 fatal("Framework configuration file not found: %s" % configfile)
573
574 for l in f:
575 if l.startswith(versionline):
576 f.close()
577 return l
578
579 fatal("Version variable %s not found in framework configuration file: %s"
580 % (versionline, configfile))
581
Thomas Wouters477c8d52006-05-27 19:21:47 +0000582def checkEnvironment():
583 """
584 Check that we're running on a supported system.
585 """
586
Ned Deily936533c2020-11-23 19:04:40 -0500587 if sys.version_info[0:2] < (2, 7):
588 fatal("This script must be run with Python 2.7 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000589
Thomas Wouters477c8d52006-05-27 19:21:47 +0000590 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500591 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000592
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000593 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500594 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000595
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000596 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500597 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000598 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500599 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
600 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
601 # enforce that the version of the user-installed framework also
602 # exists in the system-supplied Tcl/Tk frameworks. Time to support
603 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500604 if not internalTk():
605 frameworks = {}
606 for framework in ['Tcl', 'Tk']:
607 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
608 libfw = os.path.join('/', fwpth)
609 usrfw = os.path.join(os.getenv('HOME'), fwpth)
610 frameworks[framework] = os.readlink(libfw)
611 if not os.path.exists(libfw):
612 fatal("Please install a link to a current %s %s as %s so "
613 "the user can override the system framework."
614 % (framework, frameworks[framework], libfw))
615 if os.path.exists(usrfw):
616 fatal("Please rename %s to avoid possible dynamic load issues."
617 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000618
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500619 if frameworks['Tcl'] != frameworks['Tk']:
620 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000621
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500622 print(" -- Building with external Tcl/Tk %s frameworks"
623 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800624
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500625 # add files to check after build
626 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
627 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
628 % frameworks['Tcl'],
629 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
630 % frameworks['Tk'],
631 ]
632 else:
633 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500634 print("")
635
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000636 # Remove inherited environment variables which might influence build
637 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
638 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
639 for ev in list(os.environ):
640 for prefix in environ_var_prefixes:
641 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800642 print("INFO: deleting environment variable %s=%s" % (
643 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000644 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000645
Ned Deily4a96a372013-01-29 00:08:32 -0800646 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
647 if 'SDK_TOOLS_BIN' in os.environ:
648 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
649 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
650 # add its fixed location here if it exists
651 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
652 if os.path.isdir(OLD_DEVELOPER_TOOLS):
653 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
654 os.environ['PATH'] = base_path
655 print("Setting default PATH: %s"%(os.environ['PATH']))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000656
Thomas Wouters89f507f2006-12-13 04:49:30 +0000657def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000658 """
659 Parse arguments and update global settings.
660 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500661 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800662 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800663 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400664 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665
666 if args is None:
667 args = sys.argv[1:]
668
669 try:
670 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000671 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
672 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800673 except getopt.GetoptError:
674 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000675 sys.exit(1)
676
677 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800678 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000679 sys.exit(1)
680
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000681 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000682 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000683 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800684 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000685 sys.exit(0)
686
687 elif k in ('-d', '--build-dir'):
688 WORKDIR=v
689
690 elif k in ('--third-party',):
691 DEPSRC=v
692
693 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500694 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000695
696 elif k in ('--src-dir',):
697 SRCDIR=v
698
Ronald Oussoren1943f862009-03-30 19:39:14 +0000699 elif k in ('--dep-target', ):
700 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000701 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000702
703 elif k in ('--universal-archs', ):
704 if v in UNIVERSALOPTS:
705 UNIVERSALARCHS = v
706 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000707 if deptarget is None:
708 # Select alternate default deployment
709 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500710 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000711 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800712 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000713
Thomas Wouters477c8d52006-05-27 19:21:47 +0000714 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800715 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000716
717 SRCDIR=os.path.abspath(SRCDIR)
718 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000719 DEPSRC=os.path.abspath(DEPSRC)
720
Ned Deily04cdfa12014-06-25 13:36:14 -0700721 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000722
Ned Deily5d3febf2014-12-13 00:17:46 -0800723 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400724 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800725
726 print("-- Settings:")
727 print(" * Source directory: %s" % SRCDIR)
728 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800729 print(" * Third-party source: %s" % DEPSRC)
730 print(" * Deployment target: %s" % DEPTARGET)
731 print(" * Universal archs: %s" % str(ARCHLIST))
732 print(" * C compiler: %s" % CC)
733 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800734 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800735 print(" -- Building a Python %s framework at patch level %s"
736 % (getVersion(), getFullVersion()))
737 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000738
739def extractArchive(builddir, archiveName):
740 """
741 Extract a source archive into 'builddir'. Returns the path of the
742 extracted archive.
743
744 XXX: This function assumes that archives contain a toplevel directory
745 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700746 safe enough for almost anything we use. Unfortunately, it does not
747 work for current Tcl and Tk source releases where the basename of
748 the archive ends with "-src" but the uncompressed directory does not.
749 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000750 """
751 curdir = os.getcwd()
752 try:
753 os.chdir(builddir)
754 if archiveName.endswith('.tar.gz'):
755 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700756 if ((retval.startswith('tcl') or retval.startswith('tk'))
757 and retval.endswith('-src')):
758 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000759 if os.path.exists(retval):
760 shutil.rmtree(retval)
761 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
762
763 elif archiveName.endswith('.tar.bz2'):
764 retval = os.path.basename(archiveName[:-8])
765 if os.path.exists(retval):
766 shutil.rmtree(retval)
767 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
768
769 elif archiveName.endswith('.tar'):
770 retval = os.path.basename(archiveName[:-4])
771 if os.path.exists(retval):
772 shutil.rmtree(retval)
773 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
774
775 elif archiveName.endswith('.zip'):
776 retval = os.path.basename(archiveName[:-4])
777 if os.path.exists(retval):
778 shutil.rmtree(retval)
779 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
780
781 data = fp.read()
782 xit = fp.close()
783 if xit is not None:
784 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800785 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000786
787 return os.path.join(builddir, retval)
788
789 finally:
790 os.chdir(curdir)
791
Thomas Wouters477c8d52006-05-27 19:21:47 +0000792def downloadURL(url, fname):
793 """
794 Download the contents of the url into the file.
795 """
Ned Deily4a96a372013-01-29 00:08:32 -0800796 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000797 fpOut = open(fname, 'wb')
798 block = fpIn.read(10240)
799 try:
800 while block:
801 fpOut.write(block)
802 block = fpIn.read(10240)
803 fpIn.close()
804 fpOut.close()
805 except:
806 try:
807 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300808 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000809 pass
810
Ned Deily4a96a372013-01-29 00:08:32 -0800811def verifyThirdPartyFile(url, checksum, fname):
812 """
813 Download file from url to filename fname if it does not already exist.
814 Abort if file contents does not match supplied md5 checksum.
815 """
816 name = os.path.basename(fname)
817 if os.path.exists(fname):
818 print("Using local copy of %s"%(name,))
819 else:
820 print("Did not find local copy of %s"%(name,))
821 print("Downloading %s"%(name,))
822 downloadURL(url, fname)
823 print("Archive for %s stored as %s"%(name, fname))
824 if os.system(
825 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
826 % (shellQuote(fname), checksum) ):
827 fatal('MD5 checksum mismatch for file %s' % fname)
828
Ned Deily5d3febf2014-12-13 00:17:46 -0800829def build_universal_openssl(basedir, archList):
830 """
831 Special case build recipe for universal build of openssl.
832
833 The upstream OpenSSL build system does not directly support
834 OS X universal builds. We need to build each architecture
835 separately then lipo them together into fat libraries.
836 """
837
838 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
839 # If we are building on a 10.4.x or earlier system,
840 # unilaterally disable assembly code building to avoid the problem.
841 no_asm = int(platform.release().split(".")[0]) < 9
842
843 def build_openssl_arch(archbase, arch):
844 "Build one architecture of openssl"
845 arch_opts = {
846 "i386": ["darwin-i386-cc"],
847 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100848 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800849 "ppc": ["darwin-ppc-cc"],
850 "ppc64": ["darwin64-ppc-cc"],
851 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400852
853 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
854 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
855 # building on our 10.6 gcc-4.2 environment. There have been other
856 # reports of projects running into this when using older compilers.
857 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
858 # building for 10.6.
859 if getDeptargetTuple() == (10, 6):
860 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
861
Ned Deily5d3febf2014-12-13 00:17:46 -0800862 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800863 "no-idea",
864 "no-mdc2",
865 "no-rc5",
866 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800867 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800868 # "enable-unit-test",
869 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800870 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400871 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800872 ]
873 if no_asm:
874 configure_opts.append("no-asm")
875 runCommand(" ".join(["perl", "Configure"]
876 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500877 runCommand("make depend")
878 runCommand("make all")
879 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800880 # runCommand("make test")
881 return
882
883 srcdir = os.getcwd()
884 universalbase = os.path.join(srcdir, "..",
885 os.path.basename(srcdir) + "-universal")
886 os.mkdir(universalbase)
887 archbasefws = []
888 for arch in archList:
889 # fresh copy of the source tree
890 archsrc = os.path.join(universalbase, arch, "src")
891 shutil.copytree(srcdir, archsrc, symlinks=True)
892 # install base for this arch
893 archbase = os.path.join(universalbase, arch, "root")
894 os.mkdir(archbase)
895 # Python framework base within install_prefix:
896 # the build will install into this framework..
897 # This is to ensure that the resulting shared libs have
898 # the desired real install paths built into them.
899 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
900
901 # build one architecture
902 os.chdir(archsrc)
903 build_openssl_arch(archbase, arch)
904 os.chdir(srcdir)
905 archbasefws.append(archbasefw)
906
907 # copy arch-independent files from last build into the basedir framework
908 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
909 shutil.copytree(
910 os.path.join(archbasefw, "include", "openssl"),
911 os.path.join(basefw, "include", "openssl")
912 )
913
914 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
915 "SHLIB_VERSION_NUMBER")
916 # e.g. -> "1.0.0"
917 libcrypto = "libcrypto.dylib"
918 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
919 # e.g. -> "libcrypto.1.0.0.dylib"
920 libssl = "libssl.dylib"
921 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
922 # e.g. -> "libssl.1.0.0.dylib"
923
924 try:
925 os.mkdir(os.path.join(basefw, "lib"))
926 except OSError:
927 pass
928
929 # merge the individual arch-dependent shared libs into a fat shared lib
930 archbasefws.insert(0, basefw)
931 for (lib_unversioned, lib_versioned) in [
932 (libcrypto, libcrypto_versioned),
933 (libssl, libssl_versioned)
934 ]:
935 runCommand("lipo -create -output " +
936 " ".join(shellQuote(
937 os.path.join(fw, "lib", lib_versioned))
938 for fw in archbasefws))
939 # and create an unversioned symlink of it
940 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
941
942 # Create links in the temp include and lib dirs that will be injected
943 # into the Python build so that setup.py can find them while building
944 # and the versioned links so that the setup.py post-build import test
945 # does not fail.
946 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
947 for fn in [
948 ["include", "openssl"],
949 ["lib", libcrypto],
950 ["lib", libssl],
951 ["lib", libcrypto_versioned],
952 ["lib", libssl_versioned],
953 ]:
954 os.symlink(
955 os.path.join(relative_path, *fn),
956 os.path.join(basedir, "usr", "local", *fn)
957 )
958
959 return
960
Thomas Wouters477c8d52006-05-27 19:21:47 +0000961def buildRecipe(recipe, basedir, archList):
962 """
963 Build software using a recipe. This function does the
964 'configure;make;make install' dance for C software, with a possibility
965 to customize this process, basically a poor-mans DarwinPorts.
966 """
967 curdir = os.getcwd()
968
969 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800970 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000971 url = recipe['url']
972 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800973 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000974 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
975 shellQuote(basedir)))
976
977 archiveName = os.path.split(url)[-1]
978 sourceArchive = os.path.join(DEPSRC, archiveName)
979
980 if not os.path.exists(DEPSRC):
981 os.mkdir(DEPSRC)
982
Ned Deily4a96a372013-01-29 00:08:32 -0800983 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
984 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000985 buildDir=os.path.join(WORKDIR, '_bld')
986 if not os.path.exists(buildDir):
987 os.mkdir(buildDir)
988
989 workDir = extractArchive(buildDir, sourceArchive)
990 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000991
Ned Deily4a96a372013-01-29 00:08:32 -0800992 for patch in recipe.get('patches', ()):
993 if isinstance(patch, tuple):
994 url, checksum = patch
995 fn = os.path.join(DEPSRC, os.path.basename(url))
996 verifyThirdPartyFile(url, checksum, fn)
997 else:
998 # patch is a file in the source directory
999 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001000 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1001 shellQuote(fn),))
1002
Ned Deily4a96a372013-01-29 00:08:32 -08001003 for patchscript in recipe.get('patchscripts', ()):
1004 if isinstance(patchscript, tuple):
1005 url, checksum = patchscript
1006 fn = os.path.join(DEPSRC, os.path.basename(url))
1007 verifyThirdPartyFile(url, checksum, fn)
1008 else:
1009 # patch is a file in the source directory
1010 fn = os.path.join(curdir, patchscript)
1011 if fn.endswith('.bz2'):
1012 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1013 fn = fn[:-4]
1014 runCommand('sh %s' % shellQuote(fn))
1015 os.unlink(fn)
1016
Ned Deily94764b22013-10-27 19:49:29 -07001017 if 'buildDir' in recipe:
1018 os.chdir(recipe['buildDir'])
1019
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001020 if configure is not None:
1021 configure_args = [
1022 "--prefix=/usr/local",
1023 "--enable-static",
1024 "--disable-shared",
1025 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1026 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001027
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001028 if 'configure_pre' in recipe:
1029 args = list(recipe['configure_pre'])
1030 if '--disable-static' in args:
1031 configure_args.remove('--enable-static')
1032 if '--enable-shared' in args:
1033 configure_args.remove('--disable-shared')
1034 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001035
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001036 if recipe.get('useLDFlags', 1):
1037 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001038 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001039 "-I%s/usr/local/include"%(
1040 recipe.get('extra_cflags', ''),
1041 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001042 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001043 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001044 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001045 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001046 shellQuote(basedir)[1:-1],
1047 ' -arch '.join(archList)),
1048 ])
1049 else:
1050 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001051 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001052 "-I%s/usr/local/include"%(
1053 recipe.get('extra_cflags', ''),
1054 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001055 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001056 shellQuote(basedir)[1:-1],),
1057 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001058
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001059 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001060 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001061
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001062 configure_args.insert(0, configure)
1063 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001064
Ned Deily4a96a372013-01-29 00:08:32 -08001065 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001066 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067
Ned Deily5d3febf2014-12-13 00:17:46 -08001068 if buildrecipe is not None:
1069 # call special-case build recipe, e.g. for openssl
1070 buildrecipe(basedir, archList)
1071
1072 if install is not None:
1073 print("Running install for %s"%(name,))
1074 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001075
Ned Deily4a96a372013-01-29 00:08:32 -08001076 print("Done %s"%(name,))
1077 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001078
1079 os.chdir(curdir)
1080
1081def buildLibraries():
1082 """
1083 Build our dependencies into $WORKDIR/libraries/usr/local
1084 """
Ned Deily4a96a372013-01-29 00:08:32 -08001085 print("")
1086 print("Building required libraries")
1087 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001088 universal = os.path.join(WORKDIR, 'libraries')
1089 os.mkdir(universal)
1090 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1091 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1092
Ronald Oussoren1943f862009-03-30 19:39:14 +00001093 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001094 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001095
1096
1097
1098def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001099 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001100 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001101 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001102 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001103 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001104 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001105 curDir = os.getcwd()
1106 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001107 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001108
1109 # Search third-party source directory for a pre-built version of the docs.
1110 # Use the naming convention of the docs.python.org html downloads:
1111 # python-3.9.0b1-docs-html.tar.bz2
1112 doctarfiles = [ f for f in os.listdir(DEPSRC)
1113 if f.startswith('python-'+getFullVersion())
1114 if f.endswith('-docs-html.tar.bz2') ]
1115 if doctarfiles:
1116 doctarfile = doctarfiles[0]
1117 if not os.path.exists('build'):
1118 os.mkdir('build')
1119 # if build directory existed, it was emptied by make clean, above
1120 os.chdir('build')
1121 # Extract the first archive found for this version into build
1122 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1123 # see if tar extracted a directory ending in -docs-html
1124 archivefiles = [ f for f in os.listdir('.')
1125 if f.endswith('-docs-html')
1126 if os.path.isdir(f) ]
1127 if archivefiles:
1128 archivefile = archivefiles[0]
1129 # make it our 'Docs/build/html' directory
1130 print(' -- using pre-built python documentation from %s'%archivefile)
1131 os.rename(archivefile, 'html')
1132 os.chdir(buildDir)
1133
1134 htmlDir = os.path.join('build', 'html')
1135 if not os.path.exists(htmlDir):
1136 # Create virtual environment for docs builds with blurb and sphinx
1137 runCommand('make venv')
Ned Deily63fc55b2020-06-08 03:52:43 -04001138 runCommand('make html PYTHON=venv/bin/python')
1139 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001140 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001141
1142
1143def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001144 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145
1146 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1147 rootDir = os.path.join(WORKDIR, '_root')
1148
1149 if os.path.exists(buildDir):
1150 shutil.rmtree(buildDir)
1151 if os.path.exists(rootDir):
1152 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001153 os.makedirs(buildDir)
1154 os.makedirs(rootDir)
1155 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001156 curdir = os.getcwd()
1157 os.chdir(buildDir)
1158
Thomas Wouters477c8d52006-05-27 19:21:47 +00001159 # Extract the version from the configure file, needed to calculate
1160 # several paths.
1161 version = getVersion()
1162
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001163 # Since the extra libs are not in their installed framework location
1164 # during the build, augment the library path so that the interpreter
1165 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001166
Ned Deily4a96a372013-01-29 00:08:32 -08001167 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001168 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001169 "--with-universal-archs=%s "
1170 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001171 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001172 "%s "
1173 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001174 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001175 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001176 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001177 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001178 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001179 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001180 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001181 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1182 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001183 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1184 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1185 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1186 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001187 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001188 shellQuote(WORKDIR)[1:-1]))
1189
Ned Deily1931e642020-06-25 04:51:46 -04001190 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1191 # environment variables are no longer automatically inherited
1192 # by child processes from their parents. We used to just set
1193 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1194 # in build-installer.py's process environment and it was
1195 # passed through the make utility into the environment of
1196 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1197 # the existing RUNSHARED configuration value when we call
1198 # make for extension module builds.
1199
1200 runshared_for_make = "".join([
1201 " RUNSHARED=",
1202 "'",
1203 grepValue("Makefile", "RUNSHARED"),
1204 ' DYLD_LIBRARY_PATH=',
1205 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1206 "'" ])
1207
Ned Deilyb364d9f2017-07-24 04:58:43 -04001208 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1209 # and, if defined, append its value to the make command. This allows
1210 # us to pass in version control tags, like GITTAG, to a build from a
1211 # tarball rather than from a vcs checkout, thus eliminating the need
1212 # to have a working copy of the vcs program on the build machine.
1213 #
1214 # A typical use might be:
1215 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1216 # GITVERSION='echo 123456789a' \
1217 # GITTAG='echo v3.6.0' \
1218 # GITBRANCH='echo 3.6'"
1219
1220 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1221 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001222 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001223 else:
Ned Deily1931e642020-06-25 04:51:46 -04001224 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001225 print("Running " + make_cmd)
1226 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001227
Ned Deily1931e642020-06-25 04:51:46 -04001228 make_cmd = "make install DESTDIR=%s %s"%(
1229 shellQuote(rootDir),
1230 runshared_for_make)
1231 print("Running " + make_cmd)
1232 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001233
Ned Deily1931e642020-06-25 04:51:46 -04001234 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1235 shellQuote(rootDir),
1236 runshared_for_make)
1237 print("Running " + make_cmd)
1238 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001239
Ned Deily4a96a372013-01-29 00:08:32 -08001240 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001241 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001242 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001243 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001244 'Python.framework', 'Versions', getVersion(), 'lib')
1245 fw_lib_dir = os.path.join(
1246 WORKDIR, '_root', 'Library', 'Frameworks',
1247 'Python.framework', 'Versions', getVersion(), 'lib')
1248 if internalTk():
1249 # move Tcl and Tk pkgconfig files
1250 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1251 shellQuote(build_lib_dir),
1252 shellQuote(fw_lib_dir) ))
1253 runCommand("rm -r %s/pkgconfig"%(
1254 shellQuote(build_lib_dir), ))
1255 runCommand("mv %s/* %s"%(
1256 shellQuote(build_lib_dir),
1257 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001258
Ned Deilydde4f632016-09-12 09:39:23 -04001259 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1260 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1261 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1262 # create directory for OpenSSL certificates
1263 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1264 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001265
Ned Deily4a96a372013-01-29 00:08:32 -08001266 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001267 gid = grp.getgrnam('admin').gr_gid
1268
Ned Deily4a96a372013-01-29 00:08:32 -08001269 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001270 for dirpath, dirnames, filenames in os.walk(frmDir):
1271 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001272 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001273 os.chown(os.path.join(dirpath, dn), -1, gid)
1274
Thomas Wouters477c8d52006-05-27 19:21:47 +00001275 for fn in filenames:
1276 if os.path.islink(fn):
1277 continue
1278
1279 # "chmod g+w $fn"
1280 p = os.path.join(dirpath, fn)
1281 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001282 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1283 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001284
Ned Deily4a96a372013-01-29 00:08:32 -08001285 if fn in EXPECTED_SHARED_LIBS:
1286 # check to see that this file was linked with the
1287 # expected library path and version
1288 data = captureCommand("otool -L %s" % shellQuote(p))
1289 for sl in EXPECTED_SHARED_LIBS[fn]:
1290 if ("\t%s " % sl) not in data:
1291 print("Expected shared lib %s was not linked with %s"
1292 % (sl, p))
1293 shared_lib_error = True
1294
1295 if shared_lib_error:
1296 fatal("Unexpected shared library errors.")
1297
Ned Deilye59e4c52011-01-29 18:56:28 +00001298 if PYTHON_3:
1299 LDVERSION=None
1300 VERSION=None
1301 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001302
Ned Deilye59e4c52011-01-29 18:56:28 +00001303 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001304 for ln in fp:
1305 if ln.startswith('VERSION='):
1306 VERSION=ln.split()[1]
1307 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001308 ABIFLAGS=ln.split()
1309 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001310 if ln.startswith('LDVERSION='):
1311 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001312 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001313
Ned Deilye59e4c52011-01-29 18:56:28 +00001314 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1315 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1316 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001317 if getVersionMajorMinor() >= (3, 6):
1318 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001319 else:
1320 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001321
Thomas Wouters477c8d52006-05-27 19:21:47 +00001322 # We added some directories to the search path during the configure
1323 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001324 # the end-users system. Also remove the directories from _sysconfigdata.py
1325 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001326
Ned Deilya4f6b002013-10-25 00:47:38 -07001327 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1328 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1329
Ned Deilya4f6b002013-10-25 00:47:38 -07001330 # fix Makefile
1331 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1332 fp = open(path, 'r')
1333 data = fp.read()
1334 fp.close()
1335
1336 for p in (include_path, lib_path):
1337 data = data.replace(" " + p, '')
1338 data = data.replace(p + " ", '')
1339
1340 fp = open(path, 'w')
1341 fp.write(data)
1342 fp.close()
1343
Ned Deily652bad42016-08-15 14:37:14 -04001344 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001345 #
1346 # TODO: make this more robust! test_sysconfig_module of
1347 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1348 # the output from get_config_var in both sysconfig and
1349 # distutils.sysconfig is exactly the same for both CFLAGS and
1350 # LDFLAGS. The fixing up is now complicated by the pretty
1351 # printing in _sysconfigdata.py. Also, we are using the
1352 # pprint from the Python running the installer build which
1353 # may not cosmetically format the same as the pprint in the Python
1354 # being built (and which is used to originally generate
1355 # _sysconfigdata.py).
1356
1357 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001358 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001359 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001360 path = os.path.join(path_to_lib,
1361 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001362 else:
1363 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1364 fp = open(path, 'r')
1365 data = fp.read()
1366 fp.close()
1367 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001368 if RUNNING_ON_PYTHON2:
1369 exec(data)
1370 else:
1371 g_dict = {}
1372 l_dict = {}
1373 exec(data, g_dict, l_dict)
1374 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001375 vars = {}
1376 for k, v in build_time_vars.items():
1377 if type(v) == type(''):
1378 for p in (include_path, lib_path):
1379 v = v.replace(' ' + p, '')
1380 v = v.replace(p + ' ', '')
1381 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001382
Ned Deily652bad42016-08-15 14:37:14 -04001383 fp = open(path, 'w')
1384 # duplicated from sysconfig._generate_posix_vars()
1385 fp.write('# system configuration generated and used by'
1386 ' the sysconfig module\n')
1387 fp.write('build_time_vars = ')
1388 pprint.pprint(vars, stream=fp)
1389 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001390
1391 # Add symlinks in /usr/local/bin, using relative links
1392 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1393 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1394 'Python.framework', 'Versions', version, 'bin')
1395 if os.path.exists(usr_local_bin):
1396 shutil.rmtree(usr_local_bin)
1397 os.makedirs(usr_local_bin)
1398 for fn in os.listdir(
1399 os.path.join(frmDir, 'Versions', version, 'bin')):
1400 os.symlink(os.path.join(to_framework, fn),
1401 os.path.join(usr_local_bin, fn))
1402
1403 os.chdir(curdir)
1404
Thomas Wouters477c8d52006-05-27 19:21:47 +00001405def patchFile(inPath, outPath):
1406 data = fileContents(inPath)
1407 data = data.replace('$FULL_VERSION', getFullVersion())
1408 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001409 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001410 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001411 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001412 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001413
1414 # This one is not handy as a template variable
1415 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001416 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001417 fp.write(data)
1418 fp.close()
1419
1420def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001421 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001422 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001423 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001424 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001425 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001426 fp.write(data)
1427 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001428 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001429
1430
1431
1432def packageFromRecipe(targetDir, recipe):
1433 curdir = os.getcwd()
1434 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001435 # The major version (such as 2.5) is included in the package name
1436 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001437 # common.
1438 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001439 srcdir = recipe.get('source')
1440 pkgroot = recipe.get('topdir', srcdir)
1441 postflight = recipe.get('postflight')
1442 readme = textwrap.dedent(recipe['readme'])
1443 isRequired = recipe.get('required', True)
1444
Ned Deily4a96a372013-01-29 00:08:32 -08001445 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001446
1447 # Substitute some variables
1448 textvars = dict(
1449 VER=getVersion(),
1450 FULLVER=getFullVersion(),
1451 )
1452 readme = readme % textvars
1453
1454 if pkgroot is not None:
1455 pkgroot = pkgroot % textvars
1456 else:
1457 pkgroot = '/'
1458
1459 if srcdir is not None:
1460 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1461 srcdir = srcdir % textvars
1462
1463 if postflight is not None:
1464 postflight = os.path.abspath(postflight)
1465
1466 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1467 os.makedirs(packageContents)
1468
1469 if srcdir is not None:
1470 os.chdir(srcdir)
1471 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1472 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1473 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1474
1475 fn = os.path.join(packageContents, 'PkgInfo')
1476 fp = open(fn, 'w')
1477 fp.write('pmkrpkg1')
1478 fp.close()
1479
1480 rsrcDir = os.path.join(packageContents, "Resources")
1481 os.mkdir(rsrcDir)
1482 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1483 fp.write(readme)
1484 fp.close()
1485
1486 if postflight is not None:
1487 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1488
1489 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001490 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001491 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001492 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1493 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1494 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001495 CFBundleShortVersionString=vers,
1496 IFMajorVersion=major,
1497 IFMinorVersion=minor,
1498 IFPkgFormatVersion=0.10000000149011612,
1499 IFPkgFlagAllowBackRev=False,
1500 IFPkgFlagAuthorizationAction="RootAuthorization",
1501 IFPkgFlagDefaultLocation=pkgroot,
1502 IFPkgFlagFollowLinks=True,
1503 IFPkgFlagInstallFat=True,
1504 IFPkgFlagIsRequired=isRequired,
1505 IFPkgFlagOverwritePermissions=False,
1506 IFPkgFlagRelocatable=False,
1507 IFPkgFlagRestartAction="NoRestart",
1508 IFPkgFlagRootVolumeOnly=True,
1509 IFPkgFlagUpdateInstalledLangauges=False,
1510 )
1511 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1512
Ned Deily1931e642020-06-25 04:51:46 -04001513 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001514 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001515 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001516 IFPkgDescriptionVersion=vers,
1517 )
1518 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1519
1520 finally:
1521 os.chdir(curdir)
1522
1523
1524def makeMpkgPlist(path):
1525
1526 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001527 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001528
Ned Deily1931e642020-06-25 04:51:46 -04001529 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001530 CFBundleGetInfoString="Python %s"%(vers,),
1531 CFBundleIdentifier='org.python.Python',
1532 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001533 CFBundleShortVersionString=vers,
1534 IFMajorVersion=major,
1535 IFMinorVersion=minor,
1536 IFPkgFlagComponentDirectory="Contents/Packages",
1537 IFPkgFlagPackageList=[
1538 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001539 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001540 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001541 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001542 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001543 ],
1544 IFPkgFormatVersion=0.10000000149011612,
1545 IFPkgFlagBackgroundScaling="proportional",
1546 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001547 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001548 )
1549
1550 writePlist(pl, path)
1551
1552
1553def buildInstaller():
1554
1555 # Zap all compiled files
1556 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1557 for fn in filenames:
1558 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1559 os.unlink(os.path.join(dirpath, fn))
1560
1561 outdir = os.path.join(WORKDIR, 'installer')
1562 if os.path.exists(outdir):
1563 shutil.rmtree(outdir)
1564 os.mkdir(outdir)
1565
Ronald Oussoren1943f862009-03-30 19:39:14 +00001566 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001567 pkgcontents = os.path.join(pkgroot, 'Packages')
1568 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001569 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001570 packageFromRecipe(pkgcontents, recipe)
1571
1572 rsrcDir = os.path.join(pkgroot, 'Resources')
1573
1574 fn = os.path.join(pkgroot, 'PkgInfo')
1575 fp = open(fn, 'w')
1576 fp.write('pmkrpkg1')
1577 fp.close()
1578
1579 os.mkdir(rsrcDir)
1580
1581 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001582 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001583 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001584 IFPkgDescriptionVersion=getVersion(),
1585 )
1586
1587 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1588 for fn in os.listdir('resources'):
1589 if fn == '.svn': continue
1590 if fn.endswith('.jpg'):
1591 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1592 else:
1593 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1594
Thomas Wouters477c8d52006-05-27 19:21:47 +00001595
1596def installSize(clear=False, _saved=[]):
1597 if clear:
1598 del _saved[:]
1599 if not _saved:
1600 data = captureCommand("du -ks %s"%(
1601 shellQuote(os.path.join(WORKDIR, '_root'))))
1602 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1603 return _saved[0]
1604
1605
1606def buildDMG():
1607 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001608 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001609 """
1610 outdir = os.path.join(WORKDIR, 'diskimage')
1611 if os.path.exists(outdir):
1612 shutil.rmtree(outdir)
1613
Erlend Egeberg Aaslandc94ee132021-01-04 05:48:19 +01001614 # We used to use the deployment target as the last characters of the
Ned Deily936533c2020-11-23 19:04:40 -05001615 # installer file name. With the introduction of weaklinked installer
1616 # variants, we may have two variants with the same file name, i.e.
1617 # both ending in '10.9'. To avoid this, we now use the major/minor
1618 # version numbers of the macOS version we are building on, i.e.
1619 # '10.9' as before for 10.9+ variant, '11.0' for universal2 11.0-.
1620 # it's not ideal but should cause the least disruption to packaging
1621 # workflows.
1622 build_system_version = '.'.join(platform.mac_ver()[0].split('.')[0:2])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001623 imagepath = os.path.join(outdir,
Ned Deily936533c2020-11-23 19:04:40 -05001624 'python-%s-macosx%s'%(getFullVersion(),build_system_version))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001625 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001626 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001627 imagepath = imagepath + '.dmg'
1628
1629 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001630
1631 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1632 # when hdiutil create fails with "Resource busy". For now, just retry
1633 # the create a few times and hope that it eventually works.
1634
Ronald Oussoren1943f862009-03-30 19:39:14 +00001635 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001636 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001637 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001638 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001639 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001640 for i in range(5):
1641 fd = os.popen(cmd, 'r')
1642 data = fd.read()
1643 xit = fd.close()
1644 if not xit:
1645 break
1646 sys.stdout.write(data)
1647 print(" -- retrying hdiutil create")
1648 time.sleep(5)
1649 else:
cclaussd3371692019-06-03 05:19:44 +02001650 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001651
1652 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1653 os.mkdir(os.path.join(WORKDIR, "mnt"))
1654 runCommand("hdiutil attach %s -mountroot %s"%(
1655 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1656
1657 # Custom icon for the DMG, shown when the DMG is mounted.
1658 shutil.copy("../Icons/Disk Image.icns",
1659 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001660 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001661 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1662
1663 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1664
1665 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1666 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1667 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1668 setIcon(imagepath, "../Icons/Disk Image.icns")
1669
1670 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001671
1672 return imagepath
1673
1674
1675def setIcon(filePath, icnsPath):
1676 """
1677 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001678 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001679
Ronald Oussoren70050672010-04-30 15:00:26 +00001680 dirPath = os.path.normpath(os.path.dirname(__file__))
1681 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001682 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1683 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1684 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001685 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1686 if not os.path.exists(appPath):
1687 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001688 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1689 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001690
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001691 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1692 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001693
1694def main():
1695 # First parse options and check if we can perform our work
1696 parseOptions()
1697 checkEnvironment()
1698
Ronald Oussoren1943f862009-03-30 19:39:14 +00001699 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001700 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001701 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001702
1703 if os.path.exists(WORKDIR):
1704 shutil.rmtree(WORKDIR)
1705 os.mkdir(WORKDIR)
1706
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001707 os.environ['LC_ALL'] = 'C'
1708
Thomas Wouters477c8d52006-05-27 19:21:47 +00001709 # Then build third-party libraries such as sleepycat DB4.
1710 buildLibraries()
1711
1712 # Now build python itself
1713 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001714
1715 # And then build the documentation
1716 # Remove the Deployment Target from the shell
1717 # environment, it's no longer needed and
1718 # an unexpected build target can cause problems
1719 # when Sphinx and its dependencies need to
1720 # be (re-)installed.
1721 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001722 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001723
1724
1725 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001726 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001727 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001728 fn = os.path.join(folder, "License.rtf")
1729 patchFile("resources/License.rtf", fn)
1730 fn = os.path.join(folder, "ReadMe.rtf")
1731 patchFile("resources/ReadMe.rtf", fn)
1732 fn = os.path.join(folder, "Update Shell Profile.command")
1733 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001734 fn = os.path.join(folder, "Install Certificates.command")
1735 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001736 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001737 setIcon(folder, "../Icons/Python Folder.icns")
1738
1739 # Create the installer
1740 buildInstaller()
1741
1742 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001743 patchFile('resources/ReadMe.rtf',
1744 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001745
1746 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001747 patchFile('resources/License.rtf',
1748 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001749
1750 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001751 fp.write("# BUILD INFO\n")
1752 fp.write("# Date: %s\n" % time.ctime())
1753 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001754 fp.close()
1755
Thomas Wouters477c8d52006-05-27 19:21:47 +00001756 # And copy it to a DMG
1757 buildDMG()
1758
Thomas Wouters477c8d52006-05-27 19:21:47 +00001759if __name__ == "__main__":
1760 main()