blob: 60174eb6962baebfda14543c257cf9e721bfa4fd [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++'),
Ronald Oussoren90d52392021-05-03 03:29:03 +0200164 '10.7': ('gcc', 'g++'),
165 '10.8': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700166 }
Ronald Oussoren90d52392021-05-03 03:29:03 +0200167 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000168
Ned Deily04cdfa12014-06-25 13:36:14 -0700169CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000170
Ned Deily5d3febf2014-12-13 00:17:46 -0800171PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000172
Thomas Wouters89f507f2006-12-13 04:49:30 +0000173USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000174 Usage: build_python [options]
175
176 Options:
177 -? or -h: Show this message
178 -b DIR
179 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
180 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500181 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000182 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500183 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000184 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000185""")% globals()
186
Ned Deily4a96a372013-01-29 00:08:32 -0800187# Dict of object file names with shared library names to check after building.
188# This is to ensure that we ended up dynamically linking with the shared
189# library paths and versions we expected. For example:
190# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
191# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
192# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
193EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000194
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500195# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400196# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500197def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400198 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500199
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100200# Do we use 8.6.8 when building our own copy
201# of Tcl/Tk or a modern version.
202# We use the old version when buildin on
203# old versions of macOS due to build issues.
204def useOldTk():
205 return getBuildTuple() < (10, 15)
206
Ronald Oussoren41761932020-11-08 10:05:27 +0100207
208def tweak_tcl_build(basedir, archList):
209 with open("Makefile", "r") as fp:
210 contents = fp.readlines()
211
212 # For reasons I don't understand the tcl configure script
213 # decides that some stdlib symbols aren't present, before
214 # deciding that strtod is broken.
215 new_contents = []
216 for line in contents:
217 if line.startswith("COMPAT_OBJS"):
218 # note: the space before strtod.o is intentional,
219 # the detection of a broken strtod results in
220 # "fixstrod.o" on this line.
221 for nm in ("strstr.o", "strtoul.o", " strtod.o"):
222 line = line.replace(nm, "")
223 new_contents.append(line)
224
225 with open("Makefile", "w") as fp:
226 fp.writelines(new_contents)
227
Ned Deily5d3febf2014-12-13 00:17:46 -0800228# List of names of third party software built with this installer.
229# The names will be inserted into the rtf version of the License.
230THIRD_PARTY_LIBS = []
231
Thomas Wouters477c8d52006-05-27 19:21:47 +0000232# Instructions for building libraries that are necessary for building a
233# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000234# [The recipes are defined here for convenience but instantiated later after
235# command line options have been processed.]
236def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000237 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000238
Ned Deily04cdfa12014-06-25 13:36:14 -0700239 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800240
Ned Deilydde4f632016-09-12 09:39:23 -0400241 # Since Apple removed the header files for the deprecated system
242 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
243 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800244
Ned Deilydde4f632016-09-12 09:39:23 -0400245 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800246 dict(
Christian Heimesa54fc682021-03-30 02:00:34 +0200247 name="OpenSSL 1.1.1k",
248 url="https://www.openssl.org/source/openssl-1.1.1k.tar.gz",
249 checksum='c4e7d95f782b08116afa27b30393dd27',
Ned Deily5d3febf2014-12-13 00:17:46 -0800250 buildrecipe=build_universal_openssl,
251 configure=None,
252 install=None,
253 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400254 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800255
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500256 if internalTk():
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100257 if useOldTk():
258 tcl_tk_ver='8.6.8'
259 tcl_checksum='81656d3367af032e0ae6157eff134f89'
260
261 tk_checksum='5e0faecba458ee1386078fb228d008ba'
262 tk_patches = ['tk868_on_10_8_10_9.patch']
263
264 else:
Ned Deilya38e04b2021-01-04 04:43:11 -0500265 tcl_tk_ver='8.6.11'
266 tcl_checksum='8a4c004f48984a03a7747e9ba06e4da4'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100267
Ned Deilya38e04b2021-01-04 04:43:11 -0500268 tk_checksum='c7ee71a2d05bba78dfffd76528dc17c6'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100269 tk_patches = [ ]
270
271
Ned Deily5b3582c2013-10-25 00:41:46 -0700272 result.extend([
273 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100274 name="Tcl %s"%(tcl_tk_ver,),
275 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl%s-src.tar.gz"%(tcl_tk_ver,),
276 checksum=tcl_checksum,
Ned Deily5b3582c2013-10-25 00:41:46 -0700277 buildDir="unix",
278 configure_pre=[
279 '--enable-shared',
280 '--enable-threads',
281 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
282 ],
283 useLDFlags=False,
Ronald Oussoren41761932020-11-08 10:05:27 +0100284 buildrecipe=tweak_tcl_build,
Ned Deily5b3582c2013-10-25 00:41:46 -0700285 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
286 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500287 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700288 },
289 ),
290 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100291 name="Tk %s"%(tcl_tk_ver,),
292 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk%s-src.tar.gz"%(tcl_tk_ver,),
293 checksum=tk_checksum,
294 patches=tk_patches,
Ned Deily5b3582c2013-10-25 00:41:46 -0700295 buildDir="unix",
296 configure_pre=[
297 '--enable-aqua',
298 '--enable-shared',
299 '--enable-threads',
300 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
301 ],
302 useLDFlags=False,
303 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'%{
304 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500305 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
306 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700307 },
308 ),
309 ])
310
Ned Deilyed730102014-11-14 18:55:05 -0800311 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800312 result.extend([
313 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500314 name="XZ 5.2.3",
315 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
316 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800317 configure_pre=[
318 '--disable-dependency-tracking',
319 ]
320 ),
321 ])
322
323 result.extend([
324 dict(
325 name="NCurses 5.9",
326 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
327 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
328 configure_pre=[
329 "--enable-widec",
330 "--without-cxx",
331 "--without-cxx-binding",
332 "--without-ada",
333 "--without-curses-h",
334 "--enable-shared",
335 "--with-shared",
336 "--without-debug",
337 "--without-normal",
338 "--without-tests",
339 "--without-manpages",
340 "--datadir=/usr/share",
341 "--sysconfdir=/etc",
342 "--sharedstatedir=/usr/com",
343 "--with-terminfo-dirs=/usr/share/terminfo",
344 "--with-default-terminfo-dir=/usr/share/terminfo",
345 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
346 ],
347 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200348 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800349 "f54bf02a349f96a7c4f0d00922f3a0d4"),
350 ],
351 useLDFlags=False,
352 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
353 shellQuote(os.path.join(WORKDIR, 'libraries')),
354 shellQuote(os.path.join(WORKDIR, 'libraries')),
355 getVersion(),
356 ),
357 ),
358 dict(
Erlend Egeberg Aaslandce827812021-04-27 19:19:14 +0200359 name="SQLite 3.35.5",
360 url="https://sqlite.org/2021/sqlite-autoconf-3350500.tar.gz",
361 checksum='d1d1aba394c8e0443077dc9f1a681bb8',
Ned Deily4a96a372013-01-29 00:08:32 -0800362 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700363 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800364 '-DSQLITE_ENABLE_FTS4 '
365 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500366 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800367 '-DSQLITE_ENABLE_RTREE '
Erlend Egeberg Aasland555cbbe2021-05-03 02:37:35 +0200368 '-DSQLITE_OMIT_AUTOINIT '
Ned Deily4a96a372013-01-29 00:08:32 -0800369 '-DSQLITE_TCL=0 '
370 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
371 configure_pre=[
372 '--enable-threadsafe',
373 '--enable-shared=no',
374 '--enable-static=yes',
375 '--disable-readline',
376 '--disable-dependency-tracking',
377 ]
378 ),
379 ])
380
Ned Deily04cdfa12014-06-25 13:36:14 -0700381 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000382 result.extend([
383 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000384 name="Bzip2 1.0.6",
385 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
386 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000387 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500388 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800389 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000390 shellQuote(os.path.join(WORKDIR, 'libraries')),
391 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000392 ),
393 ),
394 dict(
395 name="ZLib 1.2.3",
396 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
397 checksum='debc62758716a169df9f62e6ab2bc634',
398 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500399 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800400 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000401 shellQuote(os.path.join(WORKDIR, 'libraries')),
402 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000403 ),
404 ),
405 dict(
406 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000407 name="GNU Readline 6.1.2",
408 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
409 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000410 patchlevel='0',
411 patches=[
412 # The readline maintainers don't do actual micro releases, but
413 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800414 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
415 'c642f2e84d820884b0bf9fd176bc6c3f'),
416 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
417 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000418 ]
419 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000420 ])
421
Ned Deily4f7ff782011-01-15 05:29:12 +0000422 if not PYTHON_3:
423 result.extend([
424 dict(
425 name="Sleepycat DB 4.7.25",
426 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
427 checksum='ec2b87e833779681a0c3a814aa71359e',
428 buildDir="build_unix",
429 configure="../dist/configure",
430 configure_pre=[
431 '--includedir=/usr/local/include/db4',
432 ]
433 ),
434 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000435
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000436 return result
437
Ronald Oussorene08059e2021-05-03 05:43:52 +0200438def compilerCanOptimize():
439 """
440 Return True iff the default Xcode version can use PGO and LTO
441 """
442 # bpo-42235: The version check is pretty conservative, can be
443 # adjusted after testing
444 mac_ver = tuple(map(int, platform.mac_ver()[0].split('.')))
445 return mac_ver >= (10, 15)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000446
Thomas Wouters477c8d52006-05-27 19:21:47 +0000447# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000448def pkg_recipes():
449 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
450 result = [
451 dict(
452 name="PythonFramework",
453 long_name="Python Framework",
454 source="/Library/Frameworks/Python.framework",
455 readme="""\
456 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500457 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000458 """,
459 postflight="scripts/postflight.framework",
460 selected='selected',
461 ),
462 dict(
463 name="PythonApplications",
464 long_name="GUI Applications",
465 source="/Applications/Python %(VER)s",
466 readme="""\
467 This package installs IDLE (an interactive Python IDE),
468 Python Launcher and Build Applet (create application bundles
469 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000470
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000471 It also installs a number of examples and demos.
472 """,
473 required=False,
474 selected='selected',
475 ),
476 dict(
477 name="PythonUnixTools",
478 long_name="UNIX command-line tools",
479 source="/usr/local/bin",
480 readme="""\
481 This package installs the unix tools in /usr/local/bin for
482 compatibility with older releases of Python. This package
483 is not necessary to use Python.
484 """,
485 required=False,
486 selected='selected',
487 ),
488 dict(
489 name="PythonDocumentation",
490 long_name="Python Documentation",
491 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
492 source="/pydocs",
493 readme="""\
494 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800495 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000496 """,
497 postflight="scripts/postflight.documentation",
498 required=False,
499 selected='selected',
500 ),
501 dict(
502 name="PythonProfileChanges",
503 long_name="Shell profile updater",
504 readme="""\
505 This packages updates your shell profile to make sure that
506 the Python tools are found by your shell in preference of
507 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000508
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000509 If you don't install this package you'll have to add
510 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
511 to your PATH by hand.
512 """,
513 postflight="scripts/postflight.patch-profile",
514 topdir="/Library/Frameworks/Python.framework",
515 source="/empty-dir",
516 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800517 selected='selected',
518 ),
519 dict(
520 name="PythonInstallPip",
521 long_name="Install or upgrade pip",
522 readme="""\
523 This package installs (or upgrades from an earlier version)
524 pip, a tool for installing and managing Python packages.
525 """,
526 postflight="scripts/postflight.ensurepip",
527 topdir="/Library/Frameworks/Python.framework",
528 source="/empty-dir",
529 required=False,
530 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000531 ),
532 ]
533
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000534 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535
Thomas Wouters477c8d52006-05-27 19:21:47 +0000536def fatal(msg):
537 """
538 A fatal error, bail out.
539 """
540 sys.stderr.write('FATAL: ')
541 sys.stderr.write(msg)
542 sys.stderr.write('\n')
543 sys.exit(1)
544
545def fileContents(fn):
546 """
547 Return the contents of the named file
548 """
Ned Deily4a96a372013-01-29 00:08:32 -0800549 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000550
551def runCommand(commandline):
552 """
Ezio Melotti13925002011-03-16 11:05:33 +0200553 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000554 unless the command fails.
555 """
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 if VERBOSE:
564 sys.stdout.write(data); sys.stdout.flush()
565
566def captureCommand(commandline):
567 fd = os.popen(commandline, 'r')
568 data = fd.read()
569 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000570 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000571 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800572 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000573
574 return data
575
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000576def getTclTkVersion(configfile, versionline):
577 """
578 search Tcl or Tk configuration file for version line
579 """
580 try:
581 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300582 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000583 fatal("Framework configuration file not found: %s" % configfile)
584
585 for l in f:
586 if l.startswith(versionline):
587 f.close()
588 return l
589
590 fatal("Version variable %s not found in framework configuration file: %s"
591 % (versionline, configfile))
592
Thomas Wouters477c8d52006-05-27 19:21:47 +0000593def checkEnvironment():
594 """
595 Check that we're running on a supported system.
596 """
597
Ned Deily936533c2020-11-23 19:04:40 -0500598 if sys.version_info[0:2] < (2, 7):
599 fatal("This script must be run with Python 2.7 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000600
Thomas Wouters477c8d52006-05-27 19:21:47 +0000601 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500602 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000603
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000604 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500605 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000606
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000607 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500608 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000609 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500610 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
611 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
612 # enforce that the version of the user-installed framework also
613 # exists in the system-supplied Tcl/Tk frameworks. Time to support
614 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500615 if not internalTk():
616 frameworks = {}
617 for framework in ['Tcl', 'Tk']:
618 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
619 libfw = os.path.join('/', fwpth)
620 usrfw = os.path.join(os.getenv('HOME'), fwpth)
621 frameworks[framework] = os.readlink(libfw)
622 if not os.path.exists(libfw):
623 fatal("Please install a link to a current %s %s as %s so "
624 "the user can override the system framework."
625 % (framework, frameworks[framework], libfw))
626 if os.path.exists(usrfw):
627 fatal("Please rename %s to avoid possible dynamic load issues."
628 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000629
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500630 if frameworks['Tcl'] != frameworks['Tk']:
631 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000632
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500633 print(" -- Building with external Tcl/Tk %s frameworks"
634 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800635
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500636 # add files to check after build
637 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
638 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
639 % frameworks['Tcl'],
640 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
641 % frameworks['Tk'],
642 ]
643 else:
644 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500645 print("")
646
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000647 # Remove inherited environment variables which might influence build
648 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
649 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
650 for ev in list(os.environ):
651 for prefix in environ_var_prefixes:
652 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800653 print("INFO: deleting environment variable %s=%s" % (
654 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000655 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000656
Ned Deily4a96a372013-01-29 00:08:32 -0800657 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
658 if 'SDK_TOOLS_BIN' in os.environ:
659 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
660 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
661 # add its fixed location here if it exists
662 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
663 if os.path.isdir(OLD_DEVELOPER_TOOLS):
664 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
665 os.environ['PATH'] = base_path
666 print("Setting default PATH: %s"%(os.environ['PATH']))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000667
Thomas Wouters89f507f2006-12-13 04:49:30 +0000668def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000669 """
670 Parse arguments and update global settings.
671 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500672 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800673 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800674 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400675 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676
677 if args is None:
678 args = sys.argv[1:]
679
680 try:
681 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000682 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
683 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800684 except getopt.GetoptError:
685 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000686 sys.exit(1)
687
688 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800689 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000690 sys.exit(1)
691
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000692 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000693 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000694 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800695 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000696 sys.exit(0)
697
698 elif k in ('-d', '--build-dir'):
699 WORKDIR=v
700
701 elif k in ('--third-party',):
702 DEPSRC=v
703
704 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500705 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000706
707 elif k in ('--src-dir',):
708 SRCDIR=v
709
Ronald Oussoren1943f862009-03-30 19:39:14 +0000710 elif k in ('--dep-target', ):
711 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000712 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000713
714 elif k in ('--universal-archs', ):
715 if v in UNIVERSALOPTS:
716 UNIVERSALARCHS = v
717 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000718 if deptarget is None:
719 # Select alternate default deployment
720 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500721 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000722 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800723 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000724
Thomas Wouters477c8d52006-05-27 19:21:47 +0000725 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800726 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000727
728 SRCDIR=os.path.abspath(SRCDIR)
729 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000730 DEPSRC=os.path.abspath(DEPSRC)
731
Ned Deily04cdfa12014-06-25 13:36:14 -0700732 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000733
Ned Deily5d3febf2014-12-13 00:17:46 -0800734 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400735 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800736
737 print("-- Settings:")
738 print(" * Source directory: %s" % SRCDIR)
739 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800740 print(" * Third-party source: %s" % DEPSRC)
741 print(" * Deployment target: %s" % DEPTARGET)
742 print(" * Universal archs: %s" % str(ARCHLIST))
743 print(" * C compiler: %s" % CC)
744 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800745 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800746 print(" -- Building a Python %s framework at patch level %s"
747 % (getVersion(), getFullVersion()))
748 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000749
750def extractArchive(builddir, archiveName):
751 """
752 Extract a source archive into 'builddir'. Returns the path of the
753 extracted archive.
754
755 XXX: This function assumes that archives contain a toplevel directory
756 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700757 safe enough for almost anything we use. Unfortunately, it does not
758 work for current Tcl and Tk source releases where the basename of
759 the archive ends with "-src" but the uncompressed directory does not.
760 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000761 """
762 curdir = os.getcwd()
763 try:
764 os.chdir(builddir)
765 if archiveName.endswith('.tar.gz'):
766 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700767 if ((retval.startswith('tcl') or retval.startswith('tk'))
768 and retval.endswith('-src')):
769 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000770 if os.path.exists(retval):
771 shutil.rmtree(retval)
772 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
773
774 elif archiveName.endswith('.tar.bz2'):
775 retval = os.path.basename(archiveName[:-8])
776 if os.path.exists(retval):
777 shutil.rmtree(retval)
778 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
779
780 elif archiveName.endswith('.tar'):
781 retval = os.path.basename(archiveName[:-4])
782 if os.path.exists(retval):
783 shutil.rmtree(retval)
784 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
785
786 elif archiveName.endswith('.zip'):
787 retval = os.path.basename(archiveName[:-4])
788 if os.path.exists(retval):
789 shutil.rmtree(retval)
790 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
791
792 data = fp.read()
793 xit = fp.close()
794 if xit is not None:
795 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800796 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000797
798 return os.path.join(builddir, retval)
799
800 finally:
801 os.chdir(curdir)
802
Thomas Wouters477c8d52006-05-27 19:21:47 +0000803def downloadURL(url, fname):
804 """
805 Download the contents of the url into the file.
806 """
Ned Deily4a96a372013-01-29 00:08:32 -0800807 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000808 fpOut = open(fname, 'wb')
809 block = fpIn.read(10240)
810 try:
811 while block:
812 fpOut.write(block)
813 block = fpIn.read(10240)
814 fpIn.close()
815 fpOut.close()
816 except:
817 try:
818 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300819 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000820 pass
821
Ned Deily4a96a372013-01-29 00:08:32 -0800822def verifyThirdPartyFile(url, checksum, fname):
823 """
824 Download file from url to filename fname if it does not already exist.
825 Abort if file contents does not match supplied md5 checksum.
826 """
827 name = os.path.basename(fname)
828 if os.path.exists(fname):
829 print("Using local copy of %s"%(name,))
830 else:
831 print("Did not find local copy of %s"%(name,))
832 print("Downloading %s"%(name,))
833 downloadURL(url, fname)
834 print("Archive for %s stored as %s"%(name, fname))
835 if os.system(
836 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
837 % (shellQuote(fname), checksum) ):
838 fatal('MD5 checksum mismatch for file %s' % fname)
839
Ned Deily5d3febf2014-12-13 00:17:46 -0800840def build_universal_openssl(basedir, archList):
841 """
842 Special case build recipe for universal build of openssl.
843
844 The upstream OpenSSL build system does not directly support
845 OS X universal builds. We need to build each architecture
846 separately then lipo them together into fat libraries.
847 """
848
849 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
850 # If we are building on a 10.4.x or earlier system,
851 # unilaterally disable assembly code building to avoid the problem.
852 no_asm = int(platform.release().split(".")[0]) < 9
853
854 def build_openssl_arch(archbase, arch):
855 "Build one architecture of openssl"
856 arch_opts = {
857 "i386": ["darwin-i386-cc"],
858 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100859 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800860 "ppc": ["darwin-ppc-cc"],
861 "ppc64": ["darwin64-ppc-cc"],
862 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400863
864 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
865 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
866 # building on our 10.6 gcc-4.2 environment. There have been other
867 # reports of projects running into this when using older compilers.
868 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
869 # building for 10.6.
870 if getDeptargetTuple() == (10, 6):
871 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
872
Ned Deily5d3febf2014-12-13 00:17:46 -0800873 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800874 "no-idea",
875 "no-mdc2",
876 "no-rc5",
877 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800878 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800879 # "enable-unit-test",
880 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800881 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400882 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800883 ]
884 if no_asm:
885 configure_opts.append("no-asm")
886 runCommand(" ".join(["perl", "Configure"]
887 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500888 runCommand("make depend")
889 runCommand("make all")
890 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800891 # runCommand("make test")
892 return
893
894 srcdir = os.getcwd()
895 universalbase = os.path.join(srcdir, "..",
896 os.path.basename(srcdir) + "-universal")
897 os.mkdir(universalbase)
898 archbasefws = []
899 for arch in archList:
900 # fresh copy of the source tree
901 archsrc = os.path.join(universalbase, arch, "src")
902 shutil.copytree(srcdir, archsrc, symlinks=True)
903 # install base for this arch
904 archbase = os.path.join(universalbase, arch, "root")
905 os.mkdir(archbase)
906 # Python framework base within install_prefix:
907 # the build will install into this framework..
908 # This is to ensure that the resulting shared libs have
909 # the desired real install paths built into them.
910 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
911
912 # build one architecture
913 os.chdir(archsrc)
914 build_openssl_arch(archbase, arch)
915 os.chdir(srcdir)
916 archbasefws.append(archbasefw)
917
918 # copy arch-independent files from last build into the basedir framework
919 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
920 shutil.copytree(
921 os.path.join(archbasefw, "include", "openssl"),
922 os.path.join(basefw, "include", "openssl")
923 )
924
925 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
926 "SHLIB_VERSION_NUMBER")
927 # e.g. -> "1.0.0"
928 libcrypto = "libcrypto.dylib"
929 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
930 # e.g. -> "libcrypto.1.0.0.dylib"
931 libssl = "libssl.dylib"
932 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
933 # e.g. -> "libssl.1.0.0.dylib"
934
935 try:
936 os.mkdir(os.path.join(basefw, "lib"))
937 except OSError:
938 pass
939
940 # merge the individual arch-dependent shared libs into a fat shared lib
941 archbasefws.insert(0, basefw)
942 for (lib_unversioned, lib_versioned) in [
943 (libcrypto, libcrypto_versioned),
944 (libssl, libssl_versioned)
945 ]:
946 runCommand("lipo -create -output " +
947 " ".join(shellQuote(
948 os.path.join(fw, "lib", lib_versioned))
949 for fw in archbasefws))
950 # and create an unversioned symlink of it
951 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
952
953 # Create links in the temp include and lib dirs that will be injected
954 # into the Python build so that setup.py can find them while building
955 # and the versioned links so that the setup.py post-build import test
956 # does not fail.
957 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
958 for fn in [
959 ["include", "openssl"],
960 ["lib", libcrypto],
961 ["lib", libssl],
962 ["lib", libcrypto_versioned],
963 ["lib", libssl_versioned],
964 ]:
965 os.symlink(
966 os.path.join(relative_path, *fn),
967 os.path.join(basedir, "usr", "local", *fn)
968 )
969
970 return
971
Thomas Wouters477c8d52006-05-27 19:21:47 +0000972def buildRecipe(recipe, basedir, archList):
973 """
974 Build software using a recipe. This function does the
975 'configure;make;make install' dance for C software, with a possibility
976 to customize this process, basically a poor-mans DarwinPorts.
977 """
978 curdir = os.getcwd()
979
980 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800981 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000982 url = recipe['url']
983 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800984 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000985 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
986 shellQuote(basedir)))
987
988 archiveName = os.path.split(url)[-1]
989 sourceArchive = os.path.join(DEPSRC, archiveName)
990
991 if not os.path.exists(DEPSRC):
992 os.mkdir(DEPSRC)
993
Ned Deily4a96a372013-01-29 00:08:32 -0800994 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
995 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000996 buildDir=os.path.join(WORKDIR, '_bld')
997 if not os.path.exists(buildDir):
998 os.mkdir(buildDir)
999
1000 workDir = extractArchive(buildDir, sourceArchive)
1001 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001002
Ned Deily4a96a372013-01-29 00:08:32 -08001003 for patch in recipe.get('patches', ()):
1004 if isinstance(patch, tuple):
1005 url, checksum = patch
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, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001011 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1012 shellQuote(fn),))
1013
Ned Deily4a96a372013-01-29 00:08:32 -08001014 for patchscript in recipe.get('patchscripts', ()):
1015 if isinstance(patchscript, tuple):
1016 url, checksum = patchscript
1017 fn = os.path.join(DEPSRC, os.path.basename(url))
1018 verifyThirdPartyFile(url, checksum, fn)
1019 else:
1020 # patch is a file in the source directory
1021 fn = os.path.join(curdir, patchscript)
1022 if fn.endswith('.bz2'):
1023 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1024 fn = fn[:-4]
1025 runCommand('sh %s' % shellQuote(fn))
1026 os.unlink(fn)
1027
Ned Deily94764b22013-10-27 19:49:29 -07001028 if 'buildDir' in recipe:
1029 os.chdir(recipe['buildDir'])
1030
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001031 if configure is not None:
1032 configure_args = [
1033 "--prefix=/usr/local",
1034 "--enable-static",
1035 "--disable-shared",
1036 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1037 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001039 if 'configure_pre' in recipe:
1040 args = list(recipe['configure_pre'])
1041 if '--disable-static' in args:
1042 configure_args.remove('--enable-static')
1043 if '--enable-shared' in args:
1044 configure_args.remove('--disable-shared')
1045 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001046
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001047 if recipe.get('useLDFlags', 1):
1048 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001049 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001050 "-I%s/usr/local/include"%(
1051 recipe.get('extra_cflags', ''),
1052 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001053 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001054 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001055 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001056 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001057 shellQuote(basedir)[1:-1],
1058 ' -arch '.join(archList)),
1059 ])
1060 else:
1061 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001062 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001063 "-I%s/usr/local/include"%(
1064 recipe.get('extra_cflags', ''),
1065 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001066 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001067 shellQuote(basedir)[1:-1],),
1068 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001069
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001070 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001071 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001072
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001073 configure_args.insert(0, configure)
1074 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001075
Ned Deily4a96a372013-01-29 00:08:32 -08001076 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001077 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001078
Ned Deily5d3febf2014-12-13 00:17:46 -08001079 if buildrecipe is not None:
1080 # call special-case build recipe, e.g. for openssl
1081 buildrecipe(basedir, archList)
1082
1083 if install is not None:
1084 print("Running install for %s"%(name,))
1085 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001086
Ned Deily4a96a372013-01-29 00:08:32 -08001087 print("Done %s"%(name,))
1088 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089
1090 os.chdir(curdir)
1091
1092def buildLibraries():
1093 """
1094 Build our dependencies into $WORKDIR/libraries/usr/local
1095 """
Ned Deily4a96a372013-01-29 00:08:32 -08001096 print("")
1097 print("Building required libraries")
1098 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001099 universal = os.path.join(WORKDIR, 'libraries')
1100 os.mkdir(universal)
1101 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1102 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1103
Ronald Oussoren1943f862009-03-30 19:39:14 +00001104 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001105 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001106
1107
1108
1109def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001110 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001111 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001112 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001113 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001114 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001115 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001116 curDir = os.getcwd()
1117 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001118 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001119
1120 # Search third-party source directory for a pre-built version of the docs.
1121 # Use the naming convention of the docs.python.org html downloads:
1122 # python-3.9.0b1-docs-html.tar.bz2
1123 doctarfiles = [ f for f in os.listdir(DEPSRC)
1124 if f.startswith('python-'+getFullVersion())
1125 if f.endswith('-docs-html.tar.bz2') ]
1126 if doctarfiles:
1127 doctarfile = doctarfiles[0]
1128 if not os.path.exists('build'):
1129 os.mkdir('build')
1130 # if build directory existed, it was emptied by make clean, above
1131 os.chdir('build')
1132 # Extract the first archive found for this version into build
1133 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1134 # see if tar extracted a directory ending in -docs-html
1135 archivefiles = [ f for f in os.listdir('.')
1136 if f.endswith('-docs-html')
1137 if os.path.isdir(f) ]
1138 if archivefiles:
1139 archivefile = archivefiles[0]
1140 # make it our 'Docs/build/html' directory
1141 print(' -- using pre-built python documentation from %s'%archivefile)
1142 os.rename(archivefile, 'html')
1143 os.chdir(buildDir)
1144
1145 htmlDir = os.path.join('build', 'html')
1146 if not os.path.exists(htmlDir):
1147 # Create virtual environment for docs builds with blurb and sphinx
1148 runCommand('make venv')
Ned Deily63fc55b2020-06-08 03:52:43 -04001149 runCommand('make html PYTHON=venv/bin/python')
1150 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001151 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152
1153
1154def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001155 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001156
1157 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1158 rootDir = os.path.join(WORKDIR, '_root')
1159
1160 if os.path.exists(buildDir):
1161 shutil.rmtree(buildDir)
1162 if os.path.exists(rootDir):
1163 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001164 os.makedirs(buildDir)
1165 os.makedirs(rootDir)
1166 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001167 curdir = os.getcwd()
1168 os.chdir(buildDir)
1169
Thomas Wouters477c8d52006-05-27 19:21:47 +00001170 # Extract the version from the configure file, needed to calculate
1171 # several paths.
1172 version = getVersion()
1173
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001174 # Since the extra libs are not in their installed framework location
1175 # during the build, augment the library path so that the interpreter
1176 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001177
Ned Deily4a96a372013-01-29 00:08:32 -08001178 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001179 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001180 "--with-universal-archs=%s "
1181 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001182 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001183 "%s "
1184 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001185 "%s "
Ronald Oussorene08059e2021-05-03 05:43:52 +02001186 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001187 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001188 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001189 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001190 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001191 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001192 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001193 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1194 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001195 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1196 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1197 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1198 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussorene08059e2021-05-03 05:43:52 +02001199 (' ', "--enable-optimizations --with-lto")[compilerCanOptimize()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001200 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001201 shellQuote(WORKDIR)[1:-1]))
1202
Ned Deily1931e642020-06-25 04:51:46 -04001203 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1204 # environment variables are no longer automatically inherited
1205 # by child processes from their parents. We used to just set
1206 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1207 # in build-installer.py's process environment and it was
1208 # passed through the make utility into the environment of
1209 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1210 # the existing RUNSHARED configuration value when we call
1211 # make for extension module builds.
1212
1213 runshared_for_make = "".join([
1214 " RUNSHARED=",
1215 "'",
1216 grepValue("Makefile", "RUNSHARED"),
1217 ' DYLD_LIBRARY_PATH=',
1218 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1219 "'" ])
1220
Ned Deilyb364d9f2017-07-24 04:58:43 -04001221 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1222 # and, if defined, append its value to the make command. This allows
1223 # us to pass in version control tags, like GITTAG, to a build from a
1224 # tarball rather than from a vcs checkout, thus eliminating the need
1225 # to have a working copy of the vcs program on the build machine.
1226 #
1227 # A typical use might be:
1228 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1229 # GITVERSION='echo 123456789a' \
1230 # GITTAG='echo v3.6.0' \
1231 # GITBRANCH='echo 3.6'"
1232
1233 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1234 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001235 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001236 else:
Ned Deily1931e642020-06-25 04:51:46 -04001237 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001238 print("Running " + make_cmd)
1239 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001240
Ned Deily1931e642020-06-25 04:51:46 -04001241 make_cmd = "make install DESTDIR=%s %s"%(
1242 shellQuote(rootDir),
1243 runshared_for_make)
1244 print("Running " + make_cmd)
1245 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001246
Ned Deily1931e642020-06-25 04:51:46 -04001247 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1248 shellQuote(rootDir),
1249 runshared_for_make)
1250 print("Running " + make_cmd)
1251 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001252
Ned Deily4a96a372013-01-29 00:08:32 -08001253 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001254 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001255 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001256 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001257 'Python.framework', 'Versions', getVersion(), 'lib')
1258 fw_lib_dir = os.path.join(
1259 WORKDIR, '_root', 'Library', 'Frameworks',
1260 'Python.framework', 'Versions', getVersion(), 'lib')
1261 if internalTk():
1262 # move Tcl and Tk pkgconfig files
1263 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1264 shellQuote(build_lib_dir),
1265 shellQuote(fw_lib_dir) ))
1266 runCommand("rm -r %s/pkgconfig"%(
1267 shellQuote(build_lib_dir), ))
1268 runCommand("mv %s/* %s"%(
1269 shellQuote(build_lib_dir),
1270 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001271
Ned Deilydde4f632016-09-12 09:39:23 -04001272 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1273 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1274 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1275 # create directory for OpenSSL certificates
1276 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1277 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001278
Ned Deily4a96a372013-01-29 00:08:32 -08001279 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001280 gid = grp.getgrnam('admin').gr_gid
1281
Ned Deily4a96a372013-01-29 00:08:32 -08001282 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001283 for dirpath, dirnames, filenames in os.walk(frmDir):
1284 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001285 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001286 os.chown(os.path.join(dirpath, dn), -1, gid)
1287
Thomas Wouters477c8d52006-05-27 19:21:47 +00001288 for fn in filenames:
1289 if os.path.islink(fn):
1290 continue
1291
1292 # "chmod g+w $fn"
1293 p = os.path.join(dirpath, fn)
1294 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001295 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1296 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001297
Ned Deily4a96a372013-01-29 00:08:32 -08001298 if fn in EXPECTED_SHARED_LIBS:
1299 # check to see that this file was linked with the
1300 # expected library path and version
1301 data = captureCommand("otool -L %s" % shellQuote(p))
1302 for sl in EXPECTED_SHARED_LIBS[fn]:
1303 if ("\t%s " % sl) not in data:
1304 print("Expected shared lib %s was not linked with %s"
1305 % (sl, p))
1306 shared_lib_error = True
1307
1308 if shared_lib_error:
1309 fatal("Unexpected shared library errors.")
1310
Ned Deilye59e4c52011-01-29 18:56:28 +00001311 if PYTHON_3:
1312 LDVERSION=None
1313 VERSION=None
1314 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001315
Ned Deilye59e4c52011-01-29 18:56:28 +00001316 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001317 for ln in fp:
1318 if ln.startswith('VERSION='):
1319 VERSION=ln.split()[1]
1320 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001321 ABIFLAGS=ln.split()
1322 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001323 if ln.startswith('LDVERSION='):
1324 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001325 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001326
Ned Deilye59e4c52011-01-29 18:56:28 +00001327 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1328 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1329 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001330 if getVersionMajorMinor() >= (3, 6):
1331 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001332 else:
1333 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001334
Thomas Wouters477c8d52006-05-27 19:21:47 +00001335 # We added some directories to the search path during the configure
1336 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001337 # the end-users system. Also remove the directories from _sysconfigdata.py
1338 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339
Ned Deilya4f6b002013-10-25 00:47:38 -07001340 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1341 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1342
Ned Deilya4f6b002013-10-25 00:47:38 -07001343 # fix Makefile
1344 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1345 fp = open(path, 'r')
1346 data = fp.read()
1347 fp.close()
1348
1349 for p in (include_path, lib_path):
1350 data = data.replace(" " + p, '')
1351 data = data.replace(p + " ", '')
1352
1353 fp = open(path, 'w')
1354 fp.write(data)
1355 fp.close()
1356
Ned Deily652bad42016-08-15 14:37:14 -04001357 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001358 #
1359 # TODO: make this more robust! test_sysconfig_module of
1360 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1361 # the output from get_config_var in both sysconfig and
1362 # distutils.sysconfig is exactly the same for both CFLAGS and
1363 # LDFLAGS. The fixing up is now complicated by the pretty
1364 # printing in _sysconfigdata.py. Also, we are using the
1365 # pprint from the Python running the installer build which
1366 # may not cosmetically format the same as the pprint in the Python
1367 # being built (and which is used to originally generate
1368 # _sysconfigdata.py).
1369
1370 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001371 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001372 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001373 path = os.path.join(path_to_lib,
1374 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001375 else:
1376 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1377 fp = open(path, 'r')
1378 data = fp.read()
1379 fp.close()
1380 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001381 if RUNNING_ON_PYTHON2:
1382 exec(data)
1383 else:
1384 g_dict = {}
1385 l_dict = {}
1386 exec(data, g_dict, l_dict)
1387 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001388 vars = {}
1389 for k, v in build_time_vars.items():
1390 if type(v) == type(''):
1391 for p in (include_path, lib_path):
1392 v = v.replace(' ' + p, '')
1393 v = v.replace(p + ' ', '')
1394 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001395
Ned Deily652bad42016-08-15 14:37:14 -04001396 fp = open(path, 'w')
1397 # duplicated from sysconfig._generate_posix_vars()
1398 fp.write('# system configuration generated and used by'
1399 ' the sysconfig module\n')
1400 fp.write('build_time_vars = ')
1401 pprint.pprint(vars, stream=fp)
1402 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001403
1404 # Add symlinks in /usr/local/bin, using relative links
1405 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1406 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1407 'Python.framework', 'Versions', version, 'bin')
1408 if os.path.exists(usr_local_bin):
1409 shutil.rmtree(usr_local_bin)
1410 os.makedirs(usr_local_bin)
1411 for fn in os.listdir(
1412 os.path.join(frmDir, 'Versions', version, 'bin')):
1413 os.symlink(os.path.join(to_framework, fn),
1414 os.path.join(usr_local_bin, fn))
1415
1416 os.chdir(curdir)
1417
Thomas Wouters477c8d52006-05-27 19:21:47 +00001418def patchFile(inPath, outPath):
1419 data = fileContents(inPath)
1420 data = data.replace('$FULL_VERSION', getFullVersion())
1421 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001422 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001423 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001424 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001425 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001426
1427 # This one is not handy as a template variable
1428 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001429 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001430 fp.write(data)
1431 fp.close()
1432
1433def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001434 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001435 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001436 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001437 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001438 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001439 fp.write(data)
1440 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001441 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001442
1443
1444
1445def packageFromRecipe(targetDir, recipe):
1446 curdir = os.getcwd()
1447 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001448 # The major version (such as 2.5) is included in the package name
1449 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001450 # common.
1451 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001452 srcdir = recipe.get('source')
1453 pkgroot = recipe.get('topdir', srcdir)
1454 postflight = recipe.get('postflight')
1455 readme = textwrap.dedent(recipe['readme'])
1456 isRequired = recipe.get('required', True)
1457
Ned Deily4a96a372013-01-29 00:08:32 -08001458 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001459
1460 # Substitute some variables
1461 textvars = dict(
1462 VER=getVersion(),
1463 FULLVER=getFullVersion(),
1464 )
1465 readme = readme % textvars
1466
1467 if pkgroot is not None:
1468 pkgroot = pkgroot % textvars
1469 else:
1470 pkgroot = '/'
1471
1472 if srcdir is not None:
1473 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1474 srcdir = srcdir % textvars
1475
1476 if postflight is not None:
1477 postflight = os.path.abspath(postflight)
1478
1479 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1480 os.makedirs(packageContents)
1481
1482 if srcdir is not None:
1483 os.chdir(srcdir)
1484 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1485 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1486 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1487
1488 fn = os.path.join(packageContents, 'PkgInfo')
1489 fp = open(fn, 'w')
1490 fp.write('pmkrpkg1')
1491 fp.close()
1492
1493 rsrcDir = os.path.join(packageContents, "Resources")
1494 os.mkdir(rsrcDir)
1495 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1496 fp.write(readme)
1497 fp.close()
1498
1499 if postflight is not None:
1500 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1501
1502 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001503 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001504 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001505 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1506 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1507 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001508 CFBundleShortVersionString=vers,
1509 IFMajorVersion=major,
1510 IFMinorVersion=minor,
1511 IFPkgFormatVersion=0.10000000149011612,
1512 IFPkgFlagAllowBackRev=False,
1513 IFPkgFlagAuthorizationAction="RootAuthorization",
1514 IFPkgFlagDefaultLocation=pkgroot,
1515 IFPkgFlagFollowLinks=True,
1516 IFPkgFlagInstallFat=True,
1517 IFPkgFlagIsRequired=isRequired,
1518 IFPkgFlagOverwritePermissions=False,
1519 IFPkgFlagRelocatable=False,
1520 IFPkgFlagRestartAction="NoRestart",
1521 IFPkgFlagRootVolumeOnly=True,
1522 IFPkgFlagUpdateInstalledLangauges=False,
1523 )
1524 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1525
Ned Deily1931e642020-06-25 04:51:46 -04001526 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001527 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001528 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001529 IFPkgDescriptionVersion=vers,
1530 )
1531 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1532
1533 finally:
1534 os.chdir(curdir)
1535
1536
1537def makeMpkgPlist(path):
1538
1539 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001540 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001541
Ned Deily1931e642020-06-25 04:51:46 -04001542 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001543 CFBundleGetInfoString="Python %s"%(vers,),
1544 CFBundleIdentifier='org.python.Python',
1545 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001546 CFBundleShortVersionString=vers,
1547 IFMajorVersion=major,
1548 IFMinorVersion=minor,
1549 IFPkgFlagComponentDirectory="Contents/Packages",
1550 IFPkgFlagPackageList=[
1551 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001552 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001553 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001554 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001555 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001556 ],
1557 IFPkgFormatVersion=0.10000000149011612,
1558 IFPkgFlagBackgroundScaling="proportional",
1559 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001560 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001561 )
1562
1563 writePlist(pl, path)
1564
1565
1566def buildInstaller():
1567
1568 # Zap all compiled files
1569 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1570 for fn in filenames:
1571 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1572 os.unlink(os.path.join(dirpath, fn))
1573
1574 outdir = os.path.join(WORKDIR, 'installer')
1575 if os.path.exists(outdir):
1576 shutil.rmtree(outdir)
1577 os.mkdir(outdir)
1578
Ronald Oussoren1943f862009-03-30 19:39:14 +00001579 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001580 pkgcontents = os.path.join(pkgroot, 'Packages')
1581 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001582 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001583 packageFromRecipe(pkgcontents, recipe)
1584
1585 rsrcDir = os.path.join(pkgroot, 'Resources')
1586
1587 fn = os.path.join(pkgroot, 'PkgInfo')
1588 fp = open(fn, 'w')
1589 fp.write('pmkrpkg1')
1590 fp.close()
1591
1592 os.mkdir(rsrcDir)
1593
1594 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001595 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001596 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001597 IFPkgDescriptionVersion=getVersion(),
1598 )
1599
1600 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1601 for fn in os.listdir('resources'):
1602 if fn == '.svn': continue
1603 if fn.endswith('.jpg'):
1604 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1605 else:
1606 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1607
Thomas Wouters477c8d52006-05-27 19:21:47 +00001608
1609def installSize(clear=False, _saved=[]):
1610 if clear:
1611 del _saved[:]
1612 if not _saved:
1613 data = captureCommand("du -ks %s"%(
1614 shellQuote(os.path.join(WORKDIR, '_root'))))
1615 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1616 return _saved[0]
1617
1618
1619def buildDMG():
1620 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001621 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001622 """
1623 outdir = os.path.join(WORKDIR, 'diskimage')
1624 if os.path.exists(outdir):
1625 shutil.rmtree(outdir)
1626
Erlend Egeberg Aaslandc94ee132021-01-04 05:48:19 +01001627 # We used to use the deployment target as the last characters of the
Ned Deily936533c2020-11-23 19:04:40 -05001628 # installer file name. With the introduction of weaklinked installer
1629 # variants, we may have two variants with the same file name, i.e.
1630 # both ending in '10.9'. To avoid this, we now use the major/minor
Ned Deily8a374632021-04-27 13:23:39 -04001631 # version numbers of the macOS version we are building on.
1632 # Also, as of macOS 11, operating system version numbering has
1633 # changed from three components to two, i.e.
1634 # 10.14.1, 10.14.2, ...
1635 # 10.15.1, 10.15.2, ...
1636 # 11.1, 11.2, ...
1637 # 12.1, 12.2, ...
1638 # (A further twist is that, when running on macOS 11, binaries built
1639 # on older systems may be shown an operating system version of 10.16
1640 # instead of 11. We should not run into that situation here.)
1641 # Also we should use "macos" instead of "macosx" going forward.
1642 #
1643 # To maintain compability for legacy variants, the file name for
1644 # builds on macOS 10.15 and earlier remains:
1645 # python-3.x.y-macosx10.z.{dmg->pkg}
1646 # e.g. python-3.9.4-macosx10.9.{dmg->pkg}
1647 # and for builds on macOS 11+:
1648 # python-3.x.y-macosz.{dmg->pkg}
1649 # e.g. python-3.9.4-macos11.{dmg->pkg}
1650
1651 build_tuple = getBuildTuple()
1652 if build_tuple[0] < 11:
1653 os_name = 'macosx'
1654 build_system_version = '%s.%s' % build_tuple
1655 else:
1656 os_name = 'macos'
1657 build_system_version = str(build_tuple[0])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001658 imagepath = os.path.join(outdir,
Ned Deily8a374632021-04-27 13:23:39 -04001659 'python-%s-%s%s'%(getFullVersion(),os_name,build_system_version))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001660 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001661 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001662 imagepath = imagepath + '.dmg'
1663
1664 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001665
1666 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1667 # when hdiutil create fails with "Resource busy". For now, just retry
1668 # the create a few times and hope that it eventually works.
1669
Ronald Oussoren1943f862009-03-30 19:39:14 +00001670 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001671 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001672 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001673 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001674 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001675 for i in range(5):
1676 fd = os.popen(cmd, 'r')
1677 data = fd.read()
1678 xit = fd.close()
1679 if not xit:
1680 break
1681 sys.stdout.write(data)
1682 print(" -- retrying hdiutil create")
1683 time.sleep(5)
1684 else:
cclaussd3371692019-06-03 05:19:44 +02001685 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001686
1687 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1688 os.mkdir(os.path.join(WORKDIR, "mnt"))
1689 runCommand("hdiutil attach %s -mountroot %s"%(
1690 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1691
1692 # Custom icon for the DMG, shown when the DMG is mounted.
1693 shutil.copy("../Icons/Disk Image.icns",
1694 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001695 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001696 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1697
1698 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1699
1700 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1701 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1702 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1703 setIcon(imagepath, "../Icons/Disk Image.icns")
1704
1705 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001706
1707 return imagepath
1708
1709
1710def setIcon(filePath, icnsPath):
1711 """
1712 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001713 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001714
Ronald Oussoren70050672010-04-30 15:00:26 +00001715 dirPath = os.path.normpath(os.path.dirname(__file__))
1716 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001717 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1718 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1719 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001720 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1721 if not os.path.exists(appPath):
1722 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001723 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1724 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001725
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001726 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1727 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001728
1729def main():
1730 # First parse options and check if we can perform our work
1731 parseOptions()
1732 checkEnvironment()
1733
Ronald Oussoren1943f862009-03-30 19:39:14 +00001734 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001735 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001736 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001737
1738 if os.path.exists(WORKDIR):
1739 shutil.rmtree(WORKDIR)
1740 os.mkdir(WORKDIR)
1741
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001742 os.environ['LC_ALL'] = 'C'
1743
Thomas Wouters477c8d52006-05-27 19:21:47 +00001744 # Then build third-party libraries such as sleepycat DB4.
1745 buildLibraries()
1746
1747 # Now build python itself
1748 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001749
1750 # And then build the documentation
1751 # Remove the Deployment Target from the shell
1752 # environment, it's no longer needed and
1753 # an unexpected build target can cause problems
1754 # when Sphinx and its dependencies need to
1755 # be (re-)installed.
1756 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001757 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001758
1759
1760 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001761 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001762 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001763 fn = os.path.join(folder, "License.rtf")
1764 patchFile("resources/License.rtf", fn)
1765 fn = os.path.join(folder, "ReadMe.rtf")
1766 patchFile("resources/ReadMe.rtf", fn)
1767 fn = os.path.join(folder, "Update Shell Profile.command")
1768 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001769 fn = os.path.join(folder, "Install Certificates.command")
1770 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001771 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001772 setIcon(folder, "../Icons/Python Folder.icns")
1773
1774 # Create the installer
1775 buildInstaller()
1776
1777 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001778 patchFile('resources/ReadMe.rtf',
1779 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001780
1781 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001782 patchFile('resources/License.rtf',
1783 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001784
1785 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001786 fp.write("# BUILD INFO\n")
1787 fp.write("# Date: %s\n" % time.ctime())
1788 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001789 fp.close()
1790
Thomas Wouters477c8d52006-05-27 19:21:47 +00001791 # And copy it to a DMG
1792 buildDMG()
1793
Thomas Wouters477c8d52006-05-27 19:21:47 +00001794if __name__ == "__main__":
1795 main()