blob: 2afdc32f544ca39911e25e12639e13d1e90487ed [file] [log] [blame]
Ned Deily4a96a372013-01-29 00:08:32 -08001#!/usr/bin/env python
Thomas Wouters477c8d52006-05-27 19:21:47 +00002"""
Ned Deily8c9bb722018-01-30 07:42:14 -05003This script is used to build "official" universal installers on macOS.
4
Ned Deily1931e642020-06-25 04:51:46 -04005NEW for 3.9.0 and backports:
6- 2.7 end-of-life issues:
7 - Python 3 installs now update the Current version link
8 in /Library/Frameworks/Python.framework/Versions
9- fully support running under Python 3 as well as 2.7
10- support building on newer macOS systems with SIP
11- fully support building on macOS 10.9+
12- support 10.6+ on best effort
13- support bypassing docs build by supplying a prebuilt
14 docs html tarball in the third-party source library,
15 in the format and filename conventional of those
16 downloadable from python.org:
17 python-3.x.y-docs-html.tar.bz2
18
Ned Deily8c9bb722018-01-30 07:42:14 -050019NEW for 3.7.0:
20- support Intel 64-bit-only () and 32-bit-only installer builds
Ned Deilyb9e7fe32018-03-29 08:47:27 -040021- build and use internal Tcl/Tk 8.6 for 10.6+ builds
Ned Deily8c9bb722018-01-30 07:42:14 -050022- deprecate use of explicit SDK (--sdk-path=) since all but the oldest
23 versions of Xcode support implicit setting of an SDK via environment
24 variables (SDKROOT and friends, see the xcrun man page for more info).
25 The SDK stuff was primarily needed for building universal installers
26 for 10.4; so as of 3.7.0, building installers for 10.4 is no longer
27 supported with build-installer.
28- use generic "gcc" as compiler (CC env var) rather than "gcc-4.2"
29
30TODO:
Ned Deily1931e642020-06-25 04:51:46 -040031- test building with SDKROOT and DEVELOPER_DIR xcrun env variables
Ned Deily4a96a372013-01-29 00:08:32 -080032
Thomas Wouters477c8d52006-05-27 19:21:47 +000033Usage: see USAGE variable in the script.
34"""
Ned Deily4a96a372013-01-29 00:08:32 -080035import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
36try:
37 import urllib2 as urllib_request
38except ImportError:
39 import urllib.request as urllib_request
40
41STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
42 | stat.S_IRGRP | stat.S_IXGRP
43 | stat.S_IROTH | stat.S_IXOTH )
44
45STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
46 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
47 | stat.S_IROTH | stat.S_IXOTH )
Thomas Wouters477c8d52006-05-27 19:21:47 +000048
Thomas Wouters89f507f2006-12-13 04:49:30 +000049INCLUDE_TIMESTAMP = 1
50VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000051
Ned Deily1931e642020-06-25 04:51:46 -040052RUNNING_ON_PYTHON2 = sys.version_info.major == 2
Thomas Wouters477c8d52006-05-27 19:21:47 +000053
Ned Deily1931e642020-06-25 04:51:46 -040054if RUNNING_ON_PYTHON2:
Thomas Wouters477c8d52006-05-27 19:21:47 +000055 from plistlib import writePlist
Ned Deily1931e642020-06-25 04:51:46 -040056else:
57 from plistlib import dump
58 def writePlist(path, plist):
59 with open(plist, 'wb') as fp:
60 dump(path, fp)
Thomas Wouters477c8d52006-05-27 19:21:47 +000061
62def shellQuote(value):
63 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000064 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000065 a shell command.
66 """
67 return "'%s'"%(value.replace("'", "'\"'\"'"))
68
69def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -080070 """
71 Return the unquoted value of a variable from a file..
72 QUOTED_VALUE='quotes' -> str('quotes')
73 UNQUOTED_VALUE=noquotes -> str('noquotes')
74 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000075 variable = variable + '='
76 for ln in open(fn, 'r'):
77 if ln.startswith(variable):
78 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080079 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080080 raise RuntimeError("Cannot find variable %s" % variable[:-1])
81
82_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000083
84def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080085 global _cache_getVersion
86 if _cache_getVersion is None:
87 _cache_getVersion = grepValue(
88 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
89 return _cache_getVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000090
Ned Deily4a96a372013-01-29 00:08:32 -080091def getVersionMajorMinor():
92 return tuple([int(n) for n in getVersion().split('.', 2)])
93
94_cache_getFullVersion = None
95
Thomas Wouters477c8d52006-05-27 19:21:47 +000096def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080097 global _cache_getFullVersion
98 if _cache_getFullVersion is not None:
99 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +0000100 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
101 for ln in open(fn):
102 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -0800103 _cache_getFullVersion = ln.split()[-1][1:-1]
104 return _cache_getFullVersion
105 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106
Ned Deily5d3febf2014-12-13 00:17:46 -0800107FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
108FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
Ned Deilydde4f632016-09-12 09:39:23 -0400109FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800110
Thomas Wouters89f507f2006-12-13 04:49:30 +0000111# The directory we'll use to create the build (will be erased and recreated)
112WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000113
Thomas Wouters89f507f2006-12-13 04:49:30 +0000114# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000115# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000116DEPSRC = os.path.join(WORKDIR, 'third-party')
117DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000118
Ronald Oussoren41761932020-11-08 10:05:27 +0100119universal_opts_map = { 'universal2': ('arm64', 'x86_64'),
120 '32-bit': ('i386', 'ppc',),
Ronald Oussoren1943f862009-03-30 19:39:14 +0000121 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000122 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500123 'intel-32': ('i386',),
124 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000125 '3-way': ('ppc', 'i386', 'x86_64'),
126 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
127default_target_map = {
Ronald Oussoren41761932020-11-08 10:05:27 +0100128 'universal2': '10.9',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000129 '64-bit': '10.5',
130 '3-way': '10.5',
131 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500132 'intel-32': '10.4',
133 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000134 'all': '10.5',
135}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000136
137UNIVERSALOPTS = tuple(universal_opts_map.keys())
138
139UNIVERSALARCHS = '32-bit'
140
141ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000142
Ezio Melotti42da6632011-03-15 05:18:48 +0200143# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000144SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000145 os.path.dirname(
146 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000147 os.path.abspath(__file__
148 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149
Ronald Oussoren1943f862009-03-30 19:39:14 +0000150# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500151DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000152
Ned Deily04cdfa12014-06-25 13:36:14 -0700153def getDeptargetTuple():
154 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
155
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100156def getBuildTuple():
157 return tuple([int(n) for n in platform.mac_ver()[0].split('.')[0:2]])
158
Ned Deily04cdfa12014-06-25 13:36:14 -0700159def getTargetCompilers():
160 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800161 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500162 '10.5': ('gcc', 'g++'),
163 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700164 }
Ned Deilyacd71632018-02-24 14:30:44 -0500165 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000166
Ned Deily04cdfa12014-06-25 13:36:14 -0700167CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000168
Ned Deily5d3febf2014-12-13 00:17:46 -0800169PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000170
Thomas Wouters89f507f2006-12-13 04:49:30 +0000171USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000172 Usage: build_python [options]
173
174 Options:
175 -? or -h: Show this message
176 -b DIR
177 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
178 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500179 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000180 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500181 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000182 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000183""")% globals()
184
Ned Deily4a96a372013-01-29 00:08:32 -0800185# Dict of object file names with shared library names to check after building.
186# This is to ensure that we ended up dynamically linking with the shared
187# library paths and versions we expected. For example:
188# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
189# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
190# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
191EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000192
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500193# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400194# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500195def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400196 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500197
Ronald Oussoren690a5fa2020-11-22 03:05:34 +0100198# Do we use 8.6.8 when building our own copy
199# of Tcl/Tk or a modern version.
200# We use the old version when buildin on
201# old versions of macOS due to build issues.
202def useOldTk():
203 return getBuildTuple() < (10, 15)
204
Ronald Oussoren41761932020-11-08 10:05:27 +0100205
206def tweak_tcl_build(basedir, archList):
207 with open("Makefile", "r") as fp:
208 contents = fp.readlines()
209
210 # For reasons I don't understand the tcl configure script
211 # decides that some stdlib symbols aren't present, before
212 # deciding that strtod is broken.
213 new_contents = []
214 for line in contents:
215 if line.startswith("COMPAT_OBJS"):
216 # note: the space before strtod.o is intentional,
217 # the detection of a broken strtod results in
218 # "fixstrod.o" on this line.
219 for nm in ("strstr.o", "strtoul.o", " strtod.o"):
220 line = line.replace(nm, "")
221 new_contents.append(line)
222
223 with open("Makefile", "w") as fp:
224 fp.writelines(new_contents)
225
Ned Deily5d3febf2014-12-13 00:17:46 -0800226# List of names of third party software built with this installer.
227# The names will be inserted into the rtf version of the License.
228THIRD_PARTY_LIBS = []
229
Thomas Wouters477c8d52006-05-27 19:21:47 +0000230# Instructions for building libraries that are necessary for building a
231# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000232# [The recipes are defined here for convenience but instantiated later after
233# command line options have been processed.]
234def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000235 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000236
Ned Deily04cdfa12014-06-25 13:36:14 -0700237 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800238
Ned Deilydde4f632016-09-12 09:39:23 -0400239 # Since Apple removed the header files for the deprecated system
240 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
241 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800242
Ned Deilydde4f632016-09-12 09:39:23 -0400243 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800244 dict(
Ned Deily783a6732020-04-21 22:41:33 -0400245 name="OpenSSL 1.1.1g",
246 url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
247 checksum='76766e98997660138cdaf13a187bd234',
Ned Deily5d3febf2014-12-13 00:17:46 -0800248 buildrecipe=build_universal_openssl,
249 configure=None,
250 install=None,
Ronald Oussoren41761932020-11-08 10:05:27 +0100251 patches=[
252 "openssl-mac-arm64.patch",
253 ],
Ned Deily5d3febf2014-12-13 00:17:46 -0800254 ),
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:
266 tcl_tk_ver='8.6.10'
267 tcl_checksum='97c55573f8520bcab74e21bfd8d0aadc'
268
269 tk_checksum='602a47ad9ecac7bf655ada729d140a94'
270 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 Aaslandc94ee132021-01-04 05:48:19 +0100360 name="SQLite 3.34.0",
361 url="https://sqlite.org/2020/sqlite-autoconf-3340000.tar.gz",
362 checksum='7f33c9db7b713957fcb9271fe9049fef',
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 '
369 '-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
Thomas Wouters477c8d52006-05-27 19:21:47 +0000438
Thomas Wouters477c8d52006-05-27 19:21:47 +0000439# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000440def pkg_recipes():
441 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
442 result = [
443 dict(
444 name="PythonFramework",
445 long_name="Python Framework",
446 source="/Library/Frameworks/Python.framework",
447 readme="""\
448 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500449 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000450 """,
451 postflight="scripts/postflight.framework",
452 selected='selected',
453 ),
454 dict(
455 name="PythonApplications",
456 long_name="GUI Applications",
457 source="/Applications/Python %(VER)s",
458 readme="""\
459 This package installs IDLE (an interactive Python IDE),
460 Python Launcher and Build Applet (create application bundles
461 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000462
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000463 It also installs a number of examples and demos.
464 """,
465 required=False,
466 selected='selected',
467 ),
468 dict(
469 name="PythonUnixTools",
470 long_name="UNIX command-line tools",
471 source="/usr/local/bin",
472 readme="""\
473 This package installs the unix tools in /usr/local/bin for
474 compatibility with older releases of Python. This package
475 is not necessary to use Python.
476 """,
477 required=False,
478 selected='selected',
479 ),
480 dict(
481 name="PythonDocumentation",
482 long_name="Python Documentation",
483 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
484 source="/pydocs",
485 readme="""\
486 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800487 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000488 """,
489 postflight="scripts/postflight.documentation",
490 required=False,
491 selected='selected',
492 ),
493 dict(
494 name="PythonProfileChanges",
495 long_name="Shell profile updater",
496 readme="""\
497 This packages updates your shell profile to make sure that
498 the Python tools are found by your shell in preference of
499 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000500
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000501 If you don't install this package you'll have to add
502 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
503 to your PATH by hand.
504 """,
505 postflight="scripts/postflight.patch-profile",
506 topdir="/Library/Frameworks/Python.framework",
507 source="/empty-dir",
508 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800509 selected='selected',
510 ),
511 dict(
512 name="PythonInstallPip",
513 long_name="Install or upgrade pip",
514 readme="""\
515 This package installs (or upgrades from an earlier version)
516 pip, a tool for installing and managing Python packages.
517 """,
518 postflight="scripts/postflight.ensurepip",
519 topdir="/Library/Frameworks/Python.framework",
520 source="/empty-dir",
521 required=False,
522 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000523 ),
524 ]
525
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000526 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000527
Thomas Wouters477c8d52006-05-27 19:21:47 +0000528def fatal(msg):
529 """
530 A fatal error, bail out.
531 """
532 sys.stderr.write('FATAL: ')
533 sys.stderr.write(msg)
534 sys.stderr.write('\n')
535 sys.exit(1)
536
537def fileContents(fn):
538 """
539 Return the contents of the named file
540 """
Ned Deily4a96a372013-01-29 00:08:32 -0800541 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000542
543def runCommand(commandline):
544 """
Ezio Melotti13925002011-03-16 11:05:33 +0200545 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000546 unless the command fails.
547 """
548 fd = os.popen(commandline, 'r')
549 data = fd.read()
550 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000551 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800553 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000554
555 if VERBOSE:
556 sys.stdout.write(data); sys.stdout.flush()
557
558def captureCommand(commandline):
559 fd = os.popen(commandline, 'r')
560 data = fd.read()
561 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000562 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000563 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800564 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000565
566 return data
567
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000568def getTclTkVersion(configfile, versionline):
569 """
570 search Tcl or Tk configuration file for version line
571 """
572 try:
573 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300574 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000575 fatal("Framework configuration file not found: %s" % configfile)
576
577 for l in f:
578 if l.startswith(versionline):
579 f.close()
580 return l
581
582 fatal("Version variable %s not found in framework configuration file: %s"
583 % (versionline, configfile))
584
Thomas Wouters477c8d52006-05-27 19:21:47 +0000585def checkEnvironment():
586 """
587 Check that we're running on a supported system.
588 """
589
Ned Deily936533c2020-11-23 19:04:40 -0500590 if sys.version_info[0:2] < (2, 7):
591 fatal("This script must be run with Python 2.7 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000592
Thomas Wouters477c8d52006-05-27 19:21:47 +0000593 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500594 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000595
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000596 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500597 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000598
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000599 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500600 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000601 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500602 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
603 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
604 # enforce that the version of the user-installed framework also
605 # exists in the system-supplied Tcl/Tk frameworks. Time to support
606 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500607 if not internalTk():
608 frameworks = {}
609 for framework in ['Tcl', 'Tk']:
610 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
611 libfw = os.path.join('/', fwpth)
612 usrfw = os.path.join(os.getenv('HOME'), fwpth)
613 frameworks[framework] = os.readlink(libfw)
614 if not os.path.exists(libfw):
615 fatal("Please install a link to a current %s %s as %s so "
616 "the user can override the system framework."
617 % (framework, frameworks[framework], libfw))
618 if os.path.exists(usrfw):
619 fatal("Please rename %s to avoid possible dynamic load issues."
620 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000621
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500622 if frameworks['Tcl'] != frameworks['Tk']:
623 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000624
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500625 print(" -- Building with external Tcl/Tk %s frameworks"
626 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800627
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500628 # add files to check after build
629 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
630 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
631 % frameworks['Tcl'],
632 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
633 % frameworks['Tk'],
634 ]
635 else:
636 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500637 print("")
638
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000639 # Remove inherited environment variables which might influence build
640 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
641 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
642 for ev in list(os.environ):
643 for prefix in environ_var_prefixes:
644 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800645 print("INFO: deleting environment variable %s=%s" % (
646 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000647 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000648
Ned Deily4a96a372013-01-29 00:08:32 -0800649 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
650 if 'SDK_TOOLS_BIN' in os.environ:
651 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
652 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
653 # add its fixed location here if it exists
654 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
655 if os.path.isdir(OLD_DEVELOPER_TOOLS):
656 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
657 os.environ['PATH'] = base_path
658 print("Setting default PATH: %s"%(os.environ['PATH']))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000659
Thomas Wouters89f507f2006-12-13 04:49:30 +0000660def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000661 """
662 Parse arguments and update global settings.
663 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500664 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800665 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800666 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400667 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000668
669 if args is None:
670 args = sys.argv[1:]
671
672 try:
673 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000674 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
675 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800676 except getopt.GetoptError:
677 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678 sys.exit(1)
679
680 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800681 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000682 sys.exit(1)
683
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000684 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000685 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000686 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800687 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000688 sys.exit(0)
689
690 elif k in ('-d', '--build-dir'):
691 WORKDIR=v
692
693 elif k in ('--third-party',):
694 DEPSRC=v
695
696 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500697 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000698
699 elif k in ('--src-dir',):
700 SRCDIR=v
701
Ronald Oussoren1943f862009-03-30 19:39:14 +0000702 elif k in ('--dep-target', ):
703 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000704 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000705
706 elif k in ('--universal-archs', ):
707 if v in UNIVERSALOPTS:
708 UNIVERSALARCHS = v
709 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000710 if deptarget is None:
711 # Select alternate default deployment
712 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500713 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000714 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800715 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000716
Thomas Wouters477c8d52006-05-27 19:21:47 +0000717 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800718 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000719
720 SRCDIR=os.path.abspath(SRCDIR)
721 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000722 DEPSRC=os.path.abspath(DEPSRC)
723
Ned Deily04cdfa12014-06-25 13:36:14 -0700724 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000725
Ned Deily5d3febf2014-12-13 00:17:46 -0800726 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400727 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800728
729 print("-- Settings:")
730 print(" * Source directory: %s" % SRCDIR)
731 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800732 print(" * Third-party source: %s" % DEPSRC)
733 print(" * Deployment target: %s" % DEPTARGET)
734 print(" * Universal archs: %s" % str(ARCHLIST))
735 print(" * C compiler: %s" % CC)
736 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800737 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800738 print(" -- Building a Python %s framework at patch level %s"
739 % (getVersion(), getFullVersion()))
740 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000741
742def extractArchive(builddir, archiveName):
743 """
744 Extract a source archive into 'builddir'. Returns the path of the
745 extracted archive.
746
747 XXX: This function assumes that archives contain a toplevel directory
748 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700749 safe enough for almost anything we use. Unfortunately, it does not
750 work for current Tcl and Tk source releases where the basename of
751 the archive ends with "-src" but the uncompressed directory does not.
752 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000753 """
754 curdir = os.getcwd()
755 try:
756 os.chdir(builddir)
757 if archiveName.endswith('.tar.gz'):
758 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700759 if ((retval.startswith('tcl') or retval.startswith('tk'))
760 and retval.endswith('-src')):
761 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000762 if os.path.exists(retval):
763 shutil.rmtree(retval)
764 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
765
766 elif archiveName.endswith('.tar.bz2'):
767 retval = os.path.basename(archiveName[:-8])
768 if os.path.exists(retval):
769 shutil.rmtree(retval)
770 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
771
772 elif archiveName.endswith('.tar'):
773 retval = os.path.basename(archiveName[:-4])
774 if os.path.exists(retval):
775 shutil.rmtree(retval)
776 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
777
778 elif archiveName.endswith('.zip'):
779 retval = os.path.basename(archiveName[:-4])
780 if os.path.exists(retval):
781 shutil.rmtree(retval)
782 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
783
784 data = fp.read()
785 xit = fp.close()
786 if xit is not None:
787 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800788 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000789
790 return os.path.join(builddir, retval)
791
792 finally:
793 os.chdir(curdir)
794
Thomas Wouters477c8d52006-05-27 19:21:47 +0000795def downloadURL(url, fname):
796 """
797 Download the contents of the url into the file.
798 """
Ned Deily4a96a372013-01-29 00:08:32 -0800799 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000800 fpOut = open(fname, 'wb')
801 block = fpIn.read(10240)
802 try:
803 while block:
804 fpOut.write(block)
805 block = fpIn.read(10240)
806 fpIn.close()
807 fpOut.close()
808 except:
809 try:
810 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300811 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000812 pass
813
Ned Deily4a96a372013-01-29 00:08:32 -0800814def verifyThirdPartyFile(url, checksum, fname):
815 """
816 Download file from url to filename fname if it does not already exist.
817 Abort if file contents does not match supplied md5 checksum.
818 """
819 name = os.path.basename(fname)
820 if os.path.exists(fname):
821 print("Using local copy of %s"%(name,))
822 else:
823 print("Did not find local copy of %s"%(name,))
824 print("Downloading %s"%(name,))
825 downloadURL(url, fname)
826 print("Archive for %s stored as %s"%(name, fname))
827 if os.system(
828 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
829 % (shellQuote(fname), checksum) ):
830 fatal('MD5 checksum mismatch for file %s' % fname)
831
Ned Deily5d3febf2014-12-13 00:17:46 -0800832def build_universal_openssl(basedir, archList):
833 """
834 Special case build recipe for universal build of openssl.
835
836 The upstream OpenSSL build system does not directly support
837 OS X universal builds. We need to build each architecture
838 separately then lipo them together into fat libraries.
839 """
840
841 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
842 # If we are building on a 10.4.x or earlier system,
843 # unilaterally disable assembly code building to avoid the problem.
844 no_asm = int(platform.release().split(".")[0]) < 9
845
846 def build_openssl_arch(archbase, arch):
847 "Build one architecture of openssl"
848 arch_opts = {
849 "i386": ["darwin-i386-cc"],
850 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100851 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800852 "ppc": ["darwin-ppc-cc"],
853 "ppc64": ["darwin64-ppc-cc"],
854 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400855
856 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
857 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
858 # building on our 10.6 gcc-4.2 environment. There have been other
859 # reports of projects running into this when using older compilers.
860 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
861 # building for 10.6.
862 if getDeptargetTuple() == (10, 6):
863 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
864
Ned Deily5d3febf2014-12-13 00:17:46 -0800865 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800866 "no-idea",
867 "no-mdc2",
868 "no-rc5",
869 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800870 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800871 # "enable-unit-test",
872 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800873 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400874 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800875 ]
876 if no_asm:
877 configure_opts.append("no-asm")
878 runCommand(" ".join(["perl", "Configure"]
879 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500880 runCommand("make depend")
881 runCommand("make all")
882 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800883 # runCommand("make test")
884 return
885
886 srcdir = os.getcwd()
887 universalbase = os.path.join(srcdir, "..",
888 os.path.basename(srcdir) + "-universal")
889 os.mkdir(universalbase)
890 archbasefws = []
891 for arch in archList:
892 # fresh copy of the source tree
893 archsrc = os.path.join(universalbase, arch, "src")
894 shutil.copytree(srcdir, archsrc, symlinks=True)
895 # install base for this arch
896 archbase = os.path.join(universalbase, arch, "root")
897 os.mkdir(archbase)
898 # Python framework base within install_prefix:
899 # the build will install into this framework..
900 # This is to ensure that the resulting shared libs have
901 # the desired real install paths built into them.
902 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
903
904 # build one architecture
905 os.chdir(archsrc)
906 build_openssl_arch(archbase, arch)
907 os.chdir(srcdir)
908 archbasefws.append(archbasefw)
909
910 # copy arch-independent files from last build into the basedir framework
911 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
912 shutil.copytree(
913 os.path.join(archbasefw, "include", "openssl"),
914 os.path.join(basefw, "include", "openssl")
915 )
916
917 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
918 "SHLIB_VERSION_NUMBER")
919 # e.g. -> "1.0.0"
920 libcrypto = "libcrypto.dylib"
921 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
922 # e.g. -> "libcrypto.1.0.0.dylib"
923 libssl = "libssl.dylib"
924 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
925 # e.g. -> "libssl.1.0.0.dylib"
926
927 try:
928 os.mkdir(os.path.join(basefw, "lib"))
929 except OSError:
930 pass
931
932 # merge the individual arch-dependent shared libs into a fat shared lib
933 archbasefws.insert(0, basefw)
934 for (lib_unversioned, lib_versioned) in [
935 (libcrypto, libcrypto_versioned),
936 (libssl, libssl_versioned)
937 ]:
938 runCommand("lipo -create -output " +
939 " ".join(shellQuote(
940 os.path.join(fw, "lib", lib_versioned))
941 for fw in archbasefws))
942 # and create an unversioned symlink of it
943 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
944
945 # Create links in the temp include and lib dirs that will be injected
946 # into the Python build so that setup.py can find them while building
947 # and the versioned links so that the setup.py post-build import test
948 # does not fail.
949 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
950 for fn in [
951 ["include", "openssl"],
952 ["lib", libcrypto],
953 ["lib", libssl],
954 ["lib", libcrypto_versioned],
955 ["lib", libssl_versioned],
956 ]:
957 os.symlink(
958 os.path.join(relative_path, *fn),
959 os.path.join(basedir, "usr", "local", *fn)
960 )
961
962 return
963
Thomas Wouters477c8d52006-05-27 19:21:47 +0000964def buildRecipe(recipe, basedir, archList):
965 """
966 Build software using a recipe. This function does the
967 'configure;make;make install' dance for C software, with a possibility
968 to customize this process, basically a poor-mans DarwinPorts.
969 """
970 curdir = os.getcwd()
971
972 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800973 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000974 url = recipe['url']
975 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800976 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000977 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
978 shellQuote(basedir)))
979
980 archiveName = os.path.split(url)[-1]
981 sourceArchive = os.path.join(DEPSRC, archiveName)
982
983 if not os.path.exists(DEPSRC):
984 os.mkdir(DEPSRC)
985
Ned Deily4a96a372013-01-29 00:08:32 -0800986 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
987 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000988 buildDir=os.path.join(WORKDIR, '_bld')
989 if not os.path.exists(buildDir):
990 os.mkdir(buildDir)
991
992 workDir = extractArchive(buildDir, sourceArchive)
993 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000994
Ned Deily4a96a372013-01-29 00:08:32 -0800995 for patch in recipe.get('patches', ()):
996 if isinstance(patch, tuple):
997 url, checksum = patch
998 fn = os.path.join(DEPSRC, os.path.basename(url))
999 verifyThirdPartyFile(url, checksum, fn)
1000 else:
1001 # patch is a file in the source directory
1002 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001003 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1004 shellQuote(fn),))
1005
Ned Deily4a96a372013-01-29 00:08:32 -08001006 for patchscript in recipe.get('patchscripts', ()):
1007 if isinstance(patchscript, tuple):
1008 url, checksum = patchscript
1009 fn = os.path.join(DEPSRC, os.path.basename(url))
1010 verifyThirdPartyFile(url, checksum, fn)
1011 else:
1012 # patch is a file in the source directory
1013 fn = os.path.join(curdir, patchscript)
1014 if fn.endswith('.bz2'):
1015 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1016 fn = fn[:-4]
1017 runCommand('sh %s' % shellQuote(fn))
1018 os.unlink(fn)
1019
Ned Deily94764b22013-10-27 19:49:29 -07001020 if 'buildDir' in recipe:
1021 os.chdir(recipe['buildDir'])
1022
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001023 if configure is not None:
1024 configure_args = [
1025 "--prefix=/usr/local",
1026 "--enable-static",
1027 "--disable-shared",
1028 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1029 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001030
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001031 if 'configure_pre' in recipe:
1032 args = list(recipe['configure_pre'])
1033 if '--disable-static' in args:
1034 configure_args.remove('--enable-static')
1035 if '--enable-shared' in args:
1036 configure_args.remove('--disable-shared')
1037 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001039 if recipe.get('useLDFlags', 1):
1040 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001041 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001042 "-I%s/usr/local/include"%(
1043 recipe.get('extra_cflags', ''),
1044 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001045 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001046 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001047 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001048 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001049 shellQuote(basedir)[1:-1],
1050 ' -arch '.join(archList)),
1051 ])
1052 else:
1053 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001054 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001055 "-I%s/usr/local/include"%(
1056 recipe.get('extra_cflags', ''),
1057 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001058 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001059 shellQuote(basedir)[1:-1],),
1060 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001061
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001062 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001063 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001064
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001065 configure_args.insert(0, configure)
1066 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067
Ned Deily4a96a372013-01-29 00:08:32 -08001068 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001069 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001070
Ned Deily5d3febf2014-12-13 00:17:46 -08001071 if buildrecipe is not None:
1072 # call special-case build recipe, e.g. for openssl
1073 buildrecipe(basedir, archList)
1074
1075 if install is not None:
1076 print("Running install for %s"%(name,))
1077 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001078
Ned Deily4a96a372013-01-29 00:08:32 -08001079 print("Done %s"%(name,))
1080 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001081
1082 os.chdir(curdir)
1083
1084def buildLibraries():
1085 """
1086 Build our dependencies into $WORKDIR/libraries/usr/local
1087 """
Ned Deily4a96a372013-01-29 00:08:32 -08001088 print("")
1089 print("Building required libraries")
1090 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001091 universal = os.path.join(WORKDIR, 'libraries')
1092 os.mkdir(universal)
1093 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1094 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1095
Ronald Oussoren1943f862009-03-30 19:39:14 +00001096 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001097 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001098
1099
1100
1101def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001102 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001103 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001104 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001105 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001106 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001108 curDir = os.getcwd()
1109 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001110 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001111
1112 # Search third-party source directory for a pre-built version of the docs.
1113 # Use the naming convention of the docs.python.org html downloads:
1114 # python-3.9.0b1-docs-html.tar.bz2
1115 doctarfiles = [ f for f in os.listdir(DEPSRC)
1116 if f.startswith('python-'+getFullVersion())
1117 if f.endswith('-docs-html.tar.bz2') ]
1118 if doctarfiles:
1119 doctarfile = doctarfiles[0]
1120 if not os.path.exists('build'):
1121 os.mkdir('build')
1122 # if build directory existed, it was emptied by make clean, above
1123 os.chdir('build')
1124 # Extract the first archive found for this version into build
1125 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1126 # see if tar extracted a directory ending in -docs-html
1127 archivefiles = [ f for f in os.listdir('.')
1128 if f.endswith('-docs-html')
1129 if os.path.isdir(f) ]
1130 if archivefiles:
1131 archivefile = archivefiles[0]
1132 # make it our 'Docs/build/html' directory
1133 print(' -- using pre-built python documentation from %s'%archivefile)
1134 os.rename(archivefile, 'html')
1135 os.chdir(buildDir)
1136
1137 htmlDir = os.path.join('build', 'html')
1138 if not os.path.exists(htmlDir):
1139 # Create virtual environment for docs builds with blurb and sphinx
1140 runCommand('make venv')
Ned Deily1931e642020-06-25 04:51:46 -04001141 runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
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
1622 # version numbers of the macOS version we are building on, i.e.
1623 # '10.9' as before for 10.9+ variant, '11.0' for universal2 11.0-.
1624 # it's not ideal but should cause the least disruption to packaging
1625 # workflows.
1626 build_system_version = '.'.join(platform.mac_ver()[0].split('.')[0:2])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001627 imagepath = os.path.join(outdir,
Ned Deily936533c2020-11-23 19:04:40 -05001628 'python-%s-macosx%s'%(getFullVersion(),build_system_version))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001629 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001630 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001631 imagepath = imagepath + '.dmg'
1632
1633 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001634
1635 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1636 # when hdiutil create fails with "Resource busy". For now, just retry
1637 # the create a few times and hope that it eventually works.
1638
Ronald Oussoren1943f862009-03-30 19:39:14 +00001639 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001640 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001641 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001642 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001643 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001644 for i in range(5):
1645 fd = os.popen(cmd, 'r')
1646 data = fd.read()
1647 xit = fd.close()
1648 if not xit:
1649 break
1650 sys.stdout.write(data)
1651 print(" -- retrying hdiutil create")
1652 time.sleep(5)
1653 else:
cclaussd3371692019-06-03 05:19:44 +02001654 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001655
1656 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1657 os.mkdir(os.path.join(WORKDIR, "mnt"))
1658 runCommand("hdiutil attach %s -mountroot %s"%(
1659 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1660
1661 # Custom icon for the DMG, shown when the DMG is mounted.
1662 shutil.copy("../Icons/Disk Image.icns",
1663 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001664 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001665 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1666
1667 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1668
1669 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1670 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1671 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1672 setIcon(imagepath, "../Icons/Disk Image.icns")
1673
1674 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001675
1676 return imagepath
1677
1678
1679def setIcon(filePath, icnsPath):
1680 """
1681 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001682 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001683
Ronald Oussoren70050672010-04-30 15:00:26 +00001684 dirPath = os.path.normpath(os.path.dirname(__file__))
1685 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001686 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1687 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1688 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001689 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1690 if not os.path.exists(appPath):
1691 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001692 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1693 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001694
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001695 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1696 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001697
1698def main():
1699 # First parse options and check if we can perform our work
1700 parseOptions()
1701 checkEnvironment()
1702
Ronald Oussoren1943f862009-03-30 19:39:14 +00001703 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001704 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001705 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001706
1707 if os.path.exists(WORKDIR):
1708 shutil.rmtree(WORKDIR)
1709 os.mkdir(WORKDIR)
1710
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001711 os.environ['LC_ALL'] = 'C'
1712
Thomas Wouters477c8d52006-05-27 19:21:47 +00001713 # Then build third-party libraries such as sleepycat DB4.
1714 buildLibraries()
1715
1716 # Now build python itself
1717 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001718
1719 # And then build the documentation
1720 # Remove the Deployment Target from the shell
1721 # environment, it's no longer needed and
1722 # an unexpected build target can cause problems
1723 # when Sphinx and its dependencies need to
1724 # be (re-)installed.
1725 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001726 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001727
1728
1729 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001730 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001731 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001732 fn = os.path.join(folder, "License.rtf")
1733 patchFile("resources/License.rtf", fn)
1734 fn = os.path.join(folder, "ReadMe.rtf")
1735 patchFile("resources/ReadMe.rtf", fn)
1736 fn = os.path.join(folder, "Update Shell Profile.command")
1737 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001738 fn = os.path.join(folder, "Install Certificates.command")
1739 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001740 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001741 setIcon(folder, "../Icons/Python Folder.icns")
1742
1743 # Create the installer
1744 buildInstaller()
1745
1746 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001747 patchFile('resources/ReadMe.rtf',
1748 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001749
1750 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001751 patchFile('resources/License.rtf',
1752 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001753
1754 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001755 fp.write("# BUILD INFO\n")
1756 fp.write("# Date: %s\n" % time.ctime())
1757 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001758 fp.close()
1759
Thomas Wouters477c8d52006-05-27 19:21:47 +00001760 # And copy it to a DMG
1761 buildDMG()
1762
Thomas Wouters477c8d52006-05-27 19:21:47 +00001763if __name__ == "__main__":
1764 main()