blob: 184add4c4f06c501b0846bdec54f491096264937 [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 Aasland9a764262020-10-05 10:09:16 +0200360 name="SQLite 3.33.0",
361 url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz",
362 checksum='842a8a100d7b01b09e543deb2b7951dd',
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 Deily8c9bb722018-01-30 07:42:14 -0500590 if sys.version_info[0:2] < (2, 5):
591 fatal("This script must be run with Python 2.5 (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']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400659 # Ensure we have access to sphinx-build.
660 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700661 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000662
Thomas Wouters89f507f2006-12-13 04:49:30 +0000663def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000664 """
665 Parse arguments and update global settings.
666 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500667 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800668 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800669 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400670 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000671
672 if args is None:
673 args = sys.argv[1:]
674
675 try:
676 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000677 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
678 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800679 except getopt.GetoptError:
680 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000681 sys.exit(1)
682
683 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800684 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000685 sys.exit(1)
686
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000687 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000688 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000689 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800690 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000691 sys.exit(0)
692
693 elif k in ('-d', '--build-dir'):
694 WORKDIR=v
695
696 elif k in ('--third-party',):
697 DEPSRC=v
698
699 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500700 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000701
702 elif k in ('--src-dir',):
703 SRCDIR=v
704
Ronald Oussoren1943f862009-03-30 19:39:14 +0000705 elif k in ('--dep-target', ):
706 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000707 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000708
709 elif k in ('--universal-archs', ):
710 if v in UNIVERSALOPTS:
711 UNIVERSALARCHS = v
712 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000713 if deptarget is None:
714 # Select alternate default deployment
715 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500716 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000717 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800718 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000719
Thomas Wouters477c8d52006-05-27 19:21:47 +0000720 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800721 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000722
723 SRCDIR=os.path.abspath(SRCDIR)
724 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000725 DEPSRC=os.path.abspath(DEPSRC)
726
Ned Deily04cdfa12014-06-25 13:36:14 -0700727 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000728
Ned Deily5d3febf2014-12-13 00:17:46 -0800729 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400730 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800731
732 print("-- Settings:")
733 print(" * Source directory: %s" % SRCDIR)
734 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800735 print(" * Third-party source: %s" % DEPSRC)
736 print(" * Deployment target: %s" % DEPTARGET)
737 print(" * Universal archs: %s" % str(ARCHLIST))
738 print(" * C compiler: %s" % CC)
739 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800740 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800741 print(" -- Building a Python %s framework at patch level %s"
742 % (getVersion(), getFullVersion()))
743 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000744
745def extractArchive(builddir, archiveName):
746 """
747 Extract a source archive into 'builddir'. Returns the path of the
748 extracted archive.
749
750 XXX: This function assumes that archives contain a toplevel directory
751 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700752 safe enough for almost anything we use. Unfortunately, it does not
753 work for current Tcl and Tk source releases where the basename of
754 the archive ends with "-src" but the uncompressed directory does not.
755 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000756 """
757 curdir = os.getcwd()
758 try:
759 os.chdir(builddir)
760 if archiveName.endswith('.tar.gz'):
761 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700762 if ((retval.startswith('tcl') or retval.startswith('tk'))
763 and retval.endswith('-src')):
764 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000765 if os.path.exists(retval):
766 shutil.rmtree(retval)
767 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
768
769 elif archiveName.endswith('.tar.bz2'):
770 retval = os.path.basename(archiveName[:-8])
771 if os.path.exists(retval):
772 shutil.rmtree(retval)
773 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
774
775 elif archiveName.endswith('.tar'):
776 retval = os.path.basename(archiveName[:-4])
777 if os.path.exists(retval):
778 shutil.rmtree(retval)
779 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
780
781 elif archiveName.endswith('.zip'):
782 retval = os.path.basename(archiveName[:-4])
783 if os.path.exists(retval):
784 shutil.rmtree(retval)
785 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
786
787 data = fp.read()
788 xit = fp.close()
789 if xit is not None:
790 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800791 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000792
793 return os.path.join(builddir, retval)
794
795 finally:
796 os.chdir(curdir)
797
Thomas Wouters477c8d52006-05-27 19:21:47 +0000798def downloadURL(url, fname):
799 """
800 Download the contents of the url into the file.
801 """
Ned Deily4a96a372013-01-29 00:08:32 -0800802 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000803 fpOut = open(fname, 'wb')
804 block = fpIn.read(10240)
805 try:
806 while block:
807 fpOut.write(block)
808 block = fpIn.read(10240)
809 fpIn.close()
810 fpOut.close()
811 except:
812 try:
813 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300814 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000815 pass
816
Ned Deily4a96a372013-01-29 00:08:32 -0800817def verifyThirdPartyFile(url, checksum, fname):
818 """
819 Download file from url to filename fname if it does not already exist.
820 Abort if file contents does not match supplied md5 checksum.
821 """
822 name = os.path.basename(fname)
823 if os.path.exists(fname):
824 print("Using local copy of %s"%(name,))
825 else:
826 print("Did not find local copy of %s"%(name,))
827 print("Downloading %s"%(name,))
828 downloadURL(url, fname)
829 print("Archive for %s stored as %s"%(name, fname))
830 if os.system(
831 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
832 % (shellQuote(fname), checksum) ):
833 fatal('MD5 checksum mismatch for file %s' % fname)
834
Ned Deily5d3febf2014-12-13 00:17:46 -0800835def build_universal_openssl(basedir, archList):
836 """
837 Special case build recipe for universal build of openssl.
838
839 The upstream OpenSSL build system does not directly support
840 OS X universal builds. We need to build each architecture
841 separately then lipo them together into fat libraries.
842 """
843
844 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
845 # If we are building on a 10.4.x or earlier system,
846 # unilaterally disable assembly code building to avoid the problem.
847 no_asm = int(platform.release().split(".")[0]) < 9
848
849 def build_openssl_arch(archbase, arch):
850 "Build one architecture of openssl"
851 arch_opts = {
852 "i386": ["darwin-i386-cc"],
853 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
Ronald Oussoren41761932020-11-08 10:05:27 +0100854 "arm64": ["darwin64-arm64-cc"],
Ned Deily5d3febf2014-12-13 00:17:46 -0800855 "ppc": ["darwin-ppc-cc"],
856 "ppc64": ["darwin64-ppc-cc"],
857 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400858
859 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
860 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
861 # building on our 10.6 gcc-4.2 environment. There have been other
862 # reports of projects running into this when using older compilers.
863 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
864 # building for 10.6.
865 if getDeptargetTuple() == (10, 6):
866 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
867
Ned Deily5d3febf2014-12-13 00:17:46 -0800868 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800869 "no-idea",
870 "no-mdc2",
871 "no-rc5",
872 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800873 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800874 # "enable-unit-test",
875 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800876 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400877 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800878 ]
879 if no_asm:
880 configure_opts.append("no-asm")
881 runCommand(" ".join(["perl", "Configure"]
882 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500883 runCommand("make depend")
884 runCommand("make all")
885 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800886 # runCommand("make test")
887 return
888
889 srcdir = os.getcwd()
890 universalbase = os.path.join(srcdir, "..",
891 os.path.basename(srcdir) + "-universal")
892 os.mkdir(universalbase)
893 archbasefws = []
894 for arch in archList:
895 # fresh copy of the source tree
896 archsrc = os.path.join(universalbase, arch, "src")
897 shutil.copytree(srcdir, archsrc, symlinks=True)
898 # install base for this arch
899 archbase = os.path.join(universalbase, arch, "root")
900 os.mkdir(archbase)
901 # Python framework base within install_prefix:
902 # the build will install into this framework..
903 # This is to ensure that the resulting shared libs have
904 # the desired real install paths built into them.
905 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
906
907 # build one architecture
908 os.chdir(archsrc)
909 build_openssl_arch(archbase, arch)
910 os.chdir(srcdir)
911 archbasefws.append(archbasefw)
912
913 # copy arch-independent files from last build into the basedir framework
914 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
915 shutil.copytree(
916 os.path.join(archbasefw, "include", "openssl"),
917 os.path.join(basefw, "include", "openssl")
918 )
919
920 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
921 "SHLIB_VERSION_NUMBER")
922 # e.g. -> "1.0.0"
923 libcrypto = "libcrypto.dylib"
924 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
925 # e.g. -> "libcrypto.1.0.0.dylib"
926 libssl = "libssl.dylib"
927 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
928 # e.g. -> "libssl.1.0.0.dylib"
929
930 try:
931 os.mkdir(os.path.join(basefw, "lib"))
932 except OSError:
933 pass
934
935 # merge the individual arch-dependent shared libs into a fat shared lib
936 archbasefws.insert(0, basefw)
937 for (lib_unversioned, lib_versioned) in [
938 (libcrypto, libcrypto_versioned),
939 (libssl, libssl_versioned)
940 ]:
941 runCommand("lipo -create -output " +
942 " ".join(shellQuote(
943 os.path.join(fw, "lib", lib_versioned))
944 for fw in archbasefws))
945 # and create an unversioned symlink of it
946 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
947
948 # Create links in the temp include and lib dirs that will be injected
949 # into the Python build so that setup.py can find them while building
950 # and the versioned links so that the setup.py post-build import test
951 # does not fail.
952 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
953 for fn in [
954 ["include", "openssl"],
955 ["lib", libcrypto],
956 ["lib", libssl],
957 ["lib", libcrypto_versioned],
958 ["lib", libssl_versioned],
959 ]:
960 os.symlink(
961 os.path.join(relative_path, *fn),
962 os.path.join(basedir, "usr", "local", *fn)
963 )
964
965 return
966
Thomas Wouters477c8d52006-05-27 19:21:47 +0000967def buildRecipe(recipe, basedir, archList):
968 """
969 Build software using a recipe. This function does the
970 'configure;make;make install' dance for C software, with a possibility
971 to customize this process, basically a poor-mans DarwinPorts.
972 """
973 curdir = os.getcwd()
974
975 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800976 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000977 url = recipe['url']
978 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800979 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000980 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
981 shellQuote(basedir)))
982
983 archiveName = os.path.split(url)[-1]
984 sourceArchive = os.path.join(DEPSRC, archiveName)
985
986 if not os.path.exists(DEPSRC):
987 os.mkdir(DEPSRC)
988
Ned Deily4a96a372013-01-29 00:08:32 -0800989 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
990 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000991 buildDir=os.path.join(WORKDIR, '_bld')
992 if not os.path.exists(buildDir):
993 os.mkdir(buildDir)
994
995 workDir = extractArchive(buildDir, sourceArchive)
996 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000997
Ned Deily4a96a372013-01-29 00:08:32 -0800998 for patch in recipe.get('patches', ()):
999 if isinstance(patch, tuple):
1000 url, checksum = patch
1001 fn = os.path.join(DEPSRC, os.path.basename(url))
1002 verifyThirdPartyFile(url, checksum, fn)
1003 else:
1004 # patch is a file in the source directory
1005 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001006 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1007 shellQuote(fn),))
1008
Ned Deily4a96a372013-01-29 00:08:32 -08001009 for patchscript in recipe.get('patchscripts', ()):
1010 if isinstance(patchscript, tuple):
1011 url, checksum = patchscript
1012 fn = os.path.join(DEPSRC, os.path.basename(url))
1013 verifyThirdPartyFile(url, checksum, fn)
1014 else:
1015 # patch is a file in the source directory
1016 fn = os.path.join(curdir, patchscript)
1017 if fn.endswith('.bz2'):
1018 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1019 fn = fn[:-4]
1020 runCommand('sh %s' % shellQuote(fn))
1021 os.unlink(fn)
1022
Ned Deily94764b22013-10-27 19:49:29 -07001023 if 'buildDir' in recipe:
1024 os.chdir(recipe['buildDir'])
1025
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001026 if configure is not None:
1027 configure_args = [
1028 "--prefix=/usr/local",
1029 "--enable-static",
1030 "--disable-shared",
1031 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1032 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001033
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001034 if 'configure_pre' in recipe:
1035 args = list(recipe['configure_pre'])
1036 if '--disable-static' in args:
1037 configure_args.remove('--enable-static')
1038 if '--enable-shared' in args:
1039 configure_args.remove('--disable-shared')
1040 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001041
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001042 if recipe.get('useLDFlags', 1):
1043 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001044 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001045 "-I%s/usr/local/include"%(
1046 recipe.get('extra_cflags', ''),
1047 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001048 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001049 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001050 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001051 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001052 shellQuote(basedir)[1:-1],
1053 ' -arch '.join(archList)),
1054 ])
1055 else:
1056 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001057 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001058 "-I%s/usr/local/include"%(
1059 recipe.get('extra_cflags', ''),
1060 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001061 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001062 shellQuote(basedir)[1:-1],),
1063 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001064
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001065 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001066 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001068 configure_args.insert(0, configure)
1069 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001070
Ned Deily4a96a372013-01-29 00:08:32 -08001071 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001072 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001073
Ned Deily5d3febf2014-12-13 00:17:46 -08001074 if buildrecipe is not None:
1075 # call special-case build recipe, e.g. for openssl
1076 buildrecipe(basedir, archList)
1077
1078 if install is not None:
1079 print("Running install for %s"%(name,))
1080 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001081
Ned Deily4a96a372013-01-29 00:08:32 -08001082 print("Done %s"%(name,))
1083 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001084
1085 os.chdir(curdir)
1086
1087def buildLibraries():
1088 """
1089 Build our dependencies into $WORKDIR/libraries/usr/local
1090 """
Ned Deily4a96a372013-01-29 00:08:32 -08001091 print("")
1092 print("Building required libraries")
1093 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001094 universal = os.path.join(WORKDIR, 'libraries')
1095 os.mkdir(universal)
1096 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1097 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1098
Ronald Oussoren1943f862009-03-30 19:39:14 +00001099 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001100 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001101
1102
1103
1104def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001105 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001106 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001107 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001108 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001109 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001110 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001111 curDir = os.getcwd()
1112 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001113 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001114
1115 # Search third-party source directory for a pre-built version of the docs.
1116 # Use the naming convention of the docs.python.org html downloads:
1117 # python-3.9.0b1-docs-html.tar.bz2
1118 doctarfiles = [ f for f in os.listdir(DEPSRC)
1119 if f.startswith('python-'+getFullVersion())
1120 if f.endswith('-docs-html.tar.bz2') ]
1121 if doctarfiles:
1122 doctarfile = doctarfiles[0]
1123 if not os.path.exists('build'):
1124 os.mkdir('build')
1125 # if build directory existed, it was emptied by make clean, above
1126 os.chdir('build')
1127 # Extract the first archive found for this version into build
1128 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1129 # see if tar extracted a directory ending in -docs-html
1130 archivefiles = [ f for f in os.listdir('.')
1131 if f.endswith('-docs-html')
1132 if os.path.isdir(f) ]
1133 if archivefiles:
1134 archivefile = archivefiles[0]
1135 # make it our 'Docs/build/html' directory
1136 print(' -- using pre-built python documentation from %s'%archivefile)
1137 os.rename(archivefile, 'html')
1138 os.chdir(buildDir)
1139
1140 htmlDir = os.path.join('build', 'html')
1141 if not os.path.exists(htmlDir):
1142 # Create virtual environment for docs builds with blurb and sphinx
1143 runCommand('make venv')
Ned Deily1931e642020-06-25 04:51:46 -04001144 runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
Ned Deily63fc55b2020-06-08 03:52:43 -04001145 runCommand('make html PYTHON=venv/bin/python')
1146 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001147 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001148
1149
1150def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001151 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152
1153 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1154 rootDir = os.path.join(WORKDIR, '_root')
1155
1156 if os.path.exists(buildDir):
1157 shutil.rmtree(buildDir)
1158 if os.path.exists(rootDir):
1159 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001160 os.makedirs(buildDir)
1161 os.makedirs(rootDir)
1162 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001163 curdir = os.getcwd()
1164 os.chdir(buildDir)
1165
Thomas Wouters477c8d52006-05-27 19:21:47 +00001166 # Extract the version from the configure file, needed to calculate
1167 # several paths.
1168 version = getVersion()
1169
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001170 # Since the extra libs are not in their installed framework location
1171 # during the build, augment the library path so that the interpreter
1172 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001173
Ned Deily4a96a372013-01-29 00:08:32 -08001174 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001175 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001176 "--with-universal-archs=%s "
1177 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001178 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001179 "%s "
1180 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001181 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001182 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001183 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001184 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001185 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001186 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001187 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001188 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1189 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001190 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1191 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1192 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1193 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001194 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001195 shellQuote(WORKDIR)[1:-1]))
1196
Ned Deily1931e642020-06-25 04:51:46 -04001197 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1198 # environment variables are no longer automatically inherited
1199 # by child processes from their parents. We used to just set
1200 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1201 # in build-installer.py's process environment and it was
1202 # passed through the make utility into the environment of
1203 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1204 # the existing RUNSHARED configuration value when we call
1205 # make for extension module builds.
1206
1207 runshared_for_make = "".join([
1208 " RUNSHARED=",
1209 "'",
1210 grepValue("Makefile", "RUNSHARED"),
1211 ' DYLD_LIBRARY_PATH=',
1212 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1213 "'" ])
1214
Ned Deilyb364d9f2017-07-24 04:58:43 -04001215 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1216 # and, if defined, append its value to the make command. This allows
1217 # us to pass in version control tags, like GITTAG, to a build from a
1218 # tarball rather than from a vcs checkout, thus eliminating the need
1219 # to have a working copy of the vcs program on the build machine.
1220 #
1221 # A typical use might be:
1222 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1223 # GITVERSION='echo 123456789a' \
1224 # GITTAG='echo v3.6.0' \
1225 # GITBRANCH='echo 3.6'"
1226
1227 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1228 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001229 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001230 else:
Ned Deily1931e642020-06-25 04:51:46 -04001231 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001232 print("Running " + make_cmd)
1233 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001234
Ned Deily1931e642020-06-25 04:51:46 -04001235 make_cmd = "make install DESTDIR=%s %s"%(
1236 shellQuote(rootDir),
1237 runshared_for_make)
1238 print("Running " + make_cmd)
1239 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001240
Ned Deily1931e642020-06-25 04:51:46 -04001241 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1242 shellQuote(rootDir),
1243 runshared_for_make)
1244 print("Running " + make_cmd)
1245 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001246
Ned Deily4a96a372013-01-29 00:08:32 -08001247 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001248 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001249 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001250 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001251 'Python.framework', 'Versions', getVersion(), 'lib')
1252 fw_lib_dir = os.path.join(
1253 WORKDIR, '_root', 'Library', 'Frameworks',
1254 'Python.framework', 'Versions', getVersion(), 'lib')
1255 if internalTk():
1256 # move Tcl and Tk pkgconfig files
1257 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1258 shellQuote(build_lib_dir),
1259 shellQuote(fw_lib_dir) ))
1260 runCommand("rm -r %s/pkgconfig"%(
1261 shellQuote(build_lib_dir), ))
1262 runCommand("mv %s/* %s"%(
1263 shellQuote(build_lib_dir),
1264 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001265
Ned Deilydde4f632016-09-12 09:39:23 -04001266 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1267 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1268 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1269 # create directory for OpenSSL certificates
1270 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1271 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001272
Ned Deily4a96a372013-01-29 00:08:32 -08001273 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001274 gid = grp.getgrnam('admin').gr_gid
1275
Ned Deily4a96a372013-01-29 00:08:32 -08001276 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001277 for dirpath, dirnames, filenames in os.walk(frmDir):
1278 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001279 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001280 os.chown(os.path.join(dirpath, dn), -1, gid)
1281
Thomas Wouters477c8d52006-05-27 19:21:47 +00001282 for fn in filenames:
1283 if os.path.islink(fn):
1284 continue
1285
1286 # "chmod g+w $fn"
1287 p = os.path.join(dirpath, fn)
1288 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001289 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1290 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001291
Ned Deily4a96a372013-01-29 00:08:32 -08001292 if fn in EXPECTED_SHARED_LIBS:
1293 # check to see that this file was linked with the
1294 # expected library path and version
1295 data = captureCommand("otool -L %s" % shellQuote(p))
1296 for sl in EXPECTED_SHARED_LIBS[fn]:
1297 if ("\t%s " % sl) not in data:
1298 print("Expected shared lib %s was not linked with %s"
1299 % (sl, p))
1300 shared_lib_error = True
1301
1302 if shared_lib_error:
1303 fatal("Unexpected shared library errors.")
1304
Ned Deilye59e4c52011-01-29 18:56:28 +00001305 if PYTHON_3:
1306 LDVERSION=None
1307 VERSION=None
1308 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001309
Ned Deilye59e4c52011-01-29 18:56:28 +00001310 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001311 for ln in fp:
1312 if ln.startswith('VERSION='):
1313 VERSION=ln.split()[1]
1314 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001315 ABIFLAGS=ln.split()
1316 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001317 if ln.startswith('LDVERSION='):
1318 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001319 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001320
Ned Deilye59e4c52011-01-29 18:56:28 +00001321 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1322 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1323 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001324 if getVersionMajorMinor() >= (3, 6):
1325 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001326 else:
1327 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001328
Thomas Wouters477c8d52006-05-27 19:21:47 +00001329 # We added some directories to the search path during the configure
1330 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001331 # the end-users system. Also remove the directories from _sysconfigdata.py
1332 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001333
Ned Deilya4f6b002013-10-25 00:47:38 -07001334 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1335 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1336
Ned Deilya4f6b002013-10-25 00:47:38 -07001337 # fix Makefile
1338 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1339 fp = open(path, 'r')
1340 data = fp.read()
1341 fp.close()
1342
1343 for p in (include_path, lib_path):
1344 data = data.replace(" " + p, '')
1345 data = data.replace(p + " ", '')
1346
1347 fp = open(path, 'w')
1348 fp.write(data)
1349 fp.close()
1350
Ned Deily652bad42016-08-15 14:37:14 -04001351 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001352 #
1353 # TODO: make this more robust! test_sysconfig_module of
1354 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1355 # the output from get_config_var in both sysconfig and
1356 # distutils.sysconfig is exactly the same for both CFLAGS and
1357 # LDFLAGS. The fixing up is now complicated by the pretty
1358 # printing in _sysconfigdata.py. Also, we are using the
1359 # pprint from the Python running the installer build which
1360 # may not cosmetically format the same as the pprint in the Python
1361 # being built (and which is used to originally generate
1362 # _sysconfigdata.py).
1363
1364 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001365 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001366 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001367 path = os.path.join(path_to_lib,
1368 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001369 else:
1370 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1371 fp = open(path, 'r')
1372 data = fp.read()
1373 fp.close()
1374 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001375 if RUNNING_ON_PYTHON2:
1376 exec(data)
1377 else:
1378 g_dict = {}
1379 l_dict = {}
1380 exec(data, g_dict, l_dict)
1381 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001382 vars = {}
1383 for k, v in build_time_vars.items():
1384 if type(v) == type(''):
1385 for p in (include_path, lib_path):
1386 v = v.replace(' ' + p, '')
1387 v = v.replace(p + ' ', '')
1388 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001389
Ned Deily652bad42016-08-15 14:37:14 -04001390 fp = open(path, 'w')
1391 # duplicated from sysconfig._generate_posix_vars()
1392 fp.write('# system configuration generated and used by'
1393 ' the sysconfig module\n')
1394 fp.write('build_time_vars = ')
1395 pprint.pprint(vars, stream=fp)
1396 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001397
1398 # Add symlinks in /usr/local/bin, using relative links
1399 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1400 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1401 'Python.framework', 'Versions', version, 'bin')
1402 if os.path.exists(usr_local_bin):
1403 shutil.rmtree(usr_local_bin)
1404 os.makedirs(usr_local_bin)
1405 for fn in os.listdir(
1406 os.path.join(frmDir, 'Versions', version, 'bin')):
1407 os.symlink(os.path.join(to_framework, fn),
1408 os.path.join(usr_local_bin, fn))
1409
1410 os.chdir(curdir)
1411
Thomas Wouters477c8d52006-05-27 19:21:47 +00001412def patchFile(inPath, outPath):
1413 data = fileContents(inPath)
1414 data = data.replace('$FULL_VERSION', getFullVersion())
1415 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001416 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001417 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001418 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001419 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001420
1421 # This one is not handy as a template variable
1422 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001423 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001424 fp.write(data)
1425 fp.close()
1426
1427def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001428 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001429 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001430 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001431 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001432 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001433 fp.write(data)
1434 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001435 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001436
1437
1438
1439def packageFromRecipe(targetDir, recipe):
1440 curdir = os.getcwd()
1441 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001442 # The major version (such as 2.5) is included in the package name
1443 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001444 # common.
1445 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001446 srcdir = recipe.get('source')
1447 pkgroot = recipe.get('topdir', srcdir)
1448 postflight = recipe.get('postflight')
1449 readme = textwrap.dedent(recipe['readme'])
1450 isRequired = recipe.get('required', True)
1451
Ned Deily4a96a372013-01-29 00:08:32 -08001452 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001453
1454 # Substitute some variables
1455 textvars = dict(
1456 VER=getVersion(),
1457 FULLVER=getFullVersion(),
1458 )
1459 readme = readme % textvars
1460
1461 if pkgroot is not None:
1462 pkgroot = pkgroot % textvars
1463 else:
1464 pkgroot = '/'
1465
1466 if srcdir is not None:
1467 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1468 srcdir = srcdir % textvars
1469
1470 if postflight is not None:
1471 postflight = os.path.abspath(postflight)
1472
1473 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1474 os.makedirs(packageContents)
1475
1476 if srcdir is not None:
1477 os.chdir(srcdir)
1478 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1479 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1480 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1481
1482 fn = os.path.join(packageContents, 'PkgInfo')
1483 fp = open(fn, 'w')
1484 fp.write('pmkrpkg1')
1485 fp.close()
1486
1487 rsrcDir = os.path.join(packageContents, "Resources")
1488 os.mkdir(rsrcDir)
1489 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1490 fp.write(readme)
1491 fp.close()
1492
1493 if postflight is not None:
1494 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1495
1496 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001497 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001498 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001499 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1500 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1501 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001502 CFBundleShortVersionString=vers,
1503 IFMajorVersion=major,
1504 IFMinorVersion=minor,
1505 IFPkgFormatVersion=0.10000000149011612,
1506 IFPkgFlagAllowBackRev=False,
1507 IFPkgFlagAuthorizationAction="RootAuthorization",
1508 IFPkgFlagDefaultLocation=pkgroot,
1509 IFPkgFlagFollowLinks=True,
1510 IFPkgFlagInstallFat=True,
1511 IFPkgFlagIsRequired=isRequired,
1512 IFPkgFlagOverwritePermissions=False,
1513 IFPkgFlagRelocatable=False,
1514 IFPkgFlagRestartAction="NoRestart",
1515 IFPkgFlagRootVolumeOnly=True,
1516 IFPkgFlagUpdateInstalledLangauges=False,
1517 )
1518 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1519
Ned Deily1931e642020-06-25 04:51:46 -04001520 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001521 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001522 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001523 IFPkgDescriptionVersion=vers,
1524 )
1525 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1526
1527 finally:
1528 os.chdir(curdir)
1529
1530
1531def makeMpkgPlist(path):
1532
1533 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001534 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001535
Ned Deily1931e642020-06-25 04:51:46 -04001536 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001537 CFBundleGetInfoString="Python %s"%(vers,),
1538 CFBundleIdentifier='org.python.Python',
1539 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001540 CFBundleShortVersionString=vers,
1541 IFMajorVersion=major,
1542 IFMinorVersion=minor,
1543 IFPkgFlagComponentDirectory="Contents/Packages",
1544 IFPkgFlagPackageList=[
1545 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001546 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001547 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001548 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001549 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001550 ],
1551 IFPkgFormatVersion=0.10000000149011612,
1552 IFPkgFlagBackgroundScaling="proportional",
1553 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001554 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001555 )
1556
1557 writePlist(pl, path)
1558
1559
1560def buildInstaller():
1561
1562 # Zap all compiled files
1563 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1564 for fn in filenames:
1565 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1566 os.unlink(os.path.join(dirpath, fn))
1567
1568 outdir = os.path.join(WORKDIR, 'installer')
1569 if os.path.exists(outdir):
1570 shutil.rmtree(outdir)
1571 os.mkdir(outdir)
1572
Ronald Oussoren1943f862009-03-30 19:39:14 +00001573 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001574 pkgcontents = os.path.join(pkgroot, 'Packages')
1575 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001576 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001577 packageFromRecipe(pkgcontents, recipe)
1578
1579 rsrcDir = os.path.join(pkgroot, 'Resources')
1580
1581 fn = os.path.join(pkgroot, 'PkgInfo')
1582 fp = open(fn, 'w')
1583 fp.write('pmkrpkg1')
1584 fp.close()
1585
1586 os.mkdir(rsrcDir)
1587
1588 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001589 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001590 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001591 IFPkgDescriptionVersion=getVersion(),
1592 )
1593
1594 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1595 for fn in os.listdir('resources'):
1596 if fn == '.svn': continue
1597 if fn.endswith('.jpg'):
1598 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1599 else:
1600 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1601
Thomas Wouters477c8d52006-05-27 19:21:47 +00001602
1603def installSize(clear=False, _saved=[]):
1604 if clear:
1605 del _saved[:]
1606 if not _saved:
1607 data = captureCommand("du -ks %s"%(
1608 shellQuote(os.path.join(WORKDIR, '_root'))))
1609 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1610 return _saved[0]
1611
1612
1613def buildDMG():
1614 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001615 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001616 """
1617 outdir = os.path.join(WORKDIR, 'diskimage')
1618 if os.path.exists(outdir):
1619 shutil.rmtree(outdir)
1620
1621 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001622 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001623 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001624 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001625 imagepath = imagepath + '.dmg'
1626
1627 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001628
1629 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1630 # when hdiutil create fails with "Resource busy". For now, just retry
1631 # the create a few times and hope that it eventually works.
1632
Ronald Oussoren1943f862009-03-30 19:39:14 +00001633 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001634 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001635 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001636 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001637 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001638 for i in range(5):
1639 fd = os.popen(cmd, 'r')
1640 data = fd.read()
1641 xit = fd.close()
1642 if not xit:
1643 break
1644 sys.stdout.write(data)
1645 print(" -- retrying hdiutil create")
1646 time.sleep(5)
1647 else:
cclaussd3371692019-06-03 05:19:44 +02001648 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001649
1650 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1651 os.mkdir(os.path.join(WORKDIR, "mnt"))
1652 runCommand("hdiutil attach %s -mountroot %s"%(
1653 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1654
1655 # Custom icon for the DMG, shown when the DMG is mounted.
1656 shutil.copy("../Icons/Disk Image.icns",
1657 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001658 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001659 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1660
1661 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1662
1663 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1664 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1665 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1666 setIcon(imagepath, "../Icons/Disk Image.icns")
1667
1668 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001669
1670 return imagepath
1671
1672
1673def setIcon(filePath, icnsPath):
1674 """
1675 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001676 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001677
Ronald Oussoren70050672010-04-30 15:00:26 +00001678 dirPath = os.path.normpath(os.path.dirname(__file__))
1679 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001680 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1681 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1682 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001683 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1684 if not os.path.exists(appPath):
1685 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001686 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1687 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001688
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001689 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1690 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001691
1692def main():
1693 # First parse options and check if we can perform our work
1694 parseOptions()
1695 checkEnvironment()
1696
Ronald Oussoren1943f862009-03-30 19:39:14 +00001697 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001698 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001699 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001700
1701 if os.path.exists(WORKDIR):
1702 shutil.rmtree(WORKDIR)
1703 os.mkdir(WORKDIR)
1704
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001705 os.environ['LC_ALL'] = 'C'
1706
Thomas Wouters477c8d52006-05-27 19:21:47 +00001707 # Then build third-party libraries such as sleepycat DB4.
1708 buildLibraries()
1709
1710 # Now build python itself
1711 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001712
1713 # And then build the documentation
1714 # Remove the Deployment Target from the shell
1715 # environment, it's no longer needed and
1716 # an unexpected build target can cause problems
1717 # when Sphinx and its dependencies need to
1718 # be (re-)installed.
1719 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001720 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001721
1722
1723 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001724 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001725 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001726 fn = os.path.join(folder, "License.rtf")
1727 patchFile("resources/License.rtf", fn)
1728 fn = os.path.join(folder, "ReadMe.rtf")
1729 patchFile("resources/ReadMe.rtf", fn)
1730 fn = os.path.join(folder, "Update Shell Profile.command")
1731 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001732 fn = os.path.join(folder, "Install Certificates.command")
1733 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001734 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001735 setIcon(folder, "../Icons/Python Folder.icns")
1736
1737 # Create the installer
1738 buildInstaller()
1739
1740 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001741 patchFile('resources/ReadMe.rtf',
1742 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001743
1744 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001745 patchFile('resources/License.rtf',
1746 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001747
1748 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001749 fp.write("# BUILD INFO\n")
1750 fp.write("# Date: %s\n" % time.ctime())
1751 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001752 fp.close()
1753
Thomas Wouters477c8d52006-05-27 19:21:47 +00001754 # And copy it to a DMG
1755 buildDMG()
1756
Thomas Wouters477c8d52006-05-27 19:21:47 +00001757if __name__ == "__main__":
1758 main()