blob: a2cba3210211de3f7289c64e7801512cf77f824d [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
5NEW for 3.7.0:
6- support Intel 64-bit-only () and 32-bit-only installer builds
Ned Deilyb9e7fe32018-03-29 08:47:27 -04007- build and use internal Tcl/Tk 8.6 for 10.6+ builds
Ned Deily8c9bb722018-01-30 07:42:14 -05008- deprecate use of explicit SDK (--sdk-path=) since all but the oldest
9 versions of Xcode support implicit setting of an SDK via environment
10 variables (SDKROOT and friends, see the xcrun man page for more info).
11 The SDK stuff was primarily needed for building universal installers
12 for 10.4; so as of 3.7.0, building installers for 10.4 is no longer
13 supported with build-installer.
14- use generic "gcc" as compiler (CC env var) rather than "gcc-4.2"
15
16TODO:
17- support SDKROOT and DEVELOPER_DIR xcrun env variables
18- test with 10.5 and 10.4 and determine support status
Thomas Wouters477c8d52006-05-27 19:21:47 +000019
Ned Deilye59e4c52011-01-29 18:56:28 +000020Please ensure that this script keeps working with Python 2.5, to avoid
Ned Deily8c9bb722018-01-30 07:42:14 -050021bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Doc builds
22use current versions of Sphinx and require a reasonably current python3.
23Sphinx and dependencies are installed into a venv using the python3's pip
24so will fetch them from PyPI if necessary. Since python3 is now used for
25Sphinx, build-installer.py should also be converted to use python3!
Thomas Wouters477c8d52006-05-27 19:21:47 +000026
Ned Deilyb9e7fe32018-03-29 08:47:27 -040027For 3.7.0, when building for a 10.6 or higher deployment target,
28build-installer builds and links with its own copy of Tcl/Tk 8.6.
29Otherwise, it requires an installed third-party version of
Ned Deily8c9bb722018-01-30 07:42:14 -050030Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets), Tcl/TK 8.5
31(for 10.6 or later), or Tcl/TK 8.6 (for 10.9 or later)
32installed in /Library/Frameworks. When installed,
Ned Deily4a96a372013-01-29 00:08:32 -080033the Python built by this script will attempt to dynamically link first to
34Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
35back to the ones in /System/Library/Framework. For the build, we recommend
Ned Deily8c9bb722018-01-30 07:42:14 -050036installing the most recent ActiveTcl 8.6. 8.5, or 8.4 version, depending
37on the deployment target. The actual version linked to depends on the
38path of /Library/Frameworks/{Tcl,Tk}.framework/Versions/Current.
Ned Deily4a96a372013-01-29 00:08:32 -080039
Thomas Wouters477c8d52006-05-27 19:21:47 +000040Usage: see USAGE variable in the script.
41"""
Ned Deily4a96a372013-01-29 00:08:32 -080042import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
43try:
44 import urllib2 as urllib_request
45except ImportError:
46 import urllib.request as urllib_request
47
48STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
49 | stat.S_IRGRP | stat.S_IXGRP
50 | stat.S_IROTH | stat.S_IXOTH )
51
52STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
53 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
54 | stat.S_IROTH | stat.S_IXOTH )
Thomas Wouters477c8d52006-05-27 19:21:47 +000055
Thomas Wouters89f507f2006-12-13 04:49:30 +000056INCLUDE_TIMESTAMP = 1
57VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000058
59from plistlib import Plist
60
Thomas Wouters477c8d52006-05-27 19:21:47 +000061try:
62 from plistlib import writePlist
63except ImportError:
64 # We're run using python2.3
65 def writePlist(plist, path):
66 plist.write(path)
67
68def shellQuote(value):
69 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000070 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000071 a shell command.
72 """
73 return "'%s'"%(value.replace("'", "'\"'\"'"))
74
75def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -080076 """
77 Return the unquoted value of a variable from a file..
78 QUOTED_VALUE='quotes' -> str('quotes')
79 UNQUOTED_VALUE=noquotes -> str('noquotes')
80 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000081 variable = variable + '='
82 for ln in open(fn, 'r'):
83 if ln.startswith(variable):
84 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080085 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080086 raise RuntimeError("Cannot find variable %s" % variable[:-1])
87
88_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000089
90def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080091 global _cache_getVersion
92 if _cache_getVersion is None:
93 _cache_getVersion = grepValue(
94 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
95 return _cache_getVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000096
Ned Deily4a96a372013-01-29 00:08:32 -080097def getVersionMajorMinor():
98 return tuple([int(n) for n in getVersion().split('.', 2)])
99
100_cache_getFullVersion = None
101
Thomas Wouters477c8d52006-05-27 19:21:47 +0000102def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -0800103 global _cache_getFullVersion
104 if _cache_getFullVersion is not None:
105 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
107 for ln in open(fn):
108 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -0800109 _cache_getFullVersion = ln.split()[-1][1:-1]
110 return _cache_getFullVersion
111 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000112
Ned Deily5d3febf2014-12-13 00:17:46 -0800113FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
114FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
Ned Deilydde4f632016-09-12 09:39:23 -0400115FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800116
Thomas Wouters89f507f2006-12-13 04:49:30 +0000117# The directory we'll use to create the build (will be erased and recreated)
118WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000119
Thomas Wouters89f507f2006-12-13 04:49:30 +0000120# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000121# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000122DEPSRC = os.path.join(WORKDIR, 'third-party')
123DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000124
Ronald Oussoren1943f862009-03-30 19:39:14 +0000125universal_opts_map = { '32-bit': ('i386', 'ppc',),
126 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000127 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500128 'intel-32': ('i386',),
129 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000130 '3-way': ('ppc', 'i386', 'x86_64'),
131 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
132default_target_map = {
133 '64-bit': '10.5',
134 '3-way': '10.5',
135 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500136 'intel-32': '10.4',
137 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000138 'all': '10.5',
139}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000140
141UNIVERSALOPTS = tuple(universal_opts_map.keys())
142
143UNIVERSALARCHS = '32-bit'
144
145ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000146
Ezio Melotti42da6632011-03-15 05:18:48 +0200147# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000148SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149 os.path.dirname(
150 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000151 os.path.abspath(__file__
152 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000153
Ronald Oussoren1943f862009-03-30 19:39:14 +0000154# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500155DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000156
Ned Deily04cdfa12014-06-25 13:36:14 -0700157def getDeptargetTuple():
158 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
159
160def getTargetCompilers():
161 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800162 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500163 '10.5': ('gcc', 'g++'),
164 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700165 }
Ned Deilyacd71632018-02-24 14:30:44 -0500166 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000167
Ned Deily04cdfa12014-06-25 13:36:14 -0700168CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000169
Ned Deily5d3febf2014-12-13 00:17:46 -0800170PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000171
Thomas Wouters89f507f2006-12-13 04:49:30 +0000172USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000173 Usage: build_python [options]
174
175 Options:
176 -? or -h: Show this message
177 -b DIR
178 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
179 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500180 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000181 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500182 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000183 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000184""")% globals()
185
Ned Deily4a96a372013-01-29 00:08:32 -0800186# Dict of object file names with shared library names to check after building.
187# This is to ensure that we ended up dynamically linking with the shared
188# library paths and versions we expected. For example:
189# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
190# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
191# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
192EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000193
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500194# Are we building and linking with our own copy of Tcl/TK?
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400195# For now, do so if deployment target is 10.6+.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500196def internalTk():
Ned Deilyb9e7fe32018-03-29 08:47:27 -0400197 return getDeptargetTuple() >= (10, 6)
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500198
Ned Deily5d3febf2014-12-13 00:17:46 -0800199# List of names of third party software built with this installer.
200# The names will be inserted into the rtf version of the License.
201THIRD_PARTY_LIBS = []
202
Thomas Wouters477c8d52006-05-27 19:21:47 +0000203# Instructions for building libraries that are necessary for building a
204# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000205# [The recipes are defined here for convenience but instantiated later after
206# command line options have been processed.]
207def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000208 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000209
Ned Deily04cdfa12014-06-25 13:36:14 -0700210 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800211
Ned Deilydde4f632016-09-12 09:39:23 -0400212 # Since Apple removed the header files for the deprecated system
213 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
214 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800215
Ned Deilydde4f632016-09-12 09:39:23 -0400216 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800217 dict(
Ned Deily783a6732020-04-21 22:41:33 -0400218 name="OpenSSL 1.1.1g",
219 url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
220 checksum='76766e98997660138cdaf13a187bd234',
Ned Deily5d3febf2014-12-13 00:17:46 -0800221 buildrecipe=build_universal_openssl,
222 configure=None,
223 install=None,
224 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400225 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800226
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500227 if internalTk():
Ned Deily5b3582c2013-10-25 00:41:46 -0700228 result.extend([
229 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500230 name="Tcl 8.6.8",
231 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
232 checksum='81656d3367af032e0ae6157eff134f89',
Ned Deily5b3582c2013-10-25 00:41:46 -0700233 buildDir="unix",
234 configure_pre=[
235 '--enable-shared',
236 '--enable-threads',
237 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
238 ],
239 useLDFlags=False,
240 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
241 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500242 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700243 },
244 ),
245 dict(
Ned Deilya9366392018-12-27 16:13:30 -0500246 name="Tk 8.6.8",
247 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
248 checksum='5e0faecba458ee1386078fb228d008ba',
249 patches=[
250 "tk868_on_10_8_10_9.patch",
251 ],
Ned Deily5b3582c2013-10-25 00:41:46 -0700252 buildDir="unix",
253 configure_pre=[
254 '--enable-aqua',
255 '--enable-shared',
256 '--enable-threads',
257 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
258 ],
259 useLDFlags=False,
260 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'%{
261 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500262 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
263 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700264 },
265 ),
266 ])
267
Ned Deilyed730102014-11-14 18:55:05 -0800268 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800269 result.extend([
270 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500271 name="XZ 5.2.3",
272 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
273 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800274 configure_pre=[
275 '--disable-dependency-tracking',
276 ]
277 ),
278 ])
279
280 result.extend([
281 dict(
282 name="NCurses 5.9",
283 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
284 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
285 configure_pre=[
286 "--enable-widec",
287 "--without-cxx",
288 "--without-cxx-binding",
289 "--without-ada",
290 "--without-curses-h",
291 "--enable-shared",
292 "--with-shared",
293 "--without-debug",
294 "--without-normal",
295 "--without-tests",
296 "--without-manpages",
297 "--datadir=/usr/share",
298 "--sysconfdir=/etc",
299 "--sharedstatedir=/usr/com",
300 "--with-terminfo-dirs=/usr/share/terminfo",
301 "--with-default-terminfo-dir=/usr/share/terminfo",
302 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
303 ],
304 patchscripts=[
Oleg Höfling7da46b62020-05-27 12:07:15 +0200305 ("ftp://ftp.invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
Ned Deily4a96a372013-01-29 00:08:32 -0800306 "f54bf02a349f96a7c4f0d00922f3a0d4"),
307 ],
308 useLDFlags=False,
309 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
310 shellQuote(os.path.join(WORKDIR, 'libraries')),
311 shellQuote(os.path.join(WORKDIR, 'libraries')),
312 getVersion(),
313 ),
314 ),
315 dict(
Ned Deily37eed5a2020-06-07 22:24:33 -0400316 name="SQLite 3.32.2",
317 url="https://sqlite.org/2020/sqlite-autoconf-3320200.tar.gz",
318 checksum='eb498918a33159cdf8104997aad29e83',
Ned Deily4a96a372013-01-29 00:08:32 -0800319 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700320 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800321 '-DSQLITE_ENABLE_FTS4 '
322 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500323 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800324 '-DSQLITE_ENABLE_RTREE '
325 '-DSQLITE_TCL=0 '
326 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
327 configure_pre=[
328 '--enable-threadsafe',
329 '--enable-shared=no',
330 '--enable-static=yes',
331 '--disable-readline',
332 '--disable-dependency-tracking',
333 ]
334 ),
335 ])
336
Ned Deily04cdfa12014-06-25 13:36:14 -0700337 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000338 result.extend([
339 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000340 name="Bzip2 1.0.6",
341 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
342 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000343 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500344 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800345 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000346 shellQuote(os.path.join(WORKDIR, 'libraries')),
347 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000348 ),
349 ),
350 dict(
351 name="ZLib 1.2.3",
352 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
353 checksum='debc62758716a169df9f62e6ab2bc634',
354 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500355 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800356 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000357 shellQuote(os.path.join(WORKDIR, 'libraries')),
358 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000359 ),
360 ),
361 dict(
362 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000363 name="GNU Readline 6.1.2",
364 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
365 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000366 patchlevel='0',
367 patches=[
368 # The readline maintainers don't do actual micro releases, but
369 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800370 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
371 'c642f2e84d820884b0bf9fd176bc6c3f'),
372 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
373 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000374 ]
375 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000376 ])
377
Ned Deily4f7ff782011-01-15 05:29:12 +0000378 if not PYTHON_3:
379 result.extend([
380 dict(
381 name="Sleepycat DB 4.7.25",
382 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
383 checksum='ec2b87e833779681a0c3a814aa71359e',
384 buildDir="build_unix",
385 configure="../dist/configure",
386 configure_pre=[
387 '--includedir=/usr/local/include/db4',
388 ]
389 ),
390 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000391
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000392 return result
393
Thomas Wouters477c8d52006-05-27 19:21:47 +0000394
Thomas Wouters477c8d52006-05-27 19:21:47 +0000395# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000396def pkg_recipes():
397 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
398 result = [
399 dict(
400 name="PythonFramework",
401 long_name="Python Framework",
402 source="/Library/Frameworks/Python.framework",
403 readme="""\
404 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500405 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000406 """,
407 postflight="scripts/postflight.framework",
408 selected='selected',
409 ),
410 dict(
411 name="PythonApplications",
412 long_name="GUI Applications",
413 source="/Applications/Python %(VER)s",
414 readme="""\
415 This package installs IDLE (an interactive Python IDE),
416 Python Launcher and Build Applet (create application bundles
417 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000418
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000419 It also installs a number of examples and demos.
420 """,
421 required=False,
422 selected='selected',
423 ),
424 dict(
425 name="PythonUnixTools",
426 long_name="UNIX command-line tools",
427 source="/usr/local/bin",
428 readme="""\
429 This package installs the unix tools in /usr/local/bin for
430 compatibility with older releases of Python. This package
431 is not necessary to use Python.
432 """,
433 required=False,
434 selected='selected',
435 ),
436 dict(
437 name="PythonDocumentation",
438 long_name="Python Documentation",
439 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
440 source="/pydocs",
441 readme="""\
442 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800443 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000444 """,
445 postflight="scripts/postflight.documentation",
446 required=False,
447 selected='selected',
448 ),
449 dict(
450 name="PythonProfileChanges",
451 long_name="Shell profile updater",
452 readme="""\
453 This packages updates your shell profile to make sure that
454 the Python tools are found by your shell in preference of
455 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000456
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000457 If you don't install this package you'll have to add
458 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
459 to your PATH by hand.
460 """,
461 postflight="scripts/postflight.patch-profile",
462 topdir="/Library/Frameworks/Python.framework",
463 source="/empty-dir",
464 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800465 selected='selected',
466 ),
467 dict(
468 name="PythonInstallPip",
469 long_name="Install or upgrade pip",
470 readme="""\
471 This package installs (or upgrades from an earlier version)
472 pip, a tool for installing and managing Python packages.
473 """,
474 postflight="scripts/postflight.ensurepip",
475 topdir="/Library/Frameworks/Python.framework",
476 source="/empty-dir",
477 required=False,
478 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000479 ),
480 ]
481
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000482 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000483
Thomas Wouters477c8d52006-05-27 19:21:47 +0000484def fatal(msg):
485 """
486 A fatal error, bail out.
487 """
488 sys.stderr.write('FATAL: ')
489 sys.stderr.write(msg)
490 sys.stderr.write('\n')
491 sys.exit(1)
492
493def fileContents(fn):
494 """
495 Return the contents of the named file
496 """
Ned Deily4a96a372013-01-29 00:08:32 -0800497 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000498
499def runCommand(commandline):
500 """
Ezio Melotti13925002011-03-16 11:05:33 +0200501 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000502 unless the command fails.
503 """
504 fd = os.popen(commandline, 'r')
505 data = fd.read()
506 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000507 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000508 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800509 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000510
511 if VERBOSE:
512 sys.stdout.write(data); sys.stdout.flush()
513
514def captureCommand(commandline):
515 fd = os.popen(commandline, 'r')
516 data = fd.read()
517 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000518 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000519 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800520 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000521
522 return data
523
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000524def getTclTkVersion(configfile, versionline):
525 """
526 search Tcl or Tk configuration file for version line
527 """
528 try:
529 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300530 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000531 fatal("Framework configuration file not found: %s" % configfile)
532
533 for l in f:
534 if l.startswith(versionline):
535 f.close()
536 return l
537
538 fatal("Version variable %s not found in framework configuration file: %s"
539 % (versionline, configfile))
540
Thomas Wouters477c8d52006-05-27 19:21:47 +0000541def checkEnvironment():
542 """
543 Check that we're running on a supported system.
544 """
545
Ned Deily8c9bb722018-01-30 07:42:14 -0500546 if sys.version_info[0:2] < (2, 5):
547 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000548
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500550 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000551
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000552 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500553 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000554
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000555 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500556 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000557 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500558 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
559 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
560 # enforce that the version of the user-installed framework also
561 # exists in the system-supplied Tcl/Tk frameworks. Time to support
562 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500563 if not internalTk():
564 frameworks = {}
565 for framework in ['Tcl', 'Tk']:
566 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
567 libfw = os.path.join('/', fwpth)
568 usrfw = os.path.join(os.getenv('HOME'), fwpth)
569 frameworks[framework] = os.readlink(libfw)
570 if not os.path.exists(libfw):
571 fatal("Please install a link to a current %s %s as %s so "
572 "the user can override the system framework."
573 % (framework, frameworks[framework], libfw))
574 if os.path.exists(usrfw):
575 fatal("Please rename %s to avoid possible dynamic load issues."
576 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000577
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500578 if frameworks['Tcl'] != frameworks['Tk']:
579 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000580
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500581 print(" -- Building with external Tcl/Tk %s frameworks"
582 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800583
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500584 # add files to check after build
585 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
586 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
587 % frameworks['Tcl'],
588 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
589 % frameworks['Tk'],
590 ]
591 else:
592 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500593 print("")
594
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000595 # Remove inherited environment variables which might influence build
596 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
597 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
598 for ev in list(os.environ):
599 for prefix in environ_var_prefixes:
600 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800601 print("INFO: deleting environment variable %s=%s" % (
602 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000603 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000604
Ned Deily4a96a372013-01-29 00:08:32 -0800605 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
606 if 'SDK_TOOLS_BIN' in os.environ:
607 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
608 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
609 # add its fixed location here if it exists
610 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
611 if os.path.isdir(OLD_DEVELOPER_TOOLS):
612 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
613 os.environ['PATH'] = base_path
614 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400615 # Ensure we have access to sphinx-build.
616 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700617 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000618
Thomas Wouters89f507f2006-12-13 04:49:30 +0000619def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000620 """
621 Parse arguments and update global settings.
622 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500623 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800624 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800625 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400626 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000627
628 if args is None:
629 args = sys.argv[1:]
630
631 try:
632 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000633 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
634 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800635 except getopt.GetoptError:
636 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000637 sys.exit(1)
638
639 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800640 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000641 sys.exit(1)
642
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000643 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000644 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000645 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800646 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000647 sys.exit(0)
648
649 elif k in ('-d', '--build-dir'):
650 WORKDIR=v
651
652 elif k in ('--third-party',):
653 DEPSRC=v
654
655 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500656 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000657
658 elif k in ('--src-dir',):
659 SRCDIR=v
660
Ronald Oussoren1943f862009-03-30 19:39:14 +0000661 elif k in ('--dep-target', ):
662 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000663 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000664
665 elif k in ('--universal-archs', ):
666 if v in UNIVERSALOPTS:
667 UNIVERSALARCHS = v
668 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000669 if deptarget is None:
670 # Select alternate default deployment
671 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500672 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000673 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800674 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000675
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800677 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678
679 SRCDIR=os.path.abspath(SRCDIR)
680 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000681 DEPSRC=os.path.abspath(DEPSRC)
682
Ned Deily04cdfa12014-06-25 13:36:14 -0700683 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000684
Ned Deily5d3febf2014-12-13 00:17:46 -0800685 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400686 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800687
688 print("-- Settings:")
689 print(" * Source directory: %s" % SRCDIR)
690 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800691 print(" * Third-party source: %s" % DEPSRC)
692 print(" * Deployment target: %s" % DEPTARGET)
693 print(" * Universal archs: %s" % str(ARCHLIST))
694 print(" * C compiler: %s" % CC)
695 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800696 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800697 print(" -- Building a Python %s framework at patch level %s"
698 % (getVersion(), getFullVersion()))
699 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000700
701def extractArchive(builddir, archiveName):
702 """
703 Extract a source archive into 'builddir'. Returns the path of the
704 extracted archive.
705
706 XXX: This function assumes that archives contain a toplevel directory
707 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700708 safe enough for almost anything we use. Unfortunately, it does not
709 work for current Tcl and Tk source releases where the basename of
710 the archive ends with "-src" but the uncompressed directory does not.
711 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000712 """
713 curdir = os.getcwd()
714 try:
715 os.chdir(builddir)
716 if archiveName.endswith('.tar.gz'):
717 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700718 if ((retval.startswith('tcl') or retval.startswith('tk'))
719 and retval.endswith('-src')):
720 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000721 if os.path.exists(retval):
722 shutil.rmtree(retval)
723 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
724
725 elif archiveName.endswith('.tar.bz2'):
726 retval = os.path.basename(archiveName[:-8])
727 if os.path.exists(retval):
728 shutil.rmtree(retval)
729 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
730
731 elif archiveName.endswith('.tar'):
732 retval = os.path.basename(archiveName[:-4])
733 if os.path.exists(retval):
734 shutil.rmtree(retval)
735 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
736
737 elif archiveName.endswith('.zip'):
738 retval = os.path.basename(archiveName[:-4])
739 if os.path.exists(retval):
740 shutil.rmtree(retval)
741 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
742
743 data = fp.read()
744 xit = fp.close()
745 if xit is not None:
746 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800747 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000748
749 return os.path.join(builddir, retval)
750
751 finally:
752 os.chdir(curdir)
753
Thomas Wouters477c8d52006-05-27 19:21:47 +0000754def downloadURL(url, fname):
755 """
756 Download the contents of the url into the file.
757 """
Ned Deily4a96a372013-01-29 00:08:32 -0800758 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000759 fpOut = open(fname, 'wb')
760 block = fpIn.read(10240)
761 try:
762 while block:
763 fpOut.write(block)
764 block = fpIn.read(10240)
765 fpIn.close()
766 fpOut.close()
767 except:
768 try:
769 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300770 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000771 pass
772
Ned Deily4a96a372013-01-29 00:08:32 -0800773def verifyThirdPartyFile(url, checksum, fname):
774 """
775 Download file from url to filename fname if it does not already exist.
776 Abort if file contents does not match supplied md5 checksum.
777 """
778 name = os.path.basename(fname)
779 if os.path.exists(fname):
780 print("Using local copy of %s"%(name,))
781 else:
782 print("Did not find local copy of %s"%(name,))
783 print("Downloading %s"%(name,))
784 downloadURL(url, fname)
785 print("Archive for %s stored as %s"%(name, fname))
786 if os.system(
787 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
788 % (shellQuote(fname), checksum) ):
789 fatal('MD5 checksum mismatch for file %s' % fname)
790
Ned Deily5d3febf2014-12-13 00:17:46 -0800791def build_universal_openssl(basedir, archList):
792 """
793 Special case build recipe for universal build of openssl.
794
795 The upstream OpenSSL build system does not directly support
796 OS X universal builds. We need to build each architecture
797 separately then lipo them together into fat libraries.
798 """
799
800 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
801 # If we are building on a 10.4.x or earlier system,
802 # unilaterally disable assembly code building to avoid the problem.
803 no_asm = int(platform.release().split(".")[0]) < 9
804
805 def build_openssl_arch(archbase, arch):
806 "Build one architecture of openssl"
807 arch_opts = {
808 "i386": ["darwin-i386-cc"],
809 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
810 "ppc": ["darwin-ppc-cc"],
811 "ppc64": ["darwin64-ppc-cc"],
812 }
Ned Deilyf3fb8392019-06-18 04:17:33 -0400813
814 # Somewhere between OpenSSL 1.1.0j and 1.1.1c, changes cause the
815 # "enable-ec_nistp_64_gcc_128" option to get compile errors when
816 # building on our 10.6 gcc-4.2 environment. There have been other
817 # reports of projects running into this when using older compilers.
818 # So, for now, do not try to use "enable-ec_nistp_64_gcc_128" when
819 # building for 10.6.
820 if getDeptargetTuple() == (10, 6):
821 arch_opts['x86_64'].remove('enable-ec_nistp_64_gcc_128')
822
Ned Deily5d3febf2014-12-13 00:17:46 -0800823 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800824 "no-idea",
825 "no-mdc2",
826 "no-rc5",
827 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800828 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800829 # "enable-unit-test",
830 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800831 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400832 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800833 ]
834 if no_asm:
835 configure_opts.append("no-asm")
836 runCommand(" ".join(["perl", "Configure"]
837 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500838 runCommand("make depend")
839 runCommand("make all")
840 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800841 # runCommand("make test")
842 return
843
844 srcdir = os.getcwd()
845 universalbase = os.path.join(srcdir, "..",
846 os.path.basename(srcdir) + "-universal")
847 os.mkdir(universalbase)
848 archbasefws = []
849 for arch in archList:
850 # fresh copy of the source tree
851 archsrc = os.path.join(universalbase, arch, "src")
852 shutil.copytree(srcdir, archsrc, symlinks=True)
853 # install base for this arch
854 archbase = os.path.join(universalbase, arch, "root")
855 os.mkdir(archbase)
856 # Python framework base within install_prefix:
857 # the build will install into this framework..
858 # This is to ensure that the resulting shared libs have
859 # the desired real install paths built into them.
860 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
861
862 # build one architecture
863 os.chdir(archsrc)
864 build_openssl_arch(archbase, arch)
865 os.chdir(srcdir)
866 archbasefws.append(archbasefw)
867
868 # copy arch-independent files from last build into the basedir framework
869 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
870 shutil.copytree(
871 os.path.join(archbasefw, "include", "openssl"),
872 os.path.join(basefw, "include", "openssl")
873 )
874
875 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
876 "SHLIB_VERSION_NUMBER")
877 # e.g. -> "1.0.0"
878 libcrypto = "libcrypto.dylib"
879 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
880 # e.g. -> "libcrypto.1.0.0.dylib"
881 libssl = "libssl.dylib"
882 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
883 # e.g. -> "libssl.1.0.0.dylib"
884
885 try:
886 os.mkdir(os.path.join(basefw, "lib"))
887 except OSError:
888 pass
889
890 # merge the individual arch-dependent shared libs into a fat shared lib
891 archbasefws.insert(0, basefw)
892 for (lib_unversioned, lib_versioned) in [
893 (libcrypto, libcrypto_versioned),
894 (libssl, libssl_versioned)
895 ]:
896 runCommand("lipo -create -output " +
897 " ".join(shellQuote(
898 os.path.join(fw, "lib", lib_versioned))
899 for fw in archbasefws))
900 # and create an unversioned symlink of it
901 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
902
903 # Create links in the temp include and lib dirs that will be injected
904 # into the Python build so that setup.py can find them while building
905 # and the versioned links so that the setup.py post-build import test
906 # does not fail.
907 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
908 for fn in [
909 ["include", "openssl"],
910 ["lib", libcrypto],
911 ["lib", libssl],
912 ["lib", libcrypto_versioned],
913 ["lib", libssl_versioned],
914 ]:
915 os.symlink(
916 os.path.join(relative_path, *fn),
917 os.path.join(basedir, "usr", "local", *fn)
918 )
919
920 return
921
Thomas Wouters477c8d52006-05-27 19:21:47 +0000922def buildRecipe(recipe, basedir, archList):
923 """
924 Build software using a recipe. This function does the
925 'configure;make;make install' dance for C software, with a possibility
926 to customize this process, basically a poor-mans DarwinPorts.
927 """
928 curdir = os.getcwd()
929
930 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800931 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000932 url = recipe['url']
933 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800934 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000935 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
936 shellQuote(basedir)))
937
938 archiveName = os.path.split(url)[-1]
939 sourceArchive = os.path.join(DEPSRC, archiveName)
940
941 if not os.path.exists(DEPSRC):
942 os.mkdir(DEPSRC)
943
Ned Deily4a96a372013-01-29 00:08:32 -0800944 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
945 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000946 buildDir=os.path.join(WORKDIR, '_bld')
947 if not os.path.exists(buildDir):
948 os.mkdir(buildDir)
949
950 workDir = extractArchive(buildDir, sourceArchive)
951 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000952
Ned Deily4a96a372013-01-29 00:08:32 -0800953 for patch in recipe.get('patches', ()):
954 if isinstance(patch, tuple):
955 url, checksum = patch
956 fn = os.path.join(DEPSRC, os.path.basename(url))
957 verifyThirdPartyFile(url, checksum, fn)
958 else:
959 # patch is a file in the source directory
960 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000961 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
962 shellQuote(fn),))
963
Ned Deily4a96a372013-01-29 00:08:32 -0800964 for patchscript in recipe.get('patchscripts', ()):
965 if isinstance(patchscript, tuple):
966 url, checksum = patchscript
967 fn = os.path.join(DEPSRC, os.path.basename(url))
968 verifyThirdPartyFile(url, checksum, fn)
969 else:
970 # patch is a file in the source directory
971 fn = os.path.join(curdir, patchscript)
972 if fn.endswith('.bz2'):
973 runCommand('bunzip2 -fk %s' % shellQuote(fn))
974 fn = fn[:-4]
975 runCommand('sh %s' % shellQuote(fn))
976 os.unlink(fn)
977
Ned Deily94764b22013-10-27 19:49:29 -0700978 if 'buildDir' in recipe:
979 os.chdir(recipe['buildDir'])
980
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000981 if configure is not None:
982 configure_args = [
983 "--prefix=/usr/local",
984 "--enable-static",
985 "--disable-shared",
986 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
987 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000988
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000989 if 'configure_pre' in recipe:
990 args = list(recipe['configure_pre'])
991 if '--disable-static' in args:
992 configure_args.remove('--enable-static')
993 if '--enable-shared' in args:
994 configure_args.remove('--disable-shared')
995 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000996
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000997 if recipe.get('useLDFlags', 1):
998 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -0500999 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001000 "-I%s/usr/local/include"%(
1001 recipe.get('extra_cflags', ''),
1002 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001003 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001004 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -05001005 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001006 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001007 shellQuote(basedir)[1:-1],
1008 ' -arch '.join(archList)),
1009 ])
1010 else:
1011 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001012 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001013 "-I%s/usr/local/include"%(
1014 recipe.get('extra_cflags', ''),
1015 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001016 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001017 shellQuote(basedir)[1:-1],),
1018 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001019
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001020 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001021 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001022
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001023 configure_args.insert(0, configure)
1024 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001025
Ned Deily4a96a372013-01-29 00:08:32 -08001026 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001027 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001028
Ned Deily5d3febf2014-12-13 00:17:46 -08001029 if buildrecipe is not None:
1030 # call special-case build recipe, e.g. for openssl
1031 buildrecipe(basedir, archList)
1032
1033 if install is not None:
1034 print("Running install for %s"%(name,))
1035 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001036
Ned Deily4a96a372013-01-29 00:08:32 -08001037 print("Done %s"%(name,))
1038 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001039
1040 os.chdir(curdir)
1041
1042def buildLibraries():
1043 """
1044 Build our dependencies into $WORKDIR/libraries/usr/local
1045 """
Ned Deily4a96a372013-01-29 00:08:32 -08001046 print("")
1047 print("Building required libraries")
1048 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001049 universal = os.path.join(WORKDIR, 'libraries')
1050 os.mkdir(universal)
1051 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1052 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1053
Ronald Oussoren1943f862009-03-30 19:39:14 +00001054 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001055 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001056
1057
1058
1059def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001060 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001061 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001062 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001063 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001064 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001065 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001066 curDir = os.getcwd()
1067 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001068 runCommand('make clean')
Ned Deily63fc55b2020-06-08 03:52:43 -04001069
1070 # Search third-party source directory for a pre-built version of the docs.
1071 # Use the naming convention of the docs.python.org html downloads:
1072 # python-3.9.0b1-docs-html.tar.bz2
1073 doctarfiles = [ f for f in os.listdir(DEPSRC)
1074 if f.startswith('python-'+getFullVersion())
1075 if f.endswith('-docs-html.tar.bz2') ]
1076 if doctarfiles:
1077 doctarfile = doctarfiles[0]
1078 if not os.path.exists('build'):
1079 os.mkdir('build')
1080 # if build directory existed, it was emptied by make clean, above
1081 os.chdir('build')
1082 # Extract the first archive found for this version into build
1083 runCommand('tar xjf %s'%shellQuote(os.path.join(DEPSRC, doctarfile)))
1084 # see if tar extracted a directory ending in -docs-html
1085 archivefiles = [ f for f in os.listdir('.')
1086 if f.endswith('-docs-html')
1087 if os.path.isdir(f) ]
1088 if archivefiles:
1089 archivefile = archivefiles[0]
1090 # make it our 'Docs/build/html' directory
1091 print(' -- using pre-built python documentation from %s'%archivefile)
1092 os.rename(archivefile, 'html')
1093 os.chdir(buildDir)
1094
1095 htmlDir = os.path.join('build', 'html')
1096 if not os.path.exists(htmlDir):
1097 # Create virtual environment for docs builds with blurb and sphinx
1098 runCommand('make venv')
1099 runCommand('venv/bin/python3 -m pip install -U Sphinx==2.2.0')
1100 runCommand('make html PYTHON=venv/bin/python')
1101 os.rename(htmlDir, docdir)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001102 os.chdir(curDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001103
1104
1105def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001106 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107
1108 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1109 rootDir = os.path.join(WORKDIR, '_root')
1110
1111 if os.path.exists(buildDir):
1112 shutil.rmtree(buildDir)
1113 if os.path.exists(rootDir):
1114 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001115 os.makedirs(buildDir)
1116 os.makedirs(rootDir)
1117 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001118 curdir = os.getcwd()
1119 os.chdir(buildDir)
1120
Thomas Wouters477c8d52006-05-27 19:21:47 +00001121 # Extract the version from the configure file, needed to calculate
1122 # several paths.
1123 version = getVersion()
1124
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001125 # Since the extra libs are not in their installed framework location
1126 # during the build, augment the library path so that the interpreter
1127 # will find them during its extension import sanity checks.
1128 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1129 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001130 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001131 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001132 "--with-universal-archs=%s "
1133 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001134 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001135 "%s "
1136 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001137 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001138 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001139 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001140 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001141 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001142 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001143 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1144 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1145 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1146 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001147 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001148 shellQuote(WORKDIR)[1:-1]))
1149
Ned Deilyb364d9f2017-07-24 04:58:43 -04001150 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1151 # and, if defined, append its value to the make command. This allows
1152 # us to pass in version control tags, like GITTAG, to a build from a
1153 # tarball rather than from a vcs checkout, thus eliminating the need
1154 # to have a working copy of the vcs program on the build machine.
1155 #
1156 # A typical use might be:
1157 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1158 # GITVERSION='echo 123456789a' \
1159 # GITTAG='echo v3.6.0' \
1160 # GITBRANCH='echo 3.6'"
1161
1162 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1163 if make_extras:
1164 make_cmd = "make " + make_extras
1165 else:
1166 make_cmd = "make"
1167 print("Running " + make_cmd)
1168 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001169
Ned Deily4a96a372013-01-29 00:08:32 -08001170 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001171 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001172 shellQuote(rootDir)))
1173
Ned Deily4a96a372013-01-29 00:08:32 -08001174 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001175 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1176 shellQuote(rootDir)))
1177
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001178 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001179 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001180 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001181 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001182 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001183 'Python.framework', 'Versions', getVersion(), 'lib')
1184 fw_lib_dir = os.path.join(
1185 WORKDIR, '_root', 'Library', 'Frameworks',
1186 'Python.framework', 'Versions', getVersion(), 'lib')
1187 if internalTk():
1188 # move Tcl and Tk pkgconfig files
1189 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1190 shellQuote(build_lib_dir),
1191 shellQuote(fw_lib_dir) ))
1192 runCommand("rm -r %s/pkgconfig"%(
1193 shellQuote(build_lib_dir), ))
1194 runCommand("mv %s/* %s"%(
1195 shellQuote(build_lib_dir),
1196 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001197
Ned Deilydde4f632016-09-12 09:39:23 -04001198 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1199 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1200 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1201 # create directory for OpenSSL certificates
1202 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1203 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001204
Ned Deily4a96a372013-01-29 00:08:32 -08001205 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001206 gid = grp.getgrnam('admin').gr_gid
1207
Ned Deily4a96a372013-01-29 00:08:32 -08001208 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001209 for dirpath, dirnames, filenames in os.walk(frmDir):
1210 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001211 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001212 os.chown(os.path.join(dirpath, dn), -1, gid)
1213
Thomas Wouters477c8d52006-05-27 19:21:47 +00001214 for fn in filenames:
1215 if os.path.islink(fn):
1216 continue
1217
1218 # "chmod g+w $fn"
1219 p = os.path.join(dirpath, fn)
1220 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001221 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1222 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001223
Ned Deily4a96a372013-01-29 00:08:32 -08001224 if fn in EXPECTED_SHARED_LIBS:
1225 # check to see that this file was linked with the
1226 # expected library path and version
1227 data = captureCommand("otool -L %s" % shellQuote(p))
1228 for sl in EXPECTED_SHARED_LIBS[fn]:
1229 if ("\t%s " % sl) not in data:
1230 print("Expected shared lib %s was not linked with %s"
1231 % (sl, p))
1232 shared_lib_error = True
1233
1234 if shared_lib_error:
1235 fatal("Unexpected shared library errors.")
1236
Ned Deilye59e4c52011-01-29 18:56:28 +00001237 if PYTHON_3:
1238 LDVERSION=None
1239 VERSION=None
1240 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001241
Ned Deilye59e4c52011-01-29 18:56:28 +00001242 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001243 for ln in fp:
1244 if ln.startswith('VERSION='):
1245 VERSION=ln.split()[1]
1246 if ln.startswith('ABIFLAGS='):
Ned Deily9bdd6d12019-04-29 15:11:53 -04001247 ABIFLAGS=ln.split()
1248 ABIFLAGS=ABIFLAGS[1] if len(ABIFLAGS) > 1 else ''
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001249 if ln.startswith('LDVERSION='):
1250 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001251 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001252
Ned Deilye59e4c52011-01-29 18:56:28 +00001253 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1254 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1255 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001256 if getVersionMajorMinor() >= (3, 6):
1257 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001258 else:
1259 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001260
Thomas Wouters477c8d52006-05-27 19:21:47 +00001261 # We added some directories to the search path during the configure
1262 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001263 # the end-users system. Also remove the directories from _sysconfigdata.py
1264 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001265
Ned Deilya4f6b002013-10-25 00:47:38 -07001266 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1267 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1268
Ned Deilya4f6b002013-10-25 00:47:38 -07001269 # fix Makefile
1270 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1271 fp = open(path, 'r')
1272 data = fp.read()
1273 fp.close()
1274
1275 for p in (include_path, lib_path):
1276 data = data.replace(" " + p, '')
1277 data = data.replace(p + " ", '')
1278
1279 fp = open(path, 'w')
1280 fp.write(data)
1281 fp.close()
1282
Ned Deily652bad42016-08-15 14:37:14 -04001283 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001284 #
1285 # TODO: make this more robust! test_sysconfig_module of
1286 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1287 # the output from get_config_var in both sysconfig and
1288 # distutils.sysconfig is exactly the same for both CFLAGS and
1289 # LDFLAGS. The fixing up is now complicated by the pretty
1290 # printing in _sysconfigdata.py. Also, we are using the
1291 # pprint from the Python running the installer build which
1292 # may not cosmetically format the same as the pprint in the Python
1293 # being built (and which is used to originally generate
1294 # _sysconfigdata.py).
1295
1296 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001297 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001298 # XXX this is extra-fragile
Ned Deily9bdd6d12019-04-29 15:11:53 -04001299 path = os.path.join(path_to_lib,
1300 '_sysconfigdata_%s_darwin_darwin.py' % (ABIFLAGS,))
Ned Deily652bad42016-08-15 14:37:14 -04001301 else:
1302 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1303 fp = open(path, 'r')
1304 data = fp.read()
1305 fp.close()
1306 # create build_time_vars dict
1307 exec(data)
1308 vars = {}
1309 for k, v in build_time_vars.items():
1310 if type(v) == type(''):
1311 for p in (include_path, lib_path):
1312 v = v.replace(' ' + p, '')
1313 v = v.replace(p + ' ', '')
1314 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001315
Ned Deily652bad42016-08-15 14:37:14 -04001316 fp = open(path, 'w')
1317 # duplicated from sysconfig._generate_posix_vars()
1318 fp.write('# system configuration generated and used by'
1319 ' the sysconfig module\n')
1320 fp.write('build_time_vars = ')
1321 pprint.pprint(vars, stream=fp)
1322 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001323
1324 # Add symlinks in /usr/local/bin, using relative links
1325 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1326 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1327 'Python.framework', 'Versions', version, 'bin')
1328 if os.path.exists(usr_local_bin):
1329 shutil.rmtree(usr_local_bin)
1330 os.makedirs(usr_local_bin)
1331 for fn in os.listdir(
1332 os.path.join(frmDir, 'Versions', version, 'bin')):
1333 os.symlink(os.path.join(to_framework, fn),
1334 os.path.join(usr_local_bin, fn))
1335
1336 os.chdir(curdir)
1337
Thomas Wouters477c8d52006-05-27 19:21:47 +00001338def patchFile(inPath, outPath):
1339 data = fileContents(inPath)
1340 data = data.replace('$FULL_VERSION', getFullVersion())
1341 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001342 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001343 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001344 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001345 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001346
1347 # This one is not handy as a template variable
1348 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001349 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001350 fp.write(data)
1351 fp.close()
1352
1353def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001354 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001355 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001356 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001357 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001358 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001359 fp.write(data)
1360 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001361 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001362
1363
1364
1365def packageFromRecipe(targetDir, recipe):
1366 curdir = os.getcwd()
1367 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001368 # The major version (such as 2.5) is included in the package name
1369 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001370 # common.
1371 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001372 srcdir = recipe.get('source')
1373 pkgroot = recipe.get('topdir', srcdir)
1374 postflight = recipe.get('postflight')
1375 readme = textwrap.dedent(recipe['readme'])
1376 isRequired = recipe.get('required', True)
1377
Ned Deily4a96a372013-01-29 00:08:32 -08001378 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001379
1380 # Substitute some variables
1381 textvars = dict(
1382 VER=getVersion(),
1383 FULLVER=getFullVersion(),
1384 )
1385 readme = readme % textvars
1386
1387 if pkgroot is not None:
1388 pkgroot = pkgroot % textvars
1389 else:
1390 pkgroot = '/'
1391
1392 if srcdir is not None:
1393 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1394 srcdir = srcdir % textvars
1395
1396 if postflight is not None:
1397 postflight = os.path.abspath(postflight)
1398
1399 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1400 os.makedirs(packageContents)
1401
1402 if srcdir is not None:
1403 os.chdir(srcdir)
1404 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1405 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1406 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1407
1408 fn = os.path.join(packageContents, 'PkgInfo')
1409 fp = open(fn, 'w')
1410 fp.write('pmkrpkg1')
1411 fp.close()
1412
1413 rsrcDir = os.path.join(packageContents, "Resources")
1414 os.mkdir(rsrcDir)
1415 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1416 fp.write(readme)
1417 fp.close()
1418
1419 if postflight is not None:
1420 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1421
1422 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001423 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001424 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001425 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1426 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1427 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001428 CFBundleShortVersionString=vers,
1429 IFMajorVersion=major,
1430 IFMinorVersion=minor,
1431 IFPkgFormatVersion=0.10000000149011612,
1432 IFPkgFlagAllowBackRev=False,
1433 IFPkgFlagAuthorizationAction="RootAuthorization",
1434 IFPkgFlagDefaultLocation=pkgroot,
1435 IFPkgFlagFollowLinks=True,
1436 IFPkgFlagInstallFat=True,
1437 IFPkgFlagIsRequired=isRequired,
1438 IFPkgFlagOverwritePermissions=False,
1439 IFPkgFlagRelocatable=False,
1440 IFPkgFlagRestartAction="NoRestart",
1441 IFPkgFlagRootVolumeOnly=True,
1442 IFPkgFlagUpdateInstalledLangauges=False,
1443 )
1444 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1445
1446 pl = Plist(
1447 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001448 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001449 IFPkgDescriptionVersion=vers,
1450 )
1451 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1452
1453 finally:
1454 os.chdir(curdir)
1455
1456
1457def makeMpkgPlist(path):
1458
1459 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001460 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001461
1462 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001463 CFBundleGetInfoString="Python %s"%(vers,),
1464 CFBundleIdentifier='org.python.Python',
1465 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001466 CFBundleShortVersionString=vers,
1467 IFMajorVersion=major,
1468 IFMinorVersion=minor,
1469 IFPkgFlagComponentDirectory="Contents/Packages",
1470 IFPkgFlagPackageList=[
1471 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001472 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001473 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001474 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001475 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001476 ],
1477 IFPkgFormatVersion=0.10000000149011612,
1478 IFPkgFlagBackgroundScaling="proportional",
1479 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001480 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001481 )
1482
1483 writePlist(pl, path)
1484
1485
1486def buildInstaller():
1487
1488 # Zap all compiled files
1489 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1490 for fn in filenames:
1491 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1492 os.unlink(os.path.join(dirpath, fn))
1493
1494 outdir = os.path.join(WORKDIR, 'installer')
1495 if os.path.exists(outdir):
1496 shutil.rmtree(outdir)
1497 os.mkdir(outdir)
1498
Ronald Oussoren1943f862009-03-30 19:39:14 +00001499 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001500 pkgcontents = os.path.join(pkgroot, 'Packages')
1501 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001502 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001503 packageFromRecipe(pkgcontents, recipe)
1504
1505 rsrcDir = os.path.join(pkgroot, 'Resources')
1506
1507 fn = os.path.join(pkgroot, 'PkgInfo')
1508 fp = open(fn, 'w')
1509 fp.write('pmkrpkg1')
1510 fp.close()
1511
1512 os.mkdir(rsrcDir)
1513
1514 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1515 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001516 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001517 IFPkgDescriptionVersion=getVersion(),
1518 )
1519
1520 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1521 for fn in os.listdir('resources'):
1522 if fn == '.svn': continue
1523 if fn.endswith('.jpg'):
1524 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1525 else:
1526 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1527
Thomas Wouters477c8d52006-05-27 19:21:47 +00001528
1529def installSize(clear=False, _saved=[]):
1530 if clear:
1531 del _saved[:]
1532 if not _saved:
1533 data = captureCommand("du -ks %s"%(
1534 shellQuote(os.path.join(WORKDIR, '_root'))))
1535 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1536 return _saved[0]
1537
1538
1539def buildDMG():
1540 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001541 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001542 """
1543 outdir = os.path.join(WORKDIR, 'diskimage')
1544 if os.path.exists(outdir):
1545 shutil.rmtree(outdir)
1546
1547 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001548 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001549 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001550 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001551 imagepath = imagepath + '.dmg'
1552
1553 os.mkdir(outdir)
Ned Deily0133f9f2018-12-27 16:38:41 -05001554
1555 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1556 # when hdiutil create fails with "Resource busy". For now, just retry
1557 # the create a few times and hope that it eventually works.
1558
Ronald Oussoren1943f862009-03-30 19:39:14 +00001559 volname='Python %s'%(getFullVersion())
Ned Deily0133f9f2018-12-27 16:38:41 -05001560 cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001561 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001562 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001563 shellQuote(imagepath + ".tmp.dmg" )))
Ned Deily0133f9f2018-12-27 16:38:41 -05001564 for i in range(5):
1565 fd = os.popen(cmd, 'r')
1566 data = fd.read()
1567 xit = fd.close()
1568 if not xit:
1569 break
1570 sys.stdout.write(data)
1571 print(" -- retrying hdiutil create")
1572 time.sleep(5)
1573 else:
cclaussd3371692019-06-03 05:19:44 +02001574 raise RuntimeError("command failed: %s"%(cmd,))
Ronald Oussoren1943f862009-03-30 19:39:14 +00001575
1576 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1577 os.mkdir(os.path.join(WORKDIR, "mnt"))
1578 runCommand("hdiutil attach %s -mountroot %s"%(
1579 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1580
1581 # Custom icon for the DMG, shown when the DMG is mounted.
1582 shutil.copy("../Icons/Disk Image.icns",
1583 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001584 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001585 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1586
1587 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1588
1589 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1590 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1591 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1592 setIcon(imagepath, "../Icons/Disk Image.icns")
1593
1594 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001595
1596 return imagepath
1597
1598
1599def setIcon(filePath, icnsPath):
1600 """
1601 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001602 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001603
Ronald Oussoren70050672010-04-30 15:00:26 +00001604 dirPath = os.path.normpath(os.path.dirname(__file__))
1605 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001606 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1607 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1608 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001609 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1610 if not os.path.exists(appPath):
1611 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001612 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1613 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001614
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001615 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1616 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001617
1618def main():
1619 # First parse options and check if we can perform our work
1620 parseOptions()
1621 checkEnvironment()
1622
Ronald Oussoren1943f862009-03-30 19:39:14 +00001623 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001624 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001625 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001626
1627 if os.path.exists(WORKDIR):
1628 shutil.rmtree(WORKDIR)
1629 os.mkdir(WORKDIR)
1630
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001631 os.environ['LC_ALL'] = 'C'
1632
Thomas Wouters477c8d52006-05-27 19:21:47 +00001633 # Then build third-party libraries such as sleepycat DB4.
1634 buildLibraries()
1635
1636 # Now build python itself
1637 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001638
1639 # And then build the documentation
1640 # Remove the Deployment Target from the shell
1641 # environment, it's no longer needed and
1642 # an unexpected build target can cause problems
1643 # when Sphinx and its dependencies need to
1644 # be (re-)installed.
1645 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001646 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001647
1648
1649 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001650 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001651 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001652 fn = os.path.join(folder, "License.rtf")
1653 patchFile("resources/License.rtf", fn)
1654 fn = os.path.join(folder, "ReadMe.rtf")
1655 patchFile("resources/ReadMe.rtf", fn)
1656 fn = os.path.join(folder, "Update Shell Profile.command")
1657 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001658 fn = os.path.join(folder, "Install Certificates.command")
1659 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001660 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001661 setIcon(folder, "../Icons/Python Folder.icns")
1662
1663 # Create the installer
1664 buildInstaller()
1665
1666 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001667 patchFile('resources/ReadMe.rtf',
1668 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001669
1670 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001671 patchFile('resources/License.rtf',
1672 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001673
1674 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001675 fp.write("# BUILD INFO\n")
1676 fp.write("# Date: %s\n" % time.ctime())
1677 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001678 fp.close()
1679
Thomas Wouters477c8d52006-05-27 19:21:47 +00001680 # And copy it to a DMG
1681 buildDMG()
1682
Thomas Wouters477c8d52006-05-27 19:21:47 +00001683if __name__ == "__main__":
1684 main()