blob: 0e76d3ca5bbc2e1557cfaf1f14ffd68a7d12623c [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
156def getTargetCompilers():
157 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800158 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500159 '10.5': ('gcc', 'g++'),
160 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700161 }
Ned Deilyacd71632018-02-24 14:30:44 -0500162 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000163
Ned Deily04cdfa12014-06-25 13:36:14 -0700164CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000165
Ned Deily5d3febf2014-12-13 00:17:46 -0800166PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000167
Thomas Wouters89f507f2006-12-13 04:49:30 +0000168USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000169 Usage: build_python [options]
170
171 Options:
172 -? or -h: Show this message
173 -b DIR
174 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
175 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500176 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000177 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500178 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000179 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000180""")% globals()
181
Ned Deily4a96a372013-01-29 00:08:32 -0800182# Dict of object file names with shared library names to check after building.
183# This is to ensure that we ended up dynamically linking with the shared
184# library paths and versions we expected. For example:
185# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
186# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
187# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
188EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000189
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500190# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400191# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500192def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400193 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500194
Ronald Oussoren41761932020-11-08 10:05:27 +0100195
196def tweak_tcl_build(basedir, archList):
197 with open("Makefile", "r") as fp:
198 contents = fp.readlines()
199
200 # For reasons I don't understand the tcl configure script
201 # decides that some stdlib symbols aren't present, before
202 # deciding that strtod is broken.
203 new_contents = []
204 for line in contents:
205 if line.startswith("COMPAT_OBJS"):
206 # note: the space before strtod.o is intentional,
207 # the detection of a broken strtod results in
208 # "fixstrod.o" on this line.
209 for nm in ("strstr.o", "strtoul.o", " strtod.o"):
210 line = line.replace(nm, "")
211 new_contents.append(line)
212
213 with open("Makefile", "w") as fp:
214 fp.writelines(new_contents)
215
Ned Deily5d3febf2014-12-13 00:17:46 -0800216# List of names of third party software built with this installer.
217# The names will be inserted into the rtf version of the License.
218THIRD_PARTY_LIBS = []
219
Thomas Wouters477c8d52006-05-27 19:21:47 +0000220# Instructions for building libraries that are necessary for building a
221# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000222# [The recipes are defined here for convenience but instantiated later after
223# command line options have been processed.]
224def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000225 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000226
Ned Deily04cdfa12014-06-25 13:36:14 -0700227 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800228
Ned Deilydde4f632016-09-12 09:39:23 -0400229 # Since Apple removed the header files for the deprecated system
230 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
231 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800232
Ned Deilydde4f632016-09-12 09:39:23 -0400233 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800234 dict(
Ned Deily783a6732020-04-21 22:41:33 -0400235 name="OpenSSL 1.1.1g",
236 url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
237 checksum='76766e98997660138cdaf13a187bd234',
Ned Deily5d3febf2014-12-13 00:17:46 -0800238 buildrecipe=build_universal_openssl,
239 configure=None,
240 install=None,
Ronald Oussoren41761932020-11-08 10:05:27 +0100241 patches=[
242 "openssl-mac-arm64.patch",
243 ],
Ned Deily5d3febf2014-12-13 00:17:46 -0800244 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400245 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800246
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500247 if internalTk():
Ned Deily5b3582c2013-10-25 00:41:46 -0700248 result.extend([
249 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500250 name="Tcl 8.6.8",
251 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
252 checksum='81656d3367af032e0ae6157eff134f89',
Ned Deily5b3582c2013-10-25 00:41:46 -0700253 buildDir="unix",
254 configure_pre=[
255 '--enable-shared',
256 '--enable-threads',
257 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
258 ],
259 useLDFlags=False,
Ronald Oussoren41761932020-11-08 10:05:27 +0100260 buildrecipe=tweak_tcl_build,
Ned Deily5b3582c2013-10-25 00:41:46 -0700261 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
262 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500263 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700264 },
265 ),
266 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500267 name="Tk 8.6.8",
268 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
269 checksum='5e0faecba458ee1386078fb228d008ba',
270 patches=[
271 "tk868_on_10_8_10_9.patch",
272 ],
Ned Deily5b3582c2013-10-25 00:41:46 -0700273 buildDir="unix",
274 configure_pre=[
275 '--enable-aqua',
276 '--enable-shared',
277 '--enable-threads',
278 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
279 ],
280 useLDFlags=False,
281 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'%{
282 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500283 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
284 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700285 },
286 ),
287 ])
288
Ned Deilyed730102014-11-14 18:55:05 -0800289 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800290 result.extend([
291 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500292 name="XZ 5.2.3",
293 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
294 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800295 configure_pre=[
296 '--disable-dependency-tracking',
297 ]
298 ),
299 ])
300
301 result.extend([
302 dict(
303 name="NCurses 5.9",
304 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
305 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
306 configure_pre=[
307 "--enable-widec",
308 "--without-cxx",
309 "--without-cxx-binding",
310 "--without-ada",
311 "--without-curses-h",
312 "--enable-shared",
313 "--with-shared",
314 "--without-debug",
315 "--without-normal",
316 "--without-tests",
317 "--without-manpages",
318 "--datadir=/usr/share",
319 "--sysconfdir=/etc",
320 "--sharedstatedir=/usr/com",
321 "--with-terminfo-dirs=/usr/share/terminfo",
322 "--with-default-terminfo-dir=/usr/share/terminfo",
323 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
324 ],
325 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200326 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800327 "f54bf02a349f96a7c4f0d00922f3a0d4"),
328 ],
329 useLDFlags=False,
330 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
331 shellQuote(os.path.join(WORKDIR, 'libraries')),
332 shellQuote(os.path.join(WORKDIR, 'libraries')),
333 getVersion(),
334 ),
335 ),
336 dict(
Erlend Egeberg Aasland9a764262020-10-05 10:09:16 +0200337 name="SQLite 3.33.0",
338 url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz",
339 checksum='842a8a100d7b01b09e543deb2b7951dd',
Ned Deily4a96a372013-01-29 00:08:32 -0800340 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700341 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800342 '-DSQLITE_ENABLE_FTS4 '
343 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500344 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800345 '-DSQLITE_ENABLE_RTREE '
346 '-DSQLITE_TCL=0 '
347 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
348 configure_pre=[
349 '--enable-threadsafe',
350 '--enable-shared=no',
351 '--enable-static=yes',
352 '--disable-readline',
353 '--disable-dependency-tracking',
354 ]
355 ),
356 ])
357
Ned Deily04cdfa12014-06-25 13:36:14 -0700358 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000359 result.extend([
360 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000361 name="Bzip2 1.0.6",
362 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
363 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000364 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500365 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800366 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000367 shellQuote(os.path.join(WORKDIR, 'libraries')),
368 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000369 ),
370 ),
371 dict(
372 name="ZLib 1.2.3",
373 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
374 checksum='debc62758716a169df9f62e6ab2bc634',
375 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500376 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800377 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000378 shellQuote(os.path.join(WORKDIR, 'libraries')),
379 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000380 ),
381 ),
382 dict(
383 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000384 name="GNU Readline 6.1.2",
385 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
386 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000387 patchlevel='0',
388 patches=[
389 # The readline maintainers don't do actual micro releases, but
390 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800391 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
392 'c642f2e84d820884b0bf9fd176bc6c3f'),
393 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
394 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000395 ]
396 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000397 ])
398
Ned Deily4f7ff782011-01-15 05:29:12 +0000399 if not PYTHON_3:
400 result.extend([
401 dict(
402 name="Sleepycat DB 4.7.25",
403 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
404 checksum='ec2b87e833779681a0c3a814aa71359e',
405 buildDir="build_unix",
406 configure="../dist/configure",
407 configure_pre=[
408 '--includedir=/usr/local/include/db4',
409 ]
410 ),
411 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000412
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000413 return result
414
Thomas Wouters477c8d52006-05-27 19:21:47 +0000415
Thomas Wouters477c8d52006-05-27 19:21:47 +0000416# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000417def pkg_recipes():
418 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
419 result = [
420 dict(
421 name="PythonFramework",
422 long_name="Python Framework",
423 source="/Library/Frameworks/Python.framework",
424 readme="""\
425 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500426 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000427 """,
428 postflight="scripts/postflight.framework",
429 selected='selected',
430 ),
431 dict(
432 name="PythonApplications",
433 long_name="GUI Applications",
434 source="/Applications/Python %(VER)s",
435 readme="""\
436 This package installs IDLE (an interactive Python IDE),
437 Python Launcher and Build Applet (create application bundles
438 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000439
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000440 It also installs a number of examples and demos.
441 """,
442 required=False,
443 selected='selected',
444 ),
445 dict(
446 name="PythonUnixTools",
447 long_name="UNIX command-line tools",
448 source="/usr/local/bin",
449 readme="""\
450 This package installs the unix tools in /usr/local/bin for
451 compatibility with older releases of Python. This package
452 is not necessary to use Python.
453 """,
454 required=False,
455 selected='selected',
456 ),
457 dict(
458 name="PythonDocumentation",
459 long_name="Python Documentation",
460 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
461 source="/pydocs",
462 readme="""\
463 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800464 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000465 """,
466 postflight="scripts/postflight.documentation",
467 required=False,
468 selected='selected',
469 ),
470 dict(
471 name="PythonProfileChanges",
472 long_name="Shell profile updater",
473 readme="""\
474 This packages updates your shell profile to make sure that
475 the Python tools are found by your shell in preference of
476 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000477
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000478 If you don't install this package you'll have to add
479 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
480 to your PATH by hand.
481 """,
482 postflight="scripts/postflight.patch-profile",
483 topdir="/Library/Frameworks/Python.framework",
484 source="/empty-dir",
485 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800486 selected='selected',
487 ),
488 dict(
489 name="PythonInstallPip",
490 long_name="Install or upgrade pip",
491 readme="""\
492 This package installs (or upgrades from an earlier version)
493 pip, a tool for installing and managing Python packages.
494 """,
495 postflight="scripts/postflight.ensurepip",
496 topdir="/Library/Frameworks/Python.framework",
497 source="/empty-dir",
498 required=False,
499 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000500 ),
501 ]
502
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000503 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000504
Thomas Wouters477c8d52006-05-27 19:21:47 +0000505def fatal(msg):
506 """
507 A fatal error, bail out.
508 """
509 sys.stderr.write('FATAL: ')
510 sys.stderr.write(msg)
511 sys.stderr.write('\n')
512 sys.exit(1)
513
514def fileContents(fn):
515 """
516 Return the contents of the named file
517 """
Ned Deily4a96a372013-01-29 00:08:32 -0800518 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000519
520def runCommand(commandline):
521 """
Ezio Melotti13925002011-03-16 11:05:33 +0200522 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000523 unless the command fails.
524 """
525 fd = os.popen(commandline, 'r')
526 data = fd.read()
527 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000528 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000529 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800530 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000531
532 if VERBOSE:
533 sys.stdout.write(data); sys.stdout.flush()
534
535def captureCommand(commandline):
536 fd = os.popen(commandline, 'r')
537 data = fd.read()
538 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000539 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000540 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800541 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000542
543 return data
544
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000545def getTclTkVersion(configfile, versionline):
546 """
547 search Tcl or Tk configuration file for version line
548 """
549 try:
550 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300551 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000552 fatal("Framework configuration file not found: %s" % configfile)
553
554 for l in f:
555 if l.startswith(versionline):
556 f.close()
557 return l
558
559 fatal("Version variable %s not found in framework configuration file: %s"
560 % (versionline, configfile))
561
Thomas Wouters477c8d52006-05-27 19:21:47 +0000562def checkEnvironment():
563 """
564 Check that we're running on a supported system.
565 """
566
Ned Deily8c9bb722018-01-30 07:42:14 -0500567 if sys.version_info[0:2] < (2, 5):
568 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000569
Thomas Wouters477c8d52006-05-27 19:21:47 +0000570 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500571 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000572
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000573 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500574 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000575
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000576 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500577 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000578 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500579 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
580 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
581 # enforce that the version of the user-installed framework also
582 # exists in the system-supplied Tcl/Tk frameworks. Time to support
583 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500584 if not internalTk():
585 frameworks = {}
586 for framework in ['Tcl', 'Tk']:
587 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
588 libfw = os.path.join('/', fwpth)
589 usrfw = os.path.join(os.getenv('HOME'), fwpth)
590 frameworks[framework] = os.readlink(libfw)
591 if not os.path.exists(libfw):
592 fatal("Please install a link to a current %s %s as %s so "
593 "the user can override the system framework."
594 % (framework, frameworks[framework], libfw))
595 if os.path.exists(usrfw):
596 fatal("Please rename %s to avoid possible dynamic load issues."
597 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000598
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500599 if frameworks['Tcl'] != frameworks['Tk']:
600 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000601
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500602 print(" -- Building with external Tcl/Tk %s frameworks"
603 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800604
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500605 # add files to check after build
606 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
607 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
608 % frameworks['Tcl'],
609 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
610 % frameworks['Tk'],
611 ]
612 else:
613 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500614 print("")
615
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000616 # Remove inherited environment variables which might influence build
617 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
618 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
619 for ev in list(os.environ):
620 for prefix in environ_var_prefixes:
621 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800622 print("INFO: deleting environment variable %s=%s" % (
623 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000624 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000625
Ned Deily4a96a372013-01-29 00:08:32 -0800626 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
627 if 'SDK_TOOLS_BIN' in os.environ:
628 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
629 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
630 # add its fixed location here if it exists
631 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
632 if os.path.isdir(OLD_DEVELOPER_TOOLS):
633 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
634 os.environ['PATH'] = base_path
635 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400636 # Ensure we have access to sphinx-build.
637 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700638 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000639
Thomas Wouters89f507f2006-12-13 04:49:30 +0000640def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000641 """
642 Parse arguments and update global settings.
643 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500644 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800645 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800646 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400647 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000648
649 if args is None:
650 args = sys.argv[1:]
651
652 try:
653 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000654 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
655 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800656 except getopt.GetoptError:
657 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000658 sys.exit(1)
659
660 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800661 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000662 sys.exit(1)
663
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000664 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000666 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800667 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000668 sys.exit(0)
669
670 elif k in ('-d', '--build-dir'):
671 WORKDIR=v
672
673 elif k in ('--third-party',):
674 DEPSRC=v
675
676 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500677 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678
679 elif k in ('--src-dir',):
680 SRCDIR=v
681
Ronald Oussoren1943f862009-03-30 19:39:14 +0000682 elif k in ('--dep-target', ):
683 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000684 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000685
686 elif k in ('--universal-archs', ):
687 if v in UNIVERSALOPTS:
688 UNIVERSALARCHS = v
689 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000690 if deptarget is None:
691 # Select alternate default deployment
692 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500693 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000694 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800695 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000696
Thomas Wouters477c8d52006-05-27 19:21:47 +0000697 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800698 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000699
700 SRCDIR=os.path.abspath(SRCDIR)
701 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000702 DEPSRC=os.path.abspath(DEPSRC)
703
Ned Deily04cdfa12014-06-25 13:36:14 -0700704 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000705
Ned Deily5d3febf2014-12-13 00:17:46 -0800706 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400707 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800708
709 print("-- Settings:")
710 print(" * Source directory: %s" % SRCDIR)
711 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800712 print(" * Third-party source: %s" % DEPSRC)
713 print(" * Deployment target: %s" % DEPTARGET)
714 print(" * Universal archs: %s" % str(ARCHLIST))
715 print(" * C compiler: %s" % CC)
716 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800717 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800718 print(" -- Building a Python %s framework at patch level %s"
719 % (getVersion(), getFullVersion()))
720 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000721
722def extractArchive(builddir, archiveName):
723 """
724 Extract a source archive into 'builddir'. Returns the path of the
725 extracted archive.
726
727 XXX: This function assumes that archives contain a toplevel directory
728 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700729 safe enough for almost anything we use. Unfortunately, it does not
730 work for current Tcl and Tk source releases where the basename of
731 the archive ends with "-src" but the uncompressed directory does not.
732 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000733 """
734 curdir = os.getcwd()
735 try:
736 os.chdir(builddir)
737 if archiveName.endswith('.tar.gz'):
738 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700739 if ((retval.startswith('tcl') or retval.startswith('tk'))
740 and retval.endswith('-src')):
741 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000742 if os.path.exists(retval):
743 shutil.rmtree(retval)
744 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
745
746 elif archiveName.endswith('.tar.bz2'):
747 retval = os.path.basename(archiveName[:-8])
748 if os.path.exists(retval):
749 shutil.rmtree(retval)
750 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
751
752 elif archiveName.endswith('.tar'):
753 retval = os.path.basename(archiveName[:-4])
754 if os.path.exists(retval):
755 shutil.rmtree(retval)
756 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
757
758 elif archiveName.endswith('.zip'):
759 retval = os.path.basename(archiveName[:-4])
760 if os.path.exists(retval):
761 shutil.rmtree(retval)
762 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
763
764 data = fp.read()
765 xit = fp.close()
766 if xit is not None:
767 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800768 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000769
770 return os.path.join(builddir, retval)
771
772 finally:
773 os.chdir(curdir)
774
Thomas Wouters477c8d52006-05-27 19:21:47 +0000775def downloadURL(url, fname):
776 """
777 Download the contents of the url into the file.
778 """
Ned Deily4a96a372013-01-29 00:08:32 -0800779 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000780 fpOut = open(fname, 'wb')
781 block = fpIn.read(10240)
782 try:
783 while block:
784 fpOut.write(block)
785 block = fpIn.read(10240)
786 fpIn.close()
787 fpOut.close()
788 except:
789 try:
790 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300791 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000792 pass
793
Ned Deily4a96a372013-01-29 00:08:32 -0800794def verifyThirdPartyFile(url, checksum, fname):
795 """
796 Download file from url to filename fname if it does not already exist.
797 Abort if file contents does not match supplied md5 checksum.
798 """
799 name = os.path.basename(fname)
800 if os.path.exists(fname):
801 print("Using local copy of %s"%(name,))
802 else:
803 print("Did not find local copy of %s"%(name,))
804 print("Downloading %s"%(name,))
805 downloadURL(url, fname)
806 print("Archive for %s stored as %s"%(name, fname))
807 if os.system(
808 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
809 % (shellQuote(fname), checksum) ):
810 fatal('MD5 checksum mismatch for file %s' % fname)
811
Ned Deily5d3febf2014-12-13 00:17:46 -0800812def build_universal_openssl(basedir, archList):
813 """
814 Special case build recipe for universal build of openssl.
815
816 The upstream OpenSSL build system does not directly support
817 OS X universal builds. We need to build each architecture
818 separately then lipo them together into fat libraries.
819 """
820
821 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
822 # If we are building on a 10.4.x or earlier system,
823 # unilaterally disable assembly code building to avoid the problem.
824 no_asm = int(platform.release().split(".")[0]) < 9
825
826 def build_openssl_arch(archbase, arch):
827 "Build one architecture of openssl"
828 arch_opts = {
829 "i386": ["darwin-i386-cc"],
830 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100831 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800832 "ppc": ["darwin-ppc-cc"],
833 "ppc64": ["darwin64-ppc-cc"],
834 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400835
836 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
837 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
838 # building on our 10.6 gcc-4.2 environment. There have been other
839 # reports of projects running into this when using older compilers.
840 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
841 # building for 10.6.
842 if getDeptargetTuple() == (10, 6):
843 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
844
Ned Deily5d3febf2014-12-13 00:17:46 -0800845 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800846 "no-idea",
847 "no-mdc2",
848 "no-rc5",
849 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800850 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800851 # "enable-unit-test",
852 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800853 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400854 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800855 ]
856 if no_asm:
857 configure_opts.append("no-asm")
858 runCommand(" ".join(["perl", "Configure"]
859 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500860 runCommand("make depend")
861 runCommand("make all")
862 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800863 # runCommand("make test")
864 return
865
866 srcdir = os.getcwd()
867 universalbase = os.path.join(srcdir, "..",
868 os.path.basename(srcdir) + "-universal")
869 os.mkdir(universalbase)
870 archbasefws = []
871 for arch in archList:
872 # fresh copy of the source tree
873 archsrc = os.path.join(universalbase, arch, "src")
874 shutil.copytree(srcdir, archsrc, symlinks=True)
875 # install base for this arch
876 archbase = os.path.join(universalbase, arch, "root")
877 os.mkdir(archbase)
878 # Python framework base within install_prefix:
879 # the build will install into this framework..
880 # This is to ensure that the resulting shared libs have
881 # the desired real install paths built into them.
882 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
883
884 # build one architecture
885 os.chdir(archsrc)
886 build_openssl_arch(archbase, arch)
887 os.chdir(srcdir)
888 archbasefws.append(archbasefw)
889
890 # copy arch-independent files from last build into the basedir framework
891 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
892 shutil.copytree(
893 os.path.join(archbasefw, "include", "openssl"),
894 os.path.join(basefw, "include", "openssl")
895 )
896
897 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
898 "SHLIB_VERSION_NUMBER")
899 # e.g. -> "1.0.0"
900 libcrypto = "libcrypto.dylib"
901 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
902 # e.g. -> "libcrypto.1.0.0.dylib"
903 libssl = "libssl.dylib"
904 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
905 # e.g. -> "libssl.1.0.0.dylib"
906
907 try:
908 os.mkdir(os.path.join(basefw, "lib"))
909 except OSError:
910 pass
911
912 # merge the individual arch-dependent shared libs into a fat shared lib
913 archbasefws.insert(0, basefw)
914 for (lib_unversioned, lib_versioned) in [
915 (libcrypto, libcrypto_versioned),
916 (libssl, libssl_versioned)
917 ]:
918 runCommand("lipo -create -output " +
919 " ".join(shellQuote(
920 os.path.join(fw, "lib", lib_versioned))
921 for fw in archbasefws))
922 # and create an unversioned symlink of it
923 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
924
925 # Create links in the temp include and lib dirs that will be injected
926 # into the Python build so that setup.py can find them while building
927 # and the versioned links so that the setup.py post-build import test
928 # does not fail.
929 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
930 for fn in [
931 ["include", "openssl"],
932 ["lib", libcrypto],
933 ["lib", libssl],
934 ["lib", libcrypto_versioned],
935 ["lib", libssl_versioned],
936 ]:
937 os.symlink(
938 os.path.join(relative_path, *fn),
939 os.path.join(basedir, "usr", "local", *fn)
940 )
941
942 return
943
Thomas Wouters477c8d52006-05-27 19:21:47 +0000944def buildRecipe(recipe, basedir, archList):
945 """
946 Build software using a recipe. This function does the
947 'configure;make;make install' dance for C software, with a possibility
948 to customize this process, basically a poor-mans DarwinPorts.
949 """
950 curdir = os.getcwd()
951
952 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800953 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000954 url = recipe['url']
955 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800956 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000957 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
958 shellQuote(basedir)))
959
960 archiveName = os.path.split(url)[-1]
961 sourceArchive = os.path.join(DEPSRC, archiveName)
962
963 if not os.path.exists(DEPSRC):
964 os.mkdir(DEPSRC)
965
Ned Deily4a96a372013-01-29 00:08:32 -0800966 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
967 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000968 buildDir=os.path.join(WORKDIR, '_bld')
969 if not os.path.exists(buildDir):
970 os.mkdir(buildDir)
971
972 workDir = extractArchive(buildDir, sourceArchive)
973 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000974
Ned Deily4a96a372013-01-29 00:08:32 -0800975 for patch in recipe.get('patches', ()):
976 if isinstance(patch, tuple):
977 url, checksum = patch
978 fn = os.path.join(DEPSRC, os.path.basename(url))
979 verifyThirdPartyFile(url, checksum, fn)
980 else:
981 # patch is a file in the source directory
982 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000983 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
984 shellQuote(fn),))
985
Ned Deily4a96a372013-01-29 00:08:32 -0800986 for patchscript in recipe.get('patchscripts', ()):
987 if isinstance(patchscript, tuple):
988 url, checksum = patchscript
989 fn = os.path.join(DEPSRC, os.path.basename(url))
990 verifyThirdPartyFile(url, checksum, fn)
991 else:
992 # patch is a file in the source directory
993 fn = os.path.join(curdir, patchscript)
994 if fn.endswith('.bz2'):
995 runCommand('bunzip2 -fk %s' % shellQuote(fn))
996 fn = fn[:-4]
997 runCommand('sh %s' % shellQuote(fn))
998 os.unlink(fn)
999
Ned Deily94764b22013-10-27 19:49:29 -07001000 if 'buildDir' in recipe:
1001 os.chdir(recipe['buildDir'])
1002
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001003 if configure is not None:
1004 configure_args = [
1005 "--prefix=/usr/local",
1006 "--enable-static",
1007 "--disable-shared",
1008 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1009 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001010
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001011 if 'configure_pre' in recipe:
1012 args = list(recipe['configure_pre'])
1013 if '--disable-static' in args:
1014 configure_args.remove('--enable-static')
1015 if '--enable-shared' in args:
1016 configure_args.remove('--disable-shared')
1017 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001018
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001019 if recipe.get('useLDFlags', 1):
1020 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001021 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001022 "-I%s/usr/local/include"%(
1023 recipe.get('extra_cflags', ''),
1024 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001025 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001026 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001027 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001028 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001029 shellQuote(basedir)[1:-1],
1030 ' -arch '.join(archList)),
1031 ])
1032 else:
1033 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001034 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001035 "-I%s/usr/local/include"%(
1036 recipe.get('extra_cflags', ''),
1037 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001038 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001039 shellQuote(basedir)[1:-1],),
1040 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001041
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001042 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001043 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001044
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001045 configure_args.insert(0, configure)
1046 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001047
Ned Deily4a96a372013-01-29 00:08:32 -08001048 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001049 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001050
Ned Deily5d3febf2014-12-13 00:17:46 -08001051 if buildrecipe is not None:
1052 # call special-case build recipe, e.g. for openssl
1053 buildrecipe(basedir, archList)
1054
1055 if install is not None:
1056 print("Running install for %s"%(name,))
1057 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001058
Ned Deily4a96a372013-01-29 00:08:32 -08001059 print("Done %s"%(name,))
1060 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001061
1062 os.chdir(curdir)
1063
1064def buildLibraries():
1065 """
1066 Build our dependencies into $WORKDIR/libraries/usr/local
1067 """
Ned Deily4a96a372013-01-29 00:08:32 -08001068 print("")
1069 print("Building required libraries")
1070 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001071 universal = os.path.join(WORKDIR, 'libraries')
1072 os.mkdir(universal)
1073 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1074 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1075
Ronald Oussoren1943f862009-03-30 19:39:14 +00001076 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001077 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001078
1079
1080
1081def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001082 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001083 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001084 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001085 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001086 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001087 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001088 curDir = os.getcwd()
1089 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001090 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001091
1092 # Search third-party source directory for a pre-built version of the docs.
1093 # Use the naming convention of the docs.python.org html downloads:
1094 # python-3.9.0b1-docs-html.tar.bz2
1095 doctarfiles = [ f for f in os.listdir(DEPSRC)
1096 if f.startswith('python-'+getFullVersion())
1097 if f.endswith('-docs-html.tar.bz2') ]
1098 if doctarfiles:
1099 doctarfile = doctarfiles[0]
1100 if not os.path.exists('build'):
1101 os.mkdir('build')
1102 # if build directory existed, it was emptied by make clean, above
1103 os.chdir('build')
1104 # Extract the first archive found for this version into build
1105 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1106 # see if tar extracted a directory ending in -docs-html
1107 archivefiles = [ f for f in os.listdir('.')
1108 if f.endswith('-docs-html')
1109 if os.path.isdir(f) ]
1110 if archivefiles:
1111 archivefile = archivefiles[0]
1112 # make it our 'Docs/build/html' directory
1113 print(' -- using pre-built python documentation from %s'%archivefile)
1114 os.rename(archivefile, 'html')
1115 os.chdir(buildDir)
1116
1117 htmlDir = os.path.join('build', 'html')
1118 if not os.path.exists(htmlDir):
1119 # Create virtual environment for docs builds with blurb and sphinx
1120 runCommand('make venv')
Ned Deily1931e642020-06-25 04:51:46 -04001121 runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
Ned Deily63fc55b2020-06-08 03:52:43 -04001122 runCommand('make html PYTHON=venv/bin/python')
1123 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001124 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001125
1126
1127def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001128 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001129
1130 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1131 rootDir = os.path.join(WORKDIR, '_root')
1132
1133 if os.path.exists(buildDir):
1134 shutil.rmtree(buildDir)
1135 if os.path.exists(rootDir):
1136 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001137 os.makedirs(buildDir)
1138 os.makedirs(rootDir)
1139 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001140 curdir = os.getcwd()
1141 os.chdir(buildDir)
1142
Thomas Wouters477c8d52006-05-27 19:21:47 +00001143 # Extract the version from the configure file, needed to calculate
1144 # several paths.
1145 version = getVersion()
1146
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001147 # Since the extra libs are not in their installed framework location
1148 # during the build, augment the library path so that the interpreter
1149 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001150
Ned Deily4a96a372013-01-29 00:08:32 -08001151 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001152 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001153 "--with-universal-archs=%s "
1154 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001155 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001156 "%s "
1157 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001158 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001159 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001160 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001161 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001162 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001163 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001164 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001165 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1166 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001167 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1168 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1169 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1170 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001171 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001172 shellQuote(WORKDIR)[1:-1]))
1173
Ned Deily1931e642020-06-25 04:51:46 -04001174 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1175 # environment variables are no longer automatically inherited
1176 # by child processes from their parents. We used to just set
1177 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1178 # in build-installer.py's process environment and it was
1179 # passed through the make utility into the environment of
1180 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1181 # the existing RUNSHARED configuration value when we call
1182 # make for extension module builds.
1183
1184 runshared_for_make = "".join([
1185 " RUNSHARED=",
1186 "'",
1187 grepValue("Makefile", "RUNSHARED"),
1188 ' DYLD_LIBRARY_PATH=',
1189 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1190 "'" ])
1191
Ned Deilyb364d9f2017-07-24 04:58:43 -04001192 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1193 # and, if defined, append its value to the make command. This allows
1194 # us to pass in version control tags, like GITTAG, to a build from a
1195 # tarball rather than from a vcs checkout, thus eliminating the need
1196 # to have a working copy of the vcs program on the build machine.
1197 #
1198 # A typical use might be:
1199 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1200 # GITVERSION='echo 123456789a' \
1201 # GITTAG='echo v3.6.0' \
1202 # GITBRANCH='echo 3.6'"
1203
1204 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1205 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001206 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001207 else:
Ned Deily1931e642020-06-25 04:51:46 -04001208 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001209 print("Running " + make_cmd)
1210 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001211
Ned Deily1931e642020-06-25 04:51:46 -04001212 make_cmd = "make install DESTDIR=%s %s"%(
1213 shellQuote(rootDir),
1214 runshared_for_make)
1215 print("Running " + make_cmd)
1216 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001217
Ned Deily1931e642020-06-25 04:51:46 -04001218 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1219 shellQuote(rootDir),
1220 runshared_for_make)
1221 print("Running " + make_cmd)
1222 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001223
Ned Deily4a96a372013-01-29 00:08:32 -08001224 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001225 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001226 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001227 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001228 'Python.framework', 'Versions', getVersion(), 'lib')
1229 fw_lib_dir = os.path.join(
1230 WORKDIR, '_root', 'Library', 'Frameworks',
1231 'Python.framework', 'Versions', getVersion(), 'lib')
1232 if internalTk():
1233 # move Tcl and Tk pkgconfig files
1234 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1235 shellQuote(build_lib_dir),
1236 shellQuote(fw_lib_dir) ))
1237 runCommand("rm -r %s/pkgconfig"%(
1238 shellQuote(build_lib_dir), ))
1239 runCommand("mv %s/* %s"%(
1240 shellQuote(build_lib_dir),
1241 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001242
Ned Deilydde4f632016-09-12 09:39:23 -04001243 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1244 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1245 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1246 # create directory for OpenSSL certificates
1247 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1248 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001249
Ned Deily4a96a372013-01-29 00:08:32 -08001250 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001251 gid = grp.getgrnam('admin').gr_gid
1252
Ned Deily4a96a372013-01-29 00:08:32 -08001253 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001254 for dirpath, dirnames, filenames in os.walk(frmDir):
1255 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001256 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001257 os.chown(os.path.join(dirpath, dn), -1, gid)
1258
Thomas Wouters477c8d52006-05-27 19:21:47 +00001259 for fn in filenames:
1260 if os.path.islink(fn):
1261 continue
1262
1263 # "chmod g+w $fn"
1264 p = os.path.join(dirpath, fn)
1265 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001266 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1267 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001268
Ned Deily4a96a372013-01-29 00:08:32 -08001269 if fn in EXPECTED_SHARED_LIBS:
1270 # check to see that this file was linked with the
1271 # expected library path and version
1272 data = captureCommand("otool -L %s" % shellQuote(p))
1273 for sl in EXPECTED_SHARED_LIBS[fn]:
1274 if ("\t%s " % sl) not in data:
1275 print("Expected shared lib %s was not linked with %s"
1276 % (sl, p))
1277 shared_lib_error = True
1278
1279 if shared_lib_error:
1280 fatal("Unexpected shared library errors.")
1281
Ned Deilye59e4c52011-01-29 18:56:28 +00001282 if PYTHON_3:
1283 LDVERSION=None
1284 VERSION=None
1285 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001286
Ned Deilye59e4c52011-01-29 18:56:28 +00001287 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001288 for ln in fp:
1289 if ln.startswith('VERSION='):
1290 VERSION=ln.split()[1]
1291 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001292 ABIFLAGS=ln.split()
1293 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001294 if ln.startswith('LDVERSION='):
1295 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001296 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001297
Ned Deilye59e4c52011-01-29 18:56:28 +00001298 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1299 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1300 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001301 if getVersionMajorMinor() >= (3, 6):
1302 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001303 else:
1304 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001305
Thomas Wouters477c8d52006-05-27 19:21:47 +00001306 # We added some directories to the search path during the configure
1307 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001308 # the end-users system. Also remove the directories from _sysconfigdata.py
1309 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001310
Ned Deilya4f6b002013-10-25 00:47:38 -07001311 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1312 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1313
Ned Deilya4f6b002013-10-25 00:47:38 -07001314 # fix Makefile
1315 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1316 fp = open(path, 'r')
1317 data = fp.read()
1318 fp.close()
1319
1320 for p in (include_path, lib_path):
1321 data = data.replace(" " + p, '')
1322 data = data.replace(p + " ", '')
1323
1324 fp = open(path, 'w')
1325 fp.write(data)
1326 fp.close()
1327
Ned Deily652bad42016-08-15 14:37:14 -04001328 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001329 #
1330 # TODO: make this more robust! test_sysconfig_module of
1331 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1332 # the output from get_config_var in both sysconfig and
1333 # distutils.sysconfig is exactly the same for both CFLAGS and
1334 # LDFLAGS. The fixing up is now complicated by the pretty
1335 # printing in _sysconfigdata.py. Also, we are using the
1336 # pprint from the Python running the installer build which
1337 # may not cosmetically format the same as the pprint in the Python
1338 # being built (and which is used to originally generate
1339 # _sysconfigdata.py).
1340
1341 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001342 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001343 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001344 path = os.path.join(path_to_lib,
1345 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001346 else:
1347 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1348 fp = open(path, 'r')
1349 data = fp.read()
1350 fp.close()
1351 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001352 if RUNNING_ON_PYTHON2:
1353 exec(data)
1354 else:
1355 g_dict = {}
1356 l_dict = {}
1357 exec(data, g_dict, l_dict)
1358 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001359 vars = {}
1360 for k, v in build_time_vars.items():
1361 if type(v) == type(''):
1362 for p in (include_path, lib_path):
1363 v = v.replace(' ' + p, '')
1364 v = v.replace(p + ' ', '')
1365 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001366
Ned Deily652bad42016-08-15 14:37:14 -04001367 fp = open(path, 'w')
1368 # duplicated from sysconfig._generate_posix_vars()
1369 fp.write('# system configuration generated and used by'
1370 ' the sysconfig module\n')
1371 fp.write('build_time_vars = ')
1372 pprint.pprint(vars, stream=fp)
1373 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001374
1375 # Add symlinks in /usr/local/bin, using relative links
1376 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1377 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1378 'Python.framework', 'Versions', version, 'bin')
1379 if os.path.exists(usr_local_bin):
1380 shutil.rmtree(usr_local_bin)
1381 os.makedirs(usr_local_bin)
1382 for fn in os.listdir(
1383 os.path.join(frmDir, 'Versions', version, 'bin')):
1384 os.symlink(os.path.join(to_framework, fn),
1385 os.path.join(usr_local_bin, fn))
1386
1387 os.chdir(curdir)
1388
Thomas Wouters477c8d52006-05-27 19:21:47 +00001389def patchFile(inPath, outPath):
1390 data = fileContents(inPath)
1391 data = data.replace('$FULL_VERSION', getFullVersion())
1392 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001393 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001394 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001395 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001396 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001397
1398 # This one is not handy as a template variable
1399 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001400 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001401 fp.write(data)
1402 fp.close()
1403
1404def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001405 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001406 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001407 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001408 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001409 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001410 fp.write(data)
1411 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001412 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001413
1414
1415
1416def packageFromRecipe(targetDir, recipe):
1417 curdir = os.getcwd()
1418 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001419 # The major version (such as 2.5) is included in the package name
1420 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001421 # common.
1422 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001423 srcdir = recipe.get('source')
1424 pkgroot = recipe.get('topdir', srcdir)
1425 postflight = recipe.get('postflight')
1426 readme = textwrap.dedent(recipe['readme'])
1427 isRequired = recipe.get('required', True)
1428
Ned Deily4a96a372013-01-29 00:08:32 -08001429 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001430
1431 # Substitute some variables
1432 textvars = dict(
1433 VER=getVersion(),
1434 FULLVER=getFullVersion(),
1435 )
1436 readme = readme % textvars
1437
1438 if pkgroot is not None:
1439 pkgroot = pkgroot % textvars
1440 else:
1441 pkgroot = '/'
1442
1443 if srcdir is not None:
1444 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1445 srcdir = srcdir % textvars
1446
1447 if postflight is not None:
1448 postflight = os.path.abspath(postflight)
1449
1450 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1451 os.makedirs(packageContents)
1452
1453 if srcdir is not None:
1454 os.chdir(srcdir)
1455 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1456 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1457 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1458
1459 fn = os.path.join(packageContents, 'PkgInfo')
1460 fp = open(fn, 'w')
1461 fp.write('pmkrpkg1')
1462 fp.close()
1463
1464 rsrcDir = os.path.join(packageContents, "Resources")
1465 os.mkdir(rsrcDir)
1466 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1467 fp.write(readme)
1468 fp.close()
1469
1470 if postflight is not None:
1471 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1472
1473 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001474 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001475 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001476 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1477 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1478 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001479 CFBundleShortVersionString=vers,
1480 IFMajorVersion=major,
1481 IFMinorVersion=minor,
1482 IFPkgFormatVersion=0.10000000149011612,
1483 IFPkgFlagAllowBackRev=False,
1484 IFPkgFlagAuthorizationAction="RootAuthorization",
1485 IFPkgFlagDefaultLocation=pkgroot,
1486 IFPkgFlagFollowLinks=True,
1487 IFPkgFlagInstallFat=True,
1488 IFPkgFlagIsRequired=isRequired,
1489 IFPkgFlagOverwritePermissions=False,
1490 IFPkgFlagRelocatable=False,
1491 IFPkgFlagRestartAction="NoRestart",
1492 IFPkgFlagRootVolumeOnly=True,
1493 IFPkgFlagUpdateInstalledLangauges=False,
1494 )
1495 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1496
Ned Deily1931e642020-06-25 04:51:46 -04001497 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001498 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001499 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001500 IFPkgDescriptionVersion=vers,
1501 )
1502 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1503
1504 finally:
1505 os.chdir(curdir)
1506
1507
1508def makeMpkgPlist(path):
1509
1510 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001511 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001512
Ned Deily1931e642020-06-25 04:51:46 -04001513 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001514 CFBundleGetInfoString="Python %s"%(vers,),
1515 CFBundleIdentifier='org.python.Python',
1516 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001517 CFBundleShortVersionString=vers,
1518 IFMajorVersion=major,
1519 IFMinorVersion=minor,
1520 IFPkgFlagComponentDirectory="Contents/Packages",
1521 IFPkgFlagPackageList=[
1522 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001523 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001524 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001525 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001526 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001527 ],
1528 IFPkgFormatVersion=0.10000000149011612,
1529 IFPkgFlagBackgroundScaling="proportional",
1530 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001531 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001532 )
1533
1534 writePlist(pl, path)
1535
1536
1537def buildInstaller():
1538
1539 # Zap all compiled files
1540 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1541 for fn in filenames:
1542 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1543 os.unlink(os.path.join(dirpath, fn))
1544
1545 outdir = os.path.join(WORKDIR, 'installer')
1546 if os.path.exists(outdir):
1547 shutil.rmtree(outdir)
1548 os.mkdir(outdir)
1549
Ronald Oussoren1943f862009-03-30 19:39:14 +00001550 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001551 pkgcontents = os.path.join(pkgroot, 'Packages')
1552 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001553 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001554 packageFromRecipe(pkgcontents, recipe)
1555
1556 rsrcDir = os.path.join(pkgroot, 'Resources')
1557
1558 fn = os.path.join(pkgroot, 'PkgInfo')
1559 fp = open(fn, 'w')
1560 fp.write('pmkrpkg1')
1561 fp.close()
1562
1563 os.mkdir(rsrcDir)
1564
1565 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001566 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001567 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001568 IFPkgDescriptionVersion=getVersion(),
1569 )
1570
1571 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1572 for fn in os.listdir('resources'):
1573 if fn == '.svn': continue
1574 if fn.endswith('.jpg'):
1575 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1576 else:
1577 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1578
Thomas Wouters477c8d52006-05-27 19:21:47 +00001579
1580def installSize(clear=False, _saved=[]):
1581 if clear:
1582 del _saved[:]
1583 if not _saved:
1584 data = captureCommand("du -ks %s"%(
1585 shellQuote(os.path.join(WORKDIR, '_root'))))
1586 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1587 return _saved[0]
1588
1589
1590def buildDMG():
1591 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001592 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001593 """
1594 outdir = os.path.join(WORKDIR, 'diskimage')
1595 if os.path.exists(outdir):
1596 shutil.rmtree(outdir)
1597
1598 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001599 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001600 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001601 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001602 imagepath = imagepath + '.dmg'
1603
1604 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001605
1606 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1607 # when hdiutil create fails with "Resource busy". For now, just retry
1608 # the create a few times and hope that it eventually works.
1609
Ronald Oussoren1943f862009-03-30 19:39:14 +00001610 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001611 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001612 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001613 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001614 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001615 for i in range(5):
1616 fd = os.popen(cmd, 'r')
1617 data = fd.read()
1618 xit = fd.close()
1619 if not xit:
1620 break
1621 sys.stdout.write(data)
1622 print(" -- retrying hdiutil create")
1623 time.sleep(5)
1624 else:
cclaussd3371692019-06-03 05:19:44 +02001625 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001626
1627 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1628 os.mkdir(os.path.join(WORKDIR, "mnt"))
1629 runCommand("hdiutil attach %s -mountroot %s"%(
1630 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1631
1632 # Custom icon for the DMG, shown when the DMG is mounted.
1633 shutil.copy("../Icons/Disk Image.icns",
1634 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001635 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001636 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1637
1638 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1639
1640 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1641 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1642 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1643 setIcon(imagepath, "../Icons/Disk Image.icns")
1644
1645 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001646
1647 return imagepath
1648
1649
1650def setIcon(filePath, icnsPath):
1651 """
1652 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001653 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001654
Ronald Oussoren70050672010-04-30 15:00:26 +00001655 dirPath = os.path.normpath(os.path.dirname(__file__))
1656 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001657 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1658 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1659 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001660 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1661 if not os.path.exists(appPath):
1662 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001663 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1664 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001665
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001666 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1667 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001668
1669def main():
1670 # First parse options and check if we can perform our work
1671 parseOptions()
1672 checkEnvironment()
1673
Ronald Oussoren1943f862009-03-30 19:39:14 +00001674 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001675 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001676 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001677
1678 if os.path.exists(WORKDIR):
1679 shutil.rmtree(WORKDIR)
1680 os.mkdir(WORKDIR)
1681
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001682 os.environ['LC_ALL'] = 'C'
1683
Thomas Wouters477c8d52006-05-27 19:21:47 +00001684 # Then build third-party libraries such as sleepycat DB4.
1685 buildLibraries()
1686
1687 # Now build python itself
1688 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001689
1690 # And then build the documentation
1691 # Remove the Deployment Target from the shell
1692 # environment, it's no longer needed and
1693 # an unexpected build target can cause problems
1694 # when Sphinx and its dependencies need to
1695 # be (re-)installed.
1696 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001697 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001698
1699
1700 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001701 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001702 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001703 fn = os.path.join(folder, "License.rtf")
1704 patchFile("resources/License.rtf", fn)
1705 fn = os.path.join(folder, "ReadMe.rtf")
1706 patchFile("resources/ReadMe.rtf", fn)
1707 fn = os.path.join(folder, "Update Shell Profile.command")
1708 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001709 fn = os.path.join(folder, "Install Certificates.command")
1710 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001711 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001712 setIcon(folder, "../Icons/Python Folder.icns")
1713
1714 # Create the installer
1715 buildInstaller()
1716
1717 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001718 patchFile('resources/ReadMe.rtf',
1719 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001720
1721 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001722 patchFile('resources/License.rtf',
1723 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001724
1725 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001726 fp.write("# BUILD INFO\n")
1727 fp.write("# Date: %s\n" % time.ctime())
1728 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001729 fp.close()
1730
Thomas Wouters477c8d52006-05-27 19:21:47 +00001731 # And copy it to a DMG
1732 buildDMG()
1733
Thomas Wouters477c8d52006-05-27 19:21:47 +00001734if __name__ == "__main__":
1735 main()