blob: 540b5a0d1b8f93df61d28042bcdb84ec546538b8 [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++'),
166 '10.9': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700167 }
Ronald Oussoren90d52392021-05-03 03:29:03 +0200168 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000169
Ned Deily04cdfa12014-06-25 13:36:14 -0700170CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000171
Ned Deily5d3febf2014-12-13 00:17:46 -0800172PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000173
Thomas Wouters89f507f2006-12-13 04:49:30 +0000174USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000175 Usage: build_python [options]
176
177 Options:
178 -? or -h: Show this message
179 -b DIR
180 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
181 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500182 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000183 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500184 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000185 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000186""")% globals()
187
Ned Deily4a96a372013-01-29 00:08:32 -0800188# Dict of object file names with shared library names to check after building.
189# This is to ensure that we ended up dynamically linking with the shared
190# library paths and versions we expected. For example:
191# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
192# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
193# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
194EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000195
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500196# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400197# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500198def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400199 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500200
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100201# Do we use 8.6.8 when building our own copy
202# of Tcl/Tk or a modern version.
203# We use the old version when buildin on
204# old versions of macOS due to build issues.
205def useOldTk():
206 return getBuildTuple() < (10, 15)
207
Ronald Oussoren41761932020-11-08 10:05:27 +0100208
209def tweak_tcl_build(basedir, archList):
210 with open("Makefile", "r") as fp:
211 contents = fp.readlines()
212
213 # For reasons I don't understand the tcl configure script
214 # decides that some stdlib symbols aren't present, before
215 # deciding that strtod is broken.
216 new_contents = []
217 for line in contents:
218 if line.startswith("COMPAT_OBJS"):
219 # note: the space before strtod.o is intentional,
220 # the detection of a broken strtod results in
221 # "fixstrod.o" on this line.
222 for nm in ("strstr.o", "strtoul.o", " strtod.o"):
223 line = line.replace(nm, "")
224 new_contents.append(line)
225
226 with open("Makefile", "w") as fp:
227 fp.writelines(new_contents)
228
Ned Deily5d3febf2014-12-13 00:17:46 -0800229# List of names of third party software built with this installer.
230# The names will be inserted into the rtf version of the License.
231THIRD_PARTY_LIBS = []
232
Thomas Wouters477c8d52006-05-27 19:21:47 +0000233# Instructions for building libraries that are necessary for building a
234# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000235# [The recipes are defined here for convenience but instantiated later after
236# command line options have been processed.]
237def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000238 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000239
Ned Deily04cdfa12014-06-25 13:36:14 -0700240 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800241
Ned Deilydde4f632016-09-12 09:39:23 -0400242 # Since Apple removed the header files for the deprecated system
243 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
244 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800245
Ned Deilydde4f632016-09-12 09:39:23 -0400246 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800247 dict(
Christian Heimesa54fc682021-03-30 02:00:34 +0200248 name="OpenSSL 1.1.1k",
249 url="https://www.openssl.org/source/openssl-1.1.1k.tar.gz",
250 checksum='c4e7d95f782b08116afa27b30393dd27',
Ned Deily5d3febf2014-12-13 00:17:46 -0800251 buildrecipe=build_universal_openssl,
252 configure=None,
253 install=None,
254 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400255 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800256
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500257 if internalTk():
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100258 if useOldTk():
259 tcl_tk_ver='8.6.8'
260 tcl_checksum='81656d3367af032e0ae6157eff134f89'
261
262 tk_checksum='5e0faecba458ee1386078fb228d008ba'
263 tk_patches = ['tk868_on_10_8_10_9.patch']
264
265 else:
Ned Deilya38e04b2021-01-04 04:43:11 -0500266 tcl_tk_ver='8.6.11'
267 tcl_checksum='8a4c004f48984a03a7747e9ba06e4da4'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100268
Ned Deilya38e04b2021-01-04 04:43:11 -0500269 tk_checksum='c7ee71a2d05bba78dfffd76528dc17c6'
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100270 tk_patches = [ ]
271
272
Ned Deily5b3582c2013-10-25 00:41:46 -0700273 result.extend([
274 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100275 name="Tcl %s"%(tcl_tk_ver,),
276 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl%s-src.tar.gz"%(tcl_tk_ver,),
277 checksum=tcl_checksum,
Ned Deily5b3582c2013-10-25 00:41:46 -0700278 buildDir="unix",
279 configure_pre=[
280 '--enable-shared',
281 '--enable-threads',
282 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
283 ],
284 useLDFlags=False,
Ronald Oussoren41761932020-11-08 10:05:27 +0100285 buildrecipe=tweak_tcl_build,
Ned Deily5b3582c2013-10-25 00:41:46 -0700286 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
287 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500288 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700289 },
290 ),
291 dict(
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100292 name="Tk %s"%(tcl_tk_ver,),
293 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk%s-src.tar.gz"%(tcl_tk_ver,),
294 checksum=tk_checksum,
295 patches=tk_patches,
Ned Deily5b3582c2013-10-25 00:41:46 -0700296 buildDir="unix",
297 configure_pre=[
298 '--enable-aqua',
299 '--enable-shared',
300 '--enable-threads',
301 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
302 ],
303 useLDFlags=False,
304 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'%{
305 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500306 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
307 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700308 },
309 ),
310 ])
311
Ned Deilyed730102014-11-14 18:55:05 -0800312 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800313 result.extend([
314 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500315 name="XZ 5.2.3",
316 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
317 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800318 configure_pre=[
319 '--disable-dependency-tracking',
320 ]
321 ),
322 ])
323
324 result.extend([
325 dict(
326 name="NCurses 5.9",
327 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
328 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
329 configure_pre=[
330 "--enable-widec",
331 "--without-cxx",
332 "--without-cxx-binding",
333 "--without-ada",
334 "--without-curses-h",
335 "--enable-shared",
336 "--with-shared",
337 "--without-debug",
338 "--without-normal",
339 "--without-tests",
340 "--without-manpages",
341 "--datadir=/usr/share",
342 "--sysconfdir=/etc",
343 "--sharedstatedir=/usr/com",
344 "--with-terminfo-dirs=/usr/share/terminfo",
345 "--with-default-terminfo-dir=/usr/share/terminfo",
346 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
347 ],
348 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200349 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800350 "f54bf02a349f96a7c4f0d00922f3a0d4"),
351 ],
352 useLDFlags=False,
353 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
354 shellQuote(os.path.join(WORKDIR, 'libraries')),
355 shellQuote(os.path.join(WORKDIR, 'libraries')),
356 getVersion(),
357 ),
358 ),
359 dict(
Erlend Egeberg Aaslandce827812021-04-27 19:19:14 +0200360 name="SQLite 3.35.5",
361 url="https://sqlite.org/2021/sqlite-autoconf-3350500.tar.gz",
362 checksum='d1d1aba394c8e0443077dc9f1a681bb8',
Ned Deily4a96a372013-01-29 00:08:32 -0800363 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700364 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800365 '-DSQLITE_ENABLE_FTS4 '
366 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500367 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800368 '-DSQLITE_ENABLE_RTREE '
Erlend Egeberg Aasland555cbbe2021-05-03 02:37:35 +0200369 '-DSQLITE_OMIT_AUTOINIT '
Ned Deily4a96a372013-01-29 00:08:32 -0800370 '-DSQLITE_TCL=0 '
371 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
372 configure_pre=[
373 '--enable-threadsafe',
374 '--enable-shared=no',
375 '--enable-static=yes',
376 '--disable-readline',
377 '--disable-dependency-tracking',
378 ]
379 ),
380 ])
381
Ned Deily04cdfa12014-06-25 13:36:14 -0700382 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000383 result.extend([
384 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000385 name="Bzip2 1.0.6",
386 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
387 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000388 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500389 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800390 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000391 shellQuote(os.path.join(WORKDIR, 'libraries')),
392 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000393 ),
394 ),
395 dict(
396 name="ZLib 1.2.3",
397 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
398 checksum='debc62758716a169df9f62e6ab2bc634',
399 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500400 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800401 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000402 shellQuote(os.path.join(WORKDIR, 'libraries')),
403 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000404 ),
405 ),
406 dict(
407 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000408 name="GNU Readline 6.1.2",
409 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
410 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000411 patchlevel='0',
412 patches=[
413 # The readline maintainers don't do actual micro releases, but
414 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800415 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
416 'c642f2e84d820884b0bf9fd176bc6c3f'),
417 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
418 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000419 ]
420 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000421 ])
422
Ned Deily4f7ff782011-01-15 05:29:12 +0000423 if not PYTHON_3:
424 result.extend([
425 dict(
426 name="Sleepycat DB 4.7.25",
427 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
428 checksum='ec2b87e833779681a0c3a814aa71359e',
429 buildDir="build_unix",
430 configure="../dist/configure",
431 configure_pre=[
432 '--includedir=/usr/local/include/db4',
433 ]
434 ),
435 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000436
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000437 return result
438
Thomas Wouters477c8d52006-05-27 19:21:47 +0000439
Thomas Wouters477c8d52006-05-27 19:21:47 +0000440# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000441def pkg_recipes():
442 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
443 result = [
444 dict(
445 name="PythonFramework",
446 long_name="Python Framework",
447 source="/Library/Frameworks/Python.framework",
448 readme="""\
449 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500450 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000451 """,
452 postflight="scripts/postflight.framework",
453 selected='selected',
454 ),
455 dict(
456 name="PythonApplications",
457 long_name="GUI Applications",
458 source="/Applications/Python %(VER)s",
459 readme="""\
460 This package installs IDLE (an interactive Python IDE),
461 Python Launcher and Build Applet (create application bundles
462 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000463
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000464 It also installs a number of examples and demos.
465 """,
466 required=False,
467 selected='selected',
468 ),
469 dict(
470 name="PythonUnixTools",
471 long_name="UNIX command-line tools",
472 source="/usr/local/bin",
473 readme="""\
474 This package installs the unix tools in /usr/local/bin for
475 compatibility with older releases of Python. This package
476 is not necessary to use Python.
477 """,
478 required=False,
479 selected='selected',
480 ),
481 dict(
482 name="PythonDocumentation",
483 long_name="Python Documentation",
484 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
485 source="/pydocs",
486 readme="""\
487 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800488 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000489 """,
490 postflight="scripts/postflight.documentation",
491 required=False,
492 selected='selected',
493 ),
494 dict(
495 name="PythonProfileChanges",
496 long_name="Shell profile updater",
497 readme="""\
498 This packages updates your shell profile to make sure that
499 the Python tools are found by your shell in preference of
500 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000501
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000502 If you don't install this package you'll have to add
503 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
504 to your PATH by hand.
505 """,
506 postflight="scripts/postflight.patch-profile",
507 topdir="/Library/Frameworks/Python.framework",
508 source="/empty-dir",
509 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800510 selected='selected',
511 ),
512 dict(
513 name="PythonInstallPip",
514 long_name="Install or upgrade pip",
515 readme="""\
516 This package installs (or upgrades from an earlier version)
517 pip, a tool for installing and managing Python packages.
518 """,
519 postflight="scripts/postflight.ensurepip",
520 topdir="/Library/Frameworks/Python.framework",
521 source="/empty-dir",
522 required=False,
523 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000524 ),
525 ]
526
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000527 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000528
Thomas Wouters477c8d52006-05-27 19:21:47 +0000529def fatal(msg):
530 """
531 A fatal error, bail out.
532 """
533 sys.stderr.write('FATAL: ')
534 sys.stderr.write(msg)
535 sys.stderr.write('\n')
536 sys.exit(1)
537
538def fileContents(fn):
539 """
540 Return the contents of the named file
541 """
Ned Deily4a96a372013-01-29 00:08:32 -0800542 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543
544def runCommand(commandline):
545 """
Ezio Melotti13925002011-03-16 11:05:33 +0200546 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000547 unless the command fails.
548 """
549 fd = os.popen(commandline, 'r')
550 data = fd.read()
551 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000552 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000553 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800554 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000555
556 if VERBOSE:
557 sys.stdout.write(data); sys.stdout.flush()
558
559def captureCommand(commandline):
560 fd = os.popen(commandline, 'r')
561 data = fd.read()
562 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000563 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000564 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800565 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000566
567 return data
568
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000569def getTclTkVersion(configfile, versionline):
570 """
571 search Tcl or Tk configuration file for version line
572 """
573 try:
574 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300575 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000576 fatal("Framework configuration file not found: %s" % configfile)
577
578 for l in f:
579 if l.startswith(versionline):
580 f.close()
581 return l
582
583 fatal("Version variable %s not found in framework configuration file: %s"
584 % (versionline, configfile))
585
Thomas Wouters477c8d52006-05-27 19:21:47 +0000586def checkEnvironment():
587 """
588 Check that we're running on a supported system.
589 """
590
Ned Deily936533c2020-11-23 19:04:40 -0500591 if sys.version_info[0:2] < (2, 7):
592 fatal("This script must be run with Python 2.7 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000593
Thomas Wouters477c8d52006-05-27 19:21:47 +0000594 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500595 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000596
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000597 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500598 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000599
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000600 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500601 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000602 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500603 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
604 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
605 # enforce that the version of the user-installed framework also
606 # exists in the system-supplied Tcl/Tk frameworks. Time to support
607 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500608 if not internalTk():
609 frameworks = {}
610 for framework in ['Tcl', 'Tk']:
611 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
612 libfw = os.path.join('/', fwpth)
613 usrfw = os.path.join(os.getenv('HOME'), fwpth)
614 frameworks[framework] = os.readlink(libfw)
615 if not os.path.exists(libfw):
616 fatal("Please install a link to a current %s %s as %s so "
617 "the user can override the system framework."
618 % (framework, frameworks[framework], libfw))
619 if os.path.exists(usrfw):
620 fatal("Please rename %s to avoid possible dynamic load issues."
621 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000622
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500623 if frameworks['Tcl'] != frameworks['Tk']:
624 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000625
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500626 print(" -- Building with external Tcl/Tk %s frameworks"
627 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800628
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500629 # add files to check after build
630 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
631 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
632 % frameworks['Tcl'],
633 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
634 % frameworks['Tk'],
635 ]
636 else:
637 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500638 print("")
639
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000640 # Remove inherited environment variables which might influence build
641 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
642 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
643 for ev in list(os.environ):
644 for prefix in environ_var_prefixes:
645 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800646 print("INFO: deleting environment variable %s=%s" % (
647 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000648 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000649
Ned Deily4a96a372013-01-29 00:08:32 -0800650 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
651 if 'SDK_TOOLS_BIN' in os.environ:
652 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
653 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
654 # add its fixed location here if it exists
655 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
656 if os.path.isdir(OLD_DEVELOPER_TOOLS):
657 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
658 os.environ['PATH'] = base_path
659 print("Setting default PATH: %s"%(os.environ['PATH']))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000660
Thomas Wouters89f507f2006-12-13 04:49:30 +0000661def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000662 """
663 Parse arguments and update global settings.
664 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500665 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800666 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800667 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400668 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000669
670 if args is None:
671 args = sys.argv[1:]
672
673 try:
674 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000675 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
676 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800677 except getopt.GetoptError:
678 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000679 sys.exit(1)
680
681 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800682 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000683 sys.exit(1)
684
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000685 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000686 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000687 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800688 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000689 sys.exit(0)
690
691 elif k in ('-d', '--build-dir'):
692 WORKDIR=v
693
694 elif k in ('--third-party',):
695 DEPSRC=v
696
697 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500698 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000699
700 elif k in ('--src-dir',):
701 SRCDIR=v
702
Ronald Oussoren1943f862009-03-30 19:39:14 +0000703 elif k in ('--dep-target', ):
704 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000705 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000706
707 elif k in ('--universal-archs', ):
708 if v in UNIVERSALOPTS:
709 UNIVERSALARCHS = v
710 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000711 if deptarget is None:
712 # Select alternate default deployment
713 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500714 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000715 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800716 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000717
Thomas Wouters477c8d52006-05-27 19:21:47 +0000718 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800719 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000720
721 SRCDIR=os.path.abspath(SRCDIR)
722 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000723 DEPSRC=os.path.abspath(DEPSRC)
724
Ned Deily04cdfa12014-06-25 13:36:14 -0700725 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000726
Ned Deily5d3febf2014-12-13 00:17:46 -0800727 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400728 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800729
730 print("-- Settings:")
731 print(" * Source directory: %s" % SRCDIR)
732 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800733 print(" * Third-party source: %s" % DEPSRC)
734 print(" * Deployment target: %s" % DEPTARGET)
735 print(" * Universal archs: %s" % str(ARCHLIST))
736 print(" * C compiler: %s" % CC)
737 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800738 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800739 print(" -- Building a Python %s framework at patch level %s"
740 % (getVersion(), getFullVersion()))
741 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000742
743def extractArchive(builddir, archiveName):
744 """
745 Extract a source archive into 'builddir'. Returns the path of the
746 extracted archive.
747
748 XXX: This function assumes that archives contain a toplevel directory
749 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700750 safe enough for almost anything we use. Unfortunately, it does not
751 work for current Tcl and Tk source releases where the basename of
752 the archive ends with "-src" but the uncompressed directory does not.
753 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000754 """
755 curdir = os.getcwd()
756 try:
757 os.chdir(builddir)
758 if archiveName.endswith('.tar.gz'):
759 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700760 if ((retval.startswith('tcl') or retval.startswith('tk'))
761 and retval.endswith('-src')):
762 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000763 if os.path.exists(retval):
764 shutil.rmtree(retval)
765 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
766
767 elif archiveName.endswith('.tar.bz2'):
768 retval = os.path.basename(archiveName[:-8])
769 if os.path.exists(retval):
770 shutil.rmtree(retval)
771 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
772
773 elif archiveName.endswith('.tar'):
774 retval = os.path.basename(archiveName[:-4])
775 if os.path.exists(retval):
776 shutil.rmtree(retval)
777 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
778
779 elif archiveName.endswith('.zip'):
780 retval = os.path.basename(archiveName[:-4])
781 if os.path.exists(retval):
782 shutil.rmtree(retval)
783 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
784
785 data = fp.read()
786 xit = fp.close()
787 if xit is not None:
788 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800789 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000790
791 return os.path.join(builddir, retval)
792
793 finally:
794 os.chdir(curdir)
795
Thomas Wouters477c8d52006-05-27 19:21:47 +0000796def downloadURL(url, fname):
797 """
798 Download the contents of the url into the file.
799 """
Ned Deily4a96a372013-01-29 00:08:32 -0800800 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000801 fpOut = open(fname, 'wb')
802 block = fpIn.read(10240)
803 try:
804 while block:
805 fpOut.write(block)
806 block = fpIn.read(10240)
807 fpIn.close()
808 fpOut.close()
809 except:
810 try:
811 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300812 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000813 pass
814
Ned Deily4a96a372013-01-29 00:08:32 -0800815def verifyThirdPartyFile(url, checksum, fname):
816 """
817 Download file from url to filename fname if it does not already exist.
818 Abort if file contents does not match supplied md5 checksum.
819 """
820 name = os.path.basename(fname)
821 if os.path.exists(fname):
822 print("Using local copy of %s"%(name,))
823 else:
824 print("Did not find local copy of %s"%(name,))
825 print("Downloading %s"%(name,))
826 downloadURL(url, fname)
827 print("Archive for %s stored as %s"%(name, fname))
828 if os.system(
829 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
830 % (shellQuote(fname), checksum) ):
831 fatal('MD5 checksum mismatch for file %s' % fname)
832
Ned Deily5d3febf2014-12-13 00:17:46 -0800833def build_universal_openssl(basedir, archList):
834 """
835 Special case build recipe for universal build of openssl.
836
837 The upstream OpenSSL build system does not directly support
838 OS X universal builds. We need to build each architecture
839 separately then lipo them together into fat libraries.
840 """
841
842 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
843 # If we are building on a 10.4.x or earlier system,
844 # unilaterally disable assembly code building to avoid the problem.
845 no_asm = int(platform.release().split(".")[0]) < 9
846
847 def build_openssl_arch(archbase, arch):
848 "Build one architecture of openssl"
849 arch_opts = {
850 "i386": ["darwin-i386-cc"],
851 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100852 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800853 "ppc": ["darwin-ppc-cc"],
854 "ppc64": ["darwin64-ppc-cc"],
855 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400856
857 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
858 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
859 # building on our 10.6 gcc-4.2 environment. There have been other
860 # reports of projects running into this when using older compilers.
861 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
862 # building for 10.6.
863 if getDeptargetTuple() == (10, 6):
864 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
865
Ned Deily5d3febf2014-12-13 00:17:46 -0800866 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800867 "no-idea",
868 "no-mdc2",
869 "no-rc5",
870 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800871 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800872 # "enable-unit-test",
873 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800874 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400875 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800876 ]
877 if no_asm:
878 configure_opts.append("no-asm")
879 runCommand(" ".join(["perl", "Configure"]
880 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500881 runCommand("make depend")
882 runCommand("make all")
883 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800884 # runCommand("make test")
885 return
886
887 srcdir = os.getcwd()
888 universalbase = os.path.join(srcdir, "..",
889 os.path.basename(srcdir) + "-universal")
890 os.mkdir(universalbase)
891 archbasefws = []
892 for arch in archList:
893 # fresh copy of the source tree
894 archsrc = os.path.join(universalbase, arch, "src")
895 shutil.copytree(srcdir, archsrc, symlinks=True)
896 # install base for this arch
897 archbase = os.path.join(universalbase, arch, "root")
898 os.mkdir(archbase)
899 # Python framework base within install_prefix:
900 # the build will install into this framework..
901 # This is to ensure that the resulting shared libs have
902 # the desired real install paths built into them.
903 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
904
905 # build one architecture
906 os.chdir(archsrc)
907 build_openssl_arch(archbase, arch)
908 os.chdir(srcdir)
909 archbasefws.append(archbasefw)
910
911 # copy arch-independent files from last build into the basedir framework
912 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
913 shutil.copytree(
914 os.path.join(archbasefw, "include", "openssl"),
915 os.path.join(basefw, "include", "openssl")
916 )
917
918 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
919 "SHLIB_VERSION_NUMBER")
920 # e.g. -> "1.0.0"
921 libcrypto = "libcrypto.dylib"
922 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
923 # e.g. -> "libcrypto.1.0.0.dylib"
924 libssl = "libssl.dylib"
925 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
926 # e.g. -> "libssl.1.0.0.dylib"
927
928 try:
929 os.mkdir(os.path.join(basefw, "lib"))
930 except OSError:
931 pass
932
933 # merge the individual arch-dependent shared libs into a fat shared lib
934 archbasefws.insert(0, basefw)
935 for (lib_unversioned, lib_versioned) in [
936 (libcrypto, libcrypto_versioned),
937 (libssl, libssl_versioned)
938 ]:
939 runCommand("lipo -create -output " +
940 " ".join(shellQuote(
941 os.path.join(fw, "lib", lib_versioned))
942 for fw in archbasefws))
943 # and create an unversioned symlink of it
944 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
945
946 # Create links in the temp include and lib dirs that will be injected
947 # into the Python build so that setup.py can find them while building
948 # and the versioned links so that the setup.py post-build import test
949 # does not fail.
950 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
951 for fn in [
952 ["include", "openssl"],
953 ["lib", libcrypto],
954 ["lib", libssl],
955 ["lib", libcrypto_versioned],
956 ["lib", libssl_versioned],
957 ]:
958 os.symlink(
959 os.path.join(relative_path, *fn),
960 os.path.join(basedir, "usr", "local", *fn)
961 )
962
963 return
964
Thomas Wouters477c8d52006-05-27 19:21:47 +0000965def buildRecipe(recipe, basedir, archList):
966 """
967 Build software using a recipe. This function does the
968 'configure;make;make install' dance for C software, with a possibility
969 to customize this process, basically a poor-mans DarwinPorts.
970 """
971 curdir = os.getcwd()
972
973 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800974 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000975 url = recipe['url']
976 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800977 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
979 shellQuote(basedir)))
980
981 archiveName = os.path.split(url)[-1]
982 sourceArchive = os.path.join(DEPSRC, archiveName)
983
984 if not os.path.exists(DEPSRC):
985 os.mkdir(DEPSRC)
986
Ned Deily4a96a372013-01-29 00:08:32 -0800987 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
988 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000989 buildDir=os.path.join(WORKDIR, '_bld')
990 if not os.path.exists(buildDir):
991 os.mkdir(buildDir)
992
993 workDir = extractArchive(buildDir, sourceArchive)
994 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000995
Ned Deily4a96a372013-01-29 00:08:32 -0800996 for patch in recipe.get('patches', ()):
997 if isinstance(patch, tuple):
998 url, checksum = patch
999 fn = os.path.join(DEPSRC, os.path.basename(url))
1000 verifyThirdPartyFile(url, checksum, fn)
1001 else:
1002 # patch is a file in the source directory
1003 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001004 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1005 shellQuote(fn),))
1006
Ned Deily4a96a372013-01-29 00:08:32 -08001007 for patchscript in recipe.get('patchscripts', ()):
1008 if isinstance(patchscript, tuple):
1009 url, checksum = patchscript
1010 fn = os.path.join(DEPSRC, os.path.basename(url))
1011 verifyThirdPartyFile(url, checksum, fn)
1012 else:
1013 # patch is a file in the source directory
1014 fn = os.path.join(curdir, patchscript)
1015 if fn.endswith('.bz2'):
1016 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1017 fn = fn[:-4]
1018 runCommand('sh %s' % shellQuote(fn))
1019 os.unlink(fn)
1020
Ned Deily94764b22013-10-27 19:49:29 -07001021 if 'buildDir' in recipe:
1022 os.chdir(recipe['buildDir'])
1023
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001024 if configure is not None:
1025 configure_args = [
1026 "--prefix=/usr/local",
1027 "--enable-static",
1028 "--disable-shared",
1029 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1030 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001031
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001032 if 'configure_pre' in recipe:
1033 args = list(recipe['configure_pre'])
1034 if '--disable-static' in args:
1035 configure_args.remove('--enable-static')
1036 if '--enable-shared' in args:
1037 configure_args.remove('--disable-shared')
1038 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001039
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001040 if recipe.get('useLDFlags', 1):
1041 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001042 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001043 "-I%s/usr/local/include"%(
1044 recipe.get('extra_cflags', ''),
1045 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001046 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001047 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001048 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001049 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001050 shellQuote(basedir)[1:-1],
1051 ' -arch '.join(archList)),
1052 ])
1053 else:
1054 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001055 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001056 "-I%s/usr/local/include"%(
1057 recipe.get('extra_cflags', ''),
1058 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001059 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001060 shellQuote(basedir)[1:-1],),
1061 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001062
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001063 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001064 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001065
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001066 configure_args.insert(0, configure)
1067 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001068
Ned Deily4a96a372013-01-29 00:08:32 -08001069 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001070 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001071
Ned Deily5d3febf2014-12-13 00:17:46 -08001072 if buildrecipe is not None:
1073 # call special-case build recipe, e.g. for openssl
1074 buildrecipe(basedir, archList)
1075
1076 if install is not None:
1077 print("Running install for %s"%(name,))
1078 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001079
Ned Deily4a96a372013-01-29 00:08:32 -08001080 print("Done %s"%(name,))
1081 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001082
1083 os.chdir(curdir)
1084
1085def buildLibraries():
1086 """
1087 Build our dependencies into $WORKDIR/libraries/usr/local
1088 """
Ned Deily4a96a372013-01-29 00:08:32 -08001089 print("")
1090 print("Building required libraries")
1091 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001092 universal = os.path.join(WORKDIR, 'libraries')
1093 os.mkdir(universal)
1094 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1095 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1096
Ronald Oussoren1943f862009-03-30 19:39:14 +00001097 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001098 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001099
1100
1101
1102def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001103 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001104 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001105 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001106 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001107 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001108 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001109 curDir = os.getcwd()
1110 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001111 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001112
1113 # Search third-party source directory for a pre-built version of the docs.
1114 # Use the naming convention of the docs.python.org html downloads:
1115 # python-3.9.0b1-docs-html.tar.bz2
1116 doctarfiles = [ f for f in os.listdir(DEPSRC)
1117 if f.startswith('python-'+getFullVersion())
1118 if f.endswith('-docs-html.tar.bz2') ]
1119 if doctarfiles:
1120 doctarfile = doctarfiles[0]
1121 if not os.path.exists('build'):
1122 os.mkdir('build')
1123 # if build directory existed, it was emptied by make clean, above
1124 os.chdir('build')
1125 # Extract the first archive found for this version into build
1126 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1127 # see if tar extracted a directory ending in -docs-html
1128 archivefiles = [ f for f in os.listdir('.')
1129 if f.endswith('-docs-html')
1130 if os.path.isdir(f) ]
1131 if archivefiles:
1132 archivefile = archivefiles[0]
1133 # make it our 'Docs/build/html' directory
1134 print(' -- using pre-built python documentation from %s'%archivefile)
1135 os.rename(archivefile, 'html')
1136 os.chdir(buildDir)
1137
1138 htmlDir = os.path.join('build', 'html')
1139 if not os.path.exists(htmlDir):
1140 # Create virtual environment for docs builds with blurb and sphinx
1141 runCommand('make venv')
Ned Deily63fc55b2020-06-08 03:52:43 -04001142 runCommand('make html PYTHON=venv/bin/python')
1143 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001144 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145
1146
1147def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001148 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001149
1150 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1151 rootDir = os.path.join(WORKDIR, '_root')
1152
1153 if os.path.exists(buildDir):
1154 shutil.rmtree(buildDir)
1155 if os.path.exists(rootDir):
1156 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001157 os.makedirs(buildDir)
1158 os.makedirs(rootDir)
1159 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001160 curdir = os.getcwd()
1161 os.chdir(buildDir)
1162
Thomas Wouters477c8d52006-05-27 19:21:47 +00001163 # Extract the version from the configure file, needed to calculate
1164 # several paths.
1165 version = getVersion()
1166
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001167 # Since the extra libs are not in their installed framework location
1168 # during the build, augment the library path so that the interpreter
1169 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001170
Ned Deily4a96a372013-01-29 00:08:32 -08001171 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001172 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001173 "--with-universal-archs=%s "
1174 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001175 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001176 "%s "
1177 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001178 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001179 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001180 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001181 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001182 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001183 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001184 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001185 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1186 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001187 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1188 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1189 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1190 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001191 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001192 shellQuote(WORKDIR)[1:-1]))
1193
Ned Deily1931e642020-06-25 04:51:46 -04001194 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1195 # environment variables are no longer automatically inherited
1196 # by child processes from their parents. We used to just set
1197 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1198 # in build-installer.py's process environment and it was
1199 # passed through the make utility into the environment of
1200 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1201 # the existing RUNSHARED configuration value when we call
1202 # make for extension module builds.
1203
1204 runshared_for_make = "".join([
1205 " RUNSHARED=",
1206 "'",
1207 grepValue("Makefile", "RUNSHARED"),
1208 ' DYLD_LIBRARY_PATH=',
1209 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1210 "'" ])
1211
Ned Deilyb364d9f2017-07-24 04:58:43 -04001212 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1213 # and, if defined, append its value to the make command. This allows
1214 # us to pass in version control tags, like GITTAG, to a build from a
1215 # tarball rather than from a vcs checkout, thus eliminating the need
1216 # to have a working copy of the vcs program on the build machine.
1217 #
1218 # A typical use might be:
1219 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1220 # GITVERSION='echo 123456789a' \
1221 # GITTAG='echo v3.6.0' \
1222 # GITBRANCH='echo 3.6'"
1223
1224 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1225 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001226 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001227 else:
Ned Deily1931e642020-06-25 04:51:46 -04001228 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001229 print("Running " + make_cmd)
1230 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001231
Ned Deily1931e642020-06-25 04:51:46 -04001232 make_cmd = "make install DESTDIR=%s %s"%(
1233 shellQuote(rootDir),
1234 runshared_for_make)
1235 print("Running " + make_cmd)
1236 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001237
Ned Deily1931e642020-06-25 04:51:46 -04001238 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1239 shellQuote(rootDir),
1240 runshared_for_make)
1241 print("Running " + make_cmd)
1242 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001243
Ned Deily4a96a372013-01-29 00:08:32 -08001244 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001245 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001246 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001247 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001248 'Python.framework', 'Versions', getVersion(), 'lib')
1249 fw_lib_dir = os.path.join(
1250 WORKDIR, '_root', 'Library', 'Frameworks',
1251 'Python.framework', 'Versions', getVersion(), 'lib')
1252 if internalTk():
1253 # move Tcl and Tk pkgconfig files
1254 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1255 shellQuote(build_lib_dir),
1256 shellQuote(fw_lib_dir) ))
1257 runCommand("rm -r %s/pkgconfig"%(
1258 shellQuote(build_lib_dir), ))
1259 runCommand("mv %s/* %s"%(
1260 shellQuote(build_lib_dir),
1261 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001262
Ned Deilydde4f632016-09-12 09:39:23 -04001263 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1264 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1265 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1266 # create directory for OpenSSL certificates
1267 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1268 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001269
Ned Deily4a96a372013-01-29 00:08:32 -08001270 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001271 gid = grp.getgrnam('admin').gr_gid
1272
Ned Deily4a96a372013-01-29 00:08:32 -08001273 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001274 for dirpath, dirnames, filenames in os.walk(frmDir):
1275 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001276 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001277 os.chown(os.path.join(dirpath, dn), -1, gid)
1278
Thomas Wouters477c8d52006-05-27 19:21:47 +00001279 for fn in filenames:
1280 if os.path.islink(fn):
1281 continue
1282
1283 # "chmod g+w $fn"
1284 p = os.path.join(dirpath, fn)
1285 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001286 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1287 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001288
Ned Deily4a96a372013-01-29 00:08:32 -08001289 if fn in EXPECTED_SHARED_LIBS:
1290 # check to see that this file was linked with the
1291 # expected library path and version
1292 data = captureCommand("otool -L %s" % shellQuote(p))
1293 for sl in EXPECTED_SHARED_LIBS[fn]:
1294 if ("\t%s " % sl) not in data:
1295 print("Expected shared lib %s was not linked with %s"
1296 % (sl, p))
1297 shared_lib_error = True
1298
1299 if shared_lib_error:
1300 fatal("Unexpected shared library errors.")
1301
Ned Deilye59e4c52011-01-29 18:56:28 +00001302 if PYTHON_3:
1303 LDVERSION=None
1304 VERSION=None
1305 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001306
Ned Deilye59e4c52011-01-29 18:56:28 +00001307 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001308 for ln in fp:
1309 if ln.startswith('VERSION='):
1310 VERSION=ln.split()[1]
1311 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001312 ABIFLAGS=ln.split()
1313 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001314 if ln.startswith('LDVERSION='):
1315 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001316 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001317
Ned Deilye59e4c52011-01-29 18:56:28 +00001318 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1319 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1320 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001321 if getVersionMajorMinor() >= (3, 6):
1322 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001323 else:
1324 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001325
Thomas Wouters477c8d52006-05-27 19:21:47 +00001326 # We added some directories to the search path during the configure
1327 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001328 # the end-users system. Also remove the directories from _sysconfigdata.py
1329 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001330
Ned Deilya4f6b002013-10-25 00:47:38 -07001331 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1332 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1333
Ned Deilya4f6b002013-10-25 00:47:38 -07001334 # fix Makefile
1335 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1336 fp = open(path, 'r')
1337 data = fp.read()
1338 fp.close()
1339
1340 for p in (include_path, lib_path):
1341 data = data.replace(" " + p, '')
1342 data = data.replace(p + " ", '')
1343
1344 fp = open(path, 'w')
1345 fp.write(data)
1346 fp.close()
1347
Ned Deily652bad42016-08-15 14:37:14 -04001348 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001349 #
1350 # TODO: make this more robust! test_sysconfig_module of
1351 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1352 # the output from get_config_var in both sysconfig and
1353 # distutils.sysconfig is exactly the same for both CFLAGS and
1354 # LDFLAGS. The fixing up is now complicated by the pretty
1355 # printing in _sysconfigdata.py. Also, we are using the
1356 # pprint from the Python running the installer build which
1357 # may not cosmetically format the same as the pprint in the Python
1358 # being built (and which is used to originally generate
1359 # _sysconfigdata.py).
1360
1361 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001362 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001363 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001364 path = os.path.join(path_to_lib,
1365 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001366 else:
1367 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1368 fp = open(path, 'r')
1369 data = fp.read()
1370 fp.close()
1371 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001372 if RUNNING_ON_PYTHON2:
1373 exec(data)
1374 else:
1375 g_dict = {}
1376 l_dict = {}
1377 exec(data, g_dict, l_dict)
1378 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001379 vars = {}
1380 for k, v in build_time_vars.items():
1381 if type(v) == type(''):
1382 for p in (include_path, lib_path):
1383 v = v.replace(' ' + p, '')
1384 v = v.replace(p + ' ', '')
1385 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001386
Ned Deily652bad42016-08-15 14:37:14 -04001387 fp = open(path, 'w')
1388 # duplicated from sysconfig._generate_posix_vars()
1389 fp.write('# system configuration generated and used by'
1390 ' the sysconfig module\n')
1391 fp.write('build_time_vars = ')
1392 pprint.pprint(vars, stream=fp)
1393 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001394
1395 # Add symlinks in /usr/local/bin, using relative links
1396 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1397 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1398 'Python.framework', 'Versions', version, 'bin')
1399 if os.path.exists(usr_local_bin):
1400 shutil.rmtree(usr_local_bin)
1401 os.makedirs(usr_local_bin)
1402 for fn in os.listdir(
1403 os.path.join(frmDir, 'Versions', version, 'bin')):
1404 os.symlink(os.path.join(to_framework, fn),
1405 os.path.join(usr_local_bin, fn))
1406
1407 os.chdir(curdir)
1408
Thomas Wouters477c8d52006-05-27 19:21:47 +00001409def patchFile(inPath, outPath):
1410 data = fileContents(inPath)
1411 data = data.replace('$FULL_VERSION', getFullVersion())
1412 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001413 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001414 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001415 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001416 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001417
1418 # This one is not handy as a template variable
1419 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001420 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001421 fp.write(data)
1422 fp.close()
1423
1424def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001425 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001426 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001427 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001428 data = data.replace('@PYVER@', getVersion())
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()
Ned Deily4a96a372013-01-29 00:08:32 -08001432 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001433
1434
1435
1436def packageFromRecipe(targetDir, recipe):
1437 curdir = os.getcwd()
1438 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001439 # The major version (such as 2.5) is included in the package name
1440 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001441 # common.
1442 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001443 srcdir = recipe.get('source')
1444 pkgroot = recipe.get('topdir', srcdir)
1445 postflight = recipe.get('postflight')
1446 readme = textwrap.dedent(recipe['readme'])
1447 isRequired = recipe.get('required', True)
1448
Ned Deily4a96a372013-01-29 00:08:32 -08001449 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001450
1451 # Substitute some variables
1452 textvars = dict(
1453 VER=getVersion(),
1454 FULLVER=getFullVersion(),
1455 )
1456 readme = readme % textvars
1457
1458 if pkgroot is not None:
1459 pkgroot = pkgroot % textvars
1460 else:
1461 pkgroot = '/'
1462
1463 if srcdir is not None:
1464 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1465 srcdir = srcdir % textvars
1466
1467 if postflight is not None:
1468 postflight = os.path.abspath(postflight)
1469
1470 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1471 os.makedirs(packageContents)
1472
1473 if srcdir is not None:
1474 os.chdir(srcdir)
1475 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1476 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1477 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1478
1479 fn = os.path.join(packageContents, 'PkgInfo')
1480 fp = open(fn, 'w')
1481 fp.write('pmkrpkg1')
1482 fp.close()
1483
1484 rsrcDir = os.path.join(packageContents, "Resources")
1485 os.mkdir(rsrcDir)
1486 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1487 fp.write(readme)
1488 fp.close()
1489
1490 if postflight is not None:
1491 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1492
1493 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001494 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001495 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001496 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1497 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1498 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001499 CFBundleShortVersionString=vers,
1500 IFMajorVersion=major,
1501 IFMinorVersion=minor,
1502 IFPkgFormatVersion=0.10000000149011612,
1503 IFPkgFlagAllowBackRev=False,
1504 IFPkgFlagAuthorizationAction="RootAuthorization",
1505 IFPkgFlagDefaultLocation=pkgroot,
1506 IFPkgFlagFollowLinks=True,
1507 IFPkgFlagInstallFat=True,
1508 IFPkgFlagIsRequired=isRequired,
1509 IFPkgFlagOverwritePermissions=False,
1510 IFPkgFlagRelocatable=False,
1511 IFPkgFlagRestartAction="NoRestart",
1512 IFPkgFlagRootVolumeOnly=True,
1513 IFPkgFlagUpdateInstalledLangauges=False,
1514 )
1515 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1516
Ned Deily1931e642020-06-25 04:51:46 -04001517 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001518 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001519 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001520 IFPkgDescriptionVersion=vers,
1521 )
1522 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1523
1524 finally:
1525 os.chdir(curdir)
1526
1527
1528def makeMpkgPlist(path):
1529
1530 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001531 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001532
Ned Deily1931e642020-06-25 04:51:46 -04001533 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001534 CFBundleGetInfoString="Python %s"%(vers,),
1535 CFBundleIdentifier='org.python.Python',
1536 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001537 CFBundleShortVersionString=vers,
1538 IFMajorVersion=major,
1539 IFMinorVersion=minor,
1540 IFPkgFlagComponentDirectory="Contents/Packages",
1541 IFPkgFlagPackageList=[
1542 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001543 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001544 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001545 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001546 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001547 ],
1548 IFPkgFormatVersion=0.10000000149011612,
1549 IFPkgFlagBackgroundScaling="proportional",
1550 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001551 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001552 )
1553
1554 writePlist(pl, path)
1555
1556
1557def buildInstaller():
1558
1559 # Zap all compiled files
1560 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1561 for fn in filenames:
1562 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1563 os.unlink(os.path.join(dirpath, fn))
1564
1565 outdir = os.path.join(WORKDIR, 'installer')
1566 if os.path.exists(outdir):
1567 shutil.rmtree(outdir)
1568 os.mkdir(outdir)
1569
Ronald Oussoren1943f862009-03-30 19:39:14 +00001570 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001571 pkgcontents = os.path.join(pkgroot, 'Packages')
1572 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001573 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001574 packageFromRecipe(pkgcontents, recipe)
1575
1576 rsrcDir = os.path.join(pkgroot, 'Resources')
1577
1578 fn = os.path.join(pkgroot, 'PkgInfo')
1579 fp = open(fn, 'w')
1580 fp.write('pmkrpkg1')
1581 fp.close()
1582
1583 os.mkdir(rsrcDir)
1584
1585 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001586 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001587 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001588 IFPkgDescriptionVersion=getVersion(),
1589 )
1590
1591 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1592 for fn in os.listdir('resources'):
1593 if fn == '.svn': continue
1594 if fn.endswith('.jpg'):
1595 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1596 else:
1597 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1598
Thomas Wouters477c8d52006-05-27 19:21:47 +00001599
1600def installSize(clear=False, _saved=[]):
1601 if clear:
1602 del _saved[:]
1603 if not _saved:
1604 data = captureCommand("du -ks %s"%(
1605 shellQuote(os.path.join(WORKDIR, '_root'))))
1606 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1607 return _saved[0]
1608
1609
1610def buildDMG():
1611 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001612 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001613 """
1614 outdir = os.path.join(WORKDIR, 'diskimage')
1615 if os.path.exists(outdir):
1616 shutil.rmtree(outdir)
1617
Erlend Egeberg Aaslandc94ee132021-01-04 05:48:19 +01001618 # We used to use the deployment target as the last characters of the
Ned Deily936533c2020-11-23 19:04:40 -05001619 # installer file name. With the introduction of weaklinked installer
1620 # variants, we may have two variants with the same file name, i.e.
1621 # both ending in '10.9'. To avoid this, we now use the major/minor
Ned Deily8a374632021-04-27 13:23:39 -04001622 # version numbers of the macOS version we are building on.
1623 # Also, as of macOS 11, operating system version numbering has
1624 # changed from three components to two, i.e.
1625 # 10.14.1, 10.14.2, ...
1626 # 10.15.1, 10.15.2, ...
1627 # 11.1, 11.2, ...
1628 # 12.1, 12.2, ...
1629 # (A further twist is that, when running on macOS 11, binaries built
1630 # on older systems may be shown an operating system version of 10.16
1631 # instead of 11. We should not run into that situation here.)
1632 # Also we should use "macos" instead of "macosx" going forward.
1633 #
1634 # To maintain compability for legacy variants, the file name for
1635 # builds on macOS 10.15 and earlier remains:
1636 # python-3.x.y-macosx10.z.{dmg->pkg}
1637 # e.g. python-3.9.4-macosx10.9.{dmg->pkg}
1638 # and for builds on macOS 11+:
1639 # python-3.x.y-macosz.{dmg->pkg}
1640 # e.g. python-3.9.4-macos11.{dmg->pkg}
1641
1642 build_tuple = getBuildTuple()
1643 if build_tuple[0] < 11:
1644 os_name = 'macosx'
1645 build_system_version = '%s.%s' % build_tuple
1646 else:
1647 os_name = 'macos'
1648 build_system_version = str(build_tuple[0])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001649 imagepath = os.path.join(outdir,
Ned Deily8a374632021-04-27 13:23:39 -04001650 'python-%s-%s%s'%(getFullVersion(),os_name,build_system_version))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001651 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001652 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001653 imagepath = imagepath + '.dmg'
1654
1655 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001656
1657 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1658 # when hdiutil create fails with "Resource busy". For now, just retry
1659 # the create a few times and hope that it eventually works.
1660
Ronald Oussoren1943f862009-03-30 19:39:14 +00001661 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001662 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001663 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001664 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001665 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001666 for i in range(5):
1667 fd = os.popen(cmd, 'r')
1668 data = fd.read()
1669 xit = fd.close()
1670 if not xit:
1671 break
1672 sys.stdout.write(data)
1673 print(" -- retrying hdiutil create")
1674 time.sleep(5)
1675 else:
cclaussd3371692019-06-03 05:19:44 +02001676 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001677
1678 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1679 os.mkdir(os.path.join(WORKDIR, "mnt"))
1680 runCommand("hdiutil attach %s -mountroot %s"%(
1681 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1682
1683 # Custom icon for the DMG, shown when the DMG is mounted.
1684 shutil.copy("../Icons/Disk Image.icns",
1685 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001686 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001687 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1688
1689 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1690
1691 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1692 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1693 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1694 setIcon(imagepath, "../Icons/Disk Image.icns")
1695
1696 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001697
1698 return imagepath
1699
1700
1701def setIcon(filePath, icnsPath):
1702 """
1703 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001704 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001705
Ronald Oussoren70050672010-04-30 15:00:26 +00001706 dirPath = os.path.normpath(os.path.dirname(__file__))
1707 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001708 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1709 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1710 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001711 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1712 if not os.path.exists(appPath):
1713 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001714 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1715 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001716
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001717 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1718 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001719
1720def main():
1721 # First parse options and check if we can perform our work
1722 parseOptions()
1723 checkEnvironment()
1724
Ronald Oussoren1943f862009-03-30 19:39:14 +00001725 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001726 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001727 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001728
1729 if os.path.exists(WORKDIR):
1730 shutil.rmtree(WORKDIR)
1731 os.mkdir(WORKDIR)
1732
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001733 os.environ['LC_ALL'] = 'C'
1734
Thomas Wouters477c8d52006-05-27 19:21:47 +00001735 # Then build third-party libraries such as sleepycat DB4.
1736 buildLibraries()
1737
1738 # Now build python itself
1739 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001740
1741 # And then build the documentation
1742 # Remove the Deployment Target from the shell
1743 # environment, it's no longer needed and
1744 # an unexpected build target can cause problems
1745 # when Sphinx and its dependencies need to
1746 # be (re-)installed.
1747 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001748 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001749
1750
1751 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001752 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001753 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001754 fn = os.path.join(folder, "License.rtf")
1755 patchFile("resources/License.rtf", fn)
1756 fn = os.path.join(folder, "ReadMe.rtf")
1757 patchFile("resources/ReadMe.rtf", fn)
1758 fn = os.path.join(folder, "Update Shell Profile.command")
1759 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001760 fn = os.path.join(folder, "Install Certificates.command")
1761 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001762 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001763 setIcon(folder, "../Icons/Python Folder.icns")
1764
1765 # Create the installer
1766 buildInstaller()
1767
1768 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001769 patchFile('resources/ReadMe.rtf',
1770 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001771
1772 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001773 patchFile('resources/License.rtf',
1774 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001775
1776 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001777 fp.write("# BUILD INFO\n")
1778 fp.write("# Date: %s\n" % time.ctime())
1779 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001780 fp.close()
1781
Thomas Wouters477c8d52006-05-27 19:21:47 +00001782 # And copy it to a DMG
1783 buildDMG()
1784
Thomas Wouters477c8d52006-05-27 19:21:47 +00001785if __name__ == "__main__":
1786 main()