blob: 7f6228ba444f83fc2118b34ca16b3e04cf526c15 [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
7- use external Tcl/Tk 8.6 for 10.9+ builds
8- 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 Deily8c9bb722018-01-30 07:42:14 -050027build-installer currently requires an installed third-party version of
28Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets), Tcl/TK 8.5
29(for 10.6 or later), or Tcl/TK 8.6 (for 10.9 or later)
30installed in /Library/Frameworks. When installed,
Ned Deily4a96a372013-01-29 00:08:32 -080031the Python built by this script will attempt to dynamically link first to
32Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
33back to the ones in /System/Library/Framework. For the build, we recommend
Ned Deily8c9bb722018-01-30 07:42:14 -050034installing the most recent ActiveTcl 8.6. 8.5, or 8.4 version, depending
35on the deployment target. The actual version linked to depends on the
36path of /Library/Frameworks/{Tcl,Tk}.framework/Versions/Current.
Ned Deily4a96a372013-01-29 00:08:32 -080037
Thomas Wouters477c8d52006-05-27 19:21:47 +000038Usage: see USAGE variable in the script.
39"""
Ned Deily4a96a372013-01-29 00:08:32 -080040import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
41try:
42 import urllib2 as urllib_request
43except ImportError:
44 import urllib.request as urllib_request
45
46STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
47 | stat.S_IRGRP | stat.S_IXGRP
48 | stat.S_IROTH | stat.S_IXOTH )
49
50STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
51 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
52 | stat.S_IROTH | stat.S_IXOTH )
Thomas Wouters477c8d52006-05-27 19:21:47 +000053
Thomas Wouters89f507f2006-12-13 04:49:30 +000054INCLUDE_TIMESTAMP = 1
55VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000056
57from plistlib import Plist
58
Thomas Wouters477c8d52006-05-27 19:21:47 +000059try:
60 from plistlib import writePlist
61except ImportError:
62 # We're run using python2.3
63 def writePlist(plist, path):
64 plist.write(path)
65
66def shellQuote(value):
67 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000068 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000069 a shell command.
70 """
71 return "'%s'"%(value.replace("'", "'\"'\"'"))
72
73def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -080074 """
75 Return the unquoted value of a variable from a file..
76 QUOTED_VALUE='quotes' -> str('quotes')
77 UNQUOTED_VALUE=noquotes -> str('noquotes')
78 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000079 variable = variable + '='
80 for ln in open(fn, 'r'):
81 if ln.startswith(variable):
82 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080083 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080084 raise RuntimeError("Cannot find variable %s" % variable[:-1])
85
86_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000087
88def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080089 global _cache_getVersion
90 if _cache_getVersion is None:
91 _cache_getVersion = grepValue(
92 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
93 return _cache_getVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000094
Ned Deily4a96a372013-01-29 00:08:32 -080095def getVersionMajorMinor():
96 return tuple([int(n) for n in getVersion().split('.', 2)])
97
98_cache_getFullVersion = None
99
Thomas Wouters477c8d52006-05-27 19:21:47 +0000100def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -0800101 global _cache_getFullVersion
102 if _cache_getFullVersion is not None:
103 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +0000104 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
105 for ln in open(fn):
106 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -0800107 _cache_getFullVersion = ln.split()[-1][1:-1]
108 return _cache_getFullVersion
109 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000110
Ned Deily5d3febf2014-12-13 00:17:46 -0800111FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
112FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
Ned Deilydde4f632016-09-12 09:39:23 -0400113FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800114
Thomas Wouters89f507f2006-12-13 04:49:30 +0000115# The directory we'll use to create the build (will be erased and recreated)
116WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000117
Thomas Wouters89f507f2006-12-13 04:49:30 +0000118# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000119# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000120DEPSRC = os.path.join(WORKDIR, 'third-party')
121DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000122
Ronald Oussoren1943f862009-03-30 19:39:14 +0000123universal_opts_map = { '32-bit': ('i386', 'ppc',),
124 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000125 'intel': ('i386', 'x86_64'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500126 'intel-32': ('i386',),
127 'intel-64': ('x86_64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000128 '3-way': ('ppc', 'i386', 'x86_64'),
129 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
130default_target_map = {
131 '64-bit': '10.5',
132 '3-way': '10.5',
133 'intel': '10.5',
Ned Deily8c9bb722018-01-30 07:42:14 -0500134 'intel-32': '10.4',
135 'intel-64': '10.5',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000136 'all': '10.5',
137}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000138
139UNIVERSALOPTS = tuple(universal_opts_map.keys())
140
141UNIVERSALARCHS = '32-bit'
142
143ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000144
Ezio Melotti42da6632011-03-15 05:18:48 +0200145# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000146SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000147 os.path.dirname(
148 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000149 os.path.abspath(__file__
150 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000151
Ronald Oussoren1943f862009-03-30 19:39:14 +0000152# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
Ned Deily8c9bb722018-01-30 07:42:14 -0500153DEPTARGET = '10.5'
Ronald Oussoren1943f862009-03-30 19:39:14 +0000154
Ned Deily04cdfa12014-06-25 13:36:14 -0700155def getDeptargetTuple():
156 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
157
158def getTargetCompilers():
159 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800160 '10.4': ('gcc-4.0', 'g++-4.0'),
Ned Deily8c9bb722018-01-30 07:42:14 -0500161 '10.5': ('gcc', 'g++'),
162 '10.6': ('gcc', 'g++'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700163 }
Miss Islington (bot)d24c5a02018-02-24 11:59:25 -0800164 return target_cc_map.get(DEPTARGET, ('gcc', 'g++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000165
Ned Deily04cdfa12014-06-25 13:36:14 -0700166CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000167
Ned Deily5d3febf2014-12-13 00:17:46 -0800168PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000169
Thomas Wouters89f507f2006-12-13 04:49:30 +0000170USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000171 Usage: build_python [options]
172
173 Options:
174 -? or -h: Show this message
175 -b DIR
176 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
177 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500178 --sdk-path=DIR: Location of the SDK (deprecated, use SDKROOT env variable)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000179 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ned Deily8c9bb722018-01-30 07:42:14 -0500180 --dep-target=10.n macOS deployment target (default: %(DEPTARGET)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000181 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000182""")% globals()
183
Ned Deily4a96a372013-01-29 00:08:32 -0800184# Dict of object file names with shared library names to check after building.
185# This is to ensure that we ended up dynamically linking with the shared
186# library paths and versions we expected. For example:
187# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
188# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
189# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
190EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000191
Ned Deily7fdefac2018-01-30 17:29:53 -0500192# Are we building and linking with our own copy of Tcl/TK?
193# For now, do so if deployment target is 10.9+.
194def internalTk():
195 return getDeptargetTuple() >= (10, 9)
196
Ned Deily5d3febf2014-12-13 00:17:46 -0800197# List of names of third party software built with this installer.
198# The names will be inserted into the rtf version of the License.
199THIRD_PARTY_LIBS = []
200
Thomas Wouters477c8d52006-05-27 19:21:47 +0000201# Instructions for building libraries that are necessary for building a
202# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000203# [The recipes are defined here for convenience but instantiated later after
204# command line options have been processed.]
205def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000206 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000207
Ned Deily04cdfa12014-06-25 13:36:14 -0700208 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800209
Ned Deilydde4f632016-09-12 09:39:23 -0400210 # Since Apple removed the header files for the deprecated system
211 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
212 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800213
Ned Deilydde4f632016-09-12 09:39:23 -0400214 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800215 dict(
Ned Deily8c9bb722018-01-30 07:42:14 -0500216 name="OpenSSL 1.1.0g",
217 url="https://www.openssl.org/source/openssl-1.1.0g.tar.gz",
218 checksum='ba5f1b8b835b88cadbce9b35ed9531a6',
Ned Deily5d3febf2014-12-13 00:17:46 -0800219 buildrecipe=build_universal_openssl,
220 configure=None,
221 install=None,
222 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400223 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800224
Ned Deily7fdefac2018-01-30 17:29:53 -0500225 if internalTk():
Ned Deily5b3582c2013-10-25 00:41:46 -0700226 result.extend([
227 dict(
Ned Deilya7edca72018-02-27 17:36:12 -0500228 name="Tcl 8.6.8",
229 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
230 checksum='81656d3367af032e0ae6157eff134f89',
Ned Deily5b3582c2013-10-25 00:41:46 -0700231 buildDir="unix",
232 configure_pre=[
233 '--enable-shared',
234 '--enable-threads',
235 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
236 ],
237 useLDFlags=False,
238 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
239 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily7fdefac2018-01-30 17:29:53 -0500240 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700241 },
242 ),
243 dict(
Ned Deilya7edca72018-02-27 17:36:12 -0500244 name="Tk 8.6.8",
245 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
246 checksum='5e0faecba458ee1386078fb228d008ba',
247 patches=[
248 "tk868_on_10_8_10_9.patch",
249 ],
Ned Deily5b3582c2013-10-25 00:41:46 -0700250 buildDir="unix",
251 configure_pre=[
252 '--enable-aqua',
253 '--enable-shared',
254 '--enable-threads',
255 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
256 ],
257 useLDFlags=False,
258 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'%{
259 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
Ned Deily7fdefac2018-01-30 17:29:53 -0500260 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
261 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.6'%(getVersion())),
Ned Deily5b3582c2013-10-25 00:41:46 -0700262 },
263 ),
264 ])
265
Ned Deilyed730102014-11-14 18:55:05 -0800266 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800267 result.extend([
268 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500269 name="XZ 5.2.3",
270 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
271 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800272 configure_pre=[
273 '--disable-dependency-tracking',
274 ]
275 ),
276 ])
277
278 result.extend([
279 dict(
280 name="NCurses 5.9",
281 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
282 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
283 configure_pre=[
284 "--enable-widec",
285 "--without-cxx",
286 "--without-cxx-binding",
287 "--without-ada",
288 "--without-curses-h",
289 "--enable-shared",
290 "--with-shared",
291 "--without-debug",
292 "--without-normal",
293 "--without-tests",
294 "--without-manpages",
295 "--datadir=/usr/share",
296 "--sysconfdir=/etc",
297 "--sharedstatedir=/usr/com",
298 "--with-terminfo-dirs=/usr/share/terminfo",
299 "--with-default-terminfo-dir=/usr/share/terminfo",
300 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
301 ],
302 patchscripts=[
303 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
304 "f54bf02a349f96a7c4f0d00922f3a0d4"),
305 ],
306 useLDFlags=False,
307 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
308 shellQuote(os.path.join(WORKDIR, 'libraries')),
309 shellQuote(os.path.join(WORKDIR, 'libraries')),
310 getVersion(),
311 ),
312 ),
313 dict(
Ned Deily8c9bb722018-01-30 07:42:14 -0500314 name="SQLite 3.22.0",
315 url="https://www.sqlite.org/2018/sqlite-autoconf-3220000.tar.gz",
316 checksum='96b5648d542e8afa6ab7ffb8db8ddc3d',
Ned Deily4a96a372013-01-29 00:08:32 -0800317 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700318 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800319 '-DSQLITE_ENABLE_FTS4 '
320 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500321 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800322 '-DSQLITE_ENABLE_RTREE '
323 '-DSQLITE_TCL=0 '
324 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
325 configure_pre=[
326 '--enable-threadsafe',
327 '--enable-shared=no',
328 '--enable-static=yes',
329 '--disable-readline',
330 '--disable-dependency-tracking',
331 ]
332 ),
333 ])
334
Ned Deily04cdfa12014-06-25 13:36:14 -0700335 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000336 result.extend([
337 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000338 name="Bzip2 1.0.6",
339 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
340 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000341 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500342 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800343 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000344 shellQuote(os.path.join(WORKDIR, 'libraries')),
345 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000346 ),
347 ),
348 dict(
349 name="ZLib 1.2.3",
350 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
351 checksum='debc62758716a169df9f62e6ab2bc634',
352 configure=None,
Ned Deily8c9bb722018-01-30 07:42:14 -0500353 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s"'%(
Ned Deily4a96a372013-01-29 00:08:32 -0800354 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000355 shellQuote(os.path.join(WORKDIR, 'libraries')),
356 ' -arch '.join(ARCHLIST),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000357 ),
358 ),
359 dict(
360 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000361 name="GNU Readline 6.1.2",
362 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
363 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000364 patchlevel='0',
365 patches=[
366 # The readline maintainers don't do actual micro releases, but
367 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800368 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
369 'c642f2e84d820884b0bf9fd176bc6c3f'),
370 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
371 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000372 ]
373 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000374 ])
375
Ned Deily4f7ff782011-01-15 05:29:12 +0000376 if not PYTHON_3:
377 result.extend([
378 dict(
379 name="Sleepycat DB 4.7.25",
380 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
381 checksum='ec2b87e833779681a0c3a814aa71359e',
382 buildDir="build_unix",
383 configure="../dist/configure",
384 configure_pre=[
385 '--includedir=/usr/local/include/db4',
386 ]
387 ),
388 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000389
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000390 return result
391
Thomas Wouters477c8d52006-05-27 19:21:47 +0000392
Thomas Wouters477c8d52006-05-27 19:21:47 +0000393# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000394def pkg_recipes():
395 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
396 result = [
397 dict(
398 name="PythonFramework",
399 long_name="Python Framework",
400 source="/Library/Frameworks/Python.framework",
401 readme="""\
402 This package installs Python.framework, that is the python
Ned Deily8c9bb722018-01-30 07:42:14 -0500403 interpreter and the standard library.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000404 """,
405 postflight="scripts/postflight.framework",
406 selected='selected',
407 ),
408 dict(
409 name="PythonApplications",
410 long_name="GUI Applications",
411 source="/Applications/Python %(VER)s",
412 readme="""\
413 This package installs IDLE (an interactive Python IDE),
414 Python Launcher and Build Applet (create application bundles
415 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000416
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000417 It also installs a number of examples and demos.
418 """,
419 required=False,
420 selected='selected',
421 ),
422 dict(
423 name="PythonUnixTools",
424 long_name="UNIX command-line tools",
425 source="/usr/local/bin",
426 readme="""\
427 This package installs the unix tools in /usr/local/bin for
428 compatibility with older releases of Python. This package
429 is not necessary to use Python.
430 """,
431 required=False,
432 selected='selected',
433 ),
434 dict(
435 name="PythonDocumentation",
436 long_name="Python Documentation",
437 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
438 source="/pydocs",
439 readme="""\
440 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800441 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000442 """,
443 postflight="scripts/postflight.documentation",
444 required=False,
445 selected='selected',
446 ),
447 dict(
448 name="PythonProfileChanges",
449 long_name="Shell profile updater",
450 readme="""\
451 This packages updates your shell profile to make sure that
452 the Python tools are found by your shell in preference of
453 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000454
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000455 If you don't install this package you'll have to add
456 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
457 to your PATH by hand.
458 """,
459 postflight="scripts/postflight.patch-profile",
460 topdir="/Library/Frameworks/Python.framework",
461 source="/empty-dir",
462 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800463 selected='selected',
464 ),
465 dict(
466 name="PythonInstallPip",
467 long_name="Install or upgrade pip",
468 readme="""\
469 This package installs (or upgrades from an earlier version)
470 pip, a tool for installing and managing Python packages.
471 """,
472 postflight="scripts/postflight.ensurepip",
473 topdir="/Library/Frameworks/Python.framework",
474 source="/empty-dir",
475 required=False,
476 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000477 ),
478 ]
479
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000480 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000481
Thomas Wouters477c8d52006-05-27 19:21:47 +0000482def fatal(msg):
483 """
484 A fatal error, bail out.
485 """
486 sys.stderr.write('FATAL: ')
487 sys.stderr.write(msg)
488 sys.stderr.write('\n')
489 sys.exit(1)
490
491def fileContents(fn):
492 """
493 Return the contents of the named file
494 """
Ned Deily4a96a372013-01-29 00:08:32 -0800495 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000496
497def runCommand(commandline):
498 """
Ezio Melotti13925002011-03-16 11:05:33 +0200499 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000500 unless the command fails.
501 """
502 fd = os.popen(commandline, 'r')
503 data = fd.read()
504 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000505 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000506 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800507 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000508
509 if VERBOSE:
510 sys.stdout.write(data); sys.stdout.flush()
511
512def captureCommand(commandline):
513 fd = os.popen(commandline, 'r')
514 data = fd.read()
515 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000516 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000517 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800518 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000519
520 return data
521
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000522def getTclTkVersion(configfile, versionline):
523 """
524 search Tcl or Tk configuration file for version line
525 """
526 try:
527 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300528 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000529 fatal("Framework configuration file not found: %s" % configfile)
530
531 for l in f:
532 if l.startswith(versionline):
533 f.close()
534 return l
535
536 fatal("Version variable %s not found in framework configuration file: %s"
537 % (versionline, configfile))
538
Thomas Wouters477c8d52006-05-27 19:21:47 +0000539def checkEnvironment():
540 """
541 Check that we're running on a supported system.
542 """
543
Ned Deily8c9bb722018-01-30 07:42:14 -0500544 if sys.version_info[0:2] < (2, 5):
545 fatal("This script must be run with Python 2.5 (or later)")
Ned Deilye59e4c52011-01-29 18:56:28 +0000546
Thomas Wouters477c8d52006-05-27 19:21:47 +0000547 if platform.system() != 'Darwin':
Ned Deily8c9bb722018-01-30 07:42:14 -0500548 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000550 if int(platform.release().split('.')[0]) < 8:
Ned Deily8c9bb722018-01-30 07:42:14 -0500551 fatal("This script should be run on a macOS 10.5 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000553 # Because we only support dynamic load of only one major/minor version of
Ned Deily7fdefac2018-01-30 17:29:53 -0500554 # Tcl/Tk, if we are not using building and using our own private copy of
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000555 # Tcl/Tk, ensure:
Ned Deily8c9bb722018-01-30 07:42:14 -0500556 # 1. there is a user-installed framework (usually ActiveTcl) in (or linked
557 # in) SDKROOT/Library/Frameworks. As of Python 3.7.0, we no longer
558 # enforce that the version of the user-installed framework also
559 # exists in the system-supplied Tcl/Tk frameworks. Time to support
560 # Tcl/Tk 8.6 even if Apple does not.
Ned Deily7fdefac2018-01-30 17:29:53 -0500561 if not internalTk():
562 frameworks = {}
563 for framework in ['Tcl', 'Tk']:
564 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
565 libfw = os.path.join('/', fwpth)
566 usrfw = os.path.join(os.getenv('HOME'), fwpth)
567 frameworks[framework] = os.readlink(libfw)
568 if not os.path.exists(libfw):
569 fatal("Please install a link to a current %s %s as %s so "
570 "the user can override the system framework."
571 % (framework, frameworks[framework], libfw))
572 if os.path.exists(usrfw):
573 fatal("Please rename %s to avoid possible dynamic load issues."
574 % usrfw)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000575
Ned Deily7fdefac2018-01-30 17:29:53 -0500576 if frameworks['Tcl'] != frameworks['Tk']:
577 fatal("The Tcl and Tk frameworks are not the same version.")
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000578
Ned Deily7fdefac2018-01-30 17:29:53 -0500579 print(" -- Building with external Tcl/Tk %s frameworks"
580 % frameworks['Tk'])
Ned Deily4a96a372013-01-29 00:08:32 -0800581
Ned Deily7fdefac2018-01-30 17:29:53 -0500582 # add files to check after build
583 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
584 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
585 % frameworks['Tcl'],
586 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
587 % frameworks['Tk'],
588 ]
589 else:
590 print(" -- Building private copy of Tcl/Tk")
Ned Deily8c9bb722018-01-30 07:42:14 -0500591 print("")
592
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000593 # Remove inherited environment variables which might influence build
594 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
595 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
596 for ev in list(os.environ):
597 for prefix in environ_var_prefixes:
598 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800599 print("INFO: deleting environment variable %s=%s" % (
600 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000601 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000602
Ned Deily4a96a372013-01-29 00:08:32 -0800603 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
604 if 'SDK_TOOLS_BIN' in os.environ:
605 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
606 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
607 # add its fixed location here if it exists
608 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
609 if os.path.isdir(OLD_DEVELOPER_TOOLS):
610 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
611 os.environ['PATH'] = base_path
612 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400613 # Ensure we have access to sphinx-build.
614 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700615 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000616
Thomas Wouters89f507f2006-12-13 04:49:30 +0000617def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000618 """
619 Parse arguments and update global settings.
620 """
Ned Deily8c9bb722018-01-30 07:42:14 -0500621 global WORKDIR, DEPSRC, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800622 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800623 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400624 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000625
626 if args is None:
627 args = sys.argv[1:]
628
629 try:
630 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000631 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
632 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800633 except getopt.GetoptError:
634 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000635 sys.exit(1)
636
637 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800638 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000639 sys.exit(1)
640
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000641 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000642 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000643 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800644 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000645 sys.exit(0)
646
647 elif k in ('-d', '--build-dir'):
648 WORKDIR=v
649
650 elif k in ('--third-party',):
651 DEPSRC=v
652
653 elif k in ('--sdk-path',):
Ned Deily8c9bb722018-01-30 07:42:14 -0500654 print(" WARNING: --sdk-path is no longer supported")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000655
656 elif k in ('--src-dir',):
657 SRCDIR=v
658
Ronald Oussoren1943f862009-03-30 19:39:14 +0000659 elif k in ('--dep-target', ):
660 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000661 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000662
663 elif k in ('--universal-archs', ):
664 if v in UNIVERSALOPTS:
665 UNIVERSALARCHS = v
666 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000667 if deptarget is None:
668 # Select alternate default deployment
669 # target
Ned Deily8c9bb722018-01-30 07:42:14 -0500670 DEPTARGET = default_target_map.get(v, '10.5')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000671 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800672 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000673
Thomas Wouters477c8d52006-05-27 19:21:47 +0000674 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800675 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676
677 SRCDIR=os.path.abspath(SRCDIR)
678 WORKDIR=os.path.abspath(WORKDIR)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000679 DEPSRC=os.path.abspath(DEPSRC)
680
Ned Deily04cdfa12014-06-25 13:36:14 -0700681 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000682
Ned Deily5d3febf2014-12-13 00:17:46 -0800683 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400684 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800685
686 print("-- Settings:")
687 print(" * Source directory: %s" % SRCDIR)
688 print(" * Build directory: %s" % WORKDIR)
Ned Deily5d3febf2014-12-13 00:17:46 -0800689 print(" * Third-party source: %s" % DEPSRC)
690 print(" * Deployment target: %s" % DEPTARGET)
691 print(" * Universal archs: %s" % str(ARCHLIST))
692 print(" * C compiler: %s" % CC)
693 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800694 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800695 print(" -- Building a Python %s framework at patch level %s"
696 % (getVersion(), getFullVersion()))
697 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000698
699def extractArchive(builddir, archiveName):
700 """
701 Extract a source archive into 'builddir'. Returns the path of the
702 extracted archive.
703
704 XXX: This function assumes that archives contain a toplevel directory
705 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700706 safe enough for almost anything we use. Unfortunately, it does not
707 work for current Tcl and Tk source releases where the basename of
708 the archive ends with "-src" but the uncompressed directory does not.
709 For now, just special case Tcl and Tk tar.gz downloads.
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]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000719 if os.path.exists(retval):
720 shutil.rmtree(retval)
721 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
722
723 elif archiveName.endswith('.tar.bz2'):
724 retval = os.path.basename(archiveName[:-8])
725 if os.path.exists(retval):
726 shutil.rmtree(retval)
727 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
728
729 elif archiveName.endswith('.tar'):
730 retval = os.path.basename(archiveName[:-4])
731 if os.path.exists(retval):
732 shutil.rmtree(retval)
733 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
734
735 elif archiveName.endswith('.zip'):
736 retval = os.path.basename(archiveName[:-4])
737 if os.path.exists(retval):
738 shutil.rmtree(retval)
739 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
740
741 data = fp.read()
742 xit = fp.close()
743 if xit is not None:
744 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800745 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000746
747 return os.path.join(builddir, retval)
748
749 finally:
750 os.chdir(curdir)
751
Thomas Wouters477c8d52006-05-27 19:21:47 +0000752def downloadURL(url, fname):
753 """
754 Download the contents of the url into the file.
755 """
Ned Deily4a96a372013-01-29 00:08:32 -0800756 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000757 fpOut = open(fname, 'wb')
758 block = fpIn.read(10240)
759 try:
760 while block:
761 fpOut.write(block)
762 block = fpIn.read(10240)
763 fpIn.close()
764 fpOut.close()
765 except:
766 try:
767 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300768 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000769 pass
770
Ned Deily4a96a372013-01-29 00:08:32 -0800771def verifyThirdPartyFile(url, checksum, fname):
772 """
773 Download file from url to filename fname if it does not already exist.
774 Abort if file contents does not match supplied md5 checksum.
775 """
776 name = os.path.basename(fname)
777 if os.path.exists(fname):
778 print("Using local copy of %s"%(name,))
779 else:
780 print("Did not find local copy of %s"%(name,))
781 print("Downloading %s"%(name,))
782 downloadURL(url, fname)
783 print("Archive for %s stored as %s"%(name, fname))
784 if os.system(
785 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
786 % (shellQuote(fname), checksum) ):
787 fatal('MD5 checksum mismatch for file %s' % fname)
788
Ned Deily5d3febf2014-12-13 00:17:46 -0800789def build_universal_openssl(basedir, archList):
790 """
791 Special case build recipe for universal build of openssl.
792
793 The upstream OpenSSL build system does not directly support
794 OS X universal builds. We need to build each architecture
795 separately then lipo them together into fat libraries.
796 """
797
798 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
799 # If we are building on a 10.4.x or earlier system,
800 # unilaterally disable assembly code building to avoid the problem.
801 no_asm = int(platform.release().split(".")[0]) < 9
802
803 def build_openssl_arch(archbase, arch):
804 "Build one architecture of openssl"
805 arch_opts = {
806 "i386": ["darwin-i386-cc"],
807 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
808 "ppc": ["darwin-ppc-cc"],
809 "ppc64": ["darwin64-ppc-cc"],
810 }
811 configure_opts = [
Ned Deily5d3febf2014-12-13 00:17:46 -0800812 "no-idea",
813 "no-mdc2",
814 "no-rc5",
815 "no-zlib",
Ned Deily5d3febf2014-12-13 00:17:46 -0800816 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800817 # "enable-unit-test",
818 "shared",
Ned Deily5d3febf2014-12-13 00:17:46 -0800819 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400820 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800821 ]
822 if no_asm:
823 configure_opts.append("no-asm")
824 runCommand(" ".join(["perl", "Configure"]
825 + arch_opts[arch] + configure_opts))
Ned Deily8c9bb722018-01-30 07:42:14 -0500826 runCommand("make depend")
827 runCommand("make all")
828 runCommand("make install_sw DESTDIR=%s"%shellQuote(archbase))
Ned Deily5d3febf2014-12-13 00:17:46 -0800829 # runCommand("make test")
830 return
831
832 srcdir = os.getcwd()
833 universalbase = os.path.join(srcdir, "..",
834 os.path.basename(srcdir) + "-universal")
835 os.mkdir(universalbase)
836 archbasefws = []
837 for arch in archList:
838 # fresh copy of the source tree
839 archsrc = os.path.join(universalbase, arch, "src")
840 shutil.copytree(srcdir, archsrc, symlinks=True)
841 # install base for this arch
842 archbase = os.path.join(universalbase, arch, "root")
843 os.mkdir(archbase)
844 # Python framework base within install_prefix:
845 # the build will install into this framework..
846 # This is to ensure that the resulting shared libs have
847 # the desired real install paths built into them.
848 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
849
850 # build one architecture
851 os.chdir(archsrc)
852 build_openssl_arch(archbase, arch)
853 os.chdir(srcdir)
854 archbasefws.append(archbasefw)
855
856 # copy arch-independent files from last build into the basedir framework
857 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
858 shutil.copytree(
859 os.path.join(archbasefw, "include", "openssl"),
860 os.path.join(basefw, "include", "openssl")
861 )
862
863 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
864 "SHLIB_VERSION_NUMBER")
865 # e.g. -> "1.0.0"
866 libcrypto = "libcrypto.dylib"
867 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
868 # e.g. -> "libcrypto.1.0.0.dylib"
869 libssl = "libssl.dylib"
870 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
871 # e.g. -> "libssl.1.0.0.dylib"
872
873 try:
874 os.mkdir(os.path.join(basefw, "lib"))
875 except OSError:
876 pass
877
878 # merge the individual arch-dependent shared libs into a fat shared lib
879 archbasefws.insert(0, basefw)
880 for (lib_unversioned, lib_versioned) in [
881 (libcrypto, libcrypto_versioned),
882 (libssl, libssl_versioned)
883 ]:
884 runCommand("lipo -create -output " +
885 " ".join(shellQuote(
886 os.path.join(fw, "lib", lib_versioned))
887 for fw in archbasefws))
888 # and create an unversioned symlink of it
889 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
890
891 # Create links in the temp include and lib dirs that will be injected
892 # into the Python build so that setup.py can find them while building
893 # and the versioned links so that the setup.py post-build import test
894 # does not fail.
895 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
896 for fn in [
897 ["include", "openssl"],
898 ["lib", libcrypto],
899 ["lib", libssl],
900 ["lib", libcrypto_versioned],
901 ["lib", libssl_versioned],
902 ]:
903 os.symlink(
904 os.path.join(relative_path, *fn),
905 os.path.join(basedir, "usr", "local", *fn)
906 )
907
908 return
909
Thomas Wouters477c8d52006-05-27 19:21:47 +0000910def buildRecipe(recipe, basedir, archList):
911 """
912 Build software using a recipe. This function does the
913 'configure;make;make install' dance for C software, with a possibility
914 to customize this process, basically a poor-mans DarwinPorts.
915 """
916 curdir = os.getcwd()
917
918 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800919 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000920 url = recipe['url']
921 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800922 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000923 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
924 shellQuote(basedir)))
925
926 archiveName = os.path.split(url)[-1]
927 sourceArchive = os.path.join(DEPSRC, archiveName)
928
929 if not os.path.exists(DEPSRC):
930 os.mkdir(DEPSRC)
931
Ned Deily4a96a372013-01-29 00:08:32 -0800932 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
933 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000934 buildDir=os.path.join(WORKDIR, '_bld')
935 if not os.path.exists(buildDir):
936 os.mkdir(buildDir)
937
938 workDir = extractArchive(buildDir, sourceArchive)
939 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000940
Ned Deily4a96a372013-01-29 00:08:32 -0800941 for patch in recipe.get('patches', ()):
942 if isinstance(patch, tuple):
943 url, checksum = patch
944 fn = os.path.join(DEPSRC, os.path.basename(url))
945 verifyThirdPartyFile(url, checksum, fn)
946 else:
947 # patch is a file in the source directory
948 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000949 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
950 shellQuote(fn),))
951
Ned Deily4a96a372013-01-29 00:08:32 -0800952 for patchscript in recipe.get('patchscripts', ()):
953 if isinstance(patchscript, tuple):
954 url, checksum = patchscript
955 fn = os.path.join(DEPSRC, os.path.basename(url))
956 verifyThirdPartyFile(url, checksum, fn)
957 else:
958 # patch is a file in the source directory
959 fn = os.path.join(curdir, patchscript)
960 if fn.endswith('.bz2'):
961 runCommand('bunzip2 -fk %s' % shellQuote(fn))
962 fn = fn[:-4]
963 runCommand('sh %s' % shellQuote(fn))
964 os.unlink(fn)
965
Ned Deily94764b22013-10-27 19:49:29 -0700966 if 'buildDir' in recipe:
967 os.chdir(recipe['buildDir'])
968
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000969 if configure is not None:
970 configure_args = [
971 "--prefix=/usr/local",
972 "--enable-static",
973 "--disable-shared",
974 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
975 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000976
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000977 if 'configure_pre' in recipe:
978 args = list(recipe['configure_pre'])
979 if '--disable-static' in args:
980 configure_args.remove('--enable-static')
981 if '--enable-shared' in args:
982 configure_args.remove('--disable-shared')
983 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000984
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000985 if recipe.get('useLDFlags', 1):
986 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -0500987 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -0800988 "-I%s/usr/local/include"%(
989 recipe.get('extra_cflags', ''),
990 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000991 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000992 shellQuote(basedir)[1:-1],),
Ned Deily8c9bb722018-01-30 07:42:14 -0500993 "LDFLAGS=-mmacosx-version-min=%s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -0800994 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000995 shellQuote(basedir)[1:-1],
996 ' -arch '.join(archList)),
997 ])
998 else:
999 configure_args.extend([
Ned Deily8c9bb722018-01-30 07:42:14 -05001000 "CFLAGS=%s-mmacosx-version-min=%s -arch %s "
Ned Deily4a96a372013-01-29 00:08:32 -08001001 "-I%s/usr/local/include"%(
1002 recipe.get('extra_cflags', ''),
1003 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001004 ' -arch '.join(archList),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001005 shellQuote(basedir)[1:-1],),
1006 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001007
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001008 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001009 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001010
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001011 configure_args.insert(0, configure)
1012 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001013
Ned Deily4a96a372013-01-29 00:08:32 -08001014 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001015 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001016
Ned Deily5d3febf2014-12-13 00:17:46 -08001017 if buildrecipe is not None:
1018 # call special-case build recipe, e.g. for openssl
1019 buildrecipe(basedir, archList)
1020
1021 if install is not None:
1022 print("Running install for %s"%(name,))
1023 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001024
Ned Deily4a96a372013-01-29 00:08:32 -08001025 print("Done %s"%(name,))
1026 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001027
1028 os.chdir(curdir)
1029
1030def buildLibraries():
1031 """
1032 Build our dependencies into $WORKDIR/libraries/usr/local
1033 """
Ned Deily4a96a372013-01-29 00:08:32 -08001034 print("")
1035 print("Building required libraries")
1036 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001037 universal = os.path.join(WORKDIR, 'libraries')
1038 os.mkdir(universal)
1039 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1040 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1041
Ronald Oussoren1943f862009-03-30 19:39:14 +00001042 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001043 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001044
1045
1046
1047def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001048 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001049 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001050 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001052 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001053 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001054 curDir = os.getcwd()
1055 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001056 runCommand('make clean')
Ned Deily4c7532e2017-07-23 16:39:54 -04001057 # Create virtual environment for docs builds with blurb and sphinx
1058 runCommand('make venv')
1059 runCommand('make html PYTHON=venv/bin/python')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001060 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001061 if not os.path.exists(docdir):
1062 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001063 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001064
1065
1066def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001067 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001068
1069 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1070 rootDir = os.path.join(WORKDIR, '_root')
1071
1072 if os.path.exists(buildDir):
1073 shutil.rmtree(buildDir)
1074 if os.path.exists(rootDir):
1075 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001076 os.makedirs(buildDir)
1077 os.makedirs(rootDir)
1078 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001079 curdir = os.getcwd()
1080 os.chdir(buildDir)
1081
Thomas Wouters477c8d52006-05-27 19:21:47 +00001082 # Extract the version from the configure file, needed to calculate
1083 # several paths.
1084 version = getVersion()
1085
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001086 # Since the extra libs are not in their installed framework location
1087 # during the build, augment the library path so that the interpreter
1088 # will find them during its extension import sanity checks.
1089 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1090 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001091 print("Running configure...")
Ned Deily8c9bb722018-01-30 07:42:14 -05001092 runCommand("%s -C --enable-framework --enable-universalsdk=/ "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001093 "--with-universal-archs=%s "
1094 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001095 "%s "
Ned Deily7fdefac2018-01-30 17:29:53 -05001096 "%s "
1097 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001098 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001099 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ned Deily8c9bb722018-01-30 07:42:14 -05001100 shellQuote(os.path.join(SRCDIR, 'configure')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001101 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001102 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001103 (' ', '--without-ensurepip ')[PYTHON_3],
Ned Deily7fdefac2018-01-30 17:29:53 -05001104 (' ', "--with-tcltk-includes='-I%s/libraries/usr/local/include'"%(
1105 shellQuote(WORKDIR)[1:-1],))[internalTk()],
1106 (' ', "--with-tcltk-libs='-L%s/libraries/usr/local/lib -ltcl8.6 -ltk8.6'"%(
1107 shellQuote(WORKDIR)[1:-1],))[internalTk()],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001108 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001109 shellQuote(WORKDIR)[1:-1]))
1110
Ned Deilyb364d9f2017-07-24 04:58:43 -04001111 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1112 # and, if defined, append its value to the make command. This allows
1113 # us to pass in version control tags, like GITTAG, to a build from a
1114 # tarball rather than from a vcs checkout, thus eliminating the need
1115 # to have a working copy of the vcs program on the build machine.
1116 #
1117 # A typical use might be:
1118 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1119 # GITVERSION='echo 123456789a' \
1120 # GITTAG='echo v3.6.0' \
1121 # GITBRANCH='echo 3.6'"
1122
1123 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1124 if make_extras:
1125 make_cmd = "make " + make_extras
1126 else:
1127 make_cmd = "make"
1128 print("Running " + make_cmd)
1129 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001130
Ned Deily4a96a372013-01-29 00:08:32 -08001131 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001132 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001133 shellQuote(rootDir)))
1134
Ned Deily4a96a372013-01-29 00:08:32 -08001135 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001136 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1137 shellQuote(rootDir)))
1138
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001139 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001140 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001141 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
Ned Deily7fdefac2018-01-30 17:29:53 -05001142 build_lib_dir = os.path.join(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001143 WORKDIR, 'libraries', 'Library', 'Frameworks',
Ned Deily7fdefac2018-01-30 17:29:53 -05001144 'Python.framework', 'Versions', getVersion(), 'lib')
1145 fw_lib_dir = os.path.join(
1146 WORKDIR, '_root', 'Library', 'Frameworks',
1147 'Python.framework', 'Versions', getVersion(), 'lib')
1148 if internalTk():
1149 # move Tcl and Tk pkgconfig files
1150 runCommand("mv %s/pkgconfig/* %s/pkgconfig"%(
1151 shellQuote(build_lib_dir),
1152 shellQuote(fw_lib_dir) ))
1153 runCommand("rm -r %s/pkgconfig"%(
1154 shellQuote(build_lib_dir), ))
1155 runCommand("mv %s/* %s"%(
1156 shellQuote(build_lib_dir),
1157 shellQuote(fw_lib_dir) ))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001158
Ned Deilydde4f632016-09-12 09:39:23 -04001159 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1160 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1161 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1162 # create directory for OpenSSL certificates
1163 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1164 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001165
Ned Deily4a96a372013-01-29 00:08:32 -08001166 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001167 gid = grp.getgrnam('admin').gr_gid
1168
Ned Deily4a96a372013-01-29 00:08:32 -08001169 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001170 for dirpath, dirnames, filenames in os.walk(frmDir):
1171 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001172 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001173 os.chown(os.path.join(dirpath, dn), -1, gid)
1174
Thomas Wouters477c8d52006-05-27 19:21:47 +00001175 for fn in filenames:
1176 if os.path.islink(fn):
1177 continue
1178
1179 # "chmod g+w $fn"
1180 p = os.path.join(dirpath, fn)
1181 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001182 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1183 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001184
Ned Deily4a96a372013-01-29 00:08:32 -08001185 if fn in EXPECTED_SHARED_LIBS:
1186 # check to see that this file was linked with the
1187 # expected library path and version
1188 data = captureCommand("otool -L %s" % shellQuote(p))
1189 for sl in EXPECTED_SHARED_LIBS[fn]:
1190 if ("\t%s " % sl) not in data:
1191 print("Expected shared lib %s was not linked with %s"
1192 % (sl, p))
1193 shared_lib_error = True
1194
1195 if shared_lib_error:
1196 fatal("Unexpected shared library errors.")
1197
Ned Deilye59e4c52011-01-29 18:56:28 +00001198 if PYTHON_3:
1199 LDVERSION=None
1200 VERSION=None
1201 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001202
Ned Deilye59e4c52011-01-29 18:56:28 +00001203 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001204 for ln in fp:
1205 if ln.startswith('VERSION='):
1206 VERSION=ln.split()[1]
1207 if ln.startswith('ABIFLAGS='):
1208 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001209 if ln.startswith('LDVERSION='):
1210 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001211 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001212
Ned Deilye59e4c52011-01-29 18:56:28 +00001213 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1214 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1215 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001216 if getVersionMajorMinor() >= (3, 6):
1217 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001218 else:
1219 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001220
Thomas Wouters477c8d52006-05-27 19:21:47 +00001221 # We added some directories to the search path during the configure
1222 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001223 # the end-users system. Also remove the directories from _sysconfigdata.py
1224 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001225
Ned Deilya4f6b002013-10-25 00:47:38 -07001226 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1227 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1228
Ned Deilya4f6b002013-10-25 00:47:38 -07001229 # fix Makefile
1230 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1231 fp = open(path, 'r')
1232 data = fp.read()
1233 fp.close()
1234
1235 for p in (include_path, lib_path):
1236 data = data.replace(" " + p, '')
1237 data = data.replace(p + " ", '')
1238
1239 fp = open(path, 'w')
1240 fp.write(data)
1241 fp.close()
1242
Ned Deily652bad42016-08-15 14:37:14 -04001243 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001244 #
1245 # TODO: make this more robust! test_sysconfig_module of
1246 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1247 # the output from get_config_var in both sysconfig and
1248 # distutils.sysconfig is exactly the same for both CFLAGS and
1249 # LDFLAGS. The fixing up is now complicated by the pretty
1250 # printing in _sysconfigdata.py. Also, we are using the
1251 # pprint from the Python running the installer build which
1252 # may not cosmetically format the same as the pprint in the Python
1253 # being built (and which is used to originally generate
1254 # _sysconfigdata.py).
1255
1256 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001257 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001258 # XXX this is extra-fragile
1259 path = os.path.join(path_to_lib, '_sysconfigdata_m_darwin_darwin.py')
Ned Deily652bad42016-08-15 14:37:14 -04001260 else:
1261 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1262 fp = open(path, 'r')
1263 data = fp.read()
1264 fp.close()
1265 # create build_time_vars dict
1266 exec(data)
1267 vars = {}
1268 for k, v in build_time_vars.items():
1269 if type(v) == type(''):
1270 for p in (include_path, lib_path):
1271 v = v.replace(' ' + p, '')
1272 v = v.replace(p + ' ', '')
1273 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001274
Ned Deily652bad42016-08-15 14:37:14 -04001275 fp = open(path, 'w')
1276 # duplicated from sysconfig._generate_posix_vars()
1277 fp.write('# system configuration generated and used by'
1278 ' the sysconfig module\n')
1279 fp.write('build_time_vars = ')
1280 pprint.pprint(vars, stream=fp)
1281 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001282
1283 # Add symlinks in /usr/local/bin, using relative links
1284 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1285 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1286 'Python.framework', 'Versions', version, 'bin')
1287 if os.path.exists(usr_local_bin):
1288 shutil.rmtree(usr_local_bin)
1289 os.makedirs(usr_local_bin)
1290 for fn in os.listdir(
1291 os.path.join(frmDir, 'Versions', version, 'bin')):
1292 os.symlink(os.path.join(to_framework, fn),
1293 os.path.join(usr_local_bin, fn))
1294
1295 os.chdir(curdir)
1296
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001297 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001298 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001299 # with an already installed version of python 2
1300 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1301 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001302
1303def patchFile(inPath, outPath):
1304 data = fileContents(inPath)
1305 data = data.replace('$FULL_VERSION', getFullVersion())
1306 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001307 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001308 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001309 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001310 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001311
1312 # This one is not handy as a template variable
1313 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001314 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001315 fp.write(data)
1316 fp.close()
1317
1318def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001319 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001320 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001321 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001322 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001323 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001324 fp.write(data)
1325 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001326 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001327
1328
1329
1330def packageFromRecipe(targetDir, recipe):
1331 curdir = os.getcwd()
1332 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001333 # The major version (such as 2.5) is included in the package name
1334 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001335 # common.
1336 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001337 srcdir = recipe.get('source')
1338 pkgroot = recipe.get('topdir', srcdir)
1339 postflight = recipe.get('postflight')
1340 readme = textwrap.dedent(recipe['readme'])
1341 isRequired = recipe.get('required', True)
1342
Ned Deily4a96a372013-01-29 00:08:32 -08001343 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001344
1345 # Substitute some variables
1346 textvars = dict(
1347 VER=getVersion(),
1348 FULLVER=getFullVersion(),
1349 )
1350 readme = readme % textvars
1351
1352 if pkgroot is not None:
1353 pkgroot = pkgroot % textvars
1354 else:
1355 pkgroot = '/'
1356
1357 if srcdir is not None:
1358 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1359 srcdir = srcdir % textvars
1360
1361 if postflight is not None:
1362 postflight = os.path.abspath(postflight)
1363
1364 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1365 os.makedirs(packageContents)
1366
1367 if srcdir is not None:
1368 os.chdir(srcdir)
1369 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1370 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1371 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1372
1373 fn = os.path.join(packageContents, 'PkgInfo')
1374 fp = open(fn, 'w')
1375 fp.write('pmkrpkg1')
1376 fp.close()
1377
1378 rsrcDir = os.path.join(packageContents, "Resources")
1379 os.mkdir(rsrcDir)
1380 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1381 fp.write(readme)
1382 fp.close()
1383
1384 if postflight is not None:
1385 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1386
1387 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001388 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001389 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001390 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1391 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1392 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001393 CFBundleShortVersionString=vers,
1394 IFMajorVersion=major,
1395 IFMinorVersion=minor,
1396 IFPkgFormatVersion=0.10000000149011612,
1397 IFPkgFlagAllowBackRev=False,
1398 IFPkgFlagAuthorizationAction="RootAuthorization",
1399 IFPkgFlagDefaultLocation=pkgroot,
1400 IFPkgFlagFollowLinks=True,
1401 IFPkgFlagInstallFat=True,
1402 IFPkgFlagIsRequired=isRequired,
1403 IFPkgFlagOverwritePermissions=False,
1404 IFPkgFlagRelocatable=False,
1405 IFPkgFlagRestartAction="NoRestart",
1406 IFPkgFlagRootVolumeOnly=True,
1407 IFPkgFlagUpdateInstalledLangauges=False,
1408 )
1409 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1410
1411 pl = Plist(
1412 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001413 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001414 IFPkgDescriptionVersion=vers,
1415 )
1416 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1417
1418 finally:
1419 os.chdir(curdir)
1420
1421
1422def makeMpkgPlist(path):
1423
1424 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001425 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001426
1427 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001428 CFBundleGetInfoString="Python %s"%(vers,),
1429 CFBundleIdentifier='org.python.Python',
1430 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001431 CFBundleShortVersionString=vers,
1432 IFMajorVersion=major,
1433 IFMinorVersion=minor,
1434 IFPkgFlagComponentDirectory="Contents/Packages",
1435 IFPkgFlagPackageList=[
1436 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001437 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001438 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001439 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001440 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001441 ],
1442 IFPkgFormatVersion=0.10000000149011612,
1443 IFPkgFlagBackgroundScaling="proportional",
1444 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001445 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001446 )
1447
1448 writePlist(pl, path)
1449
1450
1451def buildInstaller():
1452
1453 # Zap all compiled files
1454 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1455 for fn in filenames:
1456 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1457 os.unlink(os.path.join(dirpath, fn))
1458
1459 outdir = os.path.join(WORKDIR, 'installer')
1460 if os.path.exists(outdir):
1461 shutil.rmtree(outdir)
1462 os.mkdir(outdir)
1463
Ronald Oussoren1943f862009-03-30 19:39:14 +00001464 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001465 pkgcontents = os.path.join(pkgroot, 'Packages')
1466 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001467 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001468 packageFromRecipe(pkgcontents, recipe)
1469
1470 rsrcDir = os.path.join(pkgroot, 'Resources')
1471
1472 fn = os.path.join(pkgroot, 'PkgInfo')
1473 fp = open(fn, 'w')
1474 fp.write('pmkrpkg1')
1475 fp.close()
1476
1477 os.mkdir(rsrcDir)
1478
1479 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1480 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001481 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001482 IFPkgDescriptionVersion=getVersion(),
1483 )
1484
1485 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1486 for fn in os.listdir('resources'):
1487 if fn == '.svn': continue
1488 if fn.endswith('.jpg'):
1489 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1490 else:
1491 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1492
Thomas Wouters477c8d52006-05-27 19:21:47 +00001493
1494def installSize(clear=False, _saved=[]):
1495 if clear:
1496 del _saved[:]
1497 if not _saved:
1498 data = captureCommand("du -ks %s"%(
1499 shellQuote(os.path.join(WORKDIR, '_root'))))
1500 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1501 return _saved[0]
1502
1503
1504def buildDMG():
1505 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001506 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001507 """
1508 outdir = os.path.join(WORKDIR, 'diskimage')
1509 if os.path.exists(outdir):
1510 shutil.rmtree(outdir)
1511
1512 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001513 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001514 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001515 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001516 imagepath = imagepath + '.dmg'
1517
1518 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001519 volname='Python %s'%(getFullVersion())
1520 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1521 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001522 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001523 shellQuote(imagepath + ".tmp.dmg" )))
1524
1525
1526 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1527 os.mkdir(os.path.join(WORKDIR, "mnt"))
1528 runCommand("hdiutil attach %s -mountroot %s"%(
1529 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1530
1531 # Custom icon for the DMG, shown when the DMG is mounted.
1532 shutil.copy("../Icons/Disk Image.icns",
1533 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001534 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001535 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1536
1537 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1538
1539 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1540 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1541 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1542 setIcon(imagepath, "../Icons/Disk Image.icns")
1543
1544 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001545
1546 return imagepath
1547
1548
1549def setIcon(filePath, icnsPath):
1550 """
1551 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001552 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001553
Ronald Oussoren70050672010-04-30 15:00:26 +00001554 dirPath = os.path.normpath(os.path.dirname(__file__))
1555 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001556 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1557 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1558 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001559 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1560 if not os.path.exists(appPath):
1561 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001562 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1563 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001564
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001565 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1566 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001567
1568def main():
1569 # First parse options and check if we can perform our work
1570 parseOptions()
1571 checkEnvironment()
1572
Ronald Oussoren1943f862009-03-30 19:39:14 +00001573 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001574 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001575 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001576
1577 if os.path.exists(WORKDIR):
1578 shutil.rmtree(WORKDIR)
1579 os.mkdir(WORKDIR)
1580
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001581 os.environ['LC_ALL'] = 'C'
1582
Thomas Wouters477c8d52006-05-27 19:21:47 +00001583 # Then build third-party libraries such as sleepycat DB4.
1584 buildLibraries()
1585
1586 # Now build python itself
1587 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001588
1589 # And then build the documentation
1590 # Remove the Deployment Target from the shell
1591 # environment, it's no longer needed and
1592 # an unexpected build target can cause problems
1593 # when Sphinx and its dependencies need to
1594 # be (re-)installed.
1595 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001596 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001597
1598
1599 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001600 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001601 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001602 fn = os.path.join(folder, "License.rtf")
1603 patchFile("resources/License.rtf", fn)
1604 fn = os.path.join(folder, "ReadMe.rtf")
1605 patchFile("resources/ReadMe.rtf", fn)
1606 fn = os.path.join(folder, "Update Shell Profile.command")
1607 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001608 fn = os.path.join(folder, "Install Certificates.command")
1609 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001610 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001611 setIcon(folder, "../Icons/Python Folder.icns")
1612
1613 # Create the installer
1614 buildInstaller()
1615
1616 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001617 patchFile('resources/ReadMe.rtf',
1618 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001619
1620 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001621 patchFile('resources/License.rtf',
1622 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001623
1624 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001625 fp.write("# BUILD INFO\n")
1626 fp.write("# Date: %s\n" % time.ctime())
1627 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001628 fp.close()
1629
Thomas Wouters477c8d52006-05-27 19:21:47 +00001630 # And copy it to a DMG
1631 buildDMG()
1632
Thomas Wouters477c8d52006-05-27 19:21:47 +00001633if __name__ == "__main__":
1634 main()