blob: 806f660bd644dd9f3a7367b0d923e87011449fd9 [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 Deily3ec98262018-12-11 00:24:06 -0500218 name="OpenSSL 1.1.0j",
219 url="https://www.openssl.org/source/openssl-1.1.0j.tar.gz",
220 checksum='b4ca5b78ae6ae79da80790b30dbedbdc',
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 Deily7cf3d8e2018-12-11 01:06:57 -0500230 name="Tcl 8.6.9",
231 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz",
232 checksum='aa0a121d95a0e7b73a036f26028538d4',
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 Deily7cf3d8e2018-12-11 01:06:57 -0500246 name="Tk 8.6.9.1",
247 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz",
248 checksum='9efe3976468352dc894dae0c4e785a8e',
Ned Deily5b3582c2013-10-25 00:41:46 -0700249 buildDir="unix",
250 configure_pre=[
251 '--enable-aqua',
252 '--enable-shared',
253 '--enable-threads',
254 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
255 ],
256 useLDFlags=False,
257 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'%{
258 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500259 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
260 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700261 },
262 ),
263 ])
264
Ned Deilyed730102014-11-14 18:55:05 -0800265 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800266 result.extend([
267 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500268 name="XZ 5.2.3",
269 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
270 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800271 configure_pre=[
272 '--disable-dependency-tracking',
273 ]
274 ),
275 ])
276
277 result.extend([
278 dict(
279 name="NCurses 5.9",
280 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
281 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
282 configure_pre=[
283 "--enable-widec",
284 "--without-cxx",
285 "--without-cxx-binding",
286 "--without-ada",
287 "--without-curses-h",
288 "--enable-shared",
289 "--with-shared",
290 "--without-debug",
291 "--without-normal",
292 "--without-tests",
293 "--without-manpages",
294 "--datadir=/usr/share",
295 "--sysconfdir=/etc",
296 "--sharedstatedir=/usr/com",
297 "--with-terminfo-dirs=/usr/share/terminfo",
298 "--with-default-terminfo-dir=/usr/share/terminfo",
299 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
300 ],
301 patchscripts=[
302 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
303 "f54bf02a349f96a7c4f0d00922f3a0d4"),
304 ],
305 useLDFlags=False,
306 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
307 shellQuote(os.path.join(WORKDIR, 'libraries')),
308 shellQuote(os.path.join(WORKDIR, 'libraries')),
309 getVersion(),
310 ),
311 ),
312 dict(
Ned Deily8c9bb722018-01-30 07:42:14 -0500313 name="SQLite 3.22.0",
314 url="https://www.sqlite.org/2018/sqlite-autoconf-3220000.tar.gz",
315 checksum='96b5648d542e8afa6ab7ffb8db8ddc3d',
Ned Deily4a96a372013-01-29 00:08:32 -0800316 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700317 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800318 '-DSQLITE_ENABLE_FTS4 '
319 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500320 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800321 '-DSQLITE_ENABLE_RTREE '
322 '-DSQLITE_TCL=0 '
323 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
324 configure_pre=[
325 '--enable-threadsafe',
326 '--enable-shared=no',
327 '--enable-static=yes',
328 '--disable-readline',
329 '--disable-dependency-tracking',
330 ]
331 ),
332 ])
333
Ned Deily04cdfa12014-06-25 13:36:14 -0700334 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000335 result.extend([
336 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000337 name="Bzip2 1.0.6",
338 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
339 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000340 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500341 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800342 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000343 shellQuote(os.path.join(WORKDIR, 'libraries')),
344 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000345 ),
346 ),
347 dict(
348 name="ZLib 1.2.3",
349 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
350 checksum='debc62758716a169df9f62e6ab2bc634',
351 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500352 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800353 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000354 shellQuote(os.path.join(WORKDIR, 'libraries')),
355 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000356 ),
357 ),
358 dict(
359 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000360 name="GNU Readline 6.1.2",
361 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
362 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000363 patchlevel='0',
364 patches=[
365 # The readline maintainers don't do actual micro releases, but
366 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800367 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
368 'c642f2e84d820884b0bf9fd176bc6c3f'),
369 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
370 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000371 ]
372 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000373 ])
374
Ned Deily4f7ff782011-01-15 05:29:12 +0000375 if not PYTHON_3:
376 result.extend([
377 dict(
378 name="Sleepycat DB 4.7.25",
379 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
380 checksum='ec2b87e833779681a0c3a814aa71359e',
381 buildDir="build_unix",
382 configure="../dist/configure",
383 configure_pre=[
384 '--includedir=/usr/local/include/db4',
385 ]
386 ),
387 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000388
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000389 return result
390
Thomas Wouters477c8d52006-05-27 19:21:47 +0000391
Thomas Wouters477c8d52006-05-27 19:21:47 +0000392# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000393def pkg_recipes():
394 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
395 result = [
396 dict(
397 name="PythonFramework",
398 long_name="Python Framework",
399 source="/Library/Frameworks/Python.framework",
400 readme="""\
401 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500402 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000403 """,
404 postflight="scripts/postflight.framework",
405 selected='selected',
406 ),
407 dict(
408 name="PythonApplications",
409 long_name="GUI Applications",
410 source="/Applications/Python %(VER)s",
411 readme="""\
412 This package installs IDLE (an interactive Python IDE),
413 Python Launcher and Build Applet (create application bundles
414 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000415
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000416 It also installs a number of examples and demos.
417 """,
418 required=False,
419 selected='selected',
420 ),
421 dict(
422 name="PythonUnixTools",
423 long_name="UNIX command-line tools",
424 source="/usr/local/bin",
425 readme="""\
426 This package installs the unix tools in /usr/local/bin for
427 compatibility with older releases of Python. This package
428 is not necessary to use Python.
429 """,
430 required=False,
431 selected='selected',
432 ),
433 dict(
434 name="PythonDocumentation",
435 long_name="Python Documentation",
436 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
437 source="/pydocs",
438 readme="""\
439 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800440 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000441 """,
442 postflight="scripts/postflight.documentation",
443 required=False,
444 selected='selected',
445 ),
446 dict(
447 name="PythonProfileChanges",
448 long_name="Shell profile updater",
449 readme="""\
450 This packages updates your shell profile to make sure that
451 the Python tools are found by your shell in preference of
452 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000453
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000454 If you don't install this package you'll have to add
455 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
456 to your PATH by hand.
457 """,
458 postflight="scripts/postflight.patch-profile",
459 topdir="/Library/Frameworks/Python.framework",
460 source="/empty-dir",
461 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800462 selected='selected',
463 ),
464 dict(
465 name="PythonInstallPip",
466 long_name="Install or upgrade pip",
467 readme="""\
468 This package installs (or upgrades from an earlier version)
469 pip, a tool for installing and managing Python packages.
470 """,
471 postflight="scripts/postflight.ensurepip",
472 topdir="/Library/Frameworks/Python.framework",
473 source="/empty-dir",
474 required=False,
475 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000476 ),
477 ]
478
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000479 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000480
Thomas Wouters477c8d52006-05-27 19:21:47 +0000481def fatal(msg):
482 """
483 A fatal error, bail out.
484 """
485 sys.stderr.write('FATAL: ')
486 sys.stderr.write(msg)
487 sys.stderr.write('\n')
488 sys.exit(1)
489
490def fileContents(fn):
491 """
492 Return the contents of the named file
493 """
Ned Deily4a96a372013-01-29 00:08:32 -0800494 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000495
496def runCommand(commandline):
497 """
Ezio Melotti13925002011-03-16 11:05:33 +0200498 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000499 unless the command fails.
500 """
501 fd = os.popen(commandline, 'r')
502 data = fd.read()
503 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000504 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000505 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800506 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000507
508 if VERBOSE:
509 sys.stdout.write(data); sys.stdout.flush()
510
511def captureCommand(commandline):
512 fd = os.popen(commandline, 'r')
513 data = fd.read()
514 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000515 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000516 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800517 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000518
519 return data
520
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000521def getTclTkVersion(configfile, versionline):
522 """
523 search Tcl or Tk configuration file for version line
524 """
525 try:
526 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300527 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000528 fatal("Framework configuration file not found: %s" % configfile)
529
530 for l in f:
531 if l.startswith(versionline):
532 f.close()
533 return l
534
535 fatal("Version variable %s not found in framework configuration file: %s"
536 % (versionline, configfile))
537
Thomas Wouters477c8d52006-05-27 19:21:47 +0000538def checkEnvironment():
539 """
540 Check that we're running on a supported system.
541 """
542
Ned Deily8c9bb722018-01-30 07:42:14 -0500543 if sys.version_info[0:2] < (2, 5):
544 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000545
Thomas Wouters477c8d52006-05-27 19:21:47 +0000546 if platform.system() != 'Darwin':
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
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000549 if int(platform.release().split('.')[0]) < 8:
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
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000552 # Because we only support dynamic load of only one major/minor version of
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500553 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000554 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500555 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
556 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
557 # enforce that the version of the user-installed framework also
558 # exists in the system-supplied Tcl/Tk frameworks. Time to support
559 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500560 if not internalTk():
561 frameworks = {}
562 for framework in ['Tcl', 'Tk']:
563 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
564 libfw = os.path.join('/', fwpth)
565 usrfw = os.path.join(os.getenv('HOME'), fwpth)
566 frameworks[framework] = os.readlink(libfw)
567 if not os.path.exists(libfw):
568 fatal("Please install a link to a current %s %s as %s so "
569 "the user can override the system framework."
570 % (framework, frameworks[framework], libfw))
571 if os.path.exists(usrfw):
572 fatal("Please rename %s to avoid possible dynamic load issues."
573 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000574
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500575 if frameworks['Tcl'] != frameworks['Tk']:
576 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000577
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500578 print(" -- Building with external Tcl/Tk %s frameworks"
579 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800580
Ned Deily1ca2ffd2018-01-30 17:29:53 -0500581 # add files to check after build
582 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
583 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
584 % frameworks['Tcl'],
585 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
586 % frameworks['Tk'],
587 ]
588 else:
589 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500590 print("")
591
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000592 # Remove inherited environment variables which might influence build
593 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
594 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
595 for ev in list(os.environ):
596 for prefix in environ_var_prefixes:
597 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800598 print("INFO: deleting environment variable %s=%s" % (
599 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000600 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000601
Ned Deily4a96a372013-01-29 00:08:32 -0800602 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
603 if 'SDK_TOOLS_BIN' in os.environ:
604 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
605 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
606 # add its fixed location here if it exists
607 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
608 if os.path.isdir(OLD_DEVELOPER_TOOLS):
609 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
610 os.environ['PATH'] = base_path
611 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400612 # Ensure we have access to sphinx-build.
613 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700614 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000615
Thomas Wouters89f507f2006-12-13 04:49:30 +0000616def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000617 """
618 Parse arguments and update global settings.
619 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500620 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800621 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800622 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400623 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000624
625 if args is None:
626 args = sys.argv[1:]
627
628 try:
629 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000630 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
631 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800632 except getopt.GetoptError:
633 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000634 sys.exit(1)
635
636 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800637 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000638 sys.exit(1)
639
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000640 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000641 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000642 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800643 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000644 sys.exit(0)
645
646 elif k in ('-d', '--build-dir'):
647 WORKDIR=v
648
649 elif k in ('--third-party',):
650 DEPSRC=v
651
652 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500653 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000654
655 elif k in ('--src-dir',):
656 SRCDIR=v
657
Ronald Oussoren1943f862009-03-30 19:39:14 +0000658 elif k in ('--dep-target', ):
659 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000660 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000661
662 elif k in ('--universal-archs', ):
663 if v in UNIVERSALOPTS:
664 UNIVERSALARCHS = v
665 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000666 if deptarget is None:
667 # Select alternate default deployment
668 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500669 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000670 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800671 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000672
Thomas Wouters477c8d52006-05-27 19:21:47 +0000673 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800674 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000675
676 SRCDIR=os.path.abspath(SRCDIR)
677 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678 DEPSRC=os.path.abspath(DEPSRC)
679
Ned Deily04cdfa12014-06-25 13:36:14 -0700680 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000681
Ned Deily5d3febf2014-12-13 00:17:46 -0800682 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400683 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800684
685 print("-- Settings:")
686 print(" * Source directory: %s" % SRCDIR)
687 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800688 print(" * Third-party source: %s" % DEPSRC)
689 print(" * Deployment target: %s" % DEPTARGET)
690 print(" * Universal archs: %s" % str(ARCHLIST))
691 print(" * C compiler: %s" % CC)
692 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800693 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800694 print(" -- Building a Python %s framework at patch level %s"
695 % (getVersion(), getFullVersion()))
696 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000697
698def extractArchive(builddir, archiveName):
699 """
700 Extract a source archive into 'builddir'. Returns the path of the
701 extracted archive.
702
703 XXX: This function assumes that archives contain a toplevel directory
704 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700705 safe enough for almost anything we use. Unfortunately, it does not
706 work for current Tcl and Tk source releases where the basename of
707 the archive ends with "-src" but the uncompressed directory does not.
708 For now, just special case Tcl and Tk tar.gz downloads.
Ned Deily7cf3d8e2018-12-11 01:06:57 -0500709 Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000710 """
711 curdir = os.getcwd()
712 try:
713 os.chdir(builddir)
714 if archiveName.endswith('.tar.gz'):
715 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700716 if ((retval.startswith('tcl') or retval.startswith('tk'))
717 and retval.endswith('-src')):
718 retval = retval[:-4]
Ned Deily7cf3d8e2018-12-11 01:06:57 -0500719 if retval == 'tk8.6.9.1':
720 retval = 'tk8.6.9'
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 }
813 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800814 "no-idea",
815 "no-mdc2",
816 "no-rc5",
817 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800818 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800819 # "enable-unit-test",
820 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800821 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400822 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800823 ]
824 if no_asm:
825 configure_opts.append("no-asm")
826 runCommand(" ".join(["perl", "Configure"]
827 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500828 runCommand("make depend")
829 runCommand("make all")
830 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800831 # runCommand("make test")
832 return
833
834 srcdir = os.getcwd()
835 universalbase = os.path.join(srcdir, "..",
836 os.path.basename(srcdir) + "-universal")
837 os.mkdir(universalbase)
838 archbasefws = []
839 for arch in archList:
840 # fresh copy of the source tree
841 archsrc = os.path.join(universalbase, arch, "src")
842 shutil.copytree(srcdir, archsrc, symlinks=True)
843 # install base for this arch
844 archbase = os.path.join(universalbase, arch, "root")
845 os.mkdir(archbase)
846 # Python framework base within install_prefix:
847 # the build will install into this framework..
848 # This is to ensure that the resulting shared libs have
849 # the desired real install paths built into them.
850 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
851
852 # build one architecture
853 os.chdir(archsrc)
854 build_openssl_arch(archbase, arch)
855 os.chdir(srcdir)
856 archbasefws.append(archbasefw)
857
858 # copy arch-independent files from last build into the basedir framework
859 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
860 shutil.copytree(
861 os.path.join(archbasefw, "include", "openssl"),
862 os.path.join(basefw, "include", "openssl")
863 )
864
865 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
866 "SHLIB_VERSION_NUMBER")
867 # e.g. -> "1.0.0"
868 libcrypto = "libcrypto.dylib"
869 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
870 # e.g. -> "libcrypto.1.0.0.dylib"
871 libssl = "libssl.dylib"
872 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
873 # e.g. -> "libssl.1.0.0.dylib"
874
875 try:
876 os.mkdir(os.path.join(basefw, "lib"))
877 except OSError:
878 pass
879
880 # merge the individual arch-dependent shared libs into a fat shared lib
881 archbasefws.insert(0, basefw)
882 for (lib_unversioned, lib_versioned) in [
883 (libcrypto, libcrypto_versioned),
884 (libssl, libssl_versioned)
885 ]:
886 runCommand("lipo -create -output " +
887 " ".join(shellQuote(
888 os.path.join(fw, "lib", lib_versioned))
889 for fw in archbasefws))
890 # and create an unversioned symlink of it
891 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
892
893 # Create links in the temp include and lib dirs that will be injected
894 # into the Python build so that setup.py can find them while building
895 # and the versioned links so that the setup.py post-build import test
896 # does not fail.
897 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
898 for fn in [
899 ["include", "openssl"],
900 ["lib", libcrypto],
901 ["lib", libssl],
902 ["lib", libcrypto_versioned],
903 ["lib", libssl_versioned],
904 ]:
905 os.symlink(
906 os.path.join(relative_path, *fn),
907 os.path.join(basedir, "usr", "local", *fn)
908 )
909
910 return
911
Thomas Wouters477c8d52006-05-27 19:21:47 +0000912def buildRecipe(recipe, basedir, archList):
913 """
914 Build software using a recipe. This function does the
915 'configure;make;make install' dance for C software, with a possibility
916 to customize this process, basically a poor-mans DarwinPorts.
917 """
918 curdir = os.getcwd()
919
920 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800921 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000922 url = recipe['url']
923 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800924 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000925 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
926 shellQuote(basedir)))
927
928 archiveName = os.path.split(url)[-1]
929 sourceArchive = os.path.join(DEPSRC, archiveName)
930
931 if not os.path.exists(DEPSRC):
932 os.mkdir(DEPSRC)
933
Ned Deily4a96a372013-01-29 00:08:32 -0800934 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
935 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000936 buildDir=os.path.join(WORKDIR, '_bld')
937 if not os.path.exists(buildDir):
938 os.mkdir(buildDir)
939
940 workDir = extractArchive(buildDir, sourceArchive)
941 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000942
Ned Deily4a96a372013-01-29 00:08:32 -0800943 for patch in recipe.get('patches', ()):
944 if isinstance(patch, tuple):
945 url, checksum = patch
946 fn = os.path.join(DEPSRC, os.path.basename(url))
947 verifyThirdPartyFile(url, checksum, fn)
948 else:
949 # patch is a file in the source directory
950 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000951 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
952 shellQuote(fn),))
953
Ned Deily4a96a372013-01-29 00:08:32 -0800954 for patchscript in recipe.get('patchscripts', ()):
955 if isinstance(patchscript, tuple):
956 url, checksum = patchscript
957 fn = os.path.join(DEPSRC, os.path.basename(url))
958 verifyThirdPartyFile(url, checksum, fn)
959 else:
960 # patch is a file in the source directory
961 fn = os.path.join(curdir, patchscript)
962 if fn.endswith('.bz2'):
963 runCommand('bunzip2 -fk %s' % shellQuote(fn))
964 fn = fn[:-4]
965 runCommand('sh %s' % shellQuote(fn))
966 os.unlink(fn)
967
Ned Deily94764b22013-10-27 19:49:29 -0700968 if 'buildDir' in recipe:
969 os.chdir(recipe['buildDir'])
970
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000971 if configure is not None:
972 configure_args = [
973 "--prefix=/usr/local",
974 "--enable-static",
975 "--disable-shared",
976 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
977 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000979 if 'configure_pre' in recipe:
980 args = list(recipe['configure_pre'])
981 if '--disable-static' in args:
982 configure_args.remove('--enable-static')
983 if '--enable-shared' in args:
984 configure_args.remove('--disable-shared')
985 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000986
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000987 if recipe.get('useLDFlags', 1):
988 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -0500989 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -0800990 "-I%s/usr/local/include"%(
991 recipe.get('extra_cflags', ''),
992 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000993 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000994 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -0500995 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -0800996 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000997 shellQuote(basedir)[1:-1],
998 ' -arch '.join(archList)),
999 ])
1000 else:
1001 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001002 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001003 "-I%s/usr/local/include"%(
1004 recipe.get('extra_cflags', ''),
1005 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001006 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001007 shellQuote(basedir)[1:-1],),
1008 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001009
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001010 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001011 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001012
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001013 configure_args.insert(0, configure)
1014 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001015
Ned Deily4a96a372013-01-29 00:08:32 -08001016 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001017 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001018
Ned Deily5d3febf2014-12-13 00:17:46 -08001019 if buildrecipe is not None:
1020 # call special-case build recipe, e.g. for openssl
1021 buildrecipe(basedir, archList)
1022
1023 if install is not None:
1024 print("Running install for %s"%(name,))
1025 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001026
Ned Deily4a96a372013-01-29 00:08:32 -08001027 print("Done %s"%(name,))
1028 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001029
1030 os.chdir(curdir)
1031
1032def buildLibraries():
1033 """
1034 Build our dependencies into $WORKDIR/libraries/usr/local
1035 """
Ned Deily4a96a372013-01-29 00:08:32 -08001036 print("")
1037 print("Building required libraries")
1038 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001039 universal = os.path.join(WORKDIR, 'libraries')
1040 os.mkdir(universal)
1041 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1042 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1043
Ronald Oussoren1943f862009-03-30 19:39:14 +00001044 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001045 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001046
1047
1048
1049def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001050 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001051 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001052 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001053 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001054 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001055 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001056 curDir = os.getcwd()
1057 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001058 runCommand('make clean')
Ned Deily4c7532e2017-07-23 16:39:54 -04001059 # Create virtual environment for docs builds with blurb and sphinx
1060 runCommand('make venv')
1061 runCommand('make html PYTHON=venv/bin/python')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001062 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001063 if not os.path.exists(docdir):
1064 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001065 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001066
1067
1068def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001069 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001070
1071 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1072 rootDir = os.path.join(WORKDIR, '_root')
1073
1074 if os.path.exists(buildDir):
1075 shutil.rmtree(buildDir)
1076 if os.path.exists(rootDir):
1077 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001078 os.makedirs(buildDir)
1079 os.makedirs(rootDir)
1080 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001081 curdir = os.getcwd()
1082 os.chdir(buildDir)
1083
Thomas Wouters477c8d52006-05-27 19:21:47 +00001084 # Extract the version from the configure file, needed to calculate
1085 # several paths.
1086 version = getVersion()
1087
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001088 # Since the extra libs are not in their installed framework location
1089 # during the build, augment the library path so that the interpreter
1090 # will find them during its extension import sanity checks.
1091 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1092 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001093 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001094 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001095 "--with-universal-archs=%s "
1096 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001097 "%s "
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001098 "%s "
1099 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001100 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001101 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001102 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001103 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001104 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001105 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001106 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1107 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1108 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1109 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001110 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001111 shellQuote(WORKDIR)[1:-1]))
1112
Ned Deilyb364d9f2017-07-24 04:58:43 -04001113 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1114 # and, if defined, append its value to the make command. This allows
1115 # us to pass in version control tags, like GITTAG, to a build from a
1116 # tarball rather than from a vcs checkout, thus eliminating the need
1117 # to have a working copy of the vcs program on the build machine.
1118 #
1119 # A typical use might be:
1120 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1121 # GITVERSION='echo 123456789a' \
1122 # GITTAG='echo v3.6.0' \
1123 # GITBRANCH='echo 3.6'"
1124
1125 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1126 if make_extras:
1127 make_cmd = "make " + make_extras
1128 else:
1129 make_cmd = "make"
1130 print("Running " + make_cmd)
1131 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001132
Ned Deily4a96a372013-01-29 00:08:32 -08001133 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001134 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001135 shellQuote(rootDir)))
1136
Ned Deily4a96a372013-01-29 00:08:32 -08001137 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001138 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1139 shellQuote(rootDir)))
1140
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001141 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001142 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001143 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001144 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily1ca2ffd2018-01-30 17:29:53 -05001146 'Python.framework', 'Versions', getVersion(), 'lib')
1147 fw_lib_dir = os.path.join(
1148 WORKDIR, '_root', 'Library', 'Frameworks',
1149 'Python.framework', 'Versions', getVersion(), 'lib')
1150 if internalTk():
1151 # move Tcl and Tk pkgconfig files
1152 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1153 shellQuote(build_lib_dir),
1154 shellQuote(fw_lib_dir) ))
1155 runCommand("rm -r %s/pkgconfig"%(
1156 shellQuote(build_lib_dir), ))
1157 runCommand("mv %s/* %s"%(
1158 shellQuote(build_lib_dir),
1159 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001160
Ned Deilydde4f632016-09-12 09:39:23 -04001161 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1162 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1163 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1164 # create directory for OpenSSL certificates
1165 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1166 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001167
Ned Deily4a96a372013-01-29 00:08:32 -08001168 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001169 gid = grp.getgrnam('admin').gr_gid
1170
Ned Deily4a96a372013-01-29 00:08:32 -08001171 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001172 for dirpath, dirnames, filenames in os.walk(frmDir):
1173 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001174 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001175 os.chown(os.path.join(dirpath, dn), -1, gid)
1176
Thomas Wouters477c8d52006-05-27 19:21:47 +00001177 for fn in filenames:
1178 if os.path.islink(fn):
1179 continue
1180
1181 # "chmod g+w $fn"
1182 p = os.path.join(dirpath, fn)
1183 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001184 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1185 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001186
Ned Deily4a96a372013-01-29 00:08:32 -08001187 if fn in EXPECTED_SHARED_LIBS:
1188 # check to see that this file was linked with the
1189 # expected library path and version
1190 data = captureCommand("otool -L %s" % shellQuote(p))
1191 for sl in EXPECTED_SHARED_LIBS[fn]:
1192 if ("\t%s " % sl) not in data:
1193 print("Expected shared lib %s was not linked with %s"
1194 % (sl, p))
1195 shared_lib_error = True
1196
1197 if shared_lib_error:
1198 fatal("Unexpected shared library errors.")
1199
Ned Deilye59e4c52011-01-29 18:56:28 +00001200 if PYTHON_3:
1201 LDVERSION=None
1202 VERSION=None
1203 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001204
Ned Deilye59e4c52011-01-29 18:56:28 +00001205 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001206 for ln in fp:
1207 if ln.startswith('VERSION='):
1208 VERSION=ln.split()[1]
1209 if ln.startswith('ABIFLAGS='):
1210 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001211 if ln.startswith('LDVERSION='):
1212 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001213 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001214
Ned Deilye59e4c52011-01-29 18:56:28 +00001215 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1216 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1217 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001218 if getVersionMajorMinor() >= (3, 6):
1219 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001220 else:
1221 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001222
Thomas Wouters477c8d52006-05-27 19:21:47 +00001223 # We added some directories to the search path during the configure
1224 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001225 # the end-users system. Also remove the directories from _sysconfigdata.py
1226 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001227
Ned Deilya4f6b002013-10-25 00:47:38 -07001228 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1229 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1230
Ned Deilya4f6b002013-10-25 00:47:38 -07001231 # fix Makefile
1232 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1233 fp = open(path, 'r')
1234 data = fp.read()
1235 fp.close()
1236
1237 for p in (include_path, lib_path):
1238 data = data.replace(" " + p, '')
1239 data = data.replace(p + " ", '')
1240
1241 fp = open(path, 'w')
1242 fp.write(data)
1243 fp.close()
1244
Ned Deily652bad42016-08-15 14:37:14 -04001245 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001246 #
1247 # TODO: make this more robust! test_sysconfig_module of
1248 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1249 # the output from get_config_var in both sysconfig and
1250 # distutils.sysconfig is exactly the same for both CFLAGS and
1251 # LDFLAGS. The fixing up is now complicated by the pretty
1252 # printing in _sysconfigdata.py. Also, we are using the
1253 # pprint from the Python running the installer build which
1254 # may not cosmetically format the same as the pprint in the Python
1255 # being built (and which is used to originally generate
1256 # _sysconfigdata.py).
1257
1258 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001259 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001260 # XXX this is extra-fragile
1261 path = os.path.join(path_to_lib, '_sysconfigdata_m_darwin_darwin.py')
Ned Deily652bad42016-08-15 14:37:14 -04001262 else:
1263 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1264 fp = open(path, 'r')
1265 data = fp.read()
1266 fp.close()
1267 # create build_time_vars dict
1268 exec(data)
1269 vars = {}
1270 for k, v in build_time_vars.items():
1271 if type(v) == type(''):
1272 for p in (include_path, lib_path):
1273 v = v.replace(' ' + p, '')
1274 v = v.replace(p + ' ', '')
1275 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001276
Ned Deily652bad42016-08-15 14:37:14 -04001277 fp = open(path, 'w')
1278 # duplicated from sysconfig._generate_posix_vars()
1279 fp.write('# system configuration generated and used by'
1280 ' the sysconfig module\n')
1281 fp.write('build_time_vars = ')
1282 pprint.pprint(vars, stream=fp)
1283 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001284
1285 # Add symlinks in /usr/local/bin, using relative links
1286 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1287 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1288 'Python.framework', 'Versions', version, 'bin')
1289 if os.path.exists(usr_local_bin):
1290 shutil.rmtree(usr_local_bin)
1291 os.makedirs(usr_local_bin)
1292 for fn in os.listdir(
1293 os.path.join(frmDir, 'Versions', version, 'bin')):
1294 os.symlink(os.path.join(to_framework, fn),
1295 os.path.join(usr_local_bin, fn))
1296
1297 os.chdir(curdir)
1298
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001299 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001300 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001301 # with an already installed version of python 2
1302 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1303 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001304
1305def patchFile(inPath, outPath):
1306 data = fileContents(inPath)
1307 data = data.replace('$FULL_VERSION', getFullVersion())
1308 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001309 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001310 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001311 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001312 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001313
1314 # This one is not handy as a template variable
1315 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001316 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001317 fp.write(data)
1318 fp.close()
1319
1320def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001321 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001322 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001323 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001324 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001325 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001326 fp.write(data)
1327 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001328 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001329
1330
1331
1332def packageFromRecipe(targetDir, recipe):
1333 curdir = os.getcwd()
1334 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001335 # The major version (such as 2.5) is included in the package name
1336 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001337 # common.
1338 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339 srcdir = recipe.get('source')
1340 pkgroot = recipe.get('topdir', srcdir)
1341 postflight = recipe.get('postflight')
1342 readme = textwrap.dedent(recipe['readme'])
1343 isRequired = recipe.get('required', True)
1344
Ned Deily4a96a372013-01-29 00:08:32 -08001345 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001346
1347 # Substitute some variables
1348 textvars = dict(
1349 VER=getVersion(),
1350 FULLVER=getFullVersion(),
1351 )
1352 readme = readme % textvars
1353
1354 if pkgroot is not None:
1355 pkgroot = pkgroot % textvars
1356 else:
1357 pkgroot = '/'
1358
1359 if srcdir is not None:
1360 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1361 srcdir = srcdir % textvars
1362
1363 if postflight is not None:
1364 postflight = os.path.abspath(postflight)
1365
1366 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1367 os.makedirs(packageContents)
1368
1369 if srcdir is not None:
1370 os.chdir(srcdir)
1371 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1372 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1373 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1374
1375 fn = os.path.join(packageContents, 'PkgInfo')
1376 fp = open(fn, 'w')
1377 fp.write('pmkrpkg1')
1378 fp.close()
1379
1380 rsrcDir = os.path.join(packageContents, "Resources")
1381 os.mkdir(rsrcDir)
1382 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1383 fp.write(readme)
1384 fp.close()
1385
1386 if postflight is not None:
1387 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1388
1389 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001390 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001391 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001392 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1393 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1394 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001395 CFBundleShortVersionString=vers,
1396 IFMajorVersion=major,
1397 IFMinorVersion=minor,
1398 IFPkgFormatVersion=0.10000000149011612,
1399 IFPkgFlagAllowBackRev=False,
1400 IFPkgFlagAuthorizationAction="RootAuthorization",
1401 IFPkgFlagDefaultLocation=pkgroot,
1402 IFPkgFlagFollowLinks=True,
1403 IFPkgFlagInstallFat=True,
1404 IFPkgFlagIsRequired=isRequired,
1405 IFPkgFlagOverwritePermissions=False,
1406 IFPkgFlagRelocatable=False,
1407 IFPkgFlagRestartAction="NoRestart",
1408 IFPkgFlagRootVolumeOnly=True,
1409 IFPkgFlagUpdateInstalledLangauges=False,
1410 )
1411 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1412
1413 pl = Plist(
1414 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001415 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001416 IFPkgDescriptionVersion=vers,
1417 )
1418 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1419
1420 finally:
1421 os.chdir(curdir)
1422
1423
1424def makeMpkgPlist(path):
1425
1426 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001427 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001428
1429 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001430 CFBundleGetInfoString="Python %s"%(vers,),
1431 CFBundleIdentifier='org.python.Python',
1432 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001433 CFBundleShortVersionString=vers,
1434 IFMajorVersion=major,
1435 IFMinorVersion=minor,
1436 IFPkgFlagComponentDirectory="Contents/Packages",
1437 IFPkgFlagPackageList=[
1438 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001439 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001440 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001441 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001442 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001443 ],
1444 IFPkgFormatVersion=0.10000000149011612,
1445 IFPkgFlagBackgroundScaling="proportional",
1446 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001447 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001448 )
1449
1450 writePlist(pl, path)
1451
1452
1453def buildInstaller():
1454
1455 # Zap all compiled files
1456 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1457 for fn in filenames:
1458 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1459 os.unlink(os.path.join(dirpath, fn))
1460
1461 outdir = os.path.join(WORKDIR, 'installer')
1462 if os.path.exists(outdir):
1463 shutil.rmtree(outdir)
1464 os.mkdir(outdir)
1465
Ronald Oussoren1943f862009-03-30 19:39:14 +00001466 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001467 pkgcontents = os.path.join(pkgroot, 'Packages')
1468 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001469 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001470 packageFromRecipe(pkgcontents, recipe)
1471
1472 rsrcDir = os.path.join(pkgroot, 'Resources')
1473
1474 fn = os.path.join(pkgroot, 'PkgInfo')
1475 fp = open(fn, 'w')
1476 fp.write('pmkrpkg1')
1477 fp.close()
1478
1479 os.mkdir(rsrcDir)
1480
1481 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1482 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001483 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001484 IFPkgDescriptionVersion=getVersion(),
1485 )
1486
1487 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1488 for fn in os.listdir('resources'):
1489 if fn == '.svn': continue
1490 if fn.endswith('.jpg'):
1491 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1492 else:
1493 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1494
Thomas Wouters477c8d52006-05-27 19:21:47 +00001495
1496def installSize(clear=False, _saved=[]):
1497 if clear:
1498 del _saved[:]
1499 if not _saved:
1500 data = captureCommand("du -ks %s"%(
1501 shellQuote(os.path.join(WORKDIR, '_root'))))
1502 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1503 return _saved[0]
1504
1505
1506def buildDMG():
1507 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001508 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001509 """
1510 outdir = os.path.join(WORKDIR, 'diskimage')
1511 if os.path.exists(outdir):
1512 shutil.rmtree(outdir)
1513
1514 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001515 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001516 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001517 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001518 imagepath = imagepath + '.dmg'
1519
1520 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001521 volname='Python %s'%(getFullVersion())
1522 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1523 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001524 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001525 shellQuote(imagepath + ".tmp.dmg" )))
1526
Ned Deilyfc6aa282018-05-02 01:41:15 -04001527 # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
1528 # when hdiutil fails with "Resource busy"
1529
1530 time.sleep(10)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001531
1532 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1533 os.mkdir(os.path.join(WORKDIR, "mnt"))
1534 runCommand("hdiutil attach %s -mountroot %s"%(
1535 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1536
1537 # Custom icon for the DMG, shown when the DMG is mounted.
1538 shutil.copy("../Icons/Disk Image.icns",
1539 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001540 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001541 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1542
1543 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1544
1545 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1546 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1547 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1548 setIcon(imagepath, "../Icons/Disk Image.icns")
1549
1550 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001551
1552 return imagepath
1553
1554
1555def setIcon(filePath, icnsPath):
1556 """
1557 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001558 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001559
Ronald Oussoren70050672010-04-30 15:00:26 +00001560 dirPath = os.path.normpath(os.path.dirname(__file__))
1561 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001562 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1563 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1564 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001565 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1566 if not os.path.exists(appPath):
1567 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001568 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1569 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001570
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001571 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1572 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001573
1574def main():
1575 # First parse options and check if we can perform our work
1576 parseOptions()
1577 checkEnvironment()
1578
Ronald Oussoren1943f862009-03-30 19:39:14 +00001579 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001580 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001581 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001582
1583 if os.path.exists(WORKDIR):
1584 shutil.rmtree(WORKDIR)
1585 os.mkdir(WORKDIR)
1586
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001587 os.environ['LC_ALL'] = 'C'
1588
Thomas Wouters477c8d52006-05-27 19:21:47 +00001589 # Then build third-party libraries such as sleepycat DB4.
1590 buildLibraries()
1591
1592 # Now build python itself
1593 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001594
1595 # And then build the documentation
1596 # Remove the Deployment Target from the shell
1597 # environment, it's no longer needed and
1598 # an unexpected build target can cause problems
1599 # when Sphinx and its dependencies need to
1600 # be (re-)installed.
1601 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001602 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001603
1604
1605 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001606 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001607 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001608 fn = os.path.join(folder, "License.rtf")
1609 patchFile("resources/License.rtf", fn)
1610 fn = os.path.join(folder, "ReadMe.rtf")
1611 patchFile("resources/ReadMe.rtf", fn)
1612 fn = os.path.join(folder, "Update Shell Profile.command")
1613 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001614 fn = os.path.join(folder, "Install Certificates.command")
1615 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001616 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001617 setIcon(folder, "../Icons/Python Folder.icns")
1618
1619 # Create the installer
1620 buildInstaller()
1621
1622 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001623 patchFile('resources/ReadMe.rtf',
1624 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001625
1626 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001627 patchFile('resources/License.rtf',
1628 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001629
1630 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001631 fp.write("# BUILD INFO\n")
1632 fp.write("# Date: %s\n" % time.ctime())
1633 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001634 fp.close()
1635
Thomas Wouters477c8d52006-05-27 19:21:47 +00001636 # And copy it to a DMG
1637 buildDMG()
1638
Thomas Wouters477c8d52006-05-27 19:21:47 +00001639if __name__ == "__main__":
1640 main()