blob: a58b922ce30b8363f208241ea6d1bb97d5f16112 [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 Oussoren1943f862009-03-30 19:39:14 +0000119universal_opts_map = { '32-bit': ('i386', 'ppc',),
120 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000121 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500122 'intel-32': ('i386',),
123 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000124 '3-way': ('ppc', 'i386', 'x86_64'),
125 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
126default_target_map = {
127 '64-bit': '10.5',
128 '3-way': '10.5',
129 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500130 'intel-32': '10.4',
131 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000132 'all': '10.5',
133}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000134
135UNIVERSALOPTS = tuple(universal_opts_map.keys())
136
137UNIVERSALARCHS = '32-bit'
138
139ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000140
Ezio Melotti42da6632011-03-15 05:18:48 +0200141# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000142SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143 os.path.dirname(
144 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000145 os.path.abspath(__file__
146 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000147
Ronald Oussoren1943f862009-03-30 19:39:14 +0000148# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500149DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000150
Ned Deily04cdfa12014-06-25 13:36:14 -0700151def getDeptargetTuple():
152 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
153
154def getTargetCompilers():
155 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800156 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500157 '10.5': ('gcc', 'g++'),
158 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700159 }
Ned Deilyacd71632018-02-24 14:30:44 -0500160 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000161
Ned Deily04cdfa12014-06-25 13:36:14 -0700162CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000163
Ned Deily5d3febf2014-12-13 00:17:46 -0800164PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000165
Thomas Wouters89f507f2006-12-13 04:49:30 +0000166USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000167 Usage: build_python [options]
168
169 Options:
170 -? or -h: Show this message
171 -b DIR
172 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
173 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500174 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000175 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500176 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000177 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000178""")% globals()
179
Ned Deily4a96a372013-01-29 00:08:32 -0800180# Dict of object file names with shared library names to check after building.
181# This is to ensure that we ended up dynamically linking with the shared
182# library paths and versions we expected. For example:
183# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
184# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
185# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
186EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000187
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500188# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400189# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500190def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400191 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500192
Ned Deily5d3febf2014-12-13 00:17:46 -0800193# List of names of third party software built with this installer.
194# The names will be inserted into the rtf version of the License.
195THIRD_PARTY_LIBS = []
196
Thomas Wouters477c8d52006-05-27 19:21:47 +0000197# Instructions for building libraries that are necessary for building a
198# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000199# [The recipes are defined here for convenience but instantiated later after
200# command line options have been processed.]
201def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000202 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000203
Ned Deily04cdfa12014-06-25 13:36:14 -0700204 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800205
Ned Deilydde4f632016-09-12 09:39:23 -0400206 # Since Apple removed the header files for the deprecated system
207 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
208 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800209
Ned Deilydde4f632016-09-12 09:39:23 -0400210 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800211 dict(
Ned Deily783a6732020-04-21 22:41:33 -0400212 name="OpenSSL 1.1.1g",
213 url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
214 checksum='76766e98997660138cdaf13a187bd234',
Ned Deily5d3febf2014-12-13 00:17:46 -0800215 buildrecipe=build_universal_openssl,
216 configure=None,
217 install=None,
218 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400219 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800220
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500221 if internalTk():
Ned Deily5b3582c2013-10-25 00:41:46 -0700222 result.extend([
223 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500224 name="Tcl 8.6.8",
225 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
226 checksum='81656d3367af032e0ae6157eff134f89',
Ned Deily5b3582c2013-10-25 00:41:46 -0700227 buildDir="unix",
228 configure_pre=[
229 '--enable-shared',
230 '--enable-threads',
231 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
232 ],
233 useLDFlags=False,
234 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
235 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500236 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700237 },
238 ),
239 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500240 name="Tk 8.6.8",
241 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
242 checksum='5e0faecba458ee1386078fb228d008ba',
243 patches=[
244 "tk868_on_10_8_10_9.patch",
245 ],
Ned Deily5b3582c2013-10-25 00:41:46 -0700246 buildDir="unix",
247 configure_pre=[
248 '--enable-aqua',
249 '--enable-shared',
250 '--enable-threads',
251 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
252 ],
253 useLDFlags=False,
254 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'%{
255 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500256 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
257 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700258 },
259 ),
260 ])
261
Ned Deilyed730102014-11-14 18:55:05 -0800262 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800263 result.extend([
264 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500265 name="XZ 5.2.3",
266 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
267 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800268 configure_pre=[
269 '--disable-dependency-tracking',
270 ]
271 ),
272 ])
273
274 result.extend([
275 dict(
276 name="NCurses 5.9",
277 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
278 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
279 configure_pre=[
280 "--enable-widec",
281 "--without-cxx",
282 "--without-cxx-binding",
283 "--without-ada",
284 "--without-curses-h",
285 "--enable-shared",
286 "--with-shared",
287 "--without-debug",
288 "--without-normal",
289 "--without-tests",
290 "--without-manpages",
291 "--datadir=/usr/share",
292 "--sysconfdir=/etc",
293 "--sharedstatedir=/usr/com",
294 "--with-terminfo-dirs=/usr/share/terminfo",
295 "--with-default-terminfo-dir=/usr/share/terminfo",
296 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
297 ],
298 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200299 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800300 "f54bf02a349f96a7c4f0d00922f3a0d4"),
301 ],
302 useLDFlags=False,
303 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
304 shellQuote(os.path.join(WORKDIR, 'libraries')),
305 shellQuote(os.path.join(WORKDIR, 'libraries')),
306 getVersion(),
307 ),
308 ),
309 dict(
Erlend Egeberg Aasland7cf1cb32020-07-20 08:19:18 +0200310 name="SQLite 3.32.3",
311 url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz",
312 checksum='2e3911a3c15e85c2f2d040154bbe5ce3',
Ned Deily4a96a372013-01-29 00:08:32 -0800313 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700314 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800315 '-DSQLITE_ENABLE_FTS4 '
316 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500317 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800318 '-DSQLITE_ENABLE_RTREE '
319 '-DSQLITE_TCL=0 '
320 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
321 configure_pre=[
322 '--enable-threadsafe',
323 '--enable-shared=no',
324 '--enable-static=yes',
325 '--disable-readline',
326 '--disable-dependency-tracking',
327 ]
328 ),
329 ])
330
Ned Deily04cdfa12014-06-25 13:36:14 -0700331 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000332 result.extend([
333 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000334 name="Bzip2 1.0.6",
335 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
336 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000337 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500338 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800339 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000340 shellQuote(os.path.join(WORKDIR, 'libraries')),
341 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000342 ),
343 ),
344 dict(
345 name="ZLib 1.2.3",
346 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
347 checksum='debc62758716a169df9f62e6ab2bc634',
348 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500349 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800350 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000351 shellQuote(os.path.join(WORKDIR, 'libraries')),
352 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000353 ),
354 ),
355 dict(
356 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000357 name="GNU Readline 6.1.2",
358 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
359 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000360 patchlevel='0',
361 patches=[
362 # The readline maintainers don't do actual micro releases, but
363 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800364 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
365 'c642f2e84d820884b0bf9fd176bc6c3f'),
366 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
367 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000368 ]
369 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000370 ])
371
Ned Deily4f7ff782011-01-15 05:29:12 +0000372 if not PYTHON_3:
373 result.extend([
374 dict(
375 name="Sleepycat DB 4.7.25",
376 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
377 checksum='ec2b87e833779681a0c3a814aa71359e',
378 buildDir="build_unix",
379 configure="../dist/configure",
380 configure_pre=[
381 '--includedir=/usr/local/include/db4',
382 ]
383 ),
384 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000385
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000386 return result
387
Thomas Wouters477c8d52006-05-27 19:21:47 +0000388
Thomas Wouters477c8d52006-05-27 19:21:47 +0000389# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000390def pkg_recipes():
391 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
392 result = [
393 dict(
394 name="PythonFramework",
395 long_name="Python Framework",
396 source="/Library/Frameworks/Python.framework",
397 readme="""\
398 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500399 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000400 """,
401 postflight="scripts/postflight.framework",
402 selected='selected',
403 ),
404 dict(
405 name="PythonApplications",
406 long_name="GUI Applications",
407 source="/Applications/Python %(VER)s",
408 readme="""\
409 This package installs IDLE (an interactive Python IDE),
410 Python Launcher and Build Applet (create application bundles
411 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000412
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000413 It also installs a number of examples and demos.
414 """,
415 required=False,
416 selected='selected',
417 ),
418 dict(
419 name="PythonUnixTools",
420 long_name="UNIX command-line tools",
421 source="/usr/local/bin",
422 readme="""\
423 This package installs the unix tools in /usr/local/bin for
424 compatibility with older releases of Python. This package
425 is not necessary to use Python.
426 """,
427 required=False,
428 selected='selected',
429 ),
430 dict(
431 name="PythonDocumentation",
432 long_name="Python Documentation",
433 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
434 source="/pydocs",
435 readme="""\
436 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800437 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000438 """,
439 postflight="scripts/postflight.documentation",
440 required=False,
441 selected='selected',
442 ),
443 dict(
444 name="PythonProfileChanges",
445 long_name="Shell profile updater",
446 readme="""\
447 This packages updates your shell profile to make sure that
448 the Python tools are found by your shell in preference of
449 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000450
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000451 If you don't install this package you'll have to add
452 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
453 to your PATH by hand.
454 """,
455 postflight="scripts/postflight.patch-profile",
456 topdir="/Library/Frameworks/Python.framework",
457 source="/empty-dir",
458 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800459 selected='selected',
460 ),
461 dict(
462 name="PythonInstallPip",
463 long_name="Install or upgrade pip",
464 readme="""\
465 This package installs (or upgrades from an earlier version)
466 pip, a tool for installing and managing Python packages.
467 """,
468 postflight="scripts/postflight.ensurepip",
469 topdir="/Library/Frameworks/Python.framework",
470 source="/empty-dir",
471 required=False,
472 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000473 ),
474 ]
475
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000476 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000477
Thomas Wouters477c8d52006-05-27 19:21:47 +0000478def fatal(msg):
479 """
480 A fatal error, bail out.
481 """
482 sys.stderr.write('FATAL: ')
483 sys.stderr.write(msg)
484 sys.stderr.write('\n')
485 sys.exit(1)
486
487def fileContents(fn):
488 """
489 Return the contents of the named file
490 """
Ned Deily4a96a372013-01-29 00:08:32 -0800491 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000492
493def runCommand(commandline):
494 """
Ezio Melotti13925002011-03-16 11:05:33 +0200495 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000496 unless the command fails.
497 """
498 fd = os.popen(commandline, 'r')
499 data = fd.read()
500 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000501 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000502 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800503 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000504
505 if VERBOSE:
506 sys.stdout.write(data); sys.stdout.flush()
507
508def captureCommand(commandline):
509 fd = os.popen(commandline, 'r')
510 data = fd.read()
511 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000512 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000513 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800514 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000515
516 return data
517
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000518def getTclTkVersion(configfile, versionline):
519 """
520 search Tcl or Tk configuration file for version line
521 """
522 try:
523 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300524 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000525 fatal("Framework configuration file not found: %s" % configfile)
526
527 for l in f:
528 if l.startswith(versionline):
529 f.close()
530 return l
531
532 fatal("Version variable %s not found in framework configuration file: %s"
533 % (versionline, configfile))
534
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535def checkEnvironment():
536 """
537 Check that we're running on a supported system.
538 """
539
Ned Deily8c9bb722018-01-30 07:42:14 -0500540 if sys.version_info[0:2] < (2, 5):
541 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000542
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500544 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000545
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000546 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500547 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000548
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000549 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500550 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000551 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500552 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
553 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
554 # enforce that the version of the user-installed framework also
555 # exists in the system-supplied Tcl/Tk frameworks. Time to support
556 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500557 if not internalTk():
558 frameworks = {}
559 for framework in ['Tcl', 'Tk']:
560 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
561 libfw = os.path.join('/', fwpth)
562 usrfw = os.path.join(os.getenv('HOME'), fwpth)
563 frameworks[framework] = os.readlink(libfw)
564 if not os.path.exists(libfw):
565 fatal("Please install a link to a current %s %s as %s so "
566 "the user can override the system framework."
567 % (framework, frameworks[framework], libfw))
568 if os.path.exists(usrfw):
569 fatal("Please rename %s to avoid possible dynamic load issues."
570 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000571
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500572 if frameworks['Tcl'] != frameworks['Tk']:
573 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000574
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500575 print(" -- Building with external Tcl/Tk %s frameworks"
576 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800577
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500578 # add files to check after build
579 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
580 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
581 % frameworks['Tcl'],
582 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
583 % frameworks['Tk'],
584 ]
585 else:
586 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500587 print("")
588
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000589 # Remove inherited environment variables which might influence build
590 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
591 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
592 for ev in list(os.environ):
593 for prefix in environ_var_prefixes:
594 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800595 print("INFO: deleting environment variable %s=%s" % (
596 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000597 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000598
Ned Deily4a96a372013-01-29 00:08:32 -0800599 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
600 if 'SDK_TOOLS_BIN' in os.environ:
601 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
602 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
603 # add its fixed location here if it exists
604 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
605 if os.path.isdir(OLD_DEVELOPER_TOOLS):
606 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
607 os.environ['PATH'] = base_path
608 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400609 # Ensure we have access to sphinx-build.
610 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700611 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000612
Thomas Wouters89f507f2006-12-13 04:49:30 +0000613def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000614 """
615 Parse arguments and update global settings.
616 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500617 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800618 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800619 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400620 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000621
622 if args is None:
623 args = sys.argv[1:]
624
625 try:
626 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000627 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
628 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800629 except getopt.GetoptError:
630 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000631 sys.exit(1)
632
633 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800634 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000635 sys.exit(1)
636
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000637 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000638 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000639 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800640 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000641 sys.exit(0)
642
643 elif k in ('-d', '--build-dir'):
644 WORKDIR=v
645
646 elif k in ('--third-party',):
647 DEPSRC=v
648
649 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500650 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000651
652 elif k in ('--src-dir',):
653 SRCDIR=v
654
Ronald Oussoren1943f862009-03-30 19:39:14 +0000655 elif k in ('--dep-target', ):
656 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000657 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000658
659 elif k in ('--universal-archs', ):
660 if v in UNIVERSALOPTS:
661 UNIVERSALARCHS = v
662 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000663 if deptarget is None:
664 # Select alternate default deployment
665 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500666 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000667 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800668 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000669
Thomas Wouters477c8d52006-05-27 19:21:47 +0000670 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800671 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000672
673 SRCDIR=os.path.abspath(SRCDIR)
674 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000675 DEPSRC=os.path.abspath(DEPSRC)
676
Ned Deily04cdfa12014-06-25 13:36:14 -0700677 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000678
Ned Deily5d3febf2014-12-13 00:17:46 -0800679 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400680 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800681
682 print("-- Settings:")
683 print(" * Source directory: %s" % SRCDIR)
684 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800685 print(" * Third-party source: %s" % DEPSRC)
686 print(" * Deployment target: %s" % DEPTARGET)
687 print(" * Universal archs: %s" % str(ARCHLIST))
688 print(" * C compiler: %s" % CC)
689 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800690 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800691 print(" -- Building a Python %s framework at patch level %s"
692 % (getVersion(), getFullVersion()))
693 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000694
695def extractArchive(builddir, archiveName):
696 """
697 Extract a source archive into 'builddir'. Returns the path of the
698 extracted archive.
699
700 XXX: This function assumes that archives contain a toplevel directory
701 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700702 safe enough for almost anything we use. Unfortunately, it does not
703 work for current Tcl and Tk source releases where the basename of
704 the archive ends with "-src" but the uncompressed directory does not.
705 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000706 """
707 curdir = os.getcwd()
708 try:
709 os.chdir(builddir)
710 if archiveName.endswith('.tar.gz'):
711 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700712 if ((retval.startswith('tcl') or retval.startswith('tk'))
713 and retval.endswith('-src')):
714 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000715 if os.path.exists(retval):
716 shutil.rmtree(retval)
717 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
718
719 elif archiveName.endswith('.tar.bz2'):
720 retval = os.path.basename(archiveName[:-8])
721 if os.path.exists(retval):
722 shutil.rmtree(retval)
723 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
724
725 elif archiveName.endswith('.tar'):
726 retval = os.path.basename(archiveName[:-4])
727 if os.path.exists(retval):
728 shutil.rmtree(retval)
729 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
730
731 elif archiveName.endswith('.zip'):
732 retval = os.path.basename(archiveName[:-4])
733 if os.path.exists(retval):
734 shutil.rmtree(retval)
735 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
736
737 data = fp.read()
738 xit = fp.close()
739 if xit is not None:
740 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800741 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000742
743 return os.path.join(builddir, retval)
744
745 finally:
746 os.chdir(curdir)
747
Thomas Wouters477c8d52006-05-27 19:21:47 +0000748def downloadURL(url, fname):
749 """
750 Download the contents of the url into the file.
751 """
Ned Deily4a96a372013-01-29 00:08:32 -0800752 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000753 fpOut = open(fname, 'wb')
754 block = fpIn.read(10240)
755 try:
756 while block:
757 fpOut.write(block)
758 block = fpIn.read(10240)
759 fpIn.close()
760 fpOut.close()
761 except:
762 try:
763 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300764 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000765 pass
766
Ned Deily4a96a372013-01-29 00:08:32 -0800767def verifyThirdPartyFile(url, checksum, fname):
768 """
769 Download file from url to filename fname if it does not already exist.
770 Abort if file contents does not match supplied md5 checksum.
771 """
772 name = os.path.basename(fname)
773 if os.path.exists(fname):
774 print("Using local copy of %s"%(name,))
775 else:
776 print("Did not find local copy of %s"%(name,))
777 print("Downloading %s"%(name,))
778 downloadURL(url, fname)
779 print("Archive for %s stored as %s"%(name, fname))
780 if os.system(
781 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
782 % (shellQuote(fname), checksum) ):
783 fatal('MD5 checksum mismatch for file %s' % fname)
784
Ned Deily5d3febf2014-12-13 00:17:46 -0800785def build_universal_openssl(basedir, archList):
786 """
787 Special case build recipe for universal build of openssl.
788
789 The upstream OpenSSL build system does not directly support
790 OS X universal builds. We need to build each architecture
791 separately then lipo them together into fat libraries.
792 """
793
794 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
795 # If we are building on a 10.4.x or earlier system,
796 # unilaterally disable assembly code building to avoid the problem.
797 no_asm = int(platform.release().split(".")[0]) < 9
798
799 def build_openssl_arch(archbase, arch):
800 "Build one architecture of openssl"
801 arch_opts = {
802 "i386": ["darwin-i386-cc"],
803 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
804 "ppc": ["darwin-ppc-cc"],
805 "ppc64": ["darwin64-ppc-cc"],
806 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400807
808 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
809 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
810 # building on our 10.6 gcc-4.2 environment. There have been other
811 # reports of projects running into this when using older compilers.
812 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
813 # building for 10.6.
814 if getDeptargetTuple() == (10, 6):
815 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
816
Ned Deily5d3febf2014-12-13 00:17:46 -0800817 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800818 "no-idea",
819 "no-mdc2",
820 "no-rc5",
821 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800822 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800823 # "enable-unit-test",
824 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800825 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400826 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800827 ]
828 if no_asm:
829 configure_opts.append("no-asm")
830 runCommand(" ".join(["perl", "Configure"]
831 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500832 runCommand("make depend")
833 runCommand("make all")
834 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800835 # runCommand("make test")
836 return
837
838 srcdir = os.getcwd()
839 universalbase = os.path.join(srcdir, "..",
840 os.path.basename(srcdir) + "-universal")
841 os.mkdir(universalbase)
842 archbasefws = []
843 for arch in archList:
844 # fresh copy of the source tree
845 archsrc = os.path.join(universalbase, arch, "src")
846 shutil.copytree(srcdir, archsrc, symlinks=True)
847 # install base for this arch
848 archbase = os.path.join(universalbase, arch, "root")
849 os.mkdir(archbase)
850 # Python framework base within install_prefix:
851 # the build will install into this framework..
852 # This is to ensure that the resulting shared libs have
853 # the desired real install paths built into them.
854 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
855
856 # build one architecture
857 os.chdir(archsrc)
858 build_openssl_arch(archbase, arch)
859 os.chdir(srcdir)
860 archbasefws.append(archbasefw)
861
862 # copy arch-independent files from last build into the basedir framework
863 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
864 shutil.copytree(
865 os.path.join(archbasefw, "include", "openssl"),
866 os.path.join(basefw, "include", "openssl")
867 )
868
869 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
870 "SHLIB_VERSION_NUMBER")
871 # e.g. -> "1.0.0"
872 libcrypto = "libcrypto.dylib"
873 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
874 # e.g. -> "libcrypto.1.0.0.dylib"
875 libssl = "libssl.dylib"
876 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
877 # e.g. -> "libssl.1.0.0.dylib"
878
879 try:
880 os.mkdir(os.path.join(basefw, "lib"))
881 except OSError:
882 pass
883
884 # merge the individual arch-dependent shared libs into a fat shared lib
885 archbasefws.insert(0, basefw)
886 for (lib_unversioned, lib_versioned) in [
887 (libcrypto, libcrypto_versioned),
888 (libssl, libssl_versioned)
889 ]:
890 runCommand("lipo -create -output " +
891 " ".join(shellQuote(
892 os.path.join(fw, "lib", lib_versioned))
893 for fw in archbasefws))
894 # and create an unversioned symlink of it
895 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
896
897 # Create links in the temp include and lib dirs that will be injected
898 # into the Python build so that setup.py can find them while building
899 # and the versioned links so that the setup.py post-build import test
900 # does not fail.
901 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
902 for fn in [
903 ["include", "openssl"],
904 ["lib", libcrypto],
905 ["lib", libssl],
906 ["lib", libcrypto_versioned],
907 ["lib", libssl_versioned],
908 ]:
909 os.symlink(
910 os.path.join(relative_path, *fn),
911 os.path.join(basedir, "usr", "local", *fn)
912 )
913
914 return
915
Thomas Wouters477c8d52006-05-27 19:21:47 +0000916def buildRecipe(recipe, basedir, archList):
917 """
918 Build software using a recipe. This function does the
919 'configure;make;make install' dance for C software, with a possibility
920 to customize this process, basically a poor-mans DarwinPorts.
921 """
922 curdir = os.getcwd()
923
924 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800925 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000926 url = recipe['url']
927 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800928 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000929 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
930 shellQuote(basedir)))
931
932 archiveName = os.path.split(url)[-1]
933 sourceArchive = os.path.join(DEPSRC, archiveName)
934
935 if not os.path.exists(DEPSRC):
936 os.mkdir(DEPSRC)
937
Ned Deily4a96a372013-01-29 00:08:32 -0800938 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
939 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000940 buildDir=os.path.join(WORKDIR, '_bld')
941 if not os.path.exists(buildDir):
942 os.mkdir(buildDir)
943
944 workDir = extractArchive(buildDir, sourceArchive)
945 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000946
Ned Deily4a96a372013-01-29 00:08:32 -0800947 for patch in recipe.get('patches', ()):
948 if isinstance(patch, tuple):
949 url, checksum = patch
950 fn = os.path.join(DEPSRC, os.path.basename(url))
951 verifyThirdPartyFile(url, checksum, fn)
952 else:
953 # patch is a file in the source directory
954 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000955 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
956 shellQuote(fn),))
957
Ned Deily4a96a372013-01-29 00:08:32 -0800958 for patchscript in recipe.get('patchscripts', ()):
959 if isinstance(patchscript, tuple):
960 url, checksum = patchscript
961 fn = os.path.join(DEPSRC, os.path.basename(url))
962 verifyThirdPartyFile(url, checksum, fn)
963 else:
964 # patch is a file in the source directory
965 fn = os.path.join(curdir, patchscript)
966 if fn.endswith('.bz2'):
967 runCommand('bunzip2 -fk %s' % shellQuote(fn))
968 fn = fn[:-4]
969 runCommand('sh %s' % shellQuote(fn))
970 os.unlink(fn)
971
Ned Deily94764b22013-10-27 19:49:29 -0700972 if 'buildDir' in recipe:
973 os.chdir(recipe['buildDir'])
974
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000975 if configure is not None:
976 configure_args = [
977 "--prefix=/usr/local",
978 "--enable-static",
979 "--disable-shared",
980 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
981 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000982
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000983 if 'configure_pre' in recipe:
984 args = list(recipe['configure_pre'])
985 if '--disable-static' in args:
986 configure_args.remove('--enable-static')
987 if '--enable-shared' in args:
988 configure_args.remove('--disable-shared')
989 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000990
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000991 if recipe.get('useLDFlags', 1):
992 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -0500993 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -0800994 "-I%s/usr/local/include"%(
995 recipe.get('extra_cflags', ''),
996 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000997 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000998 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -0500999 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001000 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001001 shellQuote(basedir)[1:-1],
1002 ' -arch '.join(archList)),
1003 ])
1004 else:
1005 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001006 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001007 "-I%s/usr/local/include"%(
1008 recipe.get('extra_cflags', ''),
1009 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001010 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001011 shellQuote(basedir)[1:-1],),
1012 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001013
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001014 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001015 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001016
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001017 configure_args.insert(0, configure)
1018 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001019
Ned Deily4a96a372013-01-29 00:08:32 -08001020 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001021 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001022
Ned Deily5d3febf2014-12-13 00:17:46 -08001023 if buildrecipe is not None:
1024 # call special-case build recipe, e.g. for openssl
1025 buildrecipe(basedir, archList)
1026
1027 if install is not None:
1028 print("Running install for %s"%(name,))
1029 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001030
Ned Deily4a96a372013-01-29 00:08:32 -08001031 print("Done %s"%(name,))
1032 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001033
1034 os.chdir(curdir)
1035
1036def buildLibraries():
1037 """
1038 Build our dependencies into $WORKDIR/libraries/usr/local
1039 """
Ned Deily4a96a372013-01-29 00:08:32 -08001040 print("")
1041 print("Building required libraries")
1042 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001043 universal = os.path.join(WORKDIR, 'libraries')
1044 os.mkdir(universal)
1045 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1046 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1047
Ronald Oussoren1943f862009-03-30 19:39:14 +00001048 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001049 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001050
1051
1052
1053def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001054 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001055 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001056 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001057 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001058 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001059 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001060 curDir = os.getcwd()
1061 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001062 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001063
1064 # Search third-party source directory for a pre-built version of the docs.
1065 # Use the naming convention of the docs.python.org html downloads:
1066 # python-3.9.0b1-docs-html.tar.bz2
1067 doctarfiles = [ f for f in os.listdir(DEPSRC)
1068 if f.startswith('python-'+getFullVersion())
1069 if f.endswith('-docs-html.tar.bz2') ]
1070 if doctarfiles:
1071 doctarfile = doctarfiles[0]
1072 if not os.path.exists('build'):
1073 os.mkdir('build')
1074 # if build directory existed, it was emptied by make clean, above
1075 os.chdir('build')
1076 # Extract the first archive found for this version into build
1077 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1078 # see if tar extracted a directory ending in -docs-html
1079 archivefiles = [ f for f in os.listdir('.')
1080 if f.endswith('-docs-html')
1081 if os.path.isdir(f) ]
1082 if archivefiles:
1083 archivefile = archivefiles[0]
1084 # make it our 'Docs/build/html' directory
1085 print(' -- using pre-built python documentation from %s'%archivefile)
1086 os.rename(archivefile, 'html')
1087 os.chdir(buildDir)
1088
1089 htmlDir = os.path.join('build', 'html')
1090 if not os.path.exists(htmlDir):
1091 # Create virtual environment for docs builds with blurb and sphinx
1092 runCommand('make venv')
Ned Deily1931e642020-06-25 04:51:46 -04001093 runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
Ned Deily63fc55b2020-06-08 03:52:43 -04001094 runCommand('make html PYTHON=venv/bin/python')
1095 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001096 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001097
1098
1099def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001100 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001101
1102 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1103 rootDir = os.path.join(WORKDIR, '_root')
1104
1105 if os.path.exists(buildDir):
1106 shutil.rmtree(buildDir)
1107 if os.path.exists(rootDir):
1108 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001109 os.makedirs(buildDir)
1110 os.makedirs(rootDir)
1111 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001112 curdir = os.getcwd()
1113 os.chdir(buildDir)
1114
Thomas Wouters477c8d52006-05-27 19:21:47 +00001115 # Extract the version from the configure file, needed to calculate
1116 # several paths.
1117 version = getVersion()
1118
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001119 # Since the extra libs are not in their installed framework location
1120 # during the build, augment the library path so that the interpreter
1121 # will find them during its extension import sanity checks.
Ned Deily1931e642020-06-25 04:51:46 -04001122
Ned Deily4a96a372013-01-29 00:08:32 -08001123 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001124 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001125 "--with-universal-archs=%s "
1126 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001127 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001128 "%s "
1129 "%s "
Ned Deily1931e642020-06-25 04:51:46 -04001130 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001131 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001132 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001133 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001134 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001135 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001136 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1931e642020-06-25 04:51:46 -04001137 (' ', "--with-openssl='%s/libraries/usr/local'"%(
1138 shellQuote(WORKDIR)[1:-1],))[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001139 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1140 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1141 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1142 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001143 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001144 shellQuote(WORKDIR)[1:-1]))
1145
Ned Deily1931e642020-06-25 04:51:46 -04001146 # As of macOS 10.11 with SYSTEM INTEGRITY PROTECTION, DYLD_*
1147 # environment variables are no longer automatically inherited
1148 # by child processes from their parents. We used to just set
1149 # DYLD_LIBRARY_PATH, pointing to the third-party libs,
1150 # in build-installer.py's process environment and it was
1151 # passed through the make utility into the environment of
1152 # setup.py. Instead, we now append DYLD_LIBRARY_PATH to
1153 # the existing RUNSHARED configuration value when we call
1154 # make for extension module builds.
1155
1156 runshared_for_make = "".join([
1157 " RUNSHARED=",
1158 "'",
1159 grepValue("Makefile", "RUNSHARED"),
1160 ' DYLD_LIBRARY_PATH=',
1161 os.path.join(WORKDIR, 'libraries', 'usr', 'local', 'lib'),
1162 "'" ])
1163
Ned Deilyb364d9f2017-07-24 04:58:43 -04001164 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1165 # and, if defined, append its value to the make command. This allows
1166 # us to pass in version control tags, like GITTAG, to a build from a
1167 # tarball rather than from a vcs checkout, thus eliminating the need
1168 # to have a working copy of the vcs program on the build machine.
1169 #
1170 # A typical use might be:
1171 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1172 # GITVERSION='echo 123456789a' \
1173 # GITTAG='echo v3.6.0' \
1174 # GITBRANCH='echo 3.6'"
1175
1176 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1177 if make_extras:
Ned Deily1931e642020-06-25 04:51:46 -04001178 make_cmd = "make " + make_extras + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001179 else:
Ned Deily1931e642020-06-25 04:51:46 -04001180 make_cmd = "make" + runshared_for_make
Ned Deilyb364d9f2017-07-24 04:58:43 -04001181 print("Running " + make_cmd)
1182 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001183
Ned Deily1931e642020-06-25 04:51:46 -04001184 make_cmd = "make install DESTDIR=%s %s"%(
1185 shellQuote(rootDir),
1186 runshared_for_make)
1187 print("Running " + make_cmd)
1188 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001189
Ned Deily1931e642020-06-25 04:51:46 -04001190 make_cmd = "make frameworkinstallextras DESTDIR=%s %s"%(
1191 shellQuote(rootDir),
1192 runshared_for_make)
1193 print("Running " + make_cmd)
1194 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001195
Ned Deily4a96a372013-01-29 00:08:32 -08001196 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001197 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001198 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001199 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001200 'Python.framework', 'Versions', getVersion(), 'lib')
1201 fw_lib_dir = os.path.join(
1202 WORKDIR, '_root', 'Library', 'Frameworks',
1203 'Python.framework', 'Versions', getVersion(), 'lib')
1204 if internalTk():
1205 # move Tcl and Tk pkgconfig files
1206 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1207 shellQuote(build_lib_dir),
1208 shellQuote(fw_lib_dir) ))
1209 runCommand("rm -r %s/pkgconfig"%(
1210 shellQuote(build_lib_dir), ))
1211 runCommand("mv %s/* %s"%(
1212 shellQuote(build_lib_dir),
1213 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001214
Ned Deilydde4f632016-09-12 09:39:23 -04001215 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1216 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1217 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1218 # create directory for OpenSSL certificates
1219 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1220 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001221
Ned Deily4a96a372013-01-29 00:08:32 -08001222 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001223 gid = grp.getgrnam('admin').gr_gid
1224
Ned Deily4a96a372013-01-29 00:08:32 -08001225 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001226 for dirpath, dirnames, filenames in os.walk(frmDir):
1227 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001228 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001229 os.chown(os.path.join(dirpath, dn), -1, gid)
1230
Thomas Wouters477c8d52006-05-27 19:21:47 +00001231 for fn in filenames:
1232 if os.path.islink(fn):
1233 continue
1234
1235 # "chmod g+w $fn"
1236 p = os.path.join(dirpath, fn)
1237 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001238 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1239 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001240
Ned Deily4a96a372013-01-29 00:08:32 -08001241 if fn in EXPECTED_SHARED_LIBS:
1242 # check to see that this file was linked with the
1243 # expected library path and version
1244 data = captureCommand("otool -L %s" % shellQuote(p))
1245 for sl in EXPECTED_SHARED_LIBS[fn]:
1246 if ("\t%s " % sl) not in data:
1247 print("Expected shared lib %s was not linked with %s"
1248 % (sl, p))
1249 shared_lib_error = True
1250
1251 if shared_lib_error:
1252 fatal("Unexpected shared library errors.")
1253
Ned Deilye59e4c52011-01-29 18:56:28 +00001254 if PYTHON_3:
1255 LDVERSION=None
1256 VERSION=None
1257 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001258
Ned Deilye59e4c52011-01-29 18:56:28 +00001259 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001260 for ln in fp:
1261 if ln.startswith('VERSION='):
1262 VERSION=ln.split()[1]
1263 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001264 ABIFLAGS=ln.split()
1265 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001266 if ln.startswith('LDVERSION='):
1267 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001268 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001269
Ned Deilye59e4c52011-01-29 18:56:28 +00001270 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1271 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1272 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001273 if getVersionMajorMinor() >= (3, 6):
1274 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001275 else:
1276 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001277
Thomas Wouters477c8d52006-05-27 19:21:47 +00001278 # We added some directories to the search path during the configure
1279 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001280 # the end-users system. Also remove the directories from _sysconfigdata.py
1281 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001282
Ned Deilya4f6b002013-10-25 00:47:38 -07001283 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1284 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1285
Ned Deilya4f6b002013-10-25 00:47:38 -07001286 # fix Makefile
1287 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1288 fp = open(path, 'r')
1289 data = fp.read()
1290 fp.close()
1291
1292 for p in (include_path, lib_path):
1293 data = data.replace(" " + p, '')
1294 data = data.replace(p + " ", '')
1295
1296 fp = open(path, 'w')
1297 fp.write(data)
1298 fp.close()
1299
Ned Deily652bad42016-08-15 14:37:14 -04001300 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001301 #
1302 # TODO: make this more robust! test_sysconfig_module of
1303 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1304 # the output from get_config_var in both sysconfig and
1305 # distutils.sysconfig is exactly the same for both CFLAGS and
1306 # LDFLAGS. The fixing up is now complicated by the pretty
1307 # printing in _sysconfigdata.py. Also, we are using the
1308 # pprint from the Python running the installer build which
1309 # may not cosmetically format the same as the pprint in the Python
1310 # being built (and which is used to originally generate
1311 # _sysconfigdata.py).
1312
1313 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001314 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001315 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001316 path = os.path.join(path_to_lib,
1317 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001318 else:
1319 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1320 fp = open(path, 'r')
1321 data = fp.read()
1322 fp.close()
1323 # create build_time_vars dict
Ned Deily1931e642020-06-25 04:51:46 -04001324 if RUNNING_ON_PYTHON2:
1325 exec(data)
1326 else:
1327 g_dict = {}
1328 l_dict = {}
1329 exec(data, g_dict, l_dict)
1330 build_time_vars = l_dict['build_time_vars']
Ned Deily652bad42016-08-15 14:37:14 -04001331 vars = {}
1332 for k, v in build_time_vars.items():
1333 if type(v) == type(''):
1334 for p in (include_path, lib_path):
1335 v = v.replace(' ' + p, '')
1336 v = v.replace(p + ' ', '')
1337 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001338
Ned Deily652bad42016-08-15 14:37:14 -04001339 fp = open(path, 'w')
1340 # duplicated from sysconfig._generate_posix_vars()
1341 fp.write('# system configuration generated and used by'
1342 ' the sysconfig module\n')
1343 fp.write('build_time_vars = ')
1344 pprint.pprint(vars, stream=fp)
1345 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001346
1347 # Add symlinks in /usr/local/bin, using relative links
1348 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1349 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1350 'Python.framework', 'Versions', version, 'bin')
1351 if os.path.exists(usr_local_bin):
1352 shutil.rmtree(usr_local_bin)
1353 os.makedirs(usr_local_bin)
1354 for fn in os.listdir(
1355 os.path.join(frmDir, 'Versions', version, 'bin')):
1356 os.symlink(os.path.join(to_framework, fn),
1357 os.path.join(usr_local_bin, fn))
1358
1359 os.chdir(curdir)
1360
Thomas Wouters477c8d52006-05-27 19:21:47 +00001361def patchFile(inPath, outPath):
1362 data = fileContents(inPath)
1363 data = data.replace('$FULL_VERSION', getFullVersion())
1364 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001365 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001366 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001367 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001368 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001369
1370 # This one is not handy as a template variable
1371 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001372 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001373 fp.write(data)
1374 fp.close()
1375
1376def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001377 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001378 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001379 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001380 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001381 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001382 fp.write(data)
1383 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001384 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001385
1386
1387
1388def packageFromRecipe(targetDir, recipe):
1389 curdir = os.getcwd()
1390 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001391 # The major version (such as 2.5) is included in the package name
1392 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001393 # common.
1394 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001395 srcdir = recipe.get('source')
1396 pkgroot = recipe.get('topdir', srcdir)
1397 postflight = recipe.get('postflight')
1398 readme = textwrap.dedent(recipe['readme'])
1399 isRequired = recipe.get('required', True)
1400
Ned Deily4a96a372013-01-29 00:08:32 -08001401 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001402
1403 # Substitute some variables
1404 textvars = dict(
1405 VER=getVersion(),
1406 FULLVER=getFullVersion(),
1407 )
1408 readme = readme % textvars
1409
1410 if pkgroot is not None:
1411 pkgroot = pkgroot % textvars
1412 else:
1413 pkgroot = '/'
1414
1415 if srcdir is not None:
1416 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1417 srcdir = srcdir % textvars
1418
1419 if postflight is not None:
1420 postflight = os.path.abspath(postflight)
1421
1422 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1423 os.makedirs(packageContents)
1424
1425 if srcdir is not None:
1426 os.chdir(srcdir)
1427 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1428 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1429 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1430
1431 fn = os.path.join(packageContents, 'PkgInfo')
1432 fp = open(fn, 'w')
1433 fp.write('pmkrpkg1')
1434 fp.close()
1435
1436 rsrcDir = os.path.join(packageContents, "Resources")
1437 os.mkdir(rsrcDir)
1438 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1439 fp.write(readme)
1440 fp.close()
1441
1442 if postflight is not None:
1443 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1444
1445 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001446 major, minor = getVersionMajorMinor()
Ned Deily1931e642020-06-25 04:51:46 -04001447 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001448 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1449 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1450 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001451 CFBundleShortVersionString=vers,
1452 IFMajorVersion=major,
1453 IFMinorVersion=minor,
1454 IFPkgFormatVersion=0.10000000149011612,
1455 IFPkgFlagAllowBackRev=False,
1456 IFPkgFlagAuthorizationAction="RootAuthorization",
1457 IFPkgFlagDefaultLocation=pkgroot,
1458 IFPkgFlagFollowLinks=True,
1459 IFPkgFlagInstallFat=True,
1460 IFPkgFlagIsRequired=isRequired,
1461 IFPkgFlagOverwritePermissions=False,
1462 IFPkgFlagRelocatable=False,
1463 IFPkgFlagRestartAction="NoRestart",
1464 IFPkgFlagRootVolumeOnly=True,
1465 IFPkgFlagUpdateInstalledLangauges=False,
1466 )
1467 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1468
Ned Deily1931e642020-06-25 04:51:46 -04001469 pl = dict(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001470 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001471 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001472 IFPkgDescriptionVersion=vers,
1473 )
1474 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1475
1476 finally:
1477 os.chdir(curdir)
1478
1479
1480def makeMpkgPlist(path):
1481
1482 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001483 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001484
Ned Deily1931e642020-06-25 04:51:46 -04001485 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001486 CFBundleGetInfoString="Python %s"%(vers,),
1487 CFBundleIdentifier='org.python.Python',
1488 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001489 CFBundleShortVersionString=vers,
1490 IFMajorVersion=major,
1491 IFMinorVersion=minor,
1492 IFPkgFlagComponentDirectory="Contents/Packages",
1493 IFPkgFlagPackageList=[
1494 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001495 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001496 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001497 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001498 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001499 ],
1500 IFPkgFormatVersion=0.10000000149011612,
1501 IFPkgFlagBackgroundScaling="proportional",
1502 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001503 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001504 )
1505
1506 writePlist(pl, path)
1507
1508
1509def buildInstaller():
1510
1511 # Zap all compiled files
1512 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1513 for fn in filenames:
1514 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1515 os.unlink(os.path.join(dirpath, fn))
1516
1517 outdir = os.path.join(WORKDIR, 'installer')
1518 if os.path.exists(outdir):
1519 shutil.rmtree(outdir)
1520 os.mkdir(outdir)
1521
Ronald Oussoren1943f862009-03-30 19:39:14 +00001522 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001523 pkgcontents = os.path.join(pkgroot, 'Packages')
1524 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001525 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001526 packageFromRecipe(pkgcontents, recipe)
1527
1528 rsrcDir = os.path.join(pkgroot, 'Resources')
1529
1530 fn = os.path.join(pkgroot, 'PkgInfo')
1531 fp = open(fn, 'w')
1532 fp.write('pmkrpkg1')
1533 fp.close()
1534
1535 os.mkdir(rsrcDir)
1536
1537 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
Ned Deily1931e642020-06-25 04:51:46 -04001538 pl = dict(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001539 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001540 IFPkgDescriptionVersion=getVersion(),
1541 )
1542
1543 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1544 for fn in os.listdir('resources'):
1545 if fn == '.svn': continue
1546 if fn.endswith('.jpg'):
1547 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1548 else:
1549 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1550
Thomas Wouters477c8d52006-05-27 19:21:47 +00001551
1552def installSize(clear=False, _saved=[]):
1553 if clear:
1554 del _saved[:]
1555 if not _saved:
1556 data = captureCommand("du -ks %s"%(
1557 shellQuote(os.path.join(WORKDIR, '_root'))))
1558 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1559 return _saved[0]
1560
1561
1562def buildDMG():
1563 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001564 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001565 """
1566 outdir = os.path.join(WORKDIR, 'diskimage')
1567 if os.path.exists(outdir):
1568 shutil.rmtree(outdir)
1569
1570 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001571 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001572 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001573 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001574 imagepath = imagepath + '.dmg'
1575
1576 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001577
1578 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1579 # when hdiutil create fails with "Resource busy". For now, just retry
1580 # the create a few times and hope that it eventually works.
1581
Ronald Oussoren1943f862009-03-30 19:39:14 +00001582 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001583 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001584 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001585 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001586 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001587 for i in range(5):
1588 fd = os.popen(cmd, 'r')
1589 data = fd.read()
1590 xit = fd.close()
1591 if not xit:
1592 break
1593 sys.stdout.write(data)
1594 print(" -- retrying hdiutil create")
1595 time.sleep(5)
1596 else:
cclaussd3371692019-06-03 05:19:44 +02001597 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001598
1599 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1600 os.mkdir(os.path.join(WORKDIR, "mnt"))
1601 runCommand("hdiutil attach %s -mountroot %s"%(
1602 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1603
1604 # Custom icon for the DMG, shown when the DMG is mounted.
1605 shutil.copy("../Icons/Disk Image.icns",
1606 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001607 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001608 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1609
1610 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1611
1612 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1613 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1614 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1615 setIcon(imagepath, "../Icons/Disk Image.icns")
1616
1617 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001618
1619 return imagepath
1620
1621
1622def setIcon(filePath, icnsPath):
1623 """
1624 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001625 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001626
Ronald Oussoren70050672010-04-30 15:00:26 +00001627 dirPath = os.path.normpath(os.path.dirname(__file__))
1628 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001629 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1630 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1631 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001632 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1633 if not os.path.exists(appPath):
1634 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001635 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1636 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001637
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001638 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1639 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001640
1641def main():
1642 # First parse options and check if we can perform our work
1643 parseOptions()
1644 checkEnvironment()
1645
Ronald Oussoren1943f862009-03-30 19:39:14 +00001646 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001647 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001648 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001649
1650 if os.path.exists(WORKDIR):
1651 shutil.rmtree(WORKDIR)
1652 os.mkdir(WORKDIR)
1653
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001654 os.environ['LC_ALL'] = 'C'
1655
Thomas Wouters477c8d52006-05-27 19:21:47 +00001656 # Then build third-party libraries such as sleepycat DB4.
1657 buildLibraries()
1658
1659 # Now build python itself
1660 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001661
1662 # And then build the documentation
1663 # Remove the Deployment Target from the shell
1664 # environment, it's no longer needed and
1665 # an unexpected build target can cause problems
1666 # when Sphinx and its dependencies need to
1667 # be (re-)installed.
1668 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001669 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001670
1671
1672 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001673 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001674 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001675 fn = os.path.join(folder, "License.rtf")
1676 patchFile("resources/License.rtf", fn)
1677 fn = os.path.join(folder, "ReadMe.rtf")
1678 patchFile("resources/ReadMe.rtf", fn)
1679 fn = os.path.join(folder, "Update Shell Profile.command")
1680 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001681 fn = os.path.join(folder, "Install Certificates.command")
1682 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001683 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001684 setIcon(folder, "../Icons/Python Folder.icns")
1685
1686 # Create the installer
1687 buildInstaller()
1688
1689 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001690 patchFile('resources/ReadMe.rtf',
1691 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001692
1693 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001694 patchFile('resources/License.rtf',
1695 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001696
1697 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001698 fp.write("# BUILD INFO\n")
1699 fp.write("# Date: %s\n" % time.ctime())
1700 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001701 fp.close()
1702
Thomas Wouters477c8d52006-05-27 19:21:47 +00001703 # And copy it to a DMG
1704 buildDMG()
1705
Thomas Wouters477c8d52006-05-27 19:21:47 +00001706if __name__ == "__main__":
1707 main()